]> icculus.org git repositories - btb/d2x.git/blob - main/switch.c
added documentation
[btb/d2x.git] / main / switch.c
1 /*
2 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
3 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
4 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
5 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
6 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
7 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
8 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
9 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
10 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
11 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
12 */
13
14 #ifdef HAVE_CONFIG_H
15 #include <conf.h>
16 #endif
17
18 #ifdef RCS
19 static char rcsid[] = "$Id: switch.c,v 1.5 2001-11-08 10:30:28 bradleyb Exp $";
20 #endif
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <math.h>
25 #include <string.h>
26
27 #include "gauges.h"
28 #include "newmenu.h"
29 #include "game.h"
30 #include "switch.h"
31 #include "inferno.h"
32 #include "segment.h"
33 #include "error.h"
34 #include "gameseg.h"
35 #include "mono.h"
36 #include "wall.h"
37 #include "texmap.h"
38 #include "fuelcen.h"
39 #include "cntrlcen.h"
40 #include "newdemo.h"
41 #include "player.h"
42 #include "endlevel.h"
43 #include "gameseq.h"
44 #include "multi.h"
45 #ifdef NETWORK
46 #include "network.h"
47 #endif
48 #include "palette.h"
49 #include "robot.h"
50 #include "bm.h"
51
52 #ifdef EDITOR
53 #include "editor/editor.h"
54 #endif
55
56 trigger Triggers[MAX_TRIGGERS];
57 int Num_triggers;
58
59 //link Links[MAX_WALL_LINKS];
60 //int Num_links;
61
62 #ifdef EDITOR
63 fix trigger_time_count=F1_0;
64
65 //-----------------------------------------------------------------
66 // Initializes all the switches.
67 void trigger_init()
68 {
69         int i;
70
71         Num_triggers = 0;
72
73         for (i=0;i<MAX_TRIGGERS;i++)
74                 {
75                 Triggers[i].type = 0;
76                 Triggers[i].flags = 0;
77                 Triggers[i].num_links = 0;
78                 Triggers[i].value = 0;
79                 Triggers[i].time = -1;
80                 }
81 }
82 #endif
83
84 //-----------------------------------------------------------------
85 // Executes a link, attached to a trigger.
86 // Toggles all walls linked to the switch.
87 // Opens doors, Blasts blast walls, turns off illusions.
88 void do_link(byte trigger_num)
89 {
90         int i;
91
92         mprintf((0, "Door link!\n"));
93
94         if (trigger_num != -1) {
95                 for (i=0;i<Triggers[trigger_num].num_links;i++) {
96                         wall_toggle(&Segments[Triggers[trigger_num].seg[i]], Triggers[trigger_num].side[i]); 
97                         mprintf((0," trigger_num %d : seg %d, side %d\n", 
98                                 trigger_num, Triggers[trigger_num].seg[i], Triggers[trigger_num].side[i]));
99                 }
100         }
101 }
102
103 //close a door
104 void do_close_door(byte trigger_num)
105 {
106         int i;
107
108         mprintf((0, "Door close!\n"));
109
110         if (trigger_num != -1) {
111                 for (i=0;i<Triggers[trigger_num].num_links;i++)
112                         wall_close_door(&Segments[Triggers[trigger_num].seg[i]], Triggers[trigger_num].side[i]); 
113         }
114 }
115
116 //turns lighting on.  returns true if lights were actually turned on. (they
117 //would not be if they had previously been shot out).
118 int do_light_on(byte trigger_num)
119 {
120         int i,ret=0;
121
122         mprintf((0, "Lighting on!\n"));
123
124         if (trigger_num != -1) {
125                 for (i=0;i<Triggers[trigger_num].num_links;i++) {
126                         int segnum,sidenum;
127                         segnum = Triggers[trigger_num].seg[i];
128                         sidenum = Triggers[trigger_num].side[i];
129
130                         //check if tmap2 casts light before turning the light on.  This
131                         //is to keep us from turning on blown-out lights
132                         if (TmapInfo[Segments[segnum].sides[sidenum].tmap_num2 & 0x3fff].lighting) {
133                                 ret |= add_light(segnum, sidenum);              //any light sets flag
134                                 enable_flicker(segnum, sidenum);
135                         }
136                 }
137         }
138
139         return ret;
140 }
141
142 //turns lighting off.  returns true if lights were actually turned off. (they
143 //would not be if they had previously been shot out).
144 int do_light_off(byte trigger_num)
145 {
146         int i,ret=0;
147
148         mprintf((0, "Lighting off!\n"));
149
150         if (trigger_num != -1) {
151                 for (i=0;i<Triggers[trigger_num].num_links;i++) {
152                         int segnum,sidenum;
153                         segnum = Triggers[trigger_num].seg[i];
154                         sidenum = Triggers[trigger_num].side[i];
155
156                         //check if tmap2 casts light before turning the light off.  This
157                         //is to keep us from turning off blown-out lights
158                         if (TmapInfo[Segments[segnum].sides[sidenum].tmap_num2 & 0x3fff].lighting) {
159                                 ret |= subtract_light(segnum, sidenum);         //any light sets flag
160                                 disable_flicker(segnum, sidenum);
161                         }
162                 }
163         }
164
165         return ret;
166 }
167
168 // Unlocks all doors linked to the switch.
169 void do_unlock_doors(byte trigger_num)
170 {
171         int i;
172
173         mprintf((0, "Door unlock!\n"));
174
175         if (trigger_num != -1) {
176                 for (i=0;i<Triggers[trigger_num].num_links;i++) {
177                         Walls[Segments[Triggers[trigger_num].seg[i]].sides[Triggers[trigger_num].side[i]].wall_num].flags &= ~WALL_DOOR_LOCKED;
178                         Walls[Segments[Triggers[trigger_num].seg[i]].sides[Triggers[trigger_num].side[i]].wall_num].keys = KEY_NONE;
179                 }
180         }
181 }
182
183 // Return trigger number if door is controlled by a wall switch, else return -1.
184 int door_is_wall_switched(int wall_num)
185 {
186         int i, t;
187
188         for (t=0; t<Num_triggers; t++) {
189                 for (i=0; i<Triggers[t].num_links; i++) {
190                         if (Segments[Triggers[t].seg[i]].sides[Triggers[t].side[i]].wall_num == wall_num) {
191                                 mprintf((0, "Wall #%i is keyed to trigger #%i, link #%i\n", wall_num, t, i));
192                                 return t;
193                         }
194                 }
195         }
196
197         return -1;
198 }
199
200 void flag_wall_switched_doors(void)
201 {
202         int     i;
203
204         for (i=0; i<Num_walls; i++) {
205                 if (door_is_wall_switched(i))
206                         Walls[i].flags |= WALL_WALL_SWITCH;
207         }
208
209 }
210
211 // Locks all doors linked to the switch.
212 void do_lock_doors(byte trigger_num)
213 {
214         int i;
215
216         mprintf((0, "Door lock!\n"));
217
218         if (trigger_num != -1) {
219                 for (i=0;i<Triggers[trigger_num].num_links;i++) {
220                         Walls[Segments[Triggers[trigger_num].seg[i]].sides[Triggers[trigger_num].side[i]].wall_num].flags |= WALL_DOOR_LOCKED;
221                 }
222         }
223 }
224
225 // Changes walls pointed to by a trigger. returns true if any walls changed
226 int do_change_walls(byte trigger_num)
227 {
228         int i,ret=0;
229
230         mprintf((0, "Wall remove!\n"));
231
232         if (trigger_num != -1) {
233                 for (i=0;i<Triggers[trigger_num].num_links;i++) {
234                         segment *segp,*csegp;
235                         short side,cside;
236                         int new_wall_type;
237
238                         segp = &Segments[Triggers[trigger_num].seg[i]];
239                         side = Triggers[trigger_num].side[i];
240
241                         csegp = &Segments[segp->children[side]];
242                         cside = find_connect_side(segp, csegp);
243                         Assert(cside != -1);
244
245                         //segp->sides[side].wall_num = -1;
246                         //csegp->sides[cside].wall_num = -1;
247
248                         switch (Triggers[trigger_num].type) {
249                                 case TT_OPEN_WALL:              new_wall_type = WALL_OPEN; break;
250                                 case TT_CLOSE_WALL:             new_wall_type = WALL_CLOSED; break;
251                                 case TT_ILLUSORY_WALL:  new_wall_type = WALL_ILLUSION; break;
252                                 default:
253                                         Assert(0); /* new_wall_type unset */
254                                         return(0);
255                                         break;
256                         }
257
258                         if (Walls[segp->sides[side].wall_num].type == new_wall_type && Walls[csegp->sides[cside].wall_num].type == new_wall_type)
259                                 continue;               //already in correct state, so skip
260
261                         ret = 1;
262
263                         switch (Triggers[trigger_num].type) {
264         
265                                 case TT_OPEN_WALL:
266                                         mprintf((0,"Open wall\n"));
267
268                                         if ((TmapInfo[segp->sides[side].tmap_num].flags & TMI_FORCE_FIELD)) {
269                                                 vms_vector pos;
270                                                 compute_center_point_on_side(&pos, segp, side );
271                                                 digi_link_sound_to_pos( SOUND_FORCEFIELD_OFF, segp-Segments, side, &pos, 0, F1_0 );
272                                                 Walls[segp->sides[side].wall_num].type = new_wall_type;
273                                                 Walls[csegp->sides[cside].wall_num].type = new_wall_type;
274                                                 digi_kill_sound_linked_to_segment(segp-Segments,side,SOUND_FORCEFIELD_HUM);
275                                                 digi_kill_sound_linked_to_segment(csegp-Segments,cside,SOUND_FORCEFIELD_HUM);
276                                         }
277                                         else
278                                                 start_wall_cloak(segp,side);
279
280                                         ret = 1;
281
282                                         break;
283
284                                 case TT_CLOSE_WALL:
285                                         mprintf((0,"Close wall\n"));
286
287                                         if ((TmapInfo[segp->sides[side].tmap_num].flags & TMI_FORCE_FIELD)) {
288                                                 vms_vector pos;
289                                                 compute_center_point_on_side(&pos, segp, side );
290                                                 digi_link_sound_to_pos(SOUND_FORCEFIELD_HUM,segp-Segments,side,&pos,1, F1_0/2);
291                                                 Walls[segp->sides[side].wall_num].type = new_wall_type;
292                                                 Walls[csegp->sides[cside].wall_num].type = new_wall_type;
293                                         }
294                                         else
295                                                 start_wall_decloak(segp,side);
296                                         break;
297
298                                 case TT_ILLUSORY_WALL:
299                                         mprintf((0,"Illusory wall\n"));
300                                         Walls[segp->sides[side].wall_num].type = new_wall_type;
301                                         Walls[csegp->sides[cside].wall_num].type = new_wall_type;
302                                         break;
303                         }
304
305
306                         kill_stuck_objects(segp->sides[side].wall_num);
307                         kill_stuck_objects(csegp->sides[cside].wall_num);
308
309                 }
310         }
311
312         return ret;
313 }
314
315 void print_trigger_message (int pnum,int trig,int shot,char *message)
316  {
317         char *pl;               //points to 's' or nothing for plural word
318
319    if (pnum!=Player_num)
320                 return;
321
322         pl = (Triggers[trig].num_links>1)?"s":"";
323   
324     if (!(Triggers[trig].flags & TF_NO_MESSAGE) && shot)
325      HUD_init_message (message,pl);
326  }
327  
328
329 void do_matcen(byte trigger_num)
330 {
331         int i;
332
333         mprintf((0, "Matcen link!\n"));
334
335         if (trigger_num != -1) {
336                 for (i=0;i<Triggers[trigger_num].num_links;i++) {
337                         trigger_matcen(Triggers[trigger_num].seg[i] ); 
338                         mprintf((0," trigger_num %d : seg %d\n", 
339                                 trigger_num, Triggers[trigger_num].seg[i]));
340                 }
341         }
342 }
343
344         
345 void do_il_on(byte trigger_num)
346 {
347         int i;
348
349         mprintf((0, "Illusion ON\n"));
350
351         if (trigger_num != -1) {
352                 for (i=0;i<Triggers[trigger_num].num_links;i++) {
353                         wall_illusion_on(&Segments[Triggers[trigger_num].seg[i]], Triggers[trigger_num].side[i]); 
354                         mprintf((0," trigger_num %d : seg %d, side %d\n", 
355                                 trigger_num, Triggers[trigger_num].seg[i], Triggers[trigger_num].side[i]));
356                 }
357         }
358 }
359
360 void do_il_off(byte trigger_num)
361 {
362         int i;
363         
364         mprintf((0, "Illusion OFF\n"));
365
366         if (trigger_num != -1) {
367                 for (i=0;i<Triggers[trigger_num].num_links;i++) {
368                         vms_vector      cp;
369                         segment         *seg = &Segments[Triggers[trigger_num].seg[i]];
370                         int                     side = Triggers[trigger_num].side[i];
371
372                         wall_illusion_off(seg, side);
373
374                         mprintf((0," trigger_num %d : seg %d, side %d\n", 
375                                 trigger_num, Triggers[trigger_num].seg[i], Triggers[trigger_num].side[i]));
376
377                         compute_center_point_on_side(&cp, seg, side );
378                         digi_link_sound_to_pos( SOUND_WALL_REMOVED, seg-Segments, side, &cp, 0, F1_0 );
379
380                 }
381         }
382 }
383
384 extern void EnterSecretLevel(void);
385 extern void ExitSecretLevel(void);
386 extern int p_secret_level_destroyed(void);
387
388 int wall_is_forcefield(trigger *trig)
389 {
390         int i;
391
392         for (i=0;i<trig->num_links;i++)
393                 if ((TmapInfo[Segments[trig->seg[i]].sides[trig->side[i]].tmap_num].flags & TMI_FORCE_FIELD))
394                         break;
395
396         return (i<trig->num_links);
397 }
398
399 int check_trigger_sub(int trigger_num, int pnum,int shot)
400 {
401         trigger *trig = &Triggers[trigger_num];
402
403         mprintf ((0,"trignum=%d type=%d shot=%d\n",trigger_num,trig->type,shot));
404
405         if (trig->flags & TF_DISABLED)
406                 return 1;               //1 means don't send trigger hit to other players
407
408         if (trig->flags & TF_ONE_SHOT)          //if this is a one-shot...
409                 trig->flags |= TF_DISABLED;             //..then don't let it happen again
410
411         switch (trig->type) {
412
413                 case TT_EXIT:
414
415                         if (pnum!=Player_num)
416                           break;
417
418                         digi_stop_all();                //kill the sounds
419                         
420                         if (Current_level_num > 0) {
421                                 start_endlevel_sequence();
422                                 mprintf((0,"WOOHOO! (leaving the mine!)\n"));
423                         } else if (Current_level_num < 0) {
424                                 if ((Players[Player_num].shields < 0) || Player_is_dead)
425                                         break;
426
427                                 ExitSecretLevel();
428                                 return 1;
429                         } else {
430                                 #ifdef EDITOR
431                                         nm_messagebox( "Yo!", 1, "You have hit the exit trigger!", "" );
432                                 #else
433                                         Int3();         //level num == 0, but no editor!
434                                 #endif
435                         }
436                         return 1;
437                         break;
438
439                 case TT_SECRET_EXIT: {
440 #ifndef SHAREWARE
441                         int     truth;
442 #endif
443  
444                         if (pnum!=Player_num)
445                                 break;
446
447                         if ((Players[Player_num].shields < 0) || Player_is_dead)
448                                 break;
449
450                         if (Game_mode & GM_MULTI) {
451                                 HUD_init_message("Secret Level Teleporter disabled in multiplayer!");
452                                 digi_play_sample( SOUND_BAD_SELECTION, F1_0 );
453                                 break;
454                         }
455
456                         #ifndef SHAREWARE
457                         truth = p_secret_level_destroyed();
458
459                         if (Newdemo_state == ND_STATE_RECORDING)                        // record whether we're really going to the secret level
460                                 newdemo_record_secret_exit_blown(truth);
461
462                         if ((Newdemo_state != ND_STATE_PLAYBACK) && truth) {
463                                 HUD_init_message("Secret Level destroyed.  Exit disabled.");
464                                 digi_play_sample( SOUND_BAD_SELECTION, F1_0 );
465                                 break;
466                         }
467                         #endif
468
469                         #ifdef SHAREWARE
470                                 HUD_init_message("Secret Level Teleporter disabled in Descent 2 Demo");
471                                 digi_play_sample( SOUND_BAD_SELECTION, F1_0 );
472                                 break;
473                         #endif
474                         
475                         if (Newdemo_state == ND_STATE_RECORDING)                // stop demo recording
476                                 Newdemo_state = ND_STATE_PAUSED;
477
478                         digi_stop_all();                //kill the sounds
479
480                         digi_play_sample( SOUND_SECRET_EXIT, F1_0 );
481                         mprintf((0,"Exiting to secret level\n"));
482
483                         // -- BOGUS -- IMPOSSIBLE -- if (Game_mode & GM_MULTI)
484                         // -- BOGUS -- IMPOSSIBLE --    multi_send_endlevel_start(1);
485                         // -- BOGUS -- IMPOSSIBLE -- 
486                         // -- BOGUS -- IMPOSSIBLE -- if (Game_mode & GM_NETWORK)
487                         // -- BOGUS -- IMPOSSIBLE --    network_do_frame(1, 1);
488
489                         gr_palette_fade_out(gr_palette, 32, 0);
490                         EnterSecretLevel();
491                         Control_center_destroyed = 0;
492                         return 1;
493                         break;
494
495                 }
496
497                 case TT_OPEN_DOOR:
498                         mprintf((0,"D"));
499                         do_link(trigger_num);
500                         print_trigger_message (pnum,trigger_num,shot,"Door%s opened!");
501                         
502                         break;
503
504                 case TT_CLOSE_DOOR:
505                         do_close_door(trigger_num);
506                         print_trigger_message (pnum,trigger_num,shot,"Door%s closed!");
507                         break;
508
509                 case TT_UNLOCK_DOOR:
510                         mprintf((0,"D"));
511                         do_unlock_doors(trigger_num);
512                         print_trigger_message (pnum,trigger_num,shot,"Door%s unlocked!");
513                         
514                         break;
515         
516                 case TT_LOCK_DOOR:
517                         mprintf((0,"D"));
518                         do_lock_doors(trigger_num);
519                         print_trigger_message (pnum,trigger_num,shot,"Door%s locked!");
520
521                         break;
522         
523                 case TT_OPEN_WALL:
524                         if (do_change_walls(trigger_num))
525                         {
526                                 if (wall_is_forcefield(trig))
527                                         print_trigger_message (pnum,trigger_num,shot,"Force field%s deactivated!");
528                                 else
529                                         print_trigger_message (pnum,trigger_num,shot,"Wall%s opened!");
530                         }
531                         break;
532
533                 case TT_CLOSE_WALL:
534                         if (do_change_walls(trigger_num))
535                         {
536                                 if (wall_is_forcefield(trig))
537                                         print_trigger_message (pnum,trigger_num,shot,"Force field%s activated!");
538                                 else
539                                         print_trigger_message (pnum,trigger_num,shot,"Wall%s closed!");
540                         }
541                         break;
542
543                 case TT_ILLUSORY_WALL:
544                         //don't know what to say, so say nothing
545                         do_change_walls(trigger_num);
546                         break;
547
548                 case TT_MATCEN:
549                         if (!(Game_mode & GM_MULTI) || (Game_mode & GM_MULTI_ROBOTS))
550                                 do_matcen(trigger_num);
551                         break;
552         
553                 case TT_ILLUSION_ON:
554                         mprintf((0,"I"));
555                         do_il_on(trigger_num);
556                         print_trigger_message (pnum,trigger_num,shot,"Illusion%s on!");
557                         break;
558         
559                 case TT_ILLUSION_OFF:
560                         mprintf((0,"i"));
561                         do_il_off(trigger_num);
562                         print_trigger_message (pnum,trigger_num,shot,"Illusion%s off!");
563                         break;
564
565                 case TT_LIGHT_OFF:
566                         if (do_light_off(trigger_num))
567                                 print_trigger_message (pnum,trigger_num,shot,"Lights off!");
568                         break;
569
570                 case TT_LIGHT_ON:
571                         if (do_light_on(trigger_num))
572                                 print_trigger_message (pnum,trigger_num,shot,"Lights on!");
573
574                         break;
575
576                 default:
577                         Int3();
578                         break;
579         }
580
581         return 0;
582 }
583
584 //-----------------------------------------------------------------
585 // Checks for a trigger whenever an object hits a trigger side.
586 void check_trigger(segment *seg, short side, short objnum,int shot)
587 {
588         int wall_num, trigger_num;      //, ctrigger_num;
589         //segment *csegp;
590         //short cside;
591
592 //      mprintf(0,"T");
593
594         if ((objnum == Players[Player_num].objnum) || ((Objects[objnum].type == OBJ_ROBOT) && (Robot_info[Objects[objnum].id].companion))) {
595
596                 if ( Newdemo_state == ND_STATE_RECORDING )
597                         newdemo_record_trigger( seg-Segments, side, objnum,shot);
598
599                 wall_num = seg->sides[side].wall_num;
600                 if ( wall_num == -1 ) return;
601                 
602                 trigger_num = Walls[wall_num].trigger;
603
604                 if (trigger_num == -1)
605                         return;
606
607                 //##if ( Newdemo_state == ND_STATE_PLAYBACK ) {
608                 //##    if (Triggers[trigger_num].type == TT_EXIT) {
609                 //##            start_endlevel_sequence();
610                 //##    }
611                 //##    //return;
612                 //##}
613
614                 if (check_trigger_sub(trigger_num, Player_num,shot))
615                         return;
616
617                 //@@if (Triggers[trigger_num].flags & TRIGGER_ONE_SHOT) {
618                 //@@    Triggers[trigger_num].flags &= ~TRIGGER_ON;
619                 //@@
620                 //@@    csegp = &Segments[seg->children[side]];
621                 //@@    cside = find_connect_side(seg, csegp);
622                 //@@    Assert(cside != -1);
623                 //@@
624                 //@@    wall_num = csegp->sides[cside].wall_num;
625                 //@@    if ( wall_num == -1 ) return;
626                 //@@    
627                 //@@    ctrigger_num = Walls[wall_num].trigger;
628                 //@@
629                 //@@    Triggers[ctrigger_num].flags &= ~TRIGGER_ON;
630                 //@@}
631
632 #ifdef NETWORK
633                 if (Game_mode & GM_MULTI)
634                         multi_send_trigger(trigger_num);
635 #endif
636         }
637 }
638   
639 void triggers_frame_process()
640 {
641         int i;
642
643         for (i=0;i<Num_triggers;i++)
644                 if (Triggers[i].time >= 0)
645                         Triggers[i].time -= FrameTime;
646 }
647