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 = §or[sp->sectnum];
106
107 if (TEST(sp->cstat, CSTAT_SPRITE_YFLIP))
108 setinterpolation(§p->ceilingz);
109 else
110 setinterpolation(§p->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 = §or[sp->sectnum];
134
135 if (TEST(sp->cstat, CSTAT_SPRITE_YFLIP))
136 stopinterpolation(§p->ceilingz);
137 else
138 stopinterpolation(§p->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 = §or[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 = §or[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 = §or[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 = §p->ceilingz;
474 amt = DoVatorMove(SpriteNum, lptr);
475 MoveSpritesWithSector(sp->sectnum, amt, 1); // ceiling
476 }
477 else
478 {
479 lptr = §p->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 == §or[sp->sectnum] ||
586 pp->hi_sectp == §or[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 = §or[sp->sectnum];
632 int zval;
633 int *lptr;
634 int amt;
635
636 if (TEST(sp->cstat, CSTAT_SPRITE_YFLIP))
637 {
638 lptr = §p->ceilingz;
639 amt = DoVatorMove(SpriteNum, lptr);
640 MoveSpritesWithSector(sp->sectnum, amt, 1); // ceiling
641 }
642 else
643 {
644 lptr = §p->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