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