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 #include "common.h"
28 
29 #include "keys.h"
30 #include "names2.h"
31 #include "panel.h"
32 #include "game.h"
33 #include "tags.h"
34 #include "common_game.h"
35 #include "break.h"
36 #include "quake.h"
37 #include "network.h"
38 #include "pal.h"
39 
40 #include "ai.h"
41 #include "weapon.h"
42 
43 #include "sprite.h"
44 #include "sector.h"
45 #include "actor.h"
46 
47 ANIMATOR NullAnimator,DoSuicide;
48 ANIMATOR DoBloodSpray;
49 int SpawnFlashBombOnActor(int16_t enemy);
50 
51 ANIMATOR DoPuff, BloodSprayFall;
52 extern STATE s_Puff[];
53 extern STATE s_FireballFlames[];
54 extern STATE s_GoreFloorSplash[];
55 extern STATE s_GoreSplash[];
56 extern SWBOOL GlobalSkipZrange;
57 
58 #define CHEMTICS SEC(40)
59 
60 #define GOREDrip 1562 //2430
61 #define BLOODSPRAY_RATE 20
62 
63 STATE s_BloodSpray[] =
64 {
65     {GOREDrip + 0, BLOODSPRAY_RATE, BloodSprayFall, &s_BloodSpray[1]},
66     {GOREDrip + 1, BLOODSPRAY_RATE, BloodSprayFall, &s_BloodSpray[2]},
67     {GOREDrip + 2, BLOODSPRAY_RATE, BloodSprayFall, &s_BloodSpray[3]},
68     {GOREDrip + 3, BLOODSPRAY_RATE, BloodSprayFall, &s_BloodSpray[4]},
69     {GOREDrip + 3, 100, DoSuicide, &s_BloodSpray[0]}
70 };
71 
72 
73 #define EXP_RATE 2
74 STATE s_PhosphorExp[] =
75 {
76     {EXP + 0, EXP_RATE, NullAnimator, &s_PhosphorExp[1]},
77     {EXP + 1, EXP_RATE, NullAnimator, &s_PhosphorExp[2]},
78     {EXP + 2, EXP_RATE, NullAnimator, &s_PhosphorExp[3]},
79     {EXP + 3, EXP_RATE, NullAnimator, &s_PhosphorExp[4]},
80     {EXP + 4, EXP_RATE, NullAnimator, &s_PhosphorExp[5]},
81     {EXP + 5, EXP_RATE, NullAnimator, &s_PhosphorExp[6]},
82     {EXP + 6, EXP_RATE, NullAnimator, &s_PhosphorExp[7]},
83     {EXP + 7, EXP_RATE, NullAnimator, &s_PhosphorExp[8]},
84     {EXP + 8, EXP_RATE, NullAnimator, &s_PhosphorExp[9]},
85     {EXP + 9, EXP_RATE, NullAnimator, &s_PhosphorExp[10]},
86     {EXP + 10, EXP_RATE, NullAnimator, &s_PhosphorExp[11]},
87     {EXP + 11, EXP_RATE, NullAnimator, &s_PhosphorExp[12]},
88     {EXP + 12, EXP_RATE, NullAnimator, &s_PhosphorExp[13]},
89     {EXP + 13, EXP_RATE, NullAnimator, &s_PhosphorExp[14]},
90     {EXP + 14, EXP_RATE, NullAnimator, &s_PhosphorExp[15]},
91     {EXP + 15, EXP_RATE, NullAnimator, &s_PhosphorExp[16]},
92     {EXP + 16, EXP_RATE, NullAnimator, &s_PhosphorExp[17]},
93     {EXP + 17, EXP_RATE, NullAnimator, &s_PhosphorExp[18]},
94     {EXP + 18, EXP_RATE, NullAnimator, &s_PhosphorExp[19]},
95     {EXP + 19, EXP_RATE, NullAnimator, &s_PhosphorExp[20]},
96     {EXP + 20, 100, DoSuicide, &s_PhosphorExp[0]}
97 };
98 
99 #define MUSHROOM_RATE 25
100 
101 STATE s_NukeMushroom[] =
102 {
103     {MUSHROOM_CLOUD + 0, MUSHROOM_RATE, NullAnimator, &s_NukeMushroom[1]},
104     {MUSHROOM_CLOUD + 1, MUSHROOM_RATE, NullAnimator, &s_NukeMushroom[2]},
105     {MUSHROOM_CLOUD + 2, MUSHROOM_RATE, NullAnimator, &s_NukeMushroom[3]},
106     {MUSHROOM_CLOUD + 3, MUSHROOM_RATE, NullAnimator, &s_NukeMushroom[4]},
107     {MUSHROOM_CLOUD + 4, MUSHROOM_RATE, NullAnimator, &s_NukeMushroom[5]},
108     {MUSHROOM_CLOUD + 5, MUSHROOM_RATE, NullAnimator, &s_NukeMushroom[6]},
109     {MUSHROOM_CLOUD + 6, MUSHROOM_RATE, NullAnimator, &s_NukeMushroom[7]},
110     {MUSHROOM_CLOUD + 7, MUSHROOM_RATE, NullAnimator, &s_NukeMushroom[8]},
111     {MUSHROOM_CLOUD + 8, MUSHROOM_RATE, NullAnimator, &s_NukeMushroom[9]},
112     {MUSHROOM_CLOUD + 9, MUSHROOM_RATE, NullAnimator, &s_NukeMushroom[10]},
113     {MUSHROOM_CLOUD + 10, MUSHROOM_RATE, NullAnimator, &s_NukeMushroom[11]},
114     {MUSHROOM_CLOUD + 11, MUSHROOM_RATE, NullAnimator, &s_NukeMushroom[12]},
115     {MUSHROOM_CLOUD + 12, MUSHROOM_RATE, NullAnimator, &s_NukeMushroom[13]},
116     {MUSHROOM_CLOUD + 13, MUSHROOM_RATE, NullAnimator, &s_NukeMushroom[14]},
117     {MUSHROOM_CLOUD + 14, MUSHROOM_RATE, NullAnimator, &s_NukeMushroom[15]},
118     {MUSHROOM_CLOUD + 15, MUSHROOM_RATE, NullAnimator, &s_NukeMushroom[16]},
119     {MUSHROOM_CLOUD + 16, MUSHROOM_RATE, NullAnimator, &s_NukeMushroom[17]},
120     {MUSHROOM_CLOUD + 17, MUSHROOM_RATE, NullAnimator, &s_NukeMushroom[18]},
121     {MUSHROOM_CLOUD + 18, MUSHROOM_RATE, NullAnimator, &s_NukeMushroom[19]},
122     {MUSHROOM_CLOUD + 19, 100, DoSuicide, &s_NukeMushroom[0]},
123 };
124 
125 ANIMATOR DoRadiationCloud;
126 
127 #define RADIATION_RATE 16
128 
129 STATE s_RadiationCloud[] =
130 {
131     {RADIATION_CLOUD + 0, RADIATION_RATE, DoRadiationCloud, &s_RadiationCloud[1]},
132     {RADIATION_CLOUD + 1, RADIATION_RATE, DoRadiationCloud, &s_RadiationCloud[2]},
133     {RADIATION_CLOUD + 2, RADIATION_RATE, DoRadiationCloud, &s_RadiationCloud[3]},
134     {RADIATION_CLOUD + 3, RADIATION_RATE, DoRadiationCloud, &s_RadiationCloud[4]},
135     {RADIATION_CLOUD + 4, RADIATION_RATE, DoRadiationCloud, &s_RadiationCloud[5]},
136     {RADIATION_CLOUD + 5, RADIATION_RATE, DoRadiationCloud, &s_RadiationCloud[6]},
137     {RADIATION_CLOUD + 6, RADIATION_RATE, DoRadiationCloud, &s_RadiationCloud[7]},
138     {RADIATION_CLOUD + 7, RADIATION_RATE, DoRadiationCloud, &s_RadiationCloud[8]},
139     {RADIATION_CLOUD + 8, RADIATION_RATE, DoRadiationCloud, &s_RadiationCloud[9]},
140     {RADIATION_CLOUD + 9, RADIATION_RATE, DoRadiationCloud, &s_RadiationCloud[10]},
141     {RADIATION_CLOUD + 10, RADIATION_RATE, DoRadiationCloud, &s_RadiationCloud[11]},
142     {RADIATION_CLOUD + 11, RADIATION_RATE, DoRadiationCloud, &s_RadiationCloud[12]},
143     {RADIATION_CLOUD + 12, RADIATION_RATE, DoRadiationCloud, &s_RadiationCloud[13]},
144     {RADIATION_CLOUD + 13, RADIATION_RATE, DoRadiationCloud, &s_RadiationCloud[14]},
145     {RADIATION_CLOUD + 14, RADIATION_RATE, DoRadiationCloud, &s_RadiationCloud[15]},
146     {RADIATION_CLOUD + 15, RADIATION_RATE, DoRadiationCloud, &s_RadiationCloud[16]},
147     {RADIATION_CLOUD + 16, RADIATION_RATE, DoRadiationCloud, &s_RadiationCloud[17]},
148     {RADIATION_CLOUD + 17, RADIATION_RATE, DoRadiationCloud, &s_RadiationCloud[18]},
149     {RADIATION_CLOUD + 18, RADIATION_RATE, DoRadiationCloud, &s_RadiationCloud[19]},
150     {RADIATION_CLOUD + 19, 100, DoSuicide, &s_RadiationCloud[0]},
151 };
152 
153 #define CHEMBOMB_FRAMES 1
154 #define CHEMBOMB_R0 3038
155 #define CHEMBOMB_R1 CHEMBOMB_R0 + (CHEMBOMB_FRAMES * 1)
156 #define CHEMBOMB_R2 CHEMBOMB_R0 + (CHEMBOMB_FRAMES * 2)
157 #define CHEMBOMB_R3 CHEMBOMB_R0 + (CHEMBOMB_FRAMES * 3)
158 #define CHEMBOMB_R4 CHEMBOMB_R0 + (CHEMBOMB_FRAMES * 4)
159 
160 #define CHEMBOMB CHEMBOMB_R0
161 #define CHEMBOMB_RATE 8
162 ANIMATOR DoChemBomb;
163 
164 STATE s_ChemBomb[5] =
165 {
166     {CHEMBOMB_R0 + 0, CHEMBOMB_RATE, DoChemBomb, &s_ChemBomb[1]},
167     {CHEMBOMB_R1 + 0, CHEMBOMB_RATE, DoChemBomb, &s_ChemBomb[2]},
168     {CHEMBOMB_R2 + 0, CHEMBOMB_RATE, DoChemBomb, &s_ChemBomb[3]},
169     {CHEMBOMB_R3 + 0, CHEMBOMB_RATE, DoChemBomb, &s_ChemBomb[4]},
170     {CHEMBOMB_R4 + 0, CHEMBOMB_RATE, DoChemBomb, &s_ChemBomb[0]},
171 };
172 
173 
174 #define CALTROPS_FRAMES 1
175 #define CALTROPS_R0 CALTROPS-1
176 
177 #define CALTROPS_RATE 8
178 
179 ANIMATOR DoCaltrops, DoCaltropsStick;
180 
181 STATE s_Caltrops[] =
182 {
183     {CALTROPS_R0 + 0, CALTROPS_RATE, DoCaltrops, &s_Caltrops[1]},
184     {CALTROPS_R0 + 1, CALTROPS_RATE, DoCaltrops, &s_Caltrops[2]},
185     {CALTROPS_R0 + 2, CALTROPS_RATE, DoCaltrops, &s_Caltrops[0]},
186 };
187 
188 STATE s_CaltropsStick[] =
189 {
190     {CALTROPS_R0 + 2, CALTROPS_RATE, DoCaltropsStick, &s_CaltropsStick[0]},
191 };
192 
193 //////////////////////
194 //
195 // CAPTURE FLAG
196 //
197 //////////////////////
198 
199 ANIMATOR DoFlag, DoCarryFlag, DoCarryFlagNoDet;
200 
201 #undef FLAG
202 #define FLAG 2520
203 #define FLAG_RATE 16
204 
205 STATE s_CarryFlag[] =
206 {
207     {FLAG + 0, FLAG_RATE, DoCarryFlag, &s_CarryFlag[1]},
208     {FLAG + 1, FLAG_RATE, DoCarryFlag, &s_CarryFlag[2]},
209     {FLAG + 2, FLAG_RATE, DoCarryFlag, &s_CarryFlag[0]}
210 };
211 
212 STATE s_CarryFlagNoDet[] =
213 {
214     {FLAG + 0, FLAG_RATE, DoCarryFlagNoDet, &s_CarryFlagNoDet[1]},
215     {FLAG + 1, FLAG_RATE, DoCarryFlagNoDet, &s_CarryFlagNoDet[2]},
216     {FLAG + 2, FLAG_RATE, DoCarryFlagNoDet, &s_CarryFlagNoDet[0]}
217 };
218 
219 STATE s_Flag[] =
220 {
221     {FLAG + 0, FLAG_RATE, DoFlag, &s_Flag[1]},
222     {FLAG + 1, FLAG_RATE, DoFlag, &s_Flag[2]},
223     {FLAG + 2, FLAG_RATE, DoFlag, &s_Flag[0]}
224 };
225 
226 #define PHOSPHORUS_RATE 8
227 ANIMATOR DoPhosphorus;
228 
229 STATE s_Phosphorus[] =
230 {
231     {PHOSPHORUS + 0, PHOSPHORUS_RATE, DoPhosphorus, &s_Phosphorus[1]},
232     {PHOSPHORUS + 1, PHOSPHORUS_RATE, DoPhosphorus, &s_Phosphorus[0]},
233 };
234 
235 ANIMATOR DoBloodSpray;
236 
237 #define CHUNK1 1685
238 STATE s_BloodSprayChunk[] =
239 {
240     {CHUNK1 + 0, 8, DoBloodSpray, &s_BloodSprayChunk[1]},
241     {CHUNK1 + 1, 8, DoBloodSpray, &s_BloodSprayChunk[2]},
242     {CHUNK1 + 2, 8, DoBloodSpray, &s_BloodSprayChunk[3]},
243     {CHUNK1 + 3, 8, DoBloodSpray, &s_BloodSprayChunk[4]},
244     {CHUNK1 + 4, 8, DoBloodSpray, &s_BloodSprayChunk[5]},
245     {CHUNK1 + 5, 8, DoBloodSpray, &s_BloodSprayChunk[0]},
246 };
247 
248 ANIMATOR DoWallBloodDrip;
249 
250 #define DRIP 1566
251 STATE s_BloodSprayDrip[] =
252 {
253     {DRIP + 0, PHOSPHORUS_RATE, DoWallBloodDrip, &s_BloodSprayDrip[1]},
254     {DRIP + 1, PHOSPHORUS_RATE, DoWallBloodDrip, &s_BloodSprayDrip[2]},
255     {DRIP + 2, PHOSPHORUS_RATE, DoWallBloodDrip, &s_BloodSprayDrip[0]},
256 };
257 
258 /////////////////////////////////////////////////////////////////////////////////////////////
259 
260 int
DoWallBloodDrip(short SpriteNum)261 DoWallBloodDrip(short SpriteNum)
262 {
263     SPRITEp sp = &sprite[SpriteNum];
264     USERp u = User[SpriteNum];
265 
266     //sp->z += (300+RANDOM_RANGE(2300)) >> 1;
267 
268     // sy & sz are the ceiling and floor of the sector you are sliding down
269     if (u->sz != u->sy)
270     {
271         // if you are between the ceiling and floor fall fast
272         if (sp->z > u->sy && sp->z < u->sz)
273         {
274             sp->zvel += 300;
275             sp->z += sp->zvel;
276         }
277         else
278         {
279             sp->zvel = (300+RANDOM_RANGE(2300)) >> 1;
280             sp->z += sp->zvel;
281         }
282     }
283     else
284     {
285         sp->zvel = (300+RANDOM_RANGE(2300)) >> 1;
286         sp->z += sp->zvel;
287     }
288 
289     if (sp->z >= u->loz)
290     {
291         sp->z = u->loz;
292         SpawnFloorSplash(SpriteNum);
293         KillSprite(SpriteNum);
294         return 0;
295     }
296 
297     return 0;
298 }
299 
300 void
SpawnMidSplash(short SpriteNum)301 SpawnMidSplash(short SpriteNum)
302 {
303     SPRITEp sp = &sprite[SpriteNum];
304     USERp u = User[SpriteNum];
305     SPRITEp np;
306     USERp nu;
307     short New;
308 
309     New = SpawnSprite(STAT_MISSILE, GOREDrip, s_GoreSplash, sp->sectnum,
310                       sp->x, sp->y, SPRITEp_MID(sp), sp->ang, 0);
311 
312     np = &sprite[New];
313     nu = User[New];
314 
315     //SetOwner(Weapon, New);
316     np->shade = -12;
317     np->xrepeat = 70-RANDOM_RANGE(20);
318     np->yrepeat = 70-RANDOM_RANGE(20);
319     nu->ox = u->ox;
320     nu->oy = u->oy;
321     nu->oz = u->oz;
322     SET(np->cstat, CSTAT_SPRITE_YCENTER);
323     RESET(np->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
324 
325     if (RANDOM_P2(1024) < 512)
326         SET(np->cstat, CSTAT_SPRITE_XFLIP);
327 
328     nu->xchange = 0;
329     nu->ychange = 0;
330     nu->zchange = 0;
331 
332     if (TEST(u->Flags, SPR_UNDERWATER))
333         SET(nu->Flags, SPR_UNDERWATER);
334 }
335 
336 void
SpawnFloorSplash(short SpriteNum)337 SpawnFloorSplash(short SpriteNum)
338 {
339     SPRITEp sp = &sprite[SpriteNum];
340     USERp u = User[SpriteNum];
341     SPRITEp np;
342     USERp nu;
343     short New;
344 
345     New = SpawnSprite(STAT_MISSILE, GOREDrip, s_GoreFloorSplash, sp->sectnum,
346                       sp->x, sp->y, sp->z, sp->ang, 0);
347 
348     np = &sprite[New];
349     nu = User[New];
350 
351     //SetOwner(Weapon, New);
352     np->shade = -12;
353     np->xrepeat = 70-RANDOM_RANGE(20);
354     np->yrepeat = 70-RANDOM_RANGE(20);
355     nu->ox = u->ox;
356     nu->oy = u->oy;
357     nu->oz = u->oz;
358     SET(np->cstat, CSTAT_SPRITE_YCENTER);
359     RESET(np->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
360 
361     if (RANDOM_P2(1024) < 512)
362         SET(np->cstat, CSTAT_SPRITE_XFLIP);
363 
364     nu->xchange = 0;
365     nu->ychange = 0;
366     nu->zchange = 0;
367 
368     if (TEST(u->Flags, SPR_UNDERWATER))
369         SET(nu->Flags, SPR_UNDERWATER);
370 }
371 
372 
373 int
DoBloodSpray(int16_t Weapon)374 DoBloodSpray(int16_t Weapon)
375 {
376     SPRITEp sp = &sprite[Weapon];
377     USERp u = User[Weapon];
378     int cz,fz;
379 
380     if (TEST(u->Flags, SPR_UNDERWATER))
381     {
382         ScaleSpriteVector(Weapon, 50000);
383 
384         u->Counter += 20;  // These are STAT_SKIIP4 now, so * 2
385         u->zchange += u->Counter;
386     }
387     else
388     {
389         u->Counter += 20;
390         u->zchange += u->Counter;
391     }
392 
393     if (sp->xvel <= 2)
394     {
395         // special stuff for blood worm
396         sp->z += (u->zchange >> 1);
397 
398         getzsofslope(sp->sectnum, sp->x, sp->y, &cz, &fz);
399         // pretend like we hit a sector
400         if (sp->z >= fz)
401         {
402             sp->z = fz;
403             SpawnFloorSplash(Weapon);
404             KillSprite((short) Weapon);
405             return TRUE;
406         }
407     }
408     else
409     {
410         u->ret = move_missile(Weapon, u->xchange, u->ychange, u->zchange,
411                               u->ceiling_dist, u->floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS);
412     }
413 
414 
415     MissileHitDiveArea(Weapon);
416 
417     if (u->ret)
418     {
419         switch (TEST(u->ret, HIT_MASK))
420         {
421         case HIT_PLAX_WALL:
422             KillSprite(Weapon);
423             return TRUE;
424         case HIT_SPRITE:
425         {
426             short wall_ang;
427             short hit_sprite = NORM_SPRITE(u->ret);
428             SPRITEp hsp = &sprite[hit_sprite];
429 
430             if (TEST(hsp->cstat, CSTAT_SPRITE_ALIGNMENT_WALL))
431             {
432                 wall_ang = NORM_ANGLE(hsp->ang);
433                 SpawnMidSplash(Weapon);
434                 QueueWallBlood(Weapon, hsp->ang);
435                 WallBounce(Weapon, wall_ang);
436                 ScaleSpriteVector(Weapon, 32000);
437             }
438             else
439             {
440                 u->xchange = u->ychange = 0;
441                 SpawnMidSplash(Weapon);
442                 QueueWallBlood(Weapon, hsp->ang);
443                 KillSprite((short) Weapon);
444                 return TRUE;
445             }
446 
447 
448             break;
449         }
450 
451         case HIT_WALL:
452         {
453             short hit_wall, nw, wall_ang;
454             WALLp wph;
455             short wb;
456 
457             hit_wall = NORM_WALL(u->ret);
458             wph = &wall[hit_wall];
459 
460             if (wph->lotag == TAG_WALL_BREAK)
461             {
462                 HitBreakWall(wph, sp->x, sp->y, sp->z, sp->ang, u->ID);
463                 u->ret = 0;
464                 break;
465             }
466 
467 
468             nw = wall[hit_wall].point2;
469             wall_ang = NORM_ANGLE(getangle(wall[nw].x - wph->x, wall[nw].y - wph->y) + 512);
470 
471             SpawnMidSplash(Weapon);
472             wb = QueueWallBlood(Weapon, NORM_ANGLE(wall_ang+1024));
473 
474             if (wb < 0)
475             {
476                 KillSprite(Weapon);
477                 return 0;
478             }
479             else
480             {
481                 if (FAF_Sector(sprite[wb].sectnum) || FAF_ConnectArea(sprite[wb].sectnum))
482                 {
483                     KillSprite(Weapon);
484                     return 0;
485                 }
486 
487                 sp->xvel = sp->yvel = u->xchange = u->ychange = 0;
488                 sp->xrepeat = sp->yrepeat = 70 - RANDOM_RANGE(25);
489                 sp->x = sprite[wb].x;
490                 sp->y = sprite[wb].y;
491 
492                 // !FRANK! bit of a hack
493                 // yvel is the hit_wall
494                 if (sprite[wb].yvel >= 0)
495                 {
496                     short wallnum = sprite[wb].yvel;
497 
498                     // sy & sz are the ceiling and floor of the sector you are sliding down
499                     if (wall[wallnum].nextsector >= 0)
500                         getzsofslope(wall[wallnum].nextsector, sp->x, sp->y, &u->sy, &u->sz);
501                     else
502                         u->sy = u->sz; // ceiling and floor are equal - white wall
503                 }
504 
505                 RESET(sp->cstat,CSTAT_SPRITE_INVISIBLE);
506                 ChangeState(Weapon, s_BloodSprayDrip);
507             }
508 
509             //WallBounce(Weapon, wall_ang);
510             //ScaleSpriteVector(Weapon, 32000);
511             break;
512         }
513 
514         case HIT_SECTOR:
515         {
516             // hit floor
517             if (sp->z > DIV2(u->hiz + u->loz))
518             {
519                 if (TEST(u->Flags, SPR_UNDERWATER))
520                     SET(u->Flags, SPR_BOUNCE);  // no bouncing
521                 // underwater
522 
523                 if (u->lo_sectp && SectUser[sp->sectnum] && SectUser[sp->sectnum]->depth)
524                     SET(u->Flags, SPR_BOUNCE);  // no bouncing on
525                 // shallow water
526 
527 #if 0
528                 if (!TEST(u->Flags, SPR_BOUNCE))
529                 {
530                     SpawnFloorSplash(Weapon);
531                     SET(u->Flags, SPR_BOUNCE);
532                     u->ret = 0;
533                     u->Counter = 0;
534                     u->zchange = -u->zchange;
535                     ScaleSpriteVector(Weapon, 32000);   // Was 18000
536                     u->zchange /= 6;
537                 }
538                 else
539 #endif
540                 {
541                     u->xchange = u->ychange = 0;
542                     SpawnFloorSplash(Weapon);
543                     KillSprite((short) Weapon);
544                     return TRUE;
545                 }
546             }
547             else
548             // hit something above
549             {
550                 u->zchange = -u->zchange;
551                 ScaleSpriteVector(Weapon, 32000);       // was 22000
552             }
553             break;
554         }
555         }
556     }
557 
558 
559 
560     // if you haven't bounced or your going slow do some puffs
561     if (!TEST(u->Flags, SPR_BOUNCE | SPR_UNDERWATER))
562     {
563         SPRITEp np;
564         USERp nu;
565         short New;
566 
567         New = SpawnSprite(STAT_MISSILE, GOREDrip, s_BloodSpray, sp->sectnum,
568                           sp->x, sp->y, sp->z, sp->ang, 100);
569 
570         np = &sprite[New];
571         nu = User[New];
572 
573         SetOwner(Weapon, New);
574         np->shade = -12;
575         np->xrepeat = 40-RANDOM_RANGE(30);
576         np->yrepeat = 40-RANDOM_RANGE(30);
577         nu->ox = u->ox;
578         nu->oy = u->oy;
579         nu->oz = u->oz;
580         SET(np->cstat, CSTAT_SPRITE_YCENTER);
581         RESET(np->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
582 
583         if (RANDOM_P2(1024) < 512)
584             SET(np->cstat, CSTAT_SPRITE_XFLIP);
585         if (RANDOM_P2(1024) < 512)
586             SET(np->cstat, CSTAT_SPRITE_YFLIP);
587 
588         nu->xchange = u->xchange;
589         nu->ychange = u->ychange;
590         nu->zchange = u->zchange;
591 
592         ScaleSpriteVector(New, 20000);
593 
594         if (TEST(u->Flags, SPR_UNDERWATER))
595             SET(nu->Flags, SPR_UNDERWATER);
596     }
597 
598     return FALSE;
599 }
600 
601 
602 int
DoPhosphorus(int16_t Weapon)603 DoPhosphorus(int16_t Weapon)
604 {
605     SPRITEp sp = &sprite[Weapon];
606     USERp u = User[Weapon];
607 
608     if (TEST(u->Flags, SPR_UNDERWATER))
609     {
610         ScaleSpriteVector(Weapon, 50000);
611 
612         u->Counter += 20*2;
613         u->zchange += u->Counter;
614     }
615     else
616     {
617         u->Counter += 20*2;
618         u->zchange += u->Counter;
619     }
620 
621     u->ret = move_missile(Weapon, u->xchange, u->ychange, u->zchange,
622                           u->ceiling_dist, u->floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS*2);
623 
624     MissileHitDiveArea(Weapon);
625 
626     if (TEST(u->Flags, SPR_UNDERWATER) && (RANDOM_P2(1024 << 4) >> 4) < 256)
627         SpawnBubble(Weapon);
628 
629     if (u->ret)
630     {
631         switch (TEST(u->ret, HIT_MASK))
632         {
633         case HIT_PLAX_WALL:
634             KillSprite(Weapon);
635             return TRUE;
636         case HIT_SPRITE:
637         {
638             short wall_ang;
639             short hit_sprite = -2;
640             SPRITEp hsp;
641             USERp hu;
642 
643 
644             hit_sprite = NORM_SPRITE(u->ret);
645             hsp = &sprite[hit_sprite];
646             hu = User[hit_sprite];
647 
648             if (TEST(hsp->cstat, CSTAT_SPRITE_ALIGNMENT_WALL))
649             {
650                 wall_ang = NORM_ANGLE(hsp->ang);
651                 WallBounce(Weapon, wall_ang);
652                 ScaleSpriteVector(Weapon, 32000);
653             }
654             else
655             {
656                 if (TEST(hsp->extra, SPRX_BURNABLE))
657                 {
658                     if (!hu)
659                         hu = SpawnUser(hit_sprite, hsp->picnum, NULL);
660                     SpawnFireballExp(Weapon);
661                     if (hu)
662                         SpawnFireballFlames(Weapon, hit_sprite);
663                     DoFlamesDamageTest(Weapon);
664                 }
665                 u->xchange = u->ychange = 0;
666                 KillSprite((short) Weapon);
667                 return TRUE;
668             }
669 
670 
671             break;
672         }
673 
674         case HIT_WALL:
675         {
676             short hit_wall, nw, wall_ang;
677             WALLp wph;
678 
679             hit_wall = NORM_WALL(u->ret);
680             wph = &wall[hit_wall];
681 
682             if (wph->lotag == TAG_WALL_BREAK)
683             {
684                 HitBreakWall(wph, sp->x, sp->y, sp->z, sp->ang, u->ID);
685                 u->ret = 0;
686                 break;
687             }
688 
689 
690             nw = wall[hit_wall].point2;
691             wall_ang = NORM_ANGLE(getangle(wall[nw].x - wph->x, wall[nw].y - wph->y) + 512);
692 
693             WallBounce(Weapon, wall_ang);
694             ScaleSpriteVector(Weapon, 32000);
695             break;
696         }
697 
698         case HIT_SECTOR:
699         {
700             SWBOOL did_hit_wall;
701 
702             if (SlopeBounce(Weapon, &did_hit_wall))
703             {
704                 if (did_hit_wall)
705                 {
706                     // hit a wall
707                     ScaleSpriteVector(Weapon, 28000);
708                     u->ret = 0;
709                     u->Counter = 0;
710                 }
711                 else
712                 {
713                     // hit a sector
714                     if (sp->z > DIV2(u->hiz + u->loz))
715                     {
716                         // hit a floor
717                         if (!TEST(u->Flags, SPR_BOUNCE))
718                         {
719                             SET(u->Flags, SPR_BOUNCE);
720                             ScaleSpriteVector(Weapon, 32000);       // was 18000
721                             u->zchange /= 6;
722                             u->ret = 0;
723                             u->Counter = 0;
724                         }
725                         else
726                         {
727                             u->xchange = u->ychange = 0;
728                             SpawnFireballExp(Weapon);
729                             KillSprite((short) Weapon);
730                             return TRUE;
731                         }
732                     }
733                     else
734                     {
735                         // hit a ceiling
736                         ScaleSpriteVector(Weapon, 32000);   // was 22000
737                     }
738                 }
739             }
740             else
741             {
742                 // hit floor
743                 if (sp->z > DIV2(u->hiz + u->loz))
744                 {
745                     if (TEST(u->Flags, SPR_UNDERWATER))
746                         SET(u->Flags, SPR_BOUNCE);  // no bouncing
747                     // underwater
748 
749                     if (u->lo_sectp && SectUser[sp->sectnum] && SectUser[sp->sectnum]->depth)
750                         SET(u->Flags, SPR_BOUNCE);  // no bouncing on
751                     // shallow water
752 
753                     if (!TEST(u->Flags, SPR_BOUNCE))
754                     {
755                         SET(u->Flags, SPR_BOUNCE);
756                         u->ret = 0;
757                         u->Counter = 0;
758                         u->zchange = -u->zchange;
759                         ScaleSpriteVector(Weapon, 32000);   // Was 18000
760                         u->zchange /= 6;
761                     }
762                     else
763                     {
764                         u->xchange = u->ychange = 0;
765                         SpawnFireballExp(Weapon);
766                         KillSprite((short) Weapon);
767                         return TRUE;
768                     }
769                 }
770                 else
771                 // hit something above
772                 {
773                     u->zchange = -u->zchange;
774                     ScaleSpriteVector(Weapon, 32000);       // was 22000
775                 }
776             }
777             break;
778         }
779         }
780     }
781 
782 
783 
784     // if you haven't bounced or your going slow do some puffs
785     if (!TEST(u->Flags, SPR_BOUNCE | SPR_UNDERWATER) && !TEST(sp->cstat, CSTAT_SPRITE_INVISIBLE))
786     {
787         SPRITEp np;
788         USERp nu;
789         short New;
790 
791         New = SpawnSprite(STAT_SKIP4, PUFF, s_PhosphorExp, sp->sectnum,
792                           sp->x, sp->y, sp->z, sp->ang, 100);
793 
794         np = &sprite[New];
795         nu = User[New];
796 
797         np->hitag = LUMINOUS;           // Always full brightness
798         SetOwner(Weapon, New);
799         np->shade = -40;
800         np->xrepeat = 12 + RANDOM_RANGE(10);
801         np->yrepeat = 12 + RANDOM_RANGE(10);
802         nu->ox = u->ox;
803         nu->oy = u->oy;
804         nu->oz = u->oz;
805         SET(np->cstat, CSTAT_SPRITE_YCENTER);
806         RESET(np->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
807 
808         if (RANDOM_P2(1024) < 512)
809             SET(np->cstat, CSTAT_SPRITE_XFLIP);
810         if (RANDOM_P2(1024) < 512)
811             SET(np->cstat, CSTAT_SPRITE_YFLIP);
812 
813         nu->xchange = u->xchange;
814         nu->ychange = u->ychange;
815         nu->zchange = u->zchange;
816 
817         nu->spal = np->pal = PALETTE_PLAYER3;   // RED
818 
819         ScaleSpriteVector(New, 20000);
820 
821         if (TEST(u->Flags, SPR_UNDERWATER))
822             SET(nu->Flags, SPR_UNDERWATER);
823     }
824 
825     return FALSE;
826 }
827 
828 int
DoChemBomb(int16_t Weapon)829 DoChemBomb(int16_t Weapon)
830 {
831     SPRITEp sp = &sprite[Weapon];
832     USERp u = User[Weapon];
833 
834     if (TEST(u->Flags, SPR_UNDERWATER))
835     {
836         ScaleSpriteVector(Weapon, 50000);
837 
838         u->Counter += 20;
839         u->zchange += u->Counter;
840     }
841     else
842     {
843         u->Counter += 20;
844         u->zchange += u->Counter;
845     }
846 
847     u->ret = move_missile(Weapon, u->xchange, u->ychange, u->zchange,
848                           u->ceiling_dist, u->floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS);
849 
850     MissileHitDiveArea(Weapon);
851 
852     if (TEST(u->Flags, SPR_UNDERWATER) && (RANDOM_P2(1024 << 4) >> 4) < 256)
853         SpawnBubble(Weapon);
854 
855     if (u->ret)
856     {
857         switch (TEST(u->ret, HIT_MASK))
858         {
859         case HIT_PLAX_WALL:
860             KillSprite(Weapon);
861             return TRUE;
862         case HIT_SPRITE:
863         {
864             short wall_ang;
865             short hit_sprite;
866             SPRITEp hsp;
867 
868             if (!TEST(sp->cstat, CSTAT_SPRITE_INVISIBLE))
869                 PlaySound(DIGI_CHEMBOUNCE, &sp->x, &sp->y, &sp->z, v3df_dontpan);
870 
871             hit_sprite = NORM_SPRITE(u->ret);
872             hsp = &sprite[hit_sprite];
873 
874             if (TEST(hsp->cstat, CSTAT_SPRITE_ALIGNMENT_WALL))
875             {
876                 wall_ang = NORM_ANGLE(hsp->ang);
877                 WallBounce(Weapon, wall_ang);
878                 ScaleSpriteVector(Weapon, 32000);
879             }
880             else
881             {
882                 // Canister pops when first smoke starts out
883                 if (u->WaitTics == CHEMTICS && !TEST(sp->cstat, CSTAT_SPRITE_INVISIBLE))
884                 {
885                     PlaySound(DIGI_GASPOP, &sp->x, &sp->y, &sp->z, v3df_dontpan | v3df_doppler);
886                     PlaySound(DIGI_CHEMGAS, &sp->x, &sp->y, &sp->z, v3df_dontpan | v3df_doppler);
887                     Set3DSoundOwner(Weapon);
888                 }
889                 u->xchange = u->ychange = 0;
890                 u->WaitTics -= (MISSILEMOVETICS * 2);
891                 if (u->WaitTics <= 0)
892                     KillSprite((short) Weapon);
893                 return TRUE;
894             }
895 
896 
897             break;
898         }
899 
900         case HIT_WALL:
901         {
902             short hit_wall, nw, wall_ang;
903             WALLp wph;
904 
905             hit_wall = NORM_WALL(u->ret);
906             wph = &wall[hit_wall];
907 
908             if (wph->lotag == TAG_WALL_BREAK)
909             {
910                 HitBreakWall(wph, sp->x, sp->y, sp->z, sp->ang, u->ID);
911                 u->ret = 0;
912                 break;
913             }
914 
915             if (!TEST(sp->cstat, CSTAT_SPRITE_INVISIBLE))
916                 PlaySound(DIGI_CHEMBOUNCE, &sp->x, &sp->y, &sp->z, v3df_dontpan);
917 
918             nw = wall[hit_wall].point2;
919             wall_ang = NORM_ANGLE(getangle(wall[nw].x - wph->x, wall[nw].y - wph->y) + 512);
920 
921             WallBounce(Weapon, wall_ang);
922             ScaleSpriteVector(Weapon, 32000);
923             break;
924         }
925 
926         case HIT_SECTOR:
927         {
928             SWBOOL did_hit_wall;
929 
930             if (SlopeBounce(Weapon, &did_hit_wall))
931             {
932                 if (did_hit_wall)
933                 {
934                     // hit a wall
935                     ScaleSpriteVector(Weapon, 28000);
936                     u->ret = 0;
937                     u->Counter = 0;
938                 }
939                 else
940                 {
941                     // hit a sector
942                     if (sp->z > DIV2(u->hiz + u->loz))
943                     {
944                         // hit a floor
945                         if (!TEST(u->Flags, SPR_BOUNCE))
946                         {
947                             if (!TEST(sp->cstat, CSTAT_SPRITE_INVISIBLE))
948                                 PlaySound(DIGI_CHEMBOUNCE, &sp->x, &sp->y, &sp->z, v3df_dontpan);
949                             SET(u->Flags, SPR_BOUNCE);
950                             ScaleSpriteVector(Weapon, 32000);       // was 18000
951                             u->zchange /= 6;
952                             u->ret = 0;
953                             u->Counter = 0;
954                         }
955                         else
956                         {
957                             // Canister pops when first smoke starts out
958                             if (u->WaitTics == CHEMTICS && !TEST(sp->cstat, CSTAT_SPRITE_INVISIBLE))
959                             {
960                                 PlaySound(DIGI_GASPOP, &sp->x, &sp->y, &sp->z, v3df_dontpan | v3df_doppler);
961                                 PlaySound(DIGI_CHEMGAS, &sp->x, &sp->y, &sp->z, v3df_dontpan | v3df_doppler);
962                                 Set3DSoundOwner(Weapon);
963                             }
964                             SpawnRadiationCloud(Weapon);
965                             u->xchange = u->ychange = 0;
966                             u->WaitTics -= (MISSILEMOVETICS * 2);
967                             if (u->WaitTics <= 0)
968                                 KillSprite((short) Weapon);
969                             return TRUE;
970                         }
971                     }
972                     else
973                     {
974                         // hit a ceiling
975                         ScaleSpriteVector(Weapon, 32000);   // was 22000
976                     }
977                 }
978             }
979             else
980             {
981                 // hit floor
982                 if (sp->z > DIV2(u->hiz + u->loz))
983                 {
984                     if (TEST(u->Flags, SPR_UNDERWATER))
985                         SET(u->Flags, SPR_BOUNCE);  // no bouncing
986                     // underwater
987 
988                     if (u->lo_sectp && SectUser[sp->sectnum] && SectUser[sp->sectnum]->depth)
989                         SET(u->Flags, SPR_BOUNCE);  // no bouncing on
990                     // shallow water
991 
992                     if (!TEST(u->Flags, SPR_BOUNCE))
993                     {
994                         if (!TEST(sp->cstat, CSTAT_SPRITE_INVISIBLE))
995                             PlaySound(DIGI_CHEMBOUNCE, &sp->x, &sp->y, &sp->z, v3df_dontpan);
996                         SET(u->Flags, SPR_BOUNCE);
997                         u->ret = 0;
998                         u->Counter = 0;
999                         u->zchange = -u->zchange;
1000                         ScaleSpriteVector(Weapon, 32000);   // Was 18000
1001                         u->zchange /= 6;
1002                     }
1003                     else
1004                     {
1005                         // Canister pops when first smoke starts out
1006                         if (u->WaitTics == CHEMTICS && !TEST(sp->cstat, CSTAT_SPRITE_INVISIBLE))
1007                         {
1008                             PlaySound(DIGI_GASPOP, &sp->x, &sp->y, &sp->z, v3df_dontpan | v3df_doppler);
1009                             PlaySound(DIGI_CHEMGAS, &sp->x, &sp->y, &sp->z, v3df_dontpan | v3df_doppler);
1010                             Set3DSoundOwner(Weapon);
1011                         }
1012                         // WeaponMoveHit(Weapon);
1013                         SpawnRadiationCloud(Weapon);
1014                         u->xchange = u->ychange = 0;
1015                         u->WaitTics -= (MISSILEMOVETICS * 2);
1016                         if (u->WaitTics <= 0)
1017                             KillSprite((short) Weapon);
1018                         return TRUE;
1019                     }
1020                 }
1021                 else
1022                 // hit something above
1023                 {
1024                     u->zchange = -u->zchange;
1025                     ScaleSpriteVector(Weapon, 32000);       // was 22000
1026                 }
1027             }
1028             break;
1029         }
1030         }
1031     }
1032 
1033     //if(TEST(sp->cstat, CSTAT_SPRITE_INVISIBLE))
1034     //SpawnRadiationCloud(Weapon);
1035 
1036     // if you haven't bounced or your going slow do some puffs
1037     if (!TEST(u->Flags, SPR_BOUNCE | SPR_UNDERWATER) && !TEST(sp->cstat, CSTAT_SPRITE_INVISIBLE))
1038     {
1039         SPRITEp np;
1040         USERp nu;
1041         short New;
1042 
1043         New = SpawnSprite(STAT_MISSILE, PUFF, s_Puff, sp->sectnum,
1044                           sp->x, sp->y, sp->z, sp->ang, 100);
1045 
1046         np = &sprite[New];
1047         nu = User[New];
1048 
1049         SetOwner(Weapon, New);
1050         np->shade = -40;
1051         np->xrepeat = 40;
1052         np->yrepeat = 40;
1053         nu->ox = u->ox;
1054         nu->oy = u->oy;
1055         nu->oz = u->oz;
1056         // !Frank - dont do translucent
1057         SET(np->cstat, CSTAT_SPRITE_YCENTER);
1058         // SET(np->cstat, CSTAT_SPRITE_YCENTER|CSTAT_SPRITE_TRANSLUCENT);
1059         RESET(np->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
1060 
1061         nu->xchange = u->xchange;
1062         nu->ychange = u->ychange;
1063         nu->zchange = u->zchange;
1064 
1065         nu->spal = np->pal = PALETTE_PLAYER6;
1066 
1067         ScaleSpriteVector(New, 20000);
1068 
1069         if (TEST(u->Flags, SPR_UNDERWATER))
1070             SET(nu->Flags, SPR_UNDERWATER);
1071     }
1072 
1073     return FALSE;
1074 }
1075 
1076 int
DoCaltropsStick(int16_t Weapon)1077 DoCaltropsStick(int16_t Weapon)
1078 {
1079     USERp u = User[Weapon];
1080 
1081     u->Counter = !u->Counter;
1082 
1083     if (u->Counter)
1084         DoFlamesDamageTest(Weapon);
1085 
1086     return 0;
1087 }
1088 
1089 int
DoCaltrops(int16_t Weapon)1090 DoCaltrops(int16_t Weapon)
1091 {
1092     SPRITEp sp = &sprite[Weapon];
1093     USERp u = User[Weapon];
1094 
1095     if (TEST(u->Flags, SPR_UNDERWATER))
1096     {
1097         ScaleSpriteVector(Weapon, 50000);
1098 
1099         u->Counter += 20;
1100         u->zchange += u->Counter;
1101     }
1102     else
1103     {
1104         u->Counter += 70;
1105         u->zchange += u->Counter;
1106     }
1107 
1108     u->ret = move_missile(Weapon, u->xchange, u->ychange, u->zchange,
1109                           u->ceiling_dist, u->floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS);
1110 
1111     MissileHitDiveArea(Weapon);
1112 
1113     if (u->ret)
1114     {
1115         switch (TEST(u->ret, HIT_MASK))
1116         {
1117         case HIT_PLAX_WALL:
1118             KillSprite(Weapon);
1119             return TRUE;
1120         case HIT_SPRITE:
1121         {
1122             short wall_ang;
1123             short hit_sprite;
1124             SPRITEp hsp;
1125 
1126             PlaySound(DIGI_CALTROPS, &sp->x, &sp->y, &sp->z, v3df_dontpan);
1127 
1128             hit_sprite = NORM_SPRITE(u->ret);
1129             hsp = &sprite[hit_sprite];
1130 
1131             if (TEST(hsp->cstat, CSTAT_SPRITE_ALIGNMENT_WALL))
1132             {
1133                 wall_ang = NORM_ANGLE(hsp->ang);
1134                 WallBounce(Weapon, wall_ang);
1135                 ScaleSpriteVector(Weapon, 10000);
1136             }
1137             else
1138             {
1139                 // fall to the ground
1140                 u->xchange = u->ychange = 0;
1141             }
1142 
1143 
1144             break;
1145         }
1146 
1147         case HIT_WALL:
1148         {
1149             short hit_wall, nw, wall_ang;
1150             WALLp wph;
1151 
1152             hit_wall = NORM_WALL(u->ret);
1153             wph = &wall[hit_wall];
1154 
1155             if (wph->lotag == TAG_WALL_BREAK)
1156             {
1157                 HitBreakWall(wph, sp->x, sp->y, sp->z, sp->ang, u->ID);
1158                 u->ret = 0;
1159                 break;
1160             }
1161 
1162             PlaySound(DIGI_CALTROPS, &sp->x, &sp->y, &sp->z, v3df_dontpan);
1163 
1164             nw = wall[hit_wall].point2;
1165             wall_ang = NORM_ANGLE(getangle(wall[nw].x - wph->x, wall[nw].y - wph->y) + 512);
1166 
1167             WallBounce(Weapon, wall_ang);
1168             ScaleSpriteVector(Weapon, 1000);
1169             break;
1170         }
1171 
1172         case HIT_SECTOR:
1173         {
1174             SWBOOL did_hit_wall;
1175 
1176             if (SlopeBounce(Weapon, &did_hit_wall))
1177             {
1178                 if (did_hit_wall)
1179                 {
1180                     // hit a wall
1181                     ScaleSpriteVector(Weapon, 1000);
1182                     u->ret = 0;
1183                     u->Counter = 0;
1184                 }
1185                 else
1186                 {
1187                     // hit a sector
1188                     if (sp->z > DIV2(u->hiz + u->loz))
1189                     {
1190                         // hit a floor
1191                         if (!TEST(u->Flags, SPR_BOUNCE))
1192                         {
1193                             PlaySound(DIGI_CALTROPS, &sp->x, &sp->y, &sp->z, v3df_dontpan);
1194                             SET(u->Flags, SPR_BOUNCE);
1195                             ScaleSpriteVector(Weapon, 1000);        // was 18000
1196                             u->ret = 0;
1197                             u->Counter = 0;
1198                         }
1199                         else
1200                         {
1201                             u->xchange = u->ychange = 0;
1202                             SET(sp->extra, SPRX_BREAKABLE);
1203                             SET(sp->cstat,CSTAT_SPRITE_BREAKABLE);
1204                             ChangeState(Weapon, s_CaltropsStick);
1205                             return TRUE;
1206                         }
1207                     }
1208                     else
1209                     {
1210                         // hit a ceiling
1211                         ScaleSpriteVector(Weapon, 1000);    // was 22000
1212                     }
1213                 }
1214             }
1215             else
1216             {
1217                 // hit floor
1218                 if (sp->z > DIV2(u->hiz + u->loz))
1219                 {
1220                     if (TEST(u->Flags, SPR_UNDERWATER))
1221                         SET(u->Flags, SPR_BOUNCE);  // no bouncing
1222                     // underwater
1223 
1224                     if (u->lo_sectp && SectUser[sp->sectnum] && SectUser[sp->sectnum]->depth)
1225                         SET(u->Flags, SPR_BOUNCE);  // no bouncing on
1226                     // shallow water
1227 
1228                     if (!TEST(u->Flags, SPR_BOUNCE))
1229                     {
1230                         PlaySound(DIGI_CALTROPS, &sp->x, &sp->y, &sp->z, v3df_dontpan);
1231                         SET(u->Flags, SPR_BOUNCE);
1232                         u->ret = 0;
1233                         u->Counter = 0;
1234                         u->zchange = -u->zchange;
1235                         ScaleSpriteVector(Weapon, 1000);    // Was 18000
1236                     }
1237                     else
1238                     {
1239                         u->xchange = u->ychange = 0;
1240                         SET(sp->extra, SPRX_BREAKABLE);
1241                         SET(sp->cstat,CSTAT_SPRITE_BREAKABLE);
1242                         ChangeState(Weapon, s_CaltropsStick);
1243                         return TRUE;
1244                     }
1245                 }
1246                 else
1247                 // hit something above
1248                 {
1249                     u->zchange = -u->zchange;
1250                     ScaleSpriteVector(Weapon, 1000);        // was 22000
1251                 }
1252             }
1253             break;
1254         }
1255         }
1256     }
1257 
1258 
1259     return FALSE;
1260 }
1261 
1262 /////////////////////////////
1263 //
1264 // Deadly green gas clouds
1265 //
1266 /////////////////////////////
1267 int
SpawnRadiationCloud(short SpriteNum)1268 SpawnRadiationCloud(short SpriteNum)
1269 {
1270     SPRITEp sp = &sprite[SpriteNum], np;
1271     USERp u = User[SpriteNum], nu;
1272     short New;
1273 
1274 
1275     if (!MoveSkip4)
1276         return FALSE;
1277 
1278     // This basically works like a MoveSkip8, if one existed
1279 //  u->Counter2 = !u->Counter2;
1280     if (u->ID == MUSHROOM_CLOUD || u->ID == 3121)
1281     {
1282         if ((u->Counter2++) > 16)
1283             u->Counter2 = 0;
1284         if (u->Counter2)
1285             return FALSE;
1286     }
1287     else
1288     {
1289         if ((u->Counter2++) > 2)
1290             u->Counter2 = 0;
1291         if (u->Counter2)
1292             return FALSE;
1293     }
1294 
1295     if (TEST(u->Flags, SPR_UNDERWATER))
1296         return -1;
1297 
1298     New = SpawnSprite(STAT_MISSILE, RADIATION_CLOUD, s_RadiationCloud, sp->sectnum,
1299                       sp->x, sp->y, sp->z - RANDOM_P2(Z(8)), sp->ang, 0);
1300 
1301     np = &sprite[New];
1302     nu = User[New];
1303 
1304     SetOwner(sp->owner, New);
1305     nu->WaitTics = 1 * 120;
1306     np->shade = -40;
1307     np->xrepeat = 32;
1308     np->yrepeat = 32;
1309     np->clipdist = sp->clipdist;
1310     SET(np->cstat, CSTAT_SPRITE_YCENTER);
1311     RESET(np->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
1312     nu->spal = np->pal = PALETTE_PLAYER6;
1313     // Won't take floor palettes
1314     np->hitag = SECTFU_DONT_COPY_PALETTE;
1315 
1316     if (RANDOM_P2(1024) < 512)
1317         SET(np->cstat, CSTAT_SPRITE_XFLIP);
1318     //if (RANDOM_P2(1024) < 512)
1319     //SET(np->cstat, CSTAT_SPRITE_YFLIP);
1320 
1321     np->ang = RANDOM_P2(2048);
1322     np->xvel = RANDOM_P2(32);
1323 
1324     nu->Counter = 0;
1325     nu->Counter2 = 0;
1326 
1327     if (u->ID == MUSHROOM_CLOUD || u->ID == 3121)
1328     {
1329         nu->Radius = 2000;
1330         nu->xchange = (MOVEx(np->xvel>>2, np->ang));
1331         nu->ychange = (MOVEy(np->xvel>>2, np->ang));
1332         np->zvel = Z(1) + RANDOM_P2(Z(2));
1333     }
1334     else
1335     {
1336         nu->xchange = MOVEx(np->xvel, np->ang);
1337         nu->ychange = MOVEy(np->xvel, np->ang);
1338         np->zvel = Z(4) + RANDOM_P2(Z(4));
1339         nu->Radius = 4000;
1340     }
1341 
1342     return FALSE;
1343 }
1344 
1345 int
DoRadiationCloud(short SpriteNum)1346 DoRadiationCloud(short SpriteNum)
1347 {
1348     SPRITEp sp = &sprite[SpriteNum];
1349     USERp u = User[SpriteNum];
1350 
1351     sp->z -= sp->zvel;
1352 
1353     sp->x += u->xchange;
1354     sp->y += u->ychange;
1355 
1356     if (u->ID)
1357     {
1358         DoFlamesDamageTest(SpriteNum);
1359     }
1360 
1361     return FALSE;
1362 }
1363 
1364 //////////////////////////////////////////////
1365 //
1366 // Inventory Chemical Bombs
1367 //
1368 //////////////////////////////////////////////
1369 int
PlayerInitChemBomb(PLAYERp pp)1370 PlayerInitChemBomb(PLAYERp pp)
1371 {
1372     USERp u = User[pp->PlayerSprite];
1373     USERp wu;
1374     SPRITEp wp;
1375     int nx, ny, nz;
1376     short w;
1377     short oclipdist;
1378 
1379 
1380     PlaySound(DIGI_THROW, &pp->posx, &pp->posy, &pp->posz, v3df_dontpan | v3df_doppler);
1381 
1382     if (pp->cursectnum < 0)
1383         return 0;
1384 
1385     nx = pp->posx;
1386     ny = pp->posy;
1387     nz = pp->posz + pp->bob_z + Z(8);
1388 
1389     // Spawn a shot
1390     // Inserting and setting up variables
1391     w = SpawnSprite(STAT_MISSILE, CHEMBOMB, s_ChemBomb, pp->cursectnum,
1392                     nx, ny, nz, fix16_to_int(pp->q16ang), CHEMBOMB_VELOCITY);
1393 
1394     wp = &sprite[w];
1395     wu = User[w];
1396 
1397     // don't throw it as far if crawling
1398     if (TEST(pp->Flags, PF_CRAWLING))
1399     {
1400         wp->xvel -= DIV4(wp->xvel);
1401     }
1402 
1403 //    wu->RotNum = 5;
1404 //    NewStateGroup(w, &sg_ChemBomb);
1405     SET(wu->Flags, SPR_XFLIP_TOGGLE);
1406 
1407     SetOwner(pp->PlayerSprite, w);
1408     wp->yrepeat = 32;
1409     wp->xrepeat = 32;
1410     wp->shade = -15;
1411     wu->WeaponNum = u->WeaponNum;
1412     wu->Radius = 200;
1413     wu->ceiling_dist = Z(3);
1414     wu->floor_dist = Z(3);
1415     wu->Counter = 0;
1416     SET(wp->cstat, CSTAT_SPRITE_YCENTER);
1417     SET(wp->cstat, CSTAT_SPRITE_BLOCK);
1418 
1419     if (TEST(pp->Flags, PF_DIVING) || SpriteInUnderwaterArea(wp))
1420         SET(wu->Flags, SPR_UNDERWATER);
1421 
1422     wp->zvel = ((100 - fix16_to_int(pp->q16horiz)) * HORIZ_MULT);
1423 
1424     // //DSPRINTF(ds,"horiz %d, ho %d, ho+ho %d",fix16_to_int(pp->q16horiz), fix16_to_int(pp->q16horizoff),
1425     // fix16_to_int(pp->q16horizoff + pp->q16horiz));
1426     // MONO_PRINT(ds);
1427 
1428     oclipdist = pp->SpriteP->clipdist;
1429     pp->SpriteP->clipdist = 0;
1430     wp->clipdist = 0;
1431 
1432 //    wp->ang = NORM_ANGLE(wp->ang - 512);
1433 //    HelpMissileLateral(w, 800);
1434 //    wp->ang = NORM_ANGLE(wp->ang + 512);
1435 
1436     MissileSetPos(w, DoChemBomb, 1000);
1437 
1438     pp->SpriteP->clipdist = oclipdist;
1439     wp->clipdist = 80L >> 2;
1440 
1441     wu->xchange = MOVEx(wp->xvel, wp->ang);
1442     wu->ychange = MOVEy(wp->xvel, wp->ang);
1443     wu->zchange = wp->zvel >> 1;
1444 
1445     // adjust xvel according to player velocity
1446     wu->xchange += pp->xvect >> 14;
1447     wu->ychange += pp->yvect >> 14;
1448 
1449     // Smoke will come out for this many seconds
1450     wu->WaitTics = CHEMTICS;
1451 
1452     return 0;
1453 }
1454 
1455 int
InitSpriteChemBomb(int16_t SpriteNum)1456 InitSpriteChemBomb(int16_t SpriteNum)
1457 {
1458     USERp u = User[SpriteNum];
1459     USERp wu;
1460     SPRITEp sp = &sprite[SpriteNum], wp;
1461     int nx, ny, nz;
1462     short w;
1463 
1464 
1465     PlaySound(DIGI_THROW, &sp->x, &sp->y, &sp->z, v3df_dontpan | v3df_doppler);
1466 
1467     nx = sp->x;
1468     ny = sp->y;
1469     nz = sp->z;
1470 
1471     // Spawn a shot
1472     // Inserting and setting up variables
1473     w = SpawnSprite(STAT_MISSILE, CHEMBOMB, s_ChemBomb, sp->sectnum,
1474                     nx, ny, nz, sp->ang, CHEMBOMB_VELOCITY);
1475 
1476     wp = &sprite[w];
1477     wu = User[w];
1478 
1479     SET(wu->Flags, SPR_XFLIP_TOGGLE);
1480 
1481     SetOwner(SpriteNum, w);
1482     wp->yrepeat = 32;
1483     wp->xrepeat = 32;
1484     wp->shade = -15;
1485     wu->WeaponNum = u->WeaponNum;
1486     wu->Radius = 200;
1487     wu->ceiling_dist = Z(3);
1488     wu->floor_dist = Z(3);
1489     wu->Counter = 0;
1490     SET(wp->cstat, CSTAT_SPRITE_YCENTER);
1491     SET(wp->cstat, CSTAT_SPRITE_BLOCK);
1492 
1493     wp->zvel = ((-100 - RANDOM_RANGE(100)) * HORIZ_MULT);
1494 
1495     wp->clipdist = 80L >> 2;
1496 
1497     wu->xchange = MOVEx(wp->xvel, wp->ang);
1498     wu->ychange = MOVEy(wp->xvel, wp->ang);
1499     wu->zchange = wp->zvel >> 1;
1500 
1501     // Smoke will come out for this many seconds
1502     wu->WaitTics = CHEMTICS;
1503 
1504     return 0;
1505 }
1506 
1507 
1508 int
InitChemBomb(short SpriteNum)1509 InitChemBomb(short SpriteNum)
1510 {
1511     SPRITEp sp = &sprite[SpriteNum];
1512     USERp u = User[SpriteNum];
1513     USERp wu;
1514     SPRITEp wp;
1515     int nx, ny, nz;
1516     short w;
1517 
1518 
1519 // Need to make it take away from inventory weapon list
1520 //    PlayerUpdateAmmo(pp, u->WeaponNum, -1);
1521 
1522     nx = sp->x;
1523     ny = sp->y;
1524     nz = sp->z;
1525 
1526     // Spawn a shot
1527     // Inserting and setting up variables
1528     w = SpawnSprite(STAT_MISSILE, MUSHROOM_CLOUD, s_ChemBomb, sp->sectnum,
1529                     nx, ny, nz, sp->ang, CHEMBOMB_VELOCITY);
1530 
1531     wp = &sprite[w];
1532     wu = User[w];
1533 
1534 //    wu->RotNum = 5;
1535 //    NewStateGroup(w, &sg_ChemBomb);
1536     SET(wu->Flags, SPR_XFLIP_TOGGLE);
1537 
1538 //    SetOwner(SpriteNum, w);
1539 //    SetOwner(-1, w);
1540     SetOwner(sp->owner, w); // !FRANK
1541     wp->yrepeat = 32;
1542     wp->xrepeat = 32;
1543     wp->shade = -15;
1544     wu->Radius = 200;
1545     wu->ceiling_dist = Z(3);
1546     wu->floor_dist = Z(3);
1547     wu->Counter = 0;
1548     SET(wp->cstat, CSTAT_SPRITE_YCENTER | CSTAT_SPRITE_INVISIBLE);      // Make nuke radiation
1549     // invis.
1550     RESET(wp->cstat, CSTAT_SPRITE_BLOCK);
1551 
1552     if (SpriteInUnderwaterArea(wp))
1553         SET(wu->Flags, SPR_UNDERWATER);
1554 
1555     wp->zvel = ((-100 - RANDOM_RANGE(100)) * HORIZ_MULT);
1556     wp->clipdist = 0;
1557 
1558     if (u->ID == MUSHROOM_CLOUD || u->ID == 3121 || u->ID == SUMO_RUN_R0) // 3121 == GRENADE_EXP
1559     {
1560         wu->xchange = 0;
1561         wu->ychange = 0;
1562         wu->zchange = 0;
1563         wp->xvel = wp->yvel = wp->zvel = 0;
1564         // Smoke will come out for this many seconds
1565         wu->WaitTics = 40*120;
1566     }
1567     else
1568     {
1569         wu->xchange = MOVEx(wp->xvel, wp->ang);
1570         wu->ychange = MOVEy(wp->xvel, wp->ang);
1571         wu->zchange = wp->zvel >> 1;
1572         // Smoke will come out for this many seconds
1573         wu->WaitTics = 3*120;
1574     }
1575 
1576 
1577     return 0;
1578 }
1579 
1580 //////////////////////////////////////////////
1581 //
1582 // Inventory Flash Bombs
1583 //
1584 //////////////////////////////////////////////
1585 int
PlayerInitFlashBomb(PLAYERp pp)1586 PlayerInitFlashBomb(PLAYERp pp)
1587 {
1588     short i, nexti;
1589     unsigned int stat;
1590     int dist, tx, ty, tmin;
1591     short damage;
1592     SPRITEp sp = pp->SpriteP, hp;
1593     USERp hu;
1594 
1595     PlaySound(DIGI_GASPOP, &pp->posx, &pp->posy, &pp->posz, v3df_dontpan | v3df_doppler);
1596 
1597     // Set it just a little to let player know what he just did
1598     SetFadeAmt(pp, -30, 1);             // White flash
1599 
1600     for (stat = 0; stat < SIZ(StatDamageList); stat++)
1601     {
1602         TRAVERSE_SPRITE_STAT(headspritestat[StatDamageList[stat]], i, nexti)
1603         {
1604             hp = &sprite[i];
1605             hu = User[i];
1606 
1607             if (i == pp->PlayerSprite)
1608                 break;
1609 
1610             DISTANCE(hp->x, hp->y, sp->x, sp->y, dist, tx, ty, tmin);
1611             if (dist > 16384)           // Flash radius
1612                 continue;
1613 
1614             if (!TEST(sp->cstat, CSTAT_SPRITE_BLOCK))
1615                 continue;
1616 
1617             if (!FAFcansee(hp->x, hp->y, hp->z, hp->sectnum, sp->x, sp->y, sp->z - SPRITEp_SIZE_Z(sp), sp->sectnum))
1618                 continue;
1619 
1620             damage = GetDamage(i, pp->PlayerSprite, DMG_FLASHBOMB);
1621 
1622             if (hu->sop_parent)
1623             {
1624                 break;
1625             }
1626             else if (hu->PlayerP)
1627             {
1628 //              if(hu->PlayerP->NightVision)
1629 //              {
1630 //                  SetFadeAmt(hu->PlayerP, -200, 1); // Got him with night vision on!
1631 //                  PlayerUpdateHealth(hu->PlayerP, -15); // Hurt eyes
1632 //              }else
1633                 if (damage < -70)
1634                 {
1635                     int choosesnd = 0;
1636 
1637                     choosesnd = RANDOM_RANGE(MAX_PAIN);
1638 
1639                     PlayerSound(PlayerLowHealthPainVocs[choosesnd],&pp->posx,
1640                                 &pp->posy,&pp->posy,v3df_dontpan|v3df_doppler|v3df_follow,pp);
1641                 }
1642                 SetFadeAmt(hu->PlayerP, damage, 1);     // White flash
1643             }
1644             else
1645             {
1646                 ActorPain(i);
1647                 SpawnFlashBombOnActor(i);
1648             }
1649         }
1650     }
1651 
1652     return 0;
1653 }
1654 
1655 int
InitFlashBomb(int16_t SpriteNum)1656 InitFlashBomb(int16_t SpriteNum)
1657 {
1658     short i, nexti;
1659     unsigned int stat;
1660     int dist, tx, ty, tmin;
1661     short damage;
1662     SPRITEp sp = &sprite[SpriteNum], hp;
1663     USERp hu;
1664     PLAYERp pp = Player + screenpeek;
1665 
1666     PlaySound(DIGI_GASPOP, &sp->x, &sp->y, &sp->z, v3df_dontpan | v3df_doppler);
1667 
1668     for (stat = 0; stat < SIZ(StatDamageList); stat++)
1669     {
1670         TRAVERSE_SPRITE_STAT(headspritestat[StatDamageList[stat]], i, nexti)
1671         {
1672             hp = &sprite[i];
1673             hu = User[i];
1674 
1675             DISTANCE(hp->x, hp->y, sp->x, sp->y, dist, tx, ty, tmin);
1676             if (dist > 16384)           // Flash radius
1677                 continue;
1678 
1679             if (!TEST(sp->cstat, CSTAT_SPRITE_BLOCK))
1680                 continue;
1681 
1682             if (!FAFcansee(hp->x, hp->y, hp->z, hp->sectnum, sp->x, sp->y, sp->z - SPRITEp_SIZE_Z(sp), sp->sectnum))
1683                 continue;
1684 
1685             damage = GetDamage(i, SpriteNum, DMG_FLASHBOMB);
1686 
1687             if (hu->sop_parent)
1688             {
1689                 break;
1690             }
1691             else if (hu->PlayerP)
1692             {
1693                 if (damage < -70)
1694                 {
1695                     int choosesnd = 0;
1696 
1697                     choosesnd = RANDOM_RANGE(MAX_PAIN);
1698 
1699                     PlayerSound(PlayerLowHealthPainVocs[choosesnd],&pp->posx,
1700                                 &pp->posy,&pp->posy,v3df_dontpan|v3df_doppler|v3df_follow,pp);
1701                 }
1702                 SetFadeAmt(hu->PlayerP, damage, 1);     // White flash
1703             }
1704             else
1705             {
1706                 if (i != SpriteNum)
1707                 {
1708                     ActorPain(i);
1709                     SpawnFlashBombOnActor(i);
1710                 }
1711             }
1712         }
1713     }
1714 
1715     return 0;
1716 }
1717 
1718 
1719 // This is a sneaky function to make actors look blinded by flashbomb while using flaming code
1720 int
SpawnFlashBombOnActor(int16_t enemy)1721 SpawnFlashBombOnActor(int16_t enemy)
1722 {
1723     SPRITEp ep = &sprite[enemy];
1724     USERp eu = User[enemy];
1725     SPRITEp np;
1726     USERp nu;
1727     short New;
1728 
1729 
1730     // Forget about burnable sprites
1731     if (TEST(ep->extra, SPRX_BURNABLE))
1732         return eu->flame;
1733 
1734 
1735     if (enemy >= 0)
1736     {
1737         if (!eu)
1738         {
1739             ASSERT(TRUE == FALSE);
1740         }
1741 
1742         if (eu->flame >= 0)
1743         {
1744             int sizez = SPRITEp_SIZE_Z(ep) + DIV4(SPRITEp_SIZE_Z(ep));
1745 
1746             np = &sprite[eu->flame];
1747             nu = User[eu->flame];
1748 
1749 
1750             if (nu->Counter >= SPRITEp_SIZE_Z_2_YREPEAT(np, sizez))
1751             {
1752                 // keep flame only slightly bigger than the enemy itself
1753                 nu->Counter = SPRITEp_SIZE_Z_2_YREPEAT(np, sizez) * 2;
1754             }
1755             else
1756             {
1757                 // increase max size
1758                 nu->Counter += SPRITEp_SIZE_Z_2_YREPEAT(np, 8 << 8) * 2;
1759             }
1760 
1761             // Counter is max size
1762             if (nu->Counter >= 230)
1763             {
1764                 // this is far too big
1765                 nu->Counter = 230;
1766             }
1767 
1768             if (nu->WaitTics < 2 * 120)
1769                 nu->WaitTics = 2 * 120; // allow it to grow again
1770 
1771             return eu->flame;
1772         }
1773     }
1774 
1775     New = SpawnSprite(STAT_MISSILE, FIREBALL_FLAMES, s_FireballFlames, ep->sectnum,
1776                       ep->x, ep->y, ep->z, ep->ang, 0);
1777     np = &sprite[New];
1778     nu = User[New];
1779 
1780     if (enemy >= 0)
1781         eu->flame = New;
1782 
1783     np->xrepeat = 16;
1784     np->yrepeat = 16;
1785 
1786     if (enemy >= 0)
1787     {
1788         nu->Counter = SPRITEp_SIZE_Z_2_YREPEAT(np, SPRITEp_SIZE_Z(ep) >> 1) * 4;
1789     }
1790     else
1791         nu->Counter = 0;                // max flame size
1792 
1793     np->shade = -40;
1794     SET(np->cstat, CSTAT_SPRITE_YCENTER | CSTAT_SPRITE_INVISIBLE);
1795     RESET(np->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
1796 
1797     nu->Radius = 200;
1798 
1799     if (enemy >= 0)
1800     {
1801         SetAttach(enemy, New);
1802     }
1803 
1804     return New;
1805 }
1806 
1807 //////////////////////////////////////////////
1808 //
1809 // Inventory Caltrops
1810 //
1811 //////////////////////////////////////////////
1812 int
PlayerInitCaltrops(PLAYERp pp)1813 PlayerInitCaltrops(PLAYERp pp)
1814 {
1815     USERp u = User[pp->PlayerSprite];
1816     USERp wu;
1817     SPRITEp wp;
1818     int nx, ny, nz;
1819     short w;
1820     short oclipdist;
1821 
1822 
1823     PlaySound(DIGI_THROW, &pp->posx, &pp->posy, &pp->posz, v3df_dontpan | v3df_doppler);
1824 
1825     if (pp->cursectnum < 0)
1826         return 0;
1827 
1828     nx = pp->posx;
1829     ny = pp->posy;
1830     nz = pp->posz + pp->bob_z + Z(8);
1831 
1832     // Throw out several caltrops
1833 //  for(short i=0;i<3;i++)
1834 //  {
1835     // Spawn a shot
1836     // Inserting and setting up variables
1837     w = SpawnSprite(STAT_DEAD_ACTOR, CALTROPS, s_Caltrops, pp->cursectnum,
1838                     nx, ny, nz, fix16_to_int(pp->q16ang), (CHEMBOMB_VELOCITY + RANDOM_RANGE(CHEMBOMB_VELOCITY)) / 2);
1839 
1840     wp = &sprite[w];
1841     wu = User[w];
1842 
1843     // don't throw it as far if crawling
1844     if (TEST(pp->Flags, PF_CRAWLING))
1845     {
1846         wp->xvel -= DIV4(wp->xvel);
1847     }
1848 
1849     SET(wu->Flags, SPR_XFLIP_TOGGLE);
1850 
1851     SetOwner(pp->PlayerSprite, w);
1852     wp->yrepeat = 64;
1853     wp->xrepeat = 64;
1854     wp->shade = -15;
1855     wu->WeaponNum = u->WeaponNum;
1856     wu->Radius = 200;
1857     wu->ceiling_dist = Z(3);
1858     wu->floor_dist = Z(3);
1859     wu->Counter = 0;
1860 //      SET(wp->cstat, CSTAT_SPRITE_BLOCK);
1861 
1862     if (TEST(pp->Flags, PF_DIVING) || SpriteInUnderwaterArea(wp))
1863         SET(wu->Flags, SPR_UNDERWATER);
1864 
1865     // They go out at different angles
1866 //        wp->ang = NORM_ANGLE(fix16_to_int(pp->q16ang) + (RANDOM_RANGE(50) - 25));
1867 
1868     wp->zvel = ((100 - fix16_to_int(pp->q16horiz)) * HORIZ_MULT);
1869 
1870     oclipdist = pp->SpriteP->clipdist;
1871     pp->SpriteP->clipdist = 0;
1872     wp->clipdist = 0;
1873 
1874     MissileSetPos(w, DoCaltrops, 1000);
1875 
1876     pp->SpriteP->clipdist = oclipdist;
1877     wp->clipdist = 80L >> 2;
1878 
1879     wu->xchange = MOVEx(wp->xvel, wp->ang);
1880     wu->ychange = MOVEy(wp->xvel, wp->ang);
1881     wu->zchange = wp->zvel >> 1;
1882 
1883     // adjust xvel according to player velocity
1884     wu->xchange += pp->xvect >> 14;
1885     wu->ychange += pp->yvect >> 14;
1886 
1887     // Caltrops stay around for this many seconds
1888 //      wu->WaitTics = CHEMTICS*5;
1889 //  }
1890 
1891     SetupSpriteForBreak(wp);            // Put Caltrops in the break queue
1892     return 0;
1893 }
1894 
1895 int
InitCaltrops(int16_t SpriteNum)1896 InitCaltrops(int16_t SpriteNum)
1897 {
1898     SPRITEp sp = &sprite[SpriteNum];
1899     USERp u = User[SpriteNum];
1900     USERp wu;
1901     SPRITEp wp;
1902     int nx, ny, nz;
1903     short w;
1904 
1905 
1906     PlaySound(DIGI_THROW, &sp->x, &sp->y, &sp->z, v3df_dontpan | v3df_doppler);
1907 
1908     nx = sp->x;
1909     ny = sp->y;
1910     nz = sp->z;
1911 
1912     // Spawn a shot
1913     // Inserting and setting up variables
1914     w = SpawnSprite(STAT_DEAD_ACTOR, CALTROPS, s_Caltrops, sp->sectnum,
1915                     nx, ny, nz, sp->ang, CHEMBOMB_VELOCITY / 2);
1916 
1917     wp = &sprite[w];
1918     wu = User[w];
1919 
1920     SET(wu->Flags, SPR_XFLIP_TOGGLE);
1921 
1922     SetOwner(SpriteNum, w);
1923     wp->yrepeat = 64;
1924     wp->xrepeat = 64;
1925     wp->shade = -15;
1926     // !FRANK - clipbox must be <= weapon otherwise can clip thru walls
1927     wp->clipdist = sp->clipdist;
1928     wu->WeaponNum = u->WeaponNum;
1929     wu->Radius = 200;
1930     wu->ceiling_dist = Z(3);
1931     wu->floor_dist = Z(3);
1932     wu->Counter = 0;
1933 
1934     wp->zvel = ((-100 - RANDOM_RANGE(100)) * HORIZ_MULT);
1935 
1936     // wp->clipdist = 80L>>2;
1937 
1938     wu->xchange = MOVEx(wp->xvel, wp->ang);
1939     wu->ychange = MOVEy(wp->xvel, wp->ang);
1940     wu->zchange = wp->zvel >> 1;
1941 
1942     SetupSpriteForBreak(wp);            // Put Caltrops in the break queue
1943     return 0;
1944 }
1945 
1946 int
InitPhosphorus(int16_t SpriteNum)1947 InitPhosphorus(int16_t SpriteNum)
1948 {
1949     SPRITEp sp = &sprite[SpriteNum];
1950     USERp u = User[SpriteNum];
1951     USERp wu;
1952     SPRITEp wp;
1953     int nx, ny, nz;
1954     short w;
1955     short daang;
1956 
1957 
1958     PlaySound(DIGI_FIREBALL1, &sp->x, &sp->y, &sp->z, v3df_follow);
1959 
1960     nx = sp->x;
1961     ny = sp->y;
1962     nz = sp->z;
1963 
1964     daang = NORM_ANGLE(RANDOM_RANGE(2048));
1965 
1966     // Spawn a shot
1967     // Inserting and setting up variables
1968     w = SpawnSprite(STAT_SKIP4, FIREBALL1, s_Phosphorus, sp->sectnum,
1969                     nx, ny, nz, daang, CHEMBOMB_VELOCITY/3);
1970 
1971     wp = &sprite[w];
1972     wu = User[w];
1973 
1974     wp->hitag = LUMINOUS;               // Always full brightness
1975     SET(wu->Flags, SPR_XFLIP_TOGGLE);
1976     // !Frank - don't do translucent
1977     SET(wp->cstat, CSTAT_SPRITE_YCENTER);
1978     // SET(wp->cstat, CSTAT_SPRITE_TRANSLUCENT|CSTAT_SPRITE_YCENTER);
1979     wp->shade = -128;
1980 
1981     //SetOwner(SpriteNum, w);
1982     wp->yrepeat = 64;
1983     wp->xrepeat = 64;
1984     wp->shade = -15;
1985     // !FRANK - clipbox must be <= weapon otherwise can clip thru walls
1986     if (sp->clipdist > 0)
1987         wp->clipdist = sp->clipdist-1;
1988     else
1989         wp->clipdist = sp->clipdist;
1990     wu->WeaponNum = u->WeaponNum;
1991     wu->Radius = 600;
1992     wu->ceiling_dist = Z(3);
1993     wu->floor_dist = Z(3);
1994     wu->Counter = 0;
1995 
1996     wp->zvel = ((-100 - RANDOM_RANGE(100)) * HORIZ_MULT);
1997 
1998     wu->xchange = MOVEx(wp->xvel, wp->ang);
1999     wu->ychange = MOVEy(wp->xvel, wp->ang);
2000     wu->zchange = (wp->zvel >> 1);
2001 
2002     return 0;
2003 }
2004 
2005 int
InitBloodSpray(int16_t SpriteNum,SWBOOL dogib,short velocity)2006 InitBloodSpray(int16_t SpriteNum, SWBOOL dogib, short velocity)
2007 {
2008     SPRITEp sp = &sprite[SpriteNum];
2009     USERp u = User[SpriteNum];
2010     USERp wu;
2011     SPRITEp wp;
2012     int nx, ny, nz;
2013     short w;
2014     short i, cnt, ang, vel, rnd;
2015 
2016 
2017     if (dogib)
2018         cnt = RANDOM_RANGE(3)+1;
2019     else
2020         cnt = 1;
2021 
2022     //if(dogib)
2023     //    {
2024     rnd = RANDOM_RANGE(1000);
2025     if (rnd > 650)
2026         PlaySound(DIGI_GIBS1, &sp->x, &sp->y, &sp->z, v3df_none);
2027     else if (rnd > 350)
2028         PlaySound(DIGI_GIBS2, &sp->x, &sp->y, &sp->z, v3df_none);
2029     else
2030         PlaySound(DIGI_GIBS3, &sp->x, &sp->y, &sp->z, v3df_none);
2031     //    }
2032 
2033     ang = sp->ang;
2034     vel = velocity;
2035 
2036     for (i=0; i<cnt; i++)
2037     {
2038 
2039         if (velocity == -1)
2040             vel = 105+RANDOM_RANGE(320);
2041         else if (velocity == -2)
2042             vel = 105+RANDOM_RANGE(100);
2043 
2044         if (dogib)
2045             ang = NORM_ANGLE(ang + 512 + RANDOM_RANGE(200));
2046         else
2047             ang = NORM_ANGLE(ang+1024+256 - RANDOM_RANGE(256));
2048 
2049         nx = sp->x;
2050         ny = sp->y;
2051         nz = SPRITEp_TOS(sp)-20;
2052 
2053         //RESET(sp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
2054 
2055         // Spawn a shot
2056         // Inserting and setting up variables
2057         w = SpawnSprite(STAT_MISSILE, GOREDrip, s_BloodSprayChunk, sp->sectnum,
2058                         nx, ny, nz, ang, vel*2);
2059 
2060         wp = &sprite[w];
2061         wu = User[w];
2062 
2063         SET(wu->Flags, SPR_XFLIP_TOGGLE);
2064         if (dogib)
2065             SET(wp->cstat, CSTAT_SPRITE_YCENTER);
2066         else
2067             SET(wp->cstat, CSTAT_SPRITE_YCENTER | CSTAT_SPRITE_INVISIBLE);
2068         wp->shade = -12;
2069 
2070         SetOwner(SpriteNum, w);
2071         wp->yrepeat = 64-RANDOM_RANGE(35);
2072         wp->xrepeat = 64-RANDOM_RANGE(35);
2073         wp->shade = -15;
2074         wp->clipdist = sp->clipdist;
2075         wu->WeaponNum = u->WeaponNum;
2076         wu->Radius = 600;
2077         wu->ceiling_dist = Z(3);
2078         wu->floor_dist = Z(3);
2079         wu->Counter = 0;
2080 
2081         wp->zvel = ((-10 - RANDOM_RANGE(50)) * HORIZ_MULT);
2082 
2083         wu->xchange = MOVEx(wp->xvel, wp->ang);
2084         wu->ychange = MOVEy(wp->xvel, wp->ang);
2085         wu->zchange = wp->zvel >> 1;
2086 
2087         if (!GlobalSkipZrange)
2088             DoActorZrange(w);
2089     }
2090 
2091     return 0;
2092 }
2093 
2094 int
BloodSprayFall(int16_t SpriteNum)2095 BloodSprayFall(int16_t SpriteNum)
2096 {
2097     SPRITEp sp = &sprite[SpriteNum];
2098 
2099     sp->z += 1500;
2100 
2101     return 0;
2102 }
2103 
2104 ////////////////// DEATHFLAG! ////////////////////////////////////////////////////////////////
2105 // Rules: Run to an enemy flag, run over it an it will stick to you.
2106 // The goal is to run the enemy's flag back to your startpoint.
2107 // If an enemy flag touches a friendly start sector, then the opposing team explodes and
2108 // your team wins and the level restarts.
2109 // Once you pick up a flag, you have 30 seconds to score, otherwise, the flag detonates
2110 // an explosion, killing you and anyone in the vicinity, and you don't score.
2111 //////////////////////////////////////////////////////////////////////////////////////////////
2112 
2113 // Update the scoreboard for team color that just scored.
2114 void
DoFlagScore(int16_t pal)2115 DoFlagScore(int16_t pal)
2116 {
2117     SPRITEp sp;
2118     int SpriteNum = 0, NextSprite = 0;
2119 
2120     TRAVERSE_SPRITE_STAT(headspritestat[0], SpriteNum, NextSprite)
2121     {
2122         sp = &sprite[SpriteNum];
2123 
2124         if (sp->picnum < 1900 || sp->picnum > 1999)
2125             continue;
2126 
2127         if (sp->pal == pal)
2128             sp->picnum++;               // Increment the counter
2129 
2130         if (sp->picnum > 1999)
2131             sp->picnum = 1900;          // Roll it over if you must
2132 
2133     }
2134 }
2135 
2136 int
DoFlagRangeTest(short Weapon,short range)2137 DoFlagRangeTest(short Weapon, short range)
2138 {
2139     SPRITEp wp = &sprite[Weapon];
2140 
2141     SPRITEp sp;
2142     short i, nexti;
2143     unsigned int stat;
2144     int dist, tx, ty;
2145     int tmin;
2146 
2147     for (stat = 0; stat < SIZ(StatDamageList); stat++)
2148     {
2149         TRAVERSE_SPRITE_STAT(headspritestat[StatDamageList[stat]], i, nexti)
2150         {
2151             sp = &sprite[i];
2152 
2153 
2154             DISTANCE(sp->x, sp->y, wp->x, wp->y, dist, tx, ty, tmin);
2155             if (dist > range)
2156                 continue;
2157 
2158             if (sp == wp)
2159                 continue;
2160 
2161             if (!TEST(sp->cstat, CSTAT_SPRITE_BLOCK))
2162                 continue;
2163 
2164             if (!TEST(sp->extra, SPRX_PLAYER_OR_ENEMY))
2165                 continue;
2166 
2167             if (!FAFcansee(sp->x, sp->y, sp->z, sp->sectnum, wp->x, wp->y, wp->z, wp->sectnum))
2168                 continue;
2169 
2170             dist = FindDistance3D(wp->x - sp->x, wp->y - sp->y, wp->z - sp->z);
2171             if (dist > range)
2172                 continue;
2173 
2174             return i;                   // Return the spritenum
2175         }
2176     }
2177 
2178     return -1;                          // -1 for no sprite index.  Not
2179     // found.
2180 }
2181 
2182 int
DoCarryFlag(int16_t Weapon)2183 DoCarryFlag(int16_t Weapon)
2184 {
2185     SPRITEp sp = &sprite[Weapon];
2186     USERp u = User[Weapon];
2187 
2188 #define FLAG_DETONATE_STATE 99
2189     SPRITEp fp = &sprite[u->FlagOwner];
2190     USERp fu = User[u->FlagOwner];
2191 
2192 
2193     // if no owner then die
2194     if (u->Attach >= 0)
2195     {
2196         SPRITEp ap = &sprite[u->Attach];
2197 
2198         setspritez_old(Weapon, ap->x, ap->y, SPRITEp_MID(ap));
2199         sp->ang = NORM_ANGLE(ap->ang + 1536);
2200     }
2201 
2202     // not activated yet
2203     if (!TEST(u->Flags, SPR_ACTIVE))
2204     {
2205         if ((u->WaitTics -= (MISSILEMOVETICS * 2)) > 0)
2206             return FALSE;
2207 
2208         // activate it
2209         u->WaitTics = SEC(30);          // You have 30 seconds to get it to
2210         // scorebox
2211         u->Counter2 = 0;
2212         SET(u->Flags, SPR_ACTIVE);
2213     }
2214 
2215     // limit the number of times DoFlagRangeTest is called
2216     u->Counter++;
2217     if (u->Counter > 1)
2218         u->Counter = 0;
2219 
2220     if (!u->Counter)
2221     {
2222         // not already in detonate state
2223         if (u->Counter2 < FLAG_DETONATE_STATE)
2224         {
2225             SPRITEp ap = &sprite[u->Attach];
2226             USERp au = User[u->Attach];
2227 
2228             if (!au || au->Health <= 0)
2229             {
2230                 u->Counter2 = FLAG_DETONATE_STATE;
2231                 u->WaitTics = SEC(1) / 2;
2232             }
2233             // if in score box, score.
2234             if (sector[ap->sectnum].hitag == 9000 && sector[ap->sectnum].lotag == ap->pal
2235                 && ap->pal != sp->pal)
2236             {
2237                 if (u->FlagOwner >= 0)
2238                 {
2239                     if (fp->lotag)      // Trigger everything if there is a
2240                         // lotag
2241                         DoMatchEverything(NULL, fp->lotag, ON);
2242                 }
2243                 if (!TEST_BOOL1(fp))
2244                 {
2245                     PlaySound(DIGI_BIGITEM, &ap->x, &ap->y, &ap->z, v3df_none);
2246                     DoFlagScore(ap->pal);
2247                     if (SP_TAG5(fp) > 0)
2248                     {
2249                         fu->filler++;
2250                         if (fu->filler >= SP_TAG5(fp))
2251                         {
2252                             fu->filler = 0;
2253                             DoMatchEverything(NULL, SP_TAG6(fp), ON);
2254                         }
2255                     }
2256                 }
2257                 SetSuicide(Weapon);     // Kill the flag, you scored!
2258             }
2259         }
2260         else
2261         {
2262             // Time's up! Move directly to detonate state
2263             u->Counter2 = FLAG_DETONATE_STATE;
2264             u->WaitTics = SEC(1) / 2;
2265         }
2266 
2267     }
2268 
2269     u->WaitTics -= (MISSILEMOVETICS * 2);
2270 
2271     switch (u->Counter2)
2272     {
2273     case 0:
2274         if (u->WaitTics < SEC(30))
2275         {
2276             PlaySound(DIGI_MINEBEEP, &sp->x, &sp->y, &sp->z, v3df_dontpan);
2277             u->Counter2++;
2278         }
2279         break;
2280     case 1:
2281         if (u->WaitTics < SEC(20))
2282         {
2283             PlaySound(DIGI_MINEBEEP, &sp->x, &sp->y, &sp->z, v3df_dontpan);
2284             u->Counter2++;
2285         }
2286         break;
2287     case 2:
2288         if (u->WaitTics < SEC(10))
2289         {
2290             PlaySound(DIGI_MINEBEEP, &sp->x, &sp->y, &sp->z, v3df_dontpan);
2291             u->Counter2++;
2292         }
2293         break;
2294     case 3:
2295         if (u->WaitTics < SEC(5))
2296         {
2297             PlaySound(DIGI_MINEBEEP, &sp->x, &sp->y, &sp->z, v3df_dontpan);
2298             u->Counter2++;
2299         }
2300         break;
2301     case 4:
2302         if (u->WaitTics < SEC(4))
2303         {
2304             PlaySound(DIGI_MINEBEEP, &sp->x, &sp->y, &sp->z, v3df_dontpan);
2305             u->Counter2++;
2306         }
2307         break;
2308     case 5:
2309         if (u->WaitTics < SEC(3))
2310         {
2311             PlaySound(DIGI_MINEBEEP, &sp->x, &sp->y, &sp->z, v3df_dontpan);
2312             u->Counter2++;
2313         }
2314         break;
2315     case 6:
2316         if (u->WaitTics < SEC(2))
2317         {
2318             PlaySound(DIGI_MINEBEEP, &sp->x, &sp->y, &sp->z, v3df_dontpan);
2319             u->Counter2 = FLAG_DETONATE_STATE;
2320         }
2321         break;
2322     case FLAG_DETONATE_STATE:
2323         // start frantic beeping
2324         PlaySound(DIGI_MINEBEEP, &sp->x, &sp->y, &sp->z, v3df_dontpan);
2325         u->Counter2++;
2326         break;
2327     case FLAG_DETONATE_STATE + 1:
2328         SpawnGrenadeExp(Weapon);
2329         SetSuicide(Weapon);
2330         return FALSE;
2331         break;
2332     }
2333 
2334     return FALSE;
2335 }
2336 
2337 int
DoCarryFlagNoDet(int16_t Weapon)2338 DoCarryFlagNoDet(int16_t Weapon)
2339 {
2340     SPRITEp sp = &sprite[Weapon];
2341     USERp u = User[Weapon];
2342     SPRITEp ap = &sprite[u->Attach];
2343     USERp au = User[u->Attach];
2344     SPRITEp fp = &sprite[u->FlagOwner];
2345     USERp fu = User[u->FlagOwner];
2346 
2347 
2348     if (u->FlagOwner >= 0)
2349         fu->WaitTics = 30 * 120;        // Keep setting respawn tics so it
2350     // won't respawn
2351 
2352     // if no owner then die
2353     if (u->Attach >= 0)
2354     {
2355         SPRITEp ap = &sprite[u->Attach];
2356 
2357         setspritez_old(Weapon, ap->x, ap->y, SPRITEp_MID(ap));
2358         sp->ang = NORM_ANGLE(ap->ang + 1536);
2359         sp->z = ap->z - DIV2(SPRITEp_SIZE_Z(ap));
2360     }
2361 
2362 
2363     if (!au || au->Health <= 0)
2364     {
2365         if (u->FlagOwner >= 0)
2366             fu->WaitTics = 0;           // Tell it to respawn
2367         SetSuicide(Weapon);
2368         return FALSE;
2369     }
2370 
2371     // if in score box, score.
2372     if (sector[ap->sectnum].hitag == 9000 && sector[ap->sectnum].lotag == ap->pal
2373         && ap->pal != sp->pal)
2374     {
2375         if (u->FlagOwner >= 0)
2376         {
2377             //DSPRINTF(ds, "Flag has owner %d, fp->lotag = %d", u->FlagOwner, fp->lotag);
2378             //MONO_PRINT(ds);
2379             if (fp->lotag)              // Trigger everything if there is a
2380                 // lotag
2381                 DoMatchEverything(NULL, fp->lotag, ON);
2382             fu->WaitTics = 0;           // Tell it to respawn
2383         }
2384         if (!TEST_BOOL1(fp))
2385         {
2386             PlaySound(DIGI_BIGITEM, &ap->x, &ap->y, &ap->z, v3df_none);
2387             DoFlagScore(ap->pal);
2388             if (SP_TAG5(fp) > 0)
2389             {
2390                 fu->filler++;
2391                 if (fu->filler >= SP_TAG5(fp))
2392                 {
2393                     fu->filler = 0;
2394                     DoMatchEverything(NULL, SP_TAG6(fp), ON);
2395                 }
2396             }
2397         }
2398         SetSuicide(Weapon);             // Kill the flag, you scored!
2399     }
2400 
2401     return FALSE;
2402 }
2403 
2404 
2405 int
SetCarryFlag(int16_t Weapon)2406 SetCarryFlag(int16_t Weapon)
2407 {
2408     SPRITEp sp = &sprite[Weapon];
2409     USERp u = User[Weapon];
2410 
2411     // stuck
2412     SET(u->Flags, SPR_BOUNCE);
2413     // not yet active for 1 sec
2414 //    RESET(u->Flags, SPR_ACTIVE);
2415 //    u->WaitTics = SEC(3);
2416     SET(sp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
2417     u->Counter = 0;
2418     change_sprite_stat(Weapon, STAT_ITEM);
2419     if (sp->hitag == 1)
2420         ChangeState(Weapon, s_CarryFlagNoDet);
2421     else
2422         ChangeState(Weapon, s_CarryFlag);
2423 
2424     return FALSE;
2425 }
2426 
2427 int
DoFlag(int16_t Weapon)2428 DoFlag(int16_t Weapon)
2429 {
2430     SPRITEp sp = &sprite[Weapon];
2431     USERp u = User[Weapon];
2432     int16_t hit_sprite = -1;
2433 
2434     hit_sprite = DoFlagRangeTest(Weapon, 1000);
2435 
2436     if (hit_sprite != -1)
2437     {
2438         SPRITEp hsp = &sprite[hit_sprite];
2439 
2440         SetCarryFlag(Weapon);
2441 
2442         // check to see if sprite is player or enemy
2443         if (TEST(hsp->extra, SPRX_PLAYER_OR_ENEMY))
2444         {
2445             // attach weapon to sprite
2446             RESET(sp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
2447             SetAttach(hit_sprite, Weapon);
2448             u->sz = hsp->z - DIV2(SPRITEp_SIZE_Z(hsp));
2449             //u->sz = hsp->z - SPRITEp_MID(hsp);   // Set mid way up who it hit
2450         }
2451     }
2452 
2453     return FALSE;
2454 }
2455 
2456 
2457 int
InitShell(int16_t SpriteNum,int16_t ShellNum)2458 InitShell(int16_t SpriteNum, int16_t ShellNum)
2459 {
2460     USERp u = User[SpriteNum];
2461     USERp wu;
2462     SPRITEp sp = &sprite[SpriteNum], wp;
2463     int nx, ny, nz;
2464     short w;
2465     short id=0,velocity=0;
2466     STATEp p=NULL;
2467     extern STATE s_UziShellShrap[];
2468     extern STATE s_ShotgunShellShrap[];
2469 
2470 #define UZI_SHELL 2152
2471 #define SHOT_SHELL 2180
2472 
2473     nx = sp->x;
2474     ny = sp->y;
2475     nz = DIV2(SPRITEp_TOS(sp)+ SPRITEp_BOS(sp));
2476 
2477     switch (ShellNum)
2478     {
2479     case -2:
2480     case -3:
2481         id = UZI_SHELL;
2482         p = s_UziShellShrap;
2483         velocity = 1500 + RANDOM_RANGE(1000);
2484         break;
2485     case -4:
2486         id = SHOT_SHELL;
2487         p = s_ShotgunShellShrap;
2488         velocity = 2000 + RANDOM_RANGE(1000);
2489         break;
2490     }
2491 
2492     w = SpawnSprite(STAT_SKIP4, id, p, sp->sectnum,
2493                     nx, ny, nz, sp->ang, 64);
2494 
2495     wp = &sprite[w];
2496     wu = User[w];
2497 
2498     wp->zvel = -(velocity);
2499 
2500     if (u->PlayerP)
2501     {
2502         wp->z += ((100 - fix16_to_int(u->PlayerP->q16horiz)) * (HORIZ_MULT/3));
2503     }
2504 
2505     switch (wu->ID)
2506     {
2507     case UZI_SHELL:
2508         wp->z -= Z(13);
2509 
2510         if (ShellNum == -3)
2511         {
2512             wp->ang = sp->ang;
2513             HelpMissileLateral(w,2500);
2514             wp->ang = NORM_ANGLE(wp->ang-512);
2515             HelpMissileLateral(w,1000); // Was 1500
2516             wp->ang = NORM_ANGLE(wp->ang+712);
2517         }
2518         else
2519         {
2520             wp->ang = sp->ang;
2521             HelpMissileLateral(w,2500);
2522             wp->ang = NORM_ANGLE(wp->ang+512);
2523             HelpMissileLateral(w,1500);
2524             wp->ang = NORM_ANGLE(wp->ang-128);
2525         }
2526         wp->ang += (RANDOM_P2(128<<5)>>5) - DIV2(128);
2527         wp->ang = NORM_ANGLE(wp->ang);
2528 
2529         // Set the shell number
2530         wu->ShellNum = ShellCount;
2531         wp->yrepeat = wp->xrepeat = 13;
2532         break;
2533     case SHOT_SHELL:
2534         wp->z -= Z(13);
2535         wp->ang = sp->ang;
2536         HelpMissileLateral(w,2500);
2537         wp->ang = NORM_ANGLE(wp->ang+512);
2538         HelpMissileLateral(w,1300);
2539         wp->ang = NORM_ANGLE(wp->ang-128-64);
2540         wp->ang += (RANDOM_P2(128<<5)>>5) - DIV2(128);
2541         wp->ang = NORM_ANGLE(wp->ang);
2542 
2543         // Set the shell number
2544         wu->ShellNum = ShellCount;
2545         wp->yrepeat = wp->xrepeat = 18;
2546         break;
2547     }
2548 
2549     SetOwner(SpriteNum, w);
2550     wp->shade = -15;
2551     wu->ceiling_dist = Z(1);
2552     wu->floor_dist = Z(1);
2553     wu->Counter = 0;
2554     SET(wp->cstat, CSTAT_SPRITE_YCENTER);
2555     RESET(wp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
2556     RESET(wu->Flags, SPR_BOUNCE|SPR_UNDERWATER); // Make em' bounce
2557 
2558     wu->xchange = MOVEx(wp->xvel, wp->ang);
2559     wu->ychange = MOVEy(wp->xvel, wp->ang);
2560     wu->zchange = wp->zvel;
2561     //if (TEST(u->PlayerP->Flags, PF_DIVING) || SpriteInUnderwaterArea(wp))
2562     //    SET(wu->Flags, SPR_UNDERWATER);
2563     wu->jump_speed = 200;
2564     wu->jump_speed += RANDOM_RANGE(400);
2565     wu->jump_speed = -wu->jump_speed;
2566 
2567     DoBeginJump(w);
2568     wu->jump_grav = ACTOR_GRAVITY;
2569 
2570     return 0;
2571 }
2572 
2573 
2574 #include "saveable.h"
2575 
2576 static saveable_data saveable_jweapon_data[] =
2577 {
2578     SAVE_DATA(s_BloodSpray),
2579     SAVE_DATA(s_PhosphorExp),
2580     SAVE_DATA(s_NukeMushroom),
2581     SAVE_DATA(s_RadiationCloud),
2582     SAVE_DATA(s_ChemBomb),
2583     SAVE_DATA(s_Caltrops),
2584     SAVE_DATA(s_CaltropsStick),
2585     SAVE_DATA(s_CarryFlag),
2586     SAVE_DATA(s_CarryFlagNoDet),
2587     SAVE_DATA(s_Flag),
2588     SAVE_DATA(s_Phosphorus),
2589     SAVE_DATA(s_BloodSprayChunk),
2590     SAVE_DATA(s_BloodSprayDrip),
2591 };
2592 
2593 saveable_module saveable_jweapon =
2594 {
2595     // code
2596     NULL,0,
2597 
2598     // data
2599     saveable_jweapon_data,
2600     SIZ(saveable_jweapon_data)
2601 };
2602