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