1 //-------------------------------------------------------------------------
2 /*
3 Copyright (C) 1997, 2005 - 3D Realms Entertainment
4 
5 This file is part of Shadow Warrior version 1.2
6 
7 Shadow Warrior is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; either version 2
10 of the License, or (at your option) any later version.
11 
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15 
16 See the GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21 
22 Original Source: 1997 - Frank Maddin and Jim Norwood
23 Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms
24 */
25 //-------------------------------------------------------------------------
26 #include "build.h"
27 
28 #include "names2.h"
29 #include "panel.h"
30 #include "game.h"
31 #include "net.h"
32 #include "tags.h"
33 #include "sector.h"
34 #include "interp.h"
35 #include "text.h"
36 #include "sprite.h"
37 #include "weapon.h"
38 
39 
40 short DoVatorMatch(PLAYERp pp, short match);
41 BOOL TestVatorMatchActive(short match);
42 VOID InterpSectorSprites(short sectnum, BOOL state);
43 int InitBloodSpray(short, BOOL, short);
44 
ReverseVator(short SpriteNum)45 void ReverseVator(short SpriteNum)
46     {
47     USERp u = User[SpriteNum];
48     SPRITEp sp = u->SpriteP;
49 
50     // if paused go ahead and start it up again
51     if (u->Tics)
52         {
53         u->Tics = 0;
54         SetVatorActive(SpriteNum);
55         return;
56         }
57 
58     // moving toward to OFF pos
59     if (u->z_tgt == u->oz)
60         {
61         if (sp->z == u->oz)
62             u->z_tgt = u->sz;
63         else
64         if (u->sz == u->oz)
65             u->z_tgt = sp->z;
66         }
67     else
68     if (u->z_tgt == u->sz)
69         {
70         if (sp->z == u->oz)
71             u->z_tgt = sp->z;
72         else
73         if (u->sz == u->oz)
74             u->z_tgt = u->sz;
75         }
76 
77     u->vel_rate = -u->vel_rate;
78     }
79 
80 BOOL
VatorSwitch(short match,short setting)81 VatorSwitch(short match, short setting)
82     {
83     SPRITEp sp;
84     short i,nexti;
85     BOOL found = FALSE;
86 
87     TRAVERSE_SPRITE_STAT(headspritestat[STAT_DEFAULT], i, nexti)
88         {
89         sp = &sprite[i];
90 
91         if (sp->lotag == TAG_SPRITE_SWITCH_VATOR && sp->hitag == match)
92             {
93             found = TRUE;
94             AnimateSwitch(sp, setting);
95             }
96         }
97 
98     return(found);
99     }
100 
SetVatorActive(short SpriteNum)101 void SetVatorActive(short SpriteNum)
102     {
103     USERp u = User[SpriteNum];
104     SPRITEp sp = u->SpriteP;
105     SECTORp sectp = &sector[sp->sectnum];
106 
107     if (TEST(sp->cstat, CSTAT_SPRITE_YFLIP))
108         setinterpolation(&sectp->ceilingz);
109     else
110         setinterpolation(&sectp->floorz);
111 
112     InterpSectorSprites(sp->sectnum, ON);
113 
114     // play activate sound
115     DoSoundSpotMatch(SP_TAG2(sp), 1, SOUND_OBJECT_TYPE);
116 
117     SET(u->Flags, SPR_ACTIVE);
118     u->Tics = 0;
119 
120     // moving to the ON position
121     if (u->z_tgt == sp->z)
122         VatorSwitch(SP_TAG2(sp), ON);
123     else
124     // moving to the OFF position
125     if (u->z_tgt == u->sz)
126         VatorSwitch(SP_TAG2(sp), OFF);
127     }
128 
SetVatorInactive(short SpriteNum)129 void SetVatorInactive(short SpriteNum)
130     {
131     USERp u = User[SpriteNum];
132     SPRITEp sp = u->SpriteP;
133     SECTORp sectp = &sector[sp->sectnum];
134 
135     if (TEST(sp->cstat, CSTAT_SPRITE_YFLIP))
136         stopinterpolation(&sectp->ceilingz);
137     else
138         stopinterpolation(&sectp->floorz);
139 
140     InterpSectorSprites(sp->sectnum, OFF);
141 
142     // play inactivate sound
143     DoSoundSpotMatch(SP_TAG2(sp), 2, SOUND_OBJECT_TYPE);
144 
145     RESET(u->Flags, SPR_ACTIVE);
146     }
147 
148 // called for operation from the space bar
DoVatorOperate(PLAYERp pp,short sectnum)149 short DoVatorOperate(PLAYERp pp, short sectnum)
150     {
151     USERp fu;
152     SPRITEp fsp;
153     short match;
154     short i,nexti;
155 
156     TRAVERSE_SPRITE_SECT(headspritesect[sectnum], i, nexti)
157         {
158         fsp = &sprite[i];
159 
160         if (fsp->statnum == STAT_VATOR && SP_TAG1(fsp) == SECT_VATOR && SP_TAG3(fsp) == 0)
161             {
162             fu = User[i];
163 
164             sectnum = fsp->sectnum;
165 
166             // single play only vator
167             // BOOL 8 must be set for message to display
168             if (TEST_BOOL4(fsp) && (gNet.MultiGameType == MULTI_GAME_COMMBAT || gNet.MultiGameType == MULTI_GAME_AI_BOTS))
169                 {
170                 if (pp && TEST_BOOL11(fsp)) PutStringInfo(pp,"This only opens in single play.");
171                 continue;
172                 }
173 
174             match = SP_TAG2(fsp);
175             if (match > 0)
176                 {
177                 if (TestVatorMatchActive(match))
178                     return(-1);
179                 else
180                     return(DoVatorMatch(pp, match));
181                 }
182 
183             if (pp && SectUser[sectnum] && SectUser[sectnum]->stag == SECT_LOCK_DOOR && SectUser[sectnum]->number)
184                 {
185                 short key_num;
186 
187                 key_num = SectUser[sectnum]->number;
188 
189                 #if 0
190                 if (pp->HasKey[key_num - 1])
191                     {
192                     int i;
193                     for(i=0; i<numsectors; i++)
194                         {
195                         if (SectUser[i] && SectUser[i]->stag == SECT_LOCK_DOOR && SectUser[i]->number == key_num)
196                             SectUser[i]->number = 0;  // unlock all doors of this type
197                         }
198                     UnlockKeyLock(key_num);
199                     }
200                 else
201                 #endif
202                     {
203                     PutStringInfo(pp, KeyDoorMessage[key_num - 1]);
204                     return (FALSE);
205                     }
206                 }
207 
208             SetVatorActive(i);
209             break;
210             }
211         }
212 
213     return(i);
214     }
215 
216 // called from switches and triggers
217 // returns first vator found
218 short
DoVatorMatch(PLAYERp pp,short match)219 DoVatorMatch(PLAYERp pp, short match)
220     {
221     USERp fu;
222     SPRITEp fsp;
223     short sectnum;
224     short first_vator = -1;
225 
226     short i,nexti;
227 
228     //VatorSwitch(match, ON);
229 
230     TRAVERSE_SPRITE_STAT(headspritestat[STAT_VATOR], i, nexti)
231         {
232         fsp = &sprite[i];
233 
234         if (SP_TAG1(fsp) == SECT_VATOR && SP_TAG2(fsp) == match)
235             {
236             fu = User[i];
237 
238             if (first_vator == -1)
239                 first_vator = i;
240 
241             // single play only vator
242             // BOOL 8 must be set for message to display
243             if (TEST_BOOL4(fsp) && (gNet.MultiGameType == MULTI_GAME_COMMBAT || gNet.MultiGameType == MULTI_GAME_AI_BOTS))
244                 {
245                 if (pp && TEST_BOOL11(fsp)) PutStringInfo(pp,"This only opens in single play.");
246                 continue;
247                 }
248 
249             // lock code
250             sectnum = fsp->sectnum;
251             if (pp && SectUser[sectnum] && SectUser[sectnum]->stag == SECT_LOCK_DOOR && SectUser[sectnum]->number)
252                 {
253                 short key_num;
254 
255                 key_num = SectUser[sectnum]->number;
256 
257                 #if 0
258                 if (pp->HasKey[key_num - 1])
259                     {
260                     int i;
261                     for(i=0; i<numsectors; i++)
262                         {
263                         if (SectUser[i] && SectUser[i]->stag == SECT_LOCK_DOOR && SectUser[i]->number == key_num)
264                             SectUser[i]->number = 0;  // unlock all doors of this type
265                         }
266                     UnlockKeyLock(key_num);
267                     }
268                 else
269                 #endif
270                     {
271                     PutStringInfo(pp, KeyDoorMessage[key_num - 1]);
272                     return (-1);
273                     }
274                 }
275 
276             // remember the player than activated it
277             fu->PlayerP = pp;
278 
279             if (TEST(fu->Flags, SPR_ACTIVE))
280                 {
281                 ReverseVator(i);
282                 continue;
283                 }
284 
285             SetVatorActive(i);
286             }
287         }
288 
289     return(first_vator);
290     }
291 
292 
293 BOOL
TestVatorMatchActive(short match)294 TestVatorMatchActive(short match)
295     {
296     USERp fu;
297     SPRITEp fsp;
298     short sectnum;
299 
300     short i,nexti;
301 
302     TRAVERSE_SPRITE_STAT(headspritestat[STAT_VATOR], i, nexti)
303         {
304         fsp = &sprite[i];
305 
306         if (SP_TAG1(fsp) == SECT_VATOR && SP_TAG2(fsp) == match)
307             {
308             fu = User[i];
309 
310             // Does not have to be inactive to be operated
311              if (TEST_BOOL6(fsp))
312                  continue;
313 
314             if (TEST(fu->Flags, SPR_ACTIVE) || fu->Tics)
315                 return(TRUE);
316             }
317         }
318 
319     return(FALSE);
320     }
321 
InterpSectorSprites(short sectnum,BOOL state)322 VOID InterpSectorSprites(short sectnum, BOOL state)
323     {
324     SPRITEp sp;
325     short i,nexti;
326 
327     TRAVERSE_SPRITE_SECT(headspritesect[sectnum], i, nexti)
328         {
329         sp = &sprite[i];
330 
331         if (User[i])
332             {
333             if (TEST(User[i]->Flags, SPR_SKIP4) && sp->statnum <= STAT_SKIP4_INTERP_END)
334                 continue;
335 
336             if (TEST(User[i]->Flags, SPR_SKIP2) && sp->statnum <= STAT_SKIP2_INTERP_END)
337                 continue;
338             }
339 
340         if (state)
341             setinterpolation(&sp->z);
342         else
343             stopinterpolation(&sp->z);
344         }
345     }
346 
MoveSpritesWithSector(short sectnum,int z_amt,BOOL type)347 VOID MoveSpritesWithSector(short sectnum, int z_amt, BOOL type)
348     {
349     SECTORp sectp = &sector[sectnum];
350     SPRITEp sp;
351     short i,nexti;
352     BOOL both = FALSE;
353 
354     if (SectUser[sectnum])
355         both = !!TEST(SectUser[sectnum]->flags, SECTFU_VATOR_BOTH);
356 
357     TRAVERSE_SPRITE_SECT(headspritesect[sectnum], i, nexti)
358         {
359         sp = &sprite[i];
360 
361         if (User[i])
362             {
363             switch (sp->statnum)
364                 {
365                 case STAT_ITEM:
366                 case STAT_NO_STATE:
367                 case STAT_MINE_STUCK:
368                 case STAT_WALLBLOOD_QUEUE:
369                 case STAT_FLOORBLOOD_QUEUE:
370                 case STAT_STATIC_FIRE:
371                     break;
372                 default:
373                     goto cont;
374                 }
375             }
376         else
377             {
378             switch (sp->statnum)
379                 {
380                 case STAT_STAR_QUEUE:
381                 case STAT_HOLE_QUEUE:
382 //              case STAT_WALLBLOOD_QUEUE:
383 //              case STAT_FLOORBLOOD_QUEUE:
384                     goto cont;
385                 }
386             }
387 
388         if (TEST(sp->extra, SPRX_STAY_PUT_VATOR))
389             continue;
390 
391         if (both)
392             {
393             // sprite started close to floor
394             if (TEST(sp->cstat, CSTAT_SPRITE_CLOSE_FLOOR))
395                 {
396                 // this is a ceiling
397                 if (type == 1)
398                     continue;
399                 }
400             else
401                 {
402                 // this is a floor
403                 if (type == 0)
404                     continue;
405                 }
406             }
407 
408         sp->z += z_amt;
409 
410         cont:
411         continue;
412         }
413     }
414 
DoVatorMove(short SpriteNum,int * lptr)415 int DoVatorMove(short SpriteNum, int *lptr)
416     {
417     USERp u = User[SpriteNum];
418     SPRITEp sp = u->SpriteP;
419     SECTORp sectp = &sector[sp->sectnum];
420     int zval;
421     int move_amt;
422 
423     zval = *lptr;
424 
425     // if LESS THAN goal
426     if (zval < u->z_tgt)
427         {
428         // move it DOWN
429         zval += (synctics * u->jump_speed);
430 
431         u->jump_speed += u->vel_rate * synctics;
432 
433         // if the other way make it equal
434         if (zval > u->z_tgt)
435             zval = u->z_tgt;
436         }
437 
438     // if GREATER THAN goal
439     if (zval > u->z_tgt)
440         {
441         // move it UP
442         zval -= (synctics * u->jump_speed);
443 
444         u->jump_speed += u->vel_rate * synctics;
445 
446         if (zval < u->z_tgt)
447             zval = u->z_tgt;
448         }
449 
450     move_amt = zval - *lptr;
451     *lptr = zval;
452 
453     return(move_amt);
454     }
455 
456 
DoVator(short SpriteNum)457 int DoVator(short SpriteNum)
458     {
459     USERp u = User[SpriteNum];
460     SPRITEp sp = u->SpriteP;
461     SECTORp sectp = &sector[sp->sectnum];
462     int *lptr;
463     int amt;
464 
465     // u->sz        - where the sector z started
466     // u->z_tgt     - current target z
467     // u->oz        - original z - where it initally starts off
468     // sp->z        - z of the sprite
469     // u->vel_rate  - velocity
470 
471     if (TEST(sp->cstat, CSTAT_SPRITE_YFLIP))
472         {
473         lptr = &sectp->ceilingz;
474         amt = DoVatorMove(SpriteNum, lptr);
475         MoveSpritesWithSector(sp->sectnum, amt, 1); // ceiling
476         }
477     else
478         {
479         lptr = &sectp->floorz;
480         amt = DoVatorMove(SpriteNum, lptr);
481         MoveSpritesWithSector(sp->sectnum, amt, 0); // floor
482         }
483 
484     // EQUAL this entry has finished
485     if (*lptr == u->z_tgt)
486         {
487         // in the ON position
488         if (u->z_tgt == sp->z)
489             {
490             // change target
491             u->z_tgt = u->sz;
492             u->vel_rate = -u->vel_rate;
493 
494             SetVatorInactive(SpriteNum);
495 
496             // if tag6 and nothing blocking door
497             if (SP_TAG6(sp) && !TEST_BOOL8(sp))
498                 DoMatchEverything(u->PlayerP, SP_TAG6(sp), -1);
499             }
500         else
501         // in the OFF position
502         if (u->z_tgt == u->sz)
503             {
504             short match = SP_TAG2(sp);
505 
506             // change target
507             u->jump_speed = u->vel_tgt;
508             u->vel_rate = labs(u->vel_rate);
509             u->z_tgt = sp->z;
510 
511             RESET_BOOL8(sp);
512             SetVatorInactive(SpriteNum);
513 
514             // set owner swith back to OFF
515             // only if ALL vators are inactive
516             if (!TestVatorMatchActive(match))
517                 {
518                 //VatorSwitch(match, OFF);
519                 }
520 
521             if (SP_TAG6(sp) && TEST_BOOL5(sp))
522                 DoMatchEverything(u->PlayerP, SP_TAG6(sp), -1);
523             }
524 
525         // operate only once
526         if (TEST_BOOL2(sp))
527             {
528             SetVatorInactive(SpriteNum);
529             KillSprite(SpriteNum);
530             return(0);
531             }
532 
533         // setup to go back to the original z
534         if (*lptr != u->oz)
535             {
536             if (u->WaitTics)
537                 u->Tics = u->WaitTics;
538             }
539         }
540     else // if (*lptr == u->z_tgt)
541         {
542         // if heading for the OFF (original) position and should NOT CRUSH
543         if (TEST_BOOL3(sp) && u->z_tgt == u->oz)
544             {
545             int i,nexti;
546             SPRITEp bsp;
547             USERp bu;
548             BOOL found = FALSE;
549 
550             TRAVERSE_SPRITE_SECT(headspritesect[sp->sectnum], i, nexti)
551                 {
552                 bsp = &sprite[i];
553                 bu = User[i];
554 
555                 if (bsp->statnum == STAT_ENEMY)
556                     {
557                     if (labs(sectp->ceilingz - sectp->floorz) < SPRITEp_SIZE_Z(bsp))
558                         {
559                         InitBloodSpray(i, TRUE, -1);
560                         UpdateSinglePlayKills(i);
561                         KillSprite(i);
562                         continue;
563                         }
564                     }
565 
566                 if (bu && TEST(bsp->cstat, CSTAT_SPRITE_BLOCK) && TEST(bsp->extra, SPRX_PLAYER_OR_ENEMY))
567                     {
568                     // found something blocking so reverse to ON position
569                     ReverseVator(SpriteNum);
570                     SET_BOOL8(sp); // tell vator that something blocking door
571                     found = TRUE;
572                     break;
573                     }
574                 }
575 
576             if (!found)
577                 {
578                 short pnum;
579                 PLAYERp pp;
580                 // go ahead and look for players clip box bounds
581                 TRAVERSE_CONNECT(pnum)
582                     {
583                     pp = Player + pnum;
584 
585                     if (pp->lo_sectp == &sector[sp->sectnum] ||
586                         pp->hi_sectp == &sector[sp->sectnum])
587                         {
588                         ReverseVator(SpriteNum);
589 
590                         u->vel_rate = -u->vel_rate;
591                         found = TRUE;
592                         }
593                     }
594                 }
595             }
596         else
597             {
598             int i,nexti;
599             SPRITEp bsp;
600             USERp bu;
601             BOOL found = FALSE;
602 
603             TRAVERSE_SPRITE_SECT(headspritesect[sp->sectnum], i, nexti)
604                 {
605                 bsp = &sprite[i];
606                 bu = User[i];
607 
608                 if (bsp->statnum == STAT_ENEMY)
609                     {
610                     if (labs(sectp->ceilingz - sectp->floorz) < SPRITEp_SIZE_Z(bsp))
611                         {
612                         InitBloodSpray(i, TRUE, -1);
613                         UpdateSinglePlayKills(i);
614                         KillSprite(i);
615                         continue;
616                         }
617                     }
618                 }
619             }
620         }
621 
622 
623 
624     return(0);
625     }
626 
DoVatorAuto(short SpriteNum)627 int DoVatorAuto(short SpriteNum)
628     {
629     USERp u = User[SpriteNum];
630     SPRITEp sp = u->SpriteP;
631     SECTORp sectp = &sector[sp->sectnum];
632     int zval;
633     int *lptr;
634     int amt;
635 
636     if (TEST(sp->cstat, CSTAT_SPRITE_YFLIP))
637         {
638         lptr = &sectp->ceilingz;
639         amt = DoVatorMove(SpriteNum, lptr);
640         MoveSpritesWithSector(sp->sectnum, amt, 1); // ceiling
641         }
642     else
643         {
644         lptr = &sectp->floorz;
645         amt = DoVatorMove(SpriteNum, lptr);
646         MoveSpritesWithSector(sp->sectnum, amt, 0); // floor
647         }
648 
649     // EQUAL this entry has finished
650     if (*lptr == u->z_tgt)
651         {
652         // in the UP position
653         if (u->z_tgt == sp->z)
654             {
655             // change target
656             u->z_tgt = u->sz;
657             u->vel_rate = -u->vel_rate;
658             u->Tics = u->WaitTics;
659 
660             if (SP_TAG6(sp))
661                 DoMatchEverything(u->PlayerP, SP_TAG6(sp), -1);
662             }
663         else
664         // in the DOWN position
665         if (u->z_tgt == u->sz)
666             {
667             // change target
668             u->jump_speed = u->vel_tgt;
669             u->vel_rate = labs(u->vel_rate);
670             u->z_tgt = sp->z;
671             u->Tics = u->WaitTics;
672 
673             if (SP_TAG6(sp) && TEST_BOOL5(sp))
674                 DoMatchEverything(NULL, SP_TAG6(sp), -1);
675             }
676         }
677 
678     return(0);
679     }
680 
681 
682 #include "saveable.h"
683 
684 static saveable_code saveable_vator_code[] = {
685 	SAVE_CODE(ReverseVator),
686 	SAVE_CODE(VatorSwitch),
687 	SAVE_CODE(SetVatorActive),
688 	SAVE_CODE(SetVatorInactive),
689 	SAVE_CODE(DoVatorOperate),
690 	SAVE_CODE(DoVatorMatch),
691 	SAVE_CODE(TestVatorMatchActive),
692 	SAVE_CODE(InterpSectorSprites),
693 	SAVE_CODE(MoveSpritesWithSector),
694 	SAVE_CODE(DoVatorMove),
695 	SAVE_CODE(DoVator),
696 	SAVE_CODE(DoVatorAuto),
697 };
698 
699 saveable_module saveable_vator = {
700 	// code
701 	saveable_vator_code,
702 	SIZ(saveable_vator_code),
703 
704 	// data
705 	NULL,0
706 };
707