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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 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 "tags.h"
32 #include "sector.h"
33 #include "sprite.h"
34 
35 short DoSpikeMatch(short match);
36 SWBOOL TestSpikeMatchActive(short match);
37 int DoVatorMove(short SpriteNum, int *lptr);
38 void InterpSectorSprites(short sectnum, SWBOOL state);
39 
ReverseSpike(short SpriteNum)40 void ReverseSpike(short SpriteNum)
41 {
42     USERp u = User[SpriteNum];
43     SPRITEp sp = u->SpriteP;
44 
45     // if paused go ahead and start it up again
46     if (u->Tics)
47     {
48         u->Tics = 0;
49         SetSpikeActive(SpriteNum);
50         return;
51     }
52 
53     // moving toward to OFF pos
54     if (u->z_tgt == u->oz)
55     {
56         if (sp->z == u->oz)
57             u->z_tgt = u->sz;
58         else if (u->sz == u->oz)
59             u->z_tgt = sp->z;
60     }
61     else if (u->z_tgt == u->sz)
62     {
63         if (sp->z == u->oz)
64             u->z_tgt = sp->z;
65         else if (u->sz == u->oz)
66             u->z_tgt = u->sz;
67     }
68 
69     u->vel_rate = -u->vel_rate;
70 }
71 
72 SWBOOL
SpikeSwitch(short match,short setting)73 SpikeSwitch(short match, short setting)
74 {
75     SPRITEp sp;
76     short i,nexti;
77     SWBOOL found = FALSE;
78 
79     TRAVERSE_SPRITE_STAT(headspritestat[STAT_DEFAULT], i, nexti)
80     {
81         sp = &sprite[i];
82 
83         if (sp->lotag == TAG_SPRITE_SWITCH_VATOR && sp->hitag == match)
84         {
85             found = TRUE;
86             AnimateSwitch(sp, setting);
87         }
88     }
89 
90     return found;
91 }
92 
SetSpikeActive(short SpriteNum)93 void SetSpikeActive(short SpriteNum)
94 {
95     USERp u = User[SpriteNum];
96     SPRITEp sp = u->SpriteP;
97     SECTORp sectp = &sector[sp->sectnum];
98 
99     if (TEST(sp->cstat, CSTAT_SPRITE_YFLIP))
100         short_setinterpolation(&sectp->ceilingheinum);
101     else
102         short_setinterpolation(&sectp->floorheinum);
103 
104     InterpSectorSprites(sp->sectnum, ON);
105 
106     // play activate sound
107     DoSoundSpotMatch(SP_TAG2(sp), 1, SOUND_OBJECT_TYPE);
108 
109     SET(u->Flags, SPR_ACTIVE);
110     u->Tics = 0;
111 
112     // moving to the ON position
113     if (u->z_tgt == sp->z)
114         VatorSwitch(SP_TAG2(sp), ON);
115     else
116     // moving to the OFF position
117     if (u->z_tgt == u->sz)
118         VatorSwitch(SP_TAG2(sp), OFF);
119 }
120 
SetSpikeInactive(short SpriteNum)121 void SetSpikeInactive(short SpriteNum)
122 {
123     USERp u = User[SpriteNum];
124     SPRITEp sp = u->SpriteP;
125     SECTORp sectp = &sector[sp->sectnum];
126 
127     if (TEST(sp->cstat, CSTAT_SPRITE_YFLIP))
128         short_stopinterpolation(&sectp->ceilingheinum);
129     else
130         short_stopinterpolation(&sectp->floorheinum);
131 
132     InterpSectorSprites(sp->sectnum, OFF);
133 
134     // play activate sound
135     DoSoundSpotMatch(SP_TAG2(sp), 2, SOUND_OBJECT_TYPE);
136 
137     RESET(u->Flags, SPR_ACTIVE);
138 }
139 
140 // called for operation from the space bar
DoSpikeOperate(short sectnum)141 short DoSpikeOperate(short sectnum)
142 {
143     SPRITEp fsp;
144     short match;
145     short i,nexti;
146 
147     TRAVERSE_SPRITE_SECT(headspritesect[sectnum], i, nexti)
148     {
149         fsp = &sprite[i];
150 
151         if (fsp->statnum == STAT_SPIKE && SP_TAG1(fsp) == SECT_SPIKE && SP_TAG3(fsp) == 0)
152         {
153             sectnum = fsp->sectnum;
154 
155             match = SP_TAG2(fsp);
156             if (match > 0)
157             {
158                 if (TestSpikeMatchActive(match))
159                     return -1;
160                 else
161                     return DoSpikeMatch(match);
162             }
163 
164             SetSpikeActive(i);
165             break;
166         }
167     }
168 
169     return i;
170 }
171 
172 // called from switches and triggers
173 // returns first spike found
174 short
DoSpikeMatch(short match)175 DoSpikeMatch(short match)
176 {
177     USERp fu;
178     SPRITEp fsp;
179     short first_spike = -1;
180 
181     short i,nexti;
182 
183     //SpikeSwitch(match, ON);
184 
185     TRAVERSE_SPRITE_STAT(headspritestat[STAT_SPIKE], i, nexti)
186     {
187         fsp = &sprite[i];
188 
189         if (SP_TAG1(fsp) == SECT_SPIKE && SP_TAG2(fsp) == match)
190         {
191             fu = User[i];
192 
193             if (first_spike == -1)
194                 first_spike = i;
195 
196             if (TEST(fu->Flags, SPR_ACTIVE))
197             {
198                 ReverseSpike(i);
199                 continue;
200             }
201 
202             SetSpikeActive(i);
203         }
204     }
205 
206     return first_spike;
207 }
208 
209 
210 SWBOOL
TestSpikeMatchActive(short match)211 TestSpikeMatchActive(short match)
212 {
213     USERp fu;
214     SPRITEp fsp;
215 
216     short i,nexti;
217 
218     TRAVERSE_SPRITE_STAT(headspritestat[STAT_SPIKE], i, nexti)
219     {
220         fsp = &sprite[i];
221 
222         if (SP_TAG1(fsp) == SECT_SPIKE && SP_TAG2(fsp) == match)
223         {
224             fu = User[i];
225 
226             // door war
227             if (TEST_BOOL6(fsp))
228                 continue;
229 
230             if (TEST(fu->Flags, SPR_ACTIVE) || fu->Tics)
231                 return TRUE;
232         }
233     }
234 
235     return FALSE;
236 }
237 
DoSpikeMove(short SpriteNum,int * lptr)238 int DoSpikeMove(short SpriteNum, int *lptr)
239 {
240     USERp u = User[SpriteNum];
241     int zval;
242 
243     zval = *lptr;
244 
245     // if LESS THAN goal
246     if (zval < u->z_tgt)
247     {
248         // move it DOWN
249         zval += (synctics * u->jump_speed);
250 
251         u->jump_speed += u->vel_rate * synctics;
252 
253         // if the other way make it equal
254         if (zval > u->z_tgt)
255             zval = u->z_tgt;
256     }
257 
258     // if GREATER THAN goal
259     if (zval > u->z_tgt)
260     {
261         // move it UP
262         zval -= (synctics * u->jump_speed);
263 
264         u->jump_speed += u->vel_rate * synctics;
265 
266         if (zval < u->z_tgt)
267             zval = u->z_tgt;
268     }
269 
270     *lptr = zval;
271 
272     return 0;
273 }
274 
SpikeAlign(short SpriteNum)275 void SpikeAlign(short SpriteNum)
276 {
277     USERp u = User[SpriteNum];
278     SPRITEp sp = u->SpriteP;
279 
280     // either work on single sector or all tagged in SOBJ
281     if ((int8_t)SP_TAG7(sp) < 0)
282     {
283         if (TEST(sp->cstat, CSTAT_SPRITE_YFLIP))
284             alignceilslope(sp->sectnum, sp->x, sp->y, u->zclip);
285         else
286             alignflorslope(sp->sectnum, sp->x, sp->y, u->zclip);
287     }
288     else
289     {
290         if (TEST(sp->cstat, CSTAT_SPRITE_YFLIP))
291             SOBJ_AlignCeilingToPoint(&SectorObject[SP_TAG7(sp)], sp->x, sp->y, u->zclip);
292         else
293             SOBJ_AlignFloorToPoint(&SectorObject[SP_TAG7(sp)], sp->x, sp->y, u->zclip);
294     }
295 }
296 
MoveSpritesWithSpike(short sectnum)297 void MoveSpritesWithSpike(short sectnum)
298 {
299     SPRITEp sp;
300     short i,nexti;
301     int cz,fz;
302 
303     TRAVERSE_SPRITE_SECT(headspritesect[sectnum], i, nexti)
304     {
305         sp = &sprite[i];
306 
307         if (User[i])
308             continue;
309 
310         if (TEST(sp->extra, SPRX_STAY_PUT_VATOR))
311             continue;
312 
313         getzsofslope(sectnum, sp->x, sp->y, &cz, &fz);
314         sp->z = fz;
315     }
316 }
317 
DoSpike(short SpriteNum)318 int DoSpike(short SpriteNum)
319 {
320     USERp u = User[SpriteNum];
321     SPRITEp sp = u->SpriteP;
322     int *lptr;
323 
324     // zclip = floor or ceiling z
325     // oz = original z
326     // z_tgt = target z - on pos
327     // sz = starting z - off pos
328 
329     lptr = &u->zclip;
330 
331     DoSpikeMove(SpriteNum, lptr);
332     MoveSpritesWithSpike(sp->sectnum);
333     SpikeAlign(SpriteNum);
334 
335     // EQUAL this entry has finished
336     if (*lptr == u->z_tgt)
337     {
338         // in the ON position
339         if (u->z_tgt == sp->z)
340         {
341             // change target
342             u->z_tgt = u->sz;
343             u->vel_rate = -u->vel_rate;
344 
345             SetSpikeInactive(SpriteNum);
346 
347             if (SP_TAG6(sp))
348                 DoMatchEverything(NULL, SP_TAG6(sp), -1);
349         }
350         else
351         // in the OFF position
352         if (u->z_tgt == u->sz)
353         {
354             short match = SP_TAG2(sp);
355 
356             // change target
357             u->jump_speed = u->vel_tgt;
358             u->vel_rate = labs(u->vel_rate);
359             u->z_tgt = sp->z;
360 
361             SetSpikeInactive(SpriteNum);
362 
363             // set owner swith back to OFF
364             // only if ALL spikes are inactive
365             if (!TestSpikeMatchActive(match))
366             {
367                 //SpikeSwitch(match, OFF);
368             }
369 
370             if (SP_TAG6(sp) && TEST_BOOL5(sp))
371                 DoMatchEverything(NULL, SP_TAG6(sp), -1);
372         }
373 
374         // operate only once
375         if (TEST_BOOL2(sp))
376         {
377             SetSpikeInactive(SpriteNum);
378             KillSprite(SpriteNum);
379             return 0;
380         }
381 
382         // setup to go back to the original z
383         if (*lptr != u->oz)
384         {
385             if (u->WaitTics)
386                 u->Tics = u->WaitTics;
387         }
388     }
389     else // if (*lptr == u->z_tgt)
390     {
391         // if heading for the OFF (original) position and should NOT CRUSH
392         if (TEST_BOOL3(sp) && u->z_tgt == u->oz)
393         {
394             int i,nexti;
395             SPRITEp bsp;
396             USERp bu;
397             SWBOOL found = FALSE;
398 
399             TRAVERSE_SPRITE_SECT(headspritesect[sp->sectnum], i, nexti)
400             {
401                 bsp = &sprite[i];
402                 bu = User[i];
403 
404                 if (bu && TEST(bsp->cstat, CSTAT_SPRITE_BLOCK) && TEST(bsp->extra, SPRX_PLAYER_OR_ENEMY))
405                 {
406                     ReverseSpike(SpriteNum);
407                     found = TRUE;
408                     break;
409                 }
410             }
411 
412             if (!found)
413             {
414                 short pnum;
415                 PLAYERp pp;
416                 // go ahead and look for players clip box bounds
417                 TRAVERSE_CONNECT(pnum)
418                 {
419                     pp = Player + pnum;
420 
421                     if (pp->lo_sectp == &sector[sp->sectnum] ||
422                         pp->hi_sectp == &sector[sp->sectnum])
423                     {
424                         ReverseSpike(SpriteNum);
425                         found = TRUE;
426                     }
427                 }
428             }
429         }
430     }
431 
432     return 0;
433 }
434 
DoSpikeAuto(short SpriteNum)435 int DoSpikeAuto(short SpriteNum)
436 {
437     USERp u = User[SpriteNum];
438     SPRITEp sp = u->SpriteP;
439     int *lptr;
440 
441     lptr = &u->zclip;
442 
443     DoSpikeMove(SpriteNum, lptr);
444     MoveSpritesWithSpike(sp->sectnum);
445     SpikeAlign(SpriteNum);
446 
447     // EQUAL this entry has finished
448     if (*lptr == u->z_tgt)
449     {
450         // in the UP position
451         if (u->z_tgt == sp->z)
452         {
453             // change target
454             u->z_tgt = u->sz;
455             u->vel_rate = -u->vel_rate;
456             u->Tics = u->WaitTics;
457 
458             if (SP_TAG6(sp))
459                 DoMatchEverything(NULL, SP_TAG6(sp), -1);
460         }
461         else
462         // in the DOWN position
463         if (u->z_tgt == u->sz)
464         {
465             // change target
466             u->jump_speed = u->vel_tgt;
467             u->vel_rate = labs(u->vel_rate);
468             u->z_tgt = sp->z;
469             u->Tics = u->WaitTics;
470 
471             if (SP_TAG6(sp) && TEST_BOOL5(sp))
472                 DoMatchEverything(NULL, SP_TAG6(sp), -1);
473         }
474     }
475 
476     return 0;
477 }
478 
479 
480 #include "saveable.h"
481 
482 static saveable_code saveable_spike_code[] =
483 {
484     SAVE_CODE(ReverseSpike),
485     SAVE_CODE(SpikeSwitch),
486     SAVE_CODE(SetSpikeActive),
487     SAVE_CODE(SetSpikeInactive),
488     SAVE_CODE(DoSpikeOperate),
489     SAVE_CODE(DoSpikeMatch),
490     SAVE_CODE(TestSpikeMatchActive),
491     SAVE_CODE(DoSpikeMove),
492     SAVE_CODE(SpikeAlign),
493     SAVE_CODE(MoveSpritesWithSpike),
494     SAVE_CODE(DoSpike),
495     SAVE_CODE(DoSpikeAuto),
496 };
497 
498 saveable_module saveable_spike =
499 {
500     // code
501     saveable_spike_code,
502     SIZ(saveable_spike_code),
503 
504     // data
505     NULL,0
506 };
507