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