1 //-------------------------------------------------------------------------
2 /*
3 Copyright (C) 2016 EDuke32 developers and contributors
4
5 This file is part of EDuke32.
6
7 EDuke32 is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License version 2
9 as published by the Free Software Foundation.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
15 See the GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 */
21 //-------------------------------------------------------------------------
22
23 #define actors_c_
24
25 #ifndef EDUKE32_STANDALONE
26 #include <map>
27 #endif
28
29 #include "duke3d.h"
30 #include "microprofile.h"
31
32 #if KRANDDEBUG
33 # define ACTOR_STATIC
34 #else
35 # define ACTOR_STATIC static
36 #endif
37
38 uint8_t g_radiusDmgStatnums[(MAXSTATUS+7)>>3];
39
40 #define DELETE_SPRITE_AND_CONTINUE(KX) do { A_DeleteSprite(KX); goto next_sprite; } while (0)
41
42 int32_t otherp;
43
G_SetInterpolation(int32_t * const posptr)44 int G_SetInterpolation(int32_t *const posptr)
45 {
46 if (g_interpolationCnt >= MAXINTERPOLATIONS)
47 return 1;
48
49 for (bssize_t i = 0; i < g_interpolationCnt; ++i)
50 if (curipos[i] == posptr)
51 return 0;
52
53 curipos[g_interpolationCnt] = posptr;
54 oldipos[g_interpolationCnt] = *posptr;
55 g_interpolationCnt++;
56 return 0;
57 }
58
G_StopInterpolation(const int32_t * const posptr)59 void G_StopInterpolation(const int32_t * const posptr)
60 {
61 for (bssize_t i = 0; i < g_interpolationCnt; ++i)
62 if (curipos[i] == posptr)
63 {
64 g_interpolationCnt--;
65 oldipos[i] = oldipos[g_interpolationCnt];
66 bakipos[i] = bakipos[g_interpolationCnt];
67 curipos[i] = curipos[g_interpolationCnt];
68 }
69 }
70
G_DoInterpolations(int smoothRatio)71 void G_DoInterpolations(int smoothRatio)
72 {
73 if (g_interpolationLock++)
74 return;
75
76 int32_t ndelta = 0;
77
78 for (bssize_t i = 0, j = 0; i < g_interpolationCnt; ++i)
79 {
80 int32_t const odelta = ndelta;
81 bakipos[i] = *curipos[i];
82 ndelta = (*curipos[i]) - oldipos[i];
83 if (odelta != ndelta)
84 j = mulscale16(ndelta, smoothRatio);
85 *curipos[i] = oldipos[i] + j;
86 }
87 }
88
G_ClearCameraView(DukePlayer_t * ps)89 void G_ClearCameraView(DukePlayer_t *ps)
90 {
91 ps->newowner = -1;
92 ps->pos = ps->opos;
93 ps->q16ang = ps->oq16ang;
94
95 updatesector(ps->pos.x, ps->pos.y, &ps->cursectnum);
96 P_UpdateScreenPal(ps);
97
98 for (bssize_t SPRITES_OF(STAT_ACTOR, k))
99 if (sprite[k].picnum==CAMERA1)
100 sprite[k].yvel = 0;
101 }
102
A_RadiusDamageObject_Internal(int const spriteNum,int const otherSprite,int const blastRadius,int spriteDist,int const zOffset,int const dmg1,int dmg2,int dmg3,int dmg4)103 void A_RadiusDamageObject_Internal(int const spriteNum, int const otherSprite, int const blastRadius, int spriteDist,
104 int const zOffset, int const dmg1, int dmg2, int dmg3, int dmg4)
105 {
106 auto const pSprite = (uspriteptr_t)&sprite[spriteNum];
107 auto const pOther = &sprite[otherSprite];
108
109 #ifndef EDUKE32_STANDALONE
110 if (WORLDTOUR && pSprite->picnum == FLAMETHROWERFLAME)
111 {
112 // enemies in WT don't damage other enemies of the same type with FLAMETHROWERFLAME
113 if (sprite[pSprite->owner].picnum == pOther->picnum && pOther->picnum != APLAYER)
114 return;
115 }
116 #endif
117
118 // DEFAULT, ZOMBIEACTOR, MISC
119 if (pOther->statnum == STAT_DEFAULT || pOther->statnum == STAT_ZOMBIEACTOR || pOther->statnum == STAT_MISC
120 #ifndef EDUKE32_STANDALONE
121 || (!FURY && AFLAMABLE(pOther->picnum))
122 #endif
123 )
124 {
125 #ifndef EDUKE32_STANDALONE
126 if (pSprite->picnum != SHRINKSPARK || (pOther->cstat&257))
127 #endif
128 {
129 if (A_CheckEnemySprite(pOther) && !cansee(pOther->x, pOther->y, pOther->z+zOffset, pOther->sectnum, pSprite->x, pSprite->y, pSprite->z+zOffset, pSprite->sectnum))
130 return;
131
132 #ifndef EDUKE32_STANDALONE
133 if (!FURY)
134 A_DamageObject_Duke3D(otherSprite, spriteNum);
135 else
136 #endif
137 A_DamageObject_Generic(otherSprite, spriteNum);
138 }
139 }
140 else if (pOther->extra >= 0 && (uspriteptr_t)pOther != pSprite && ((pOther->cstat & 257) ||
141 #ifndef EDUKE32_STANDALONE
142 pOther->picnum == TRIPBOMB || pOther->picnum == QUEBALL || pOther->picnum == STRIPEBALL || pOther->picnum == DUKELYINGDEAD ||
143 #endif
144 A_CheckEnemySprite(pOther)))
145 {
146 #ifndef EDUKE32_STANDALONE
147 if ((pSprite->picnum == SHRINKSPARK && pOther->picnum != SHARK && (otherSprite == pSprite->owner || pOther->xrepeat < 24))
148 || (pSprite->picnum == MORTER && otherSprite == pSprite->owner))
149 return;
150 #endif
151 if (spriteDist >= blastRadius || !cansee(pOther->x, pOther->y, pOther->z - ZOFFSET3, pOther->sectnum,
152 pSprite->x, pSprite->y, pSprite->z - ZOFFSET4, pSprite->sectnum))
153 return;
154
155 if (A_CheckSpriteFlags(otherSprite, SFLAG_DAMAGEEVENT))
156 if (VM_OnEventWithReturn(EVENT_DAMAGESPRITE, spriteNum, -1, otherSprite) < 0)
157 return;
158
159 auto &dmgActor = actor[otherSprite];
160
161 dmgActor.ang = getangle(pOther->x - pSprite->x, pOther->y - pSprite->y);
162
163 if ((pOther->extra > 0 && ((A_CheckSpriteFlags(spriteNum, SFLAG_PROJECTILE) && SpriteProjectile[spriteNum].workslike & PROJECTILE_RADIUS_PICNUM)
164 #ifndef EDUKE32_STANDALONE
165 || pSprite->picnum == RPG
166 #endif
167 ))
168 #ifndef EDUKE32_STANDALONE
169 || (pSprite->picnum == SHRINKSPARK)
170 #endif
171 )
172 dmgActor.picnum = pSprite->picnum;
173 #ifndef EDUKE32_STANDALONE
174 else if (WORLDTOUR && (pSprite->picnum == FLAMETHROWERFLAME || pSprite->picnum == LAVAPOOL
175 || (pSprite->picnum == FIREBALL && sprite[pSprite->owner].picnum == APLAYER)))
176 dmgActor.picnum = FLAMETHROWERFLAME;
177 #endif
178 else
179 dmgActor.picnum = RADIUSEXPLOSION;
180
181 #ifndef EDUKE32_STANDALONE
182 if (pSprite->picnum != SHRINKSPARK && (!WORLDTOUR || pSprite->picnum != LAVAPOOL))
183 #endif
184 {
185 // this is really weird
186 int const k = blastRadius/3;
187 int dmgBase = 0, dmgFuzz = 1;
188
189 if (spriteDist < k)
190 dmgBase = dmg3, dmgFuzz = dmg4;
191 else if (spriteDist < k*2)
192 dmgBase = dmg2, dmgFuzz = dmg3;
193 else if (spriteDist < blastRadius)
194 dmgBase = dmg1, dmgFuzz = dmg2;
195
196 if (dmgBase == dmgFuzz)
197 ++dmgFuzz;
198
199 dmgActor.extra = dmgBase + (krand()%(dmgFuzz-dmgBase));
200
201 if (!A_CheckSpriteFlags(otherSprite, SFLAG_NODAMAGEPUSH))
202 {
203 if (pOther->xvel < 0) pOther->xvel = 0;
204 pOther->xvel += (pSprite->extra<<2);
205 }
206
207 if (A_CheckSpriteFlags(otherSprite, SFLAG_DAMAGEEVENT))
208 VM_OnEventWithReturn(EVENT_POSTDAMAGESPRITE, spriteNum, -1, otherSprite);
209
210 #ifndef EDUKE32_STANDALONE
211 if (!FURY)
212 {
213 switch (DYNAMICTILEMAP(pOther->picnum))
214 {
215 case PODFEM1__STATIC:
216 case FEM1__STATIC:
217 case FEM2__STATIC:
218 case FEM3__STATIC:
219 case FEM4__STATIC:
220 case FEM5__STATIC:
221 case FEM6__STATIC:
222 case FEM7__STATIC:
223 case FEM8__STATIC:
224 case FEM9__STATIC:
225 case FEM10__STATIC:
226 case STATUE__STATIC:
227 case STATUEFLASH__STATIC:
228 case SPACEMARINE__STATIC:
229 case QUEBALL__STATIC:
230 case STRIPEBALL__STATIC:
231 A_DamageObject_Duke3D(otherSprite, spriteNum);
232 break;
233 }
234 }
235 #endif
236 }
237 #ifndef EDUKE32_STANDALONE
238 else if (!FURY && pSprite->extra == 0) dmgActor.extra = 0;
239 #endif
240
241 if (pOther->picnum != RADIUSEXPLOSION &&
242 pSprite->owner >= 0 && sprite[pSprite->owner].statnum < MAXSTATUS)
243 {
244 if (pOther->picnum == APLAYER)
245 {
246 auto pPlayer = g_player[P_GetP(pOther)].ps;
247
248 if (pPlayer->newowner >= 0)
249 G_ClearCameraView(pPlayer);
250 }
251
252 dmgActor.owner = pSprite->owner;
253 }
254 }
255 }
256
257 #define MAXDAMAGESECTORS 128
258
A_RadiusDamage(int const spriteNum,int const blastRadius,int const dmg1,int const dmg2,int const dmg3,int const dmg4)259 void A_RadiusDamage(int const spriteNum, int const blastRadius, int const dmg1, int const dmg2, int const dmg3, int const dmg4)
260 {
261 // Allow checking for radius damage in EVENT_DAMAGE(SPRITE/WALL/FLOOR/CEILING) events.
262 decltype(ud.returnvar) const parms = { blastRadius, dmg1, dmg2, dmg3, dmg4 };
263 Bmemcpy(ud.returnvar, parms, sizeof(parms));
264
265 auto const pSprite = (uspriteptr_t)&sprite[spriteNum];
266
267 int16_t numSectors, sectorList[MAXDAMAGESECTORS];
268 uint8_t * const sectorMap = (uint8_t *)Balloca((numsectors+7)>>3);
269 bfirst_search_init(sectorList, sectorMap, &numSectors, numsectors, pSprite->sectnum);
270
271 #ifndef EDUKE32_STANDALONE
272 int wallDamage = true;
273
274 // rockets from the Devastator skip propagating damage to other sectors
275 if (!FURY && (pSprite->picnum == RPG && pSprite->xrepeat < 11))
276 wallDamage = false;
277 #endif
278
279 uint8_t *wallTouched;
280 wallTouched = (uint8_t *)Balloca((numwalls+7)>>3);
281 Bmemset(wallTouched, 0, (numwalls+7)>>3);
282
283 uint8_t *wallCanSee;
284 wallCanSee = (uint8_t *)Balloca((numwalls+7)>>3);
285 Bmemset(wallCanSee, 0, (numwalls+7)>>3);
286
287 for (int sectorCount=0; sectorCount < numSectors; ++sectorCount)
288 {
289 int const sectorNum = sectorList[sectorCount];
290 auto const &listSector = sector[sectorNum];
291
292 vec2_t closest = {};
293 int32_t distance = INT32_MAX;
294
295 int const startWall = listSector.wallptr;
296 int const endWall = listSector.wallnum + startWall;
297
298 int w = startWall;
299
300 for (auto pWall = (uwallptr_t)&wall[startWall]; w < endWall; ++w, ++pWall)
301 {
302 vec2_t p = pSprite->pos.vec2;
303 int32_t walldist = blastRadius - 1;
304
305 if (bitmap_test(wallTouched, w) == 0)
306 walldist = getwalldist(p, w, &p);
307
308 if (walldist < blastRadius)
309 {
310 if (walldist < distance)
311 {
312 distance = walldist;
313 closest = p;
314 }
315
316 int16_t aSector = sectorNum;
317 vec3_t vect = { (((pWall->x + wall[pWall->point2].x) >> 1) + pSprite->x) >> 1,
318 (((pWall->y + wall[pWall->point2].y) >> 1) + pSprite->y) >> 1, pSprite->z };
319
320 updatesector(vect.x, vect.y, &aSector);
321
322 if (aSector == -1)
323 {
324 vect.vec2 = p;
325 aSector = sectorNum;
326 }
327
328 bitmap_set(wallTouched, w);
329
330 if (pWall->nextwall != -1)
331 bitmap_set(wallTouched, pWall->nextwall);
332
333 if (bitmap_test(wallCanSee, w) == 1 || cansee(vect.x, vect.y, vect.z, aSector, pSprite->x, pSprite->y, pSprite->z, pSprite->sectnum))
334 {
335 bitmap_set(wallCanSee, w);
336
337 if (pWall->nextwall != -1)
338 bitmap_set(wallCanSee, pWall->nextwall);
339
340 #ifndef EDUKE32_STANDALONE
341 if (wallDamage)
342 #endif
343 A_DamageWall_Internal(spriteNum, w, { p.x, p.y, pSprite->z }, pSprite->picnum);
344 }
345
346 int const nextSector = pWall->nextsector;
347
348 if (nextSector >= 0)
349 bfirst_search_try(sectorList, sectorMap, &numSectors, nextSector);
350
351 if (numSectors == MAXDAMAGESECTORS)
352 {
353 OSD_Printf("Sprite %d tried to damage more than %d sectors!\n", spriteNum, MAXDAMAGESECTORS);
354 goto wallsfinished;
355 }
356 }
357 }
358
359 if (distance >= blastRadius)
360 continue;
361
362 int32_t floorZ, ceilZ;
363 getzsofslope(sectorNum, closest.x, closest.y, &ceilZ, &floorZ);
364
365 if (((ceilZ - pSprite->z) >> 8) < blastRadius)
366 Sect_DamageCeiling_Internal(spriteNum, sectorNum);
367
368 if (((pSprite->z - floorZ) >> 8) < blastRadius)
369 Sect_DamageFloor_Internal(spriteNum, sectorNum);
370 }
371
372 wallsfinished:
373 int const randomZOffset = -ZOFFSET2 + (krand()&(ZOFFSET5-1));
374
375 for (int sectorCount=0; sectorCount < numSectors; ++sectorCount)
376 {
377 int damageSprite = headspritesect[sectorList[sectorCount]];
378
379 while (damageSprite >= 0)
380 {
381 int const nextSprite = nextspritesect[damageSprite];
382 auto pDamage = &sprite[damageSprite];
383
384 if (bitmap_test(g_radiusDmgStatnums, pDamage->statnum))
385 {
386 int spriteDist = dist(pSprite, pDamage);
387
388 if (pDamage->picnum == APLAYER)
389 spriteDist = FindDistance3D(pSprite->x - pDamage->x, pSprite->y - pDamage->y, pSprite->z - (pDamage->z - PHEIGHT));
390
391 if (spriteDist < blastRadius)
392 A_RadiusDamageObject_Internal(spriteNum, damageSprite, blastRadius, spriteDist, randomZOffset, dmg1, dmg2, dmg3, dmg4);
393 }
394
395 damageSprite = nextSprite;
396 }
397 }
398 }
399
400 // Maybe do a projectile transport via an SE7.
401 // <spritenum>: the projectile
402 // <i>: the SE7
403 // <fromunderp>: below->above change?
Proj_MaybeDoTransport(int32_t spriteNum,uspriteptr_t const pSEffector,int32_t fromunderp,int32_t daz)404 static int32_t Proj_MaybeDoTransport(int32_t spriteNum, uspriteptr_t const pSEffector, int32_t fromunderp, int32_t daz)
405 {
406 if (((int32_t) totalclock & UINT8_MAX) == actor[spriteNum].lasttransport)
407 return 0;
408
409 auto const pSprite = &sprite[spriteNum];
410 auto const otherse = (uspriteptr_t)&sprite[pSEffector->owner];
411
412 actor[spriteNum].lasttransport = ((int32_t) totalclock & UINT8_MAX);
413
414 pSprite->x += (otherse->x - pSEffector->x);
415 pSprite->y += (otherse->y - pSEffector->y);
416
417 // above->below
418 pSprite->z = (!fromunderp) ? sector[otherse->sectnum].ceilingz - daz + sector[pSEffector->sectnum].floorz
419 : sector[otherse->sectnum].floorz - daz + sector[pSEffector->sectnum].ceilingz;
420 // below->above
421
422 actor[spriteNum].bpos = sprite[spriteNum].pos;
423 changespritesect(spriteNum, otherse->sectnum);
424
425 return 1;
426 }
427
428 // Check whether sprite <s> is on/in a non-SE7 water sector.
429 // <othersectptr>: if not NULL, the sector on the other side.
A_CheckNoSE7Water(uspriteptr_t const pSprite,int sectNum,int sectLotag,int32_t * pOther)430 int A_CheckNoSE7Water(uspriteptr_t const pSprite, int sectNum, int sectLotag, int32_t *pOther)
431 {
432 if (sectLotag == ST_1_ABOVE_WATER || sectLotag == ST_2_UNDERWATER)
433 {
434 int const otherSect =
435 yax_getneighborsect(pSprite->x, pSprite->y, sectNum, sectLotag == ST_1_ABOVE_WATER ? YAX_FLOOR : YAX_CEILING);
436 int const otherLotag = (sectLotag == ST_1_ABOVE_WATER) ? ST_2_UNDERWATER : ST_1_ABOVE_WATER;
437
438 // If submerging, the lower sector MUST have lotag 2.
439 // If emerging, the upper sector MUST have lotag 1.
440 // This way, the x/y coordinates where above/below water
441 // changes can happen are the same.
442 if (otherSect >= 0 && sector[otherSect].lotag == otherLotag)
443 {
444 if (pOther)
445 *pOther = otherSect;
446 return 1;
447 }
448 }
449
450 return 0;
451 }
452
453 // Check whether to do a z position update of sprite <spritenum>.
454 // Returns:
455 // 0 if no.
456 // 1 if yes, but stayed inside [actor[].ceilingz+1, actor[].floorz].
457 // <0 if yes, but passed a TROR no-SE7 water boundary. -returnvalue-1 is the
458 // other-side sector number.
A_CheckNeedZUpdate(int32_t spriteNum,int32_t zChange,int32_t * pZcoord,int32_t * ceilhit,int32_t * florhit)459 static int32_t A_CheckNeedZUpdate(int32_t spriteNum, int32_t zChange, int32_t *pZcoord,
460 int32_t *ceilhit, int32_t *florhit)
461 {
462 if (zChange == 0)
463 return 0;
464
465 auto const pSprite = (uspriteptr_t)&sprite[spriteNum];
466 int const newZ = pSprite->z + (zChange >> 1);
467
468 *pZcoord = newZ;
469
470 int const clipDist = A_GetClipdist(spriteNum, -1);
471
472 VM_GetZRange(spriteNum, ceilhit, florhit, pSprite->statnum == STAT_PROJECTILE ? clipDist << 3 : clipDist);
473
474 if (newZ > actor[spriteNum].ceilingz && newZ <= actor[spriteNum].floorz)
475 return 1;
476
477 #ifdef YAX_ENABLE
478 int const sectNum = pSprite->sectnum;
479 int const sectLotag = sector[sectNum].lotag;
480 int32_t otherSect;
481
482 // Non-SE7 water.
483 // PROJECTILE_CHSECT
484 if ((zChange < 0 && sectLotag == ST_2_UNDERWATER) || (zChange > 0 && sectLotag == ST_1_ABOVE_WATER))
485 {
486 if (A_CheckNoSE7Water(pSprite, sprite[spriteNum].sectnum, sectLotag, &otherSect))
487 {
488 A_Spawn(spriteNum, WATERSPLASH2);
489 // NOTE: Don't tweak its z position afterwards like with
490 // SE7-induced projectile teleportation. It doesn't look good
491 // with TROR water.
492
493 actor[spriteNum].flags |= SFLAG_DIDNOSE7WATER;
494 return -otherSect-1;
495 }
496 }
497 #endif
498
499 return 2;
500 }
501
A_GetClipdist(int spriteNum,int clipDist)502 int A_GetClipdist(int spriteNum, int clipDist)
503 {
504 if (clipDist < 0)
505 {
506 auto const pSprite = &sprite[spriteNum];
507 int const isEnemy = A_CheckEnemySprite(pSprite);
508
509 if (A_CheckSpriteFlags(spriteNum, SFLAG_REALCLIPDIST))
510 clipDist = pSprite->clipdist << 2;
511 else if ((pSprite->cstat & 48) == 16)
512 clipDist = 0;
513 else if (isEnemy)
514 {
515 if (pSprite->xrepeat > 60)
516 clipDist = 1024;
517 #ifndef EDUKE32_STANDALONE
518 else if (!FURY && pSprite->picnum == LIZMAN)
519 clipDist = 292;
520 #endif
521 else if (A_CheckSpriteFlags(spriteNum, SFLAG_BADGUY))
522 clipDist = pSprite->clipdist << 2;
523 else
524 clipDist = 192;
525 }
526 else
527 {
528 if (pSprite->statnum == STAT_PROJECTILE && (SpriteProjectile[spriteNum].workslike & PROJECTILE_REALCLIPDIST) == 0)
529 clipDist = 16;
530 else
531 clipDist = pSprite->clipdist << 2;
532 }
533 }
534
535 return clipDist;
536 }
537
A_MoveSpriteClipdist(int32_t spriteNum,vec3_t const * const change,uint32_t clipType,int32_t clipDist)538 int32_t A_MoveSpriteClipdist(int32_t spriteNum, vec3_t const * const change, uint32_t clipType, int32_t clipDist)
539 {
540 auto const pSprite = &sprite[spriteNum];
541 int const isEnemy = A_CheckEnemySprite(pSprite);
542 vec2_t const oldPos = pSprite->pos.vec2;
543
544 // check to make sure the netcode didn't leave a deleted sprite in the sprite lists.
545 Bassert(pSprite->sectnum < MAXSECTORS);
546
547 #ifndef EDUKE32_STANDALONE
548 if (!FURY && (pSprite->statnum == STAT_MISC || (isEnemy && pSprite->xrepeat < 4)))
549 {
550 pSprite->x += change->x;
551 pSprite->y += change->y;
552 pSprite->z += change->z;
553
554 if (isEnemy)
555 setsprite(spriteNum, &pSprite->pos);
556
557 return 0;
558 }
559 #endif
560
561 setsprite(spriteNum, &pSprite->pos);
562
563 if (!(change->x|change->y|change->z))
564 return 0;
565
566 clipDist = A_GetClipdist(spriteNum, clipDist);
567
568 int16_t newSectnum = pSprite->sectnum;
569 #ifndef EDUKE32_STANDALONE
570 int const oldSectnum = newSectnum;
571 #endif
572
573 // Handle horizontal movement first.
574
575 int returnValue;
576 int32_t diffZ;
577 spriteheightofs(spriteNum, &diffZ, 1);
578
579 if (pSprite->statnum == STAT_PROJECTILE)
580 returnValue = clipmovex(&pSprite->pos, &newSectnum, change->x << 13, change->y << 13, clipDist, diffZ >> 3, diffZ >> 3, clipType, 1);
581 else
582 {
583 pSprite->z -= diffZ >> 1;
584 returnValue = clipmove(&pSprite->pos, &newSectnum, change->x << 13, change->y << 13, clipDist, ZOFFSET6, ZOFFSET6, clipType);
585 pSprite->z += diffZ >> 1;
586 }
587
588 // Testing: For some reason the assert below this was tripping for clients
589 EDUKE32_UNUSED int16_t dbg_ClipMoveSectnum = newSectnum;
590
591 if (isEnemy)
592 {
593 // Handle potential stayput condition (map-provided or hard-coded).
594 if (newSectnum < 0 || ((actor[spriteNum].stayput >= 0 && actor[spriteNum].stayput != newSectnum)
595 || ((g_tile[pSprite->picnum].flags & SFLAG_NOWATERSECTOR) && sector[newSectnum].lotag == ST_1_ABOVE_WATER)
596 #ifndef EDUKE32_STANDALONE
597 || (!FURY && pSprite->picnum == BOSS2 && pSprite->pal == 0 && sector[newSectnum].lotag != ST_3)
598 || (!FURY && (pSprite->picnum == BOSS1 || pSprite->picnum == BOSS2) && sector[newSectnum].lotag == ST_1_ABOVE_WATER)
599 || (!FURY && sector[oldSectnum].lotag != ST_1_ABOVE_WATER && sector[newSectnum].lotag == ST_1_ABOVE_WATER
600 && (pSprite->picnum == LIZMAN || (pSprite->picnum == LIZTROOP && pSprite->zvel == 0)))
601 #endif
602 ))
603 {
604 pSprite->pos.vec2 = oldPos;
605
606 // NOTE: in Duke3D, LIZMAN on water takes on random angle here.
607
608 setsprite(spriteNum, &pSprite->pos);
609
610 if (newSectnum < 0)
611 newSectnum = 0;
612
613 return 16384+newSectnum;
614 }
615
616 if ((returnValue&49152) >= 32768 && actor[spriteNum].cgg==0)
617 pSprite->ang += 768;
618 }
619
620 EDUKE32_UNUSED int16_t dbg_newSectnum2 = newSectnum;
621
622 if (newSectnum == -1)
623 {
624 newSectnum = pSprite->sectnum;
625 // OSD_Printf("%s:%d wtf\n",__FILE__,__LINE__);
626 }
627 else if (newSectnum != pSprite->sectnum)
628 {
629 changespritesect(spriteNum, newSectnum);
630 // A_GetZLimits(spritenum);
631 }
632
633 Bassert(newSectnum == pSprite->sectnum);
634
635 int newZ = pSprite->z;
636 int32_t ceilhit, florhit;
637 int const doZUpdate = change->z ? A_CheckNeedZUpdate(spriteNum, change->z, &newZ, &ceilhit, &florhit) : 0;
638
639 // Update sprite's z positions and (for TROR) maybe the sector number.
640 if (doZUpdate == 2)
641 {
642 if (returnValue == 0)
643 returnValue = change->z < 0 ? ceilhit : florhit;
644 }
645 else if (doZUpdate)
646 {
647 pSprite->z = newZ;
648 #ifdef YAX_ENABLE
649 if (doZUpdate < 0)
650 {
651 // If we passed a TROR no-SE7 water boundary, signal to the outside
652 // that the ceiling/floor was not hit. However, this is not enough:
653 // later, code checks for (retval&49152)!=49152
654 // [i.e. not "was ceiling or floor hit", but "was no sprite hit"]
655 // and calls G_WeaponHitCeilingOrFloor() then, so we need to set
656 // actor[].flags |= SFLAG_DIDNOSE7WATER in A_CheckNeedZUpdate()
657 // previously.
658 // XXX: Why is this contrived data flow necessary? (If at all.)
659 changespritesect(spriteNum, -doZUpdate-1);
660 return 0;
661 }
662
663 if (yax_getbunch(newSectnum, (change->z>0))>=0
664 && (SECTORFLD(newSectnum,stat, (change->z>0))&yax_waltosecmask(clipType))==0)
665 {
666 setspritez(spriteNum, &pSprite->pos);
667 }
668 #endif
669 }
670 else if (change->z != 0 && returnValue == 0)
671 returnValue = 16384+newSectnum;
672
673 if (returnValue == 16384 + newSectnum)
674 {
675 if (pSprite->statnum == STAT_PROJECTILE)
676 {
677 // Projectile sector changes due to transport SEs (SE7_PROJECTILE).
678 // PROJECTILE_CHSECT
679 for (bssize_t SPRITES_OF(STAT_TRANSPORT, otherSpriteNum))
680 {
681 if (sprite[otherSpriteNum].sectnum == newSectnum)
682 {
683 int const sectLotag = sector[newSectnum].lotag;
684
685 if (sectLotag == ST_1_ABOVE_WATER && newZ >= actor[spriteNum].floorz)
686 if (Proj_MaybeDoTransport(spriteNum, (uspriteptr_t)&sprite[otherSpriteNum], 0, newZ))
687 return 0;
688
689 if (sectLotag == ST_2_UNDERWATER && newZ <= actor[spriteNum].ceilingz)
690 if (Proj_MaybeDoTransport(spriteNum, (uspriteptr_t)&sprite[otherSpriteNum], 1, newZ))
691 return 0;
692 }
693 }
694 }
695 }
696
697 return returnValue;
698 }
699
700 int32_t block_deletesprite = 0;
701
702 #ifdef POLYMER
A_DeleteLight(int32_t s)703 static void A_DeleteLight(int32_t s)
704 {
705 if (practor[s].lightId >= 0)
706 polymer_deletelight(practor[s].lightId);
707 practor[s].lightId = -1;
708 practor[s].lightptr = NULL;
709 }
710
G_Polymer_UnInit(void)711 void G_Polymer_UnInit(void)
712 {
713 int32_t i;
714
715 for (i=0; i<MAXSPRITES; i++)
716 A_DeleteLight(i);
717 }
718 #endif
719
720 // deletesprite() game wrapper
A_DeleteSprite(int spriteNum)721 void A_DeleteSprite(int spriteNum)
722 {
723 if (EDUKE32_PREDICT_FALSE(block_deletesprite))
724 {
725 OSD_Printf(OSD_ERROR "A_DeleteSprite(): tried to remove sprite %d in EVENT_EGS\n", spriteNum);
726 return;
727 }
728
729 if (VM_HaveEvent(EVENT_KILLIT))
730 {
731 int32_t playerDist;
732 int playerNum = A_FindPlayer(&sprite[spriteNum], &playerDist);
733
734 if (VM_ExecuteEvent(EVENT_KILLIT, spriteNum, playerNum, playerDist))
735 return;
736 }
737
738 #ifdef POLYMER
739 if (practor[spriteNum].lightptr != NULL && videoGetRenderMode() == REND_POLYMER)
740 A_DeleteLight(spriteNum);
741 #endif
742
743 // AMBIENT_SFX_PLAYING
744 if (sprite[spriteNum].picnum == MUSICANDSFX && actor[spriteNum].t_data[0] == 1)
745 S_StopEnvSound(sprite[spriteNum].lotag, spriteNum);
746
747 #ifdef NETCODE_DISABLE
748 deletesprite(spriteNum);
749 #else
750 Net_DeleteSprite(spriteNum);
751 #endif
752 }
753
A_AddToDeleteQueue(int spriteNum)754 void A_AddToDeleteQueue(int spriteNum)
755 {
756 if (g_netClient || (g_deleteQueueSize == 0)) // [75] Clients should not use SpriteDeletionQueue[] and just set the sprites invisible immediately in A_DeleteSprite
757 {
758 A_DeleteSprite(spriteNum);
759 return;
760 }
761
762 auto &deleteSpriteNum = SpriteDeletionQueue[g_spriteDeleteQueuePos];
763
764 if (deleteSpriteNum >= 0 && actor[deleteSpriteNum].flags & SFLAG_QUEUEDFORDELETE)
765 A_DeleteSprite(deleteSpriteNum);
766
767 deleteSpriteNum = spriteNum;
768 actor[spriteNum].flags |= SFLAG_QUEUEDFORDELETE;
769 g_spriteDeleteQueuePos = (g_spriteDeleteQueuePos+1)%g_deleteQueueSize;
770 }
771
A_SpawnMultiple(int spriteNum,int tileNum,int spawnCnt)772 void A_SpawnMultiple(int spriteNum, int tileNum, int spawnCnt)
773 {
774 auto const pSprite = &sprite[spriteNum];
775
776 for (; spawnCnt>0; spawnCnt--)
777 {
778 int const j = A_InsertSprite(pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z - (krand() % (47 << 8)), tileNum, -32, 8,
779 8, krand() & 2047, 0, 0, spriteNum, 5);
780 A_Spawn(-1, j);
781 sprite[j].cstat = krand()&12;
782 }
783 }
784
785 #ifndef EDUKE32_STANDALONE
A_DoGuts(int spriteNum,int tileNum,int spawnCnt)786 void A_DoGuts(int spriteNum, int tileNum, int spawnCnt)
787 {
788 auto const pSprite = (uspriteptr_t)&sprite[spriteNum];
789 vec2_t repeat = { 32, 32 };
790
791 if (A_CheckEnemySprite(pSprite) && pSprite->xrepeat < 16)
792 repeat.x = repeat.y = 8;
793
794 int gutZ = pSprite->z - ZOFFSET3;
795 int floorz = getflorzofslope(pSprite->sectnum, pSprite->x, pSprite->y);
796
797 if (gutZ > (floorz-ZOFFSET3))
798 gutZ = floorz-ZOFFSET3;
799
800 if (pSprite->picnum == COMMANDER)
801 gutZ -= (24<<8);
802
803 for (bssize_t j=spawnCnt; j>0; j--)
804 {
805 int const i = A_InsertSprite(pSprite->sectnum, pSprite->x + (krand() & 255) - 128,
806 pSprite->y + (krand() & 255) - 128, gutZ - (krand() & 8191), tileNum, -32, repeat.x,
807 repeat.y, krand() & 2047, 48 + (krand() & 31), -512 - (krand() & 2047), spriteNum, 5);
808
809 if (PN(i) == JIBS2)
810 {
811 sprite[i].xrepeat >>= 2;
812 sprite[i].yrepeat >>= 2;
813 }
814
815 sprite[i].pal = pSprite->pal;
816 }
817 }
818
A_DoGutsDir(int spriteNum,int tileNum,int spawnCnt)819 void A_DoGutsDir(int spriteNum, int tileNum, int spawnCnt)
820 {
821 auto const s = (uspriteptr_t)&sprite[spriteNum];
822 vec2_t repeat = { 32, 32 };
823
824 if (A_CheckEnemySprite(s) && s->xrepeat < 16)
825 repeat.x = repeat.y = 8;
826
827 int gutZ = s->z-ZOFFSET3;
828 int floorZ = getflorzofslope(s->sectnum,s->x,s->y);
829
830 if (gutZ > (floorZ-ZOFFSET3))
831 gutZ = floorZ-ZOFFSET3;
832
833 if (s->picnum == COMMANDER)
834 gutZ -= (24<<8);
835
836 for (bssize_t j=spawnCnt; j>0; j--)
837 {
838 int const i = A_InsertSprite(s->sectnum, s->x, s->y, gutZ, tileNum, -32, repeat.x, repeat.y, krand() & 2047,
839 256 + (krand() & 127), -512 - (krand() & 2047), spriteNum, 5);
840 sprite[i].pal = s->pal;
841 }
842 }
843 #endif
844
G_ToggleWallInterpolation(int32_t wallNum,int32_t setInterpolation)845 static int32_t G_ToggleWallInterpolation(int32_t wallNum, int32_t setInterpolation)
846 {
847 if (setInterpolation)
848 {
849 return G_SetInterpolation(&wall[wallNum].x) || G_SetInterpolation(&wall[wallNum].y);
850 }
851 else
852 {
853 G_StopInterpolation(&wall[wallNum].x);
854 G_StopInterpolation(&wall[wallNum].y);
855 return 0;
856 }
857 }
858
Sect_ToggleInterpolation(int sectNum,int setInterpolation)859 void Sect_ToggleInterpolation(int sectNum, int setInterpolation)
860 {
861 for (bssize_t j = sector[sectNum].wallptr, endwall = sector[sectNum].wallptr + sector[sectNum].wallnum; j < endwall; j++)
862 {
863 G_ToggleWallInterpolation(j, setInterpolation);
864
865 int const nextWall = wall[j].nextwall;
866
867 if (nextWall >= 0)
868 {
869 G_ToggleWallInterpolation(nextWall, setInterpolation);
870 G_ToggleWallInterpolation(wall[nextWall].point2, setInterpolation);
871 }
872 }
873 }
874
move_rotfixed_sprite(int32_t spriteNum,int32_t pivotSpriteNum,int32_t pivotAngle)875 static int32_t move_rotfixed_sprite(int32_t spriteNum, int32_t pivotSpriteNum, int32_t pivotAngle)
876 {
877 if ((ROTFIXSPR_STATNUMP(sprite[spriteNum].statnum) ||
878 ((sprite[spriteNum].statnum == STAT_ACTOR || sprite[spriteNum].statnum == STAT_ZOMBIEACTOR) &&
879 A_CheckSpriteFlags(spriteNum, SFLAG_ROTFIXED))) &&
880 actor[spriteNum].t_data[7] == (ROTFIXSPR_MAGIC | pivotSpriteNum))
881 {
882 rotatevec(*(vec2_t *)&actor[spriteNum].t_data[8], pivotAngle & 2047, &sprite[spriteNum].pos.vec2);
883 sprite[spriteNum].x += sprite[pivotSpriteNum].x;
884 sprite[spriteNum].y += sprite[pivotSpriteNum].y;
885 return 0;
886 }
887
888 return 1;
889 }
890
A_MoveSector(int spriteNum)891 void A_MoveSector(int spriteNum)
892 {
893 // T1,T2 and T3 are used for all the sector moving stuff!!!
894
895 int32_t playerDist;
896 auto const pSprite = &sprite[spriteNum];
897 int const playerNum = A_FindPlayer(pSprite, &playerDist);
898 int const rotateAngle = VM_OnEvent(EVENT_MOVESECTOR, spriteNum, playerNum, playerDist, T3(spriteNum));
899 int originIdx = T2(spriteNum);
900
901 pSprite->x += (pSprite->xvel * (sintable[(pSprite->ang + 512) & 2047])) >> 14;
902 pSprite->y += (pSprite->xvel * (sintable[pSprite->ang & 2047])) >> 14;
903
904 int const endWall = sector[pSprite->sectnum].wallptr + sector[pSprite->sectnum].wallnum;
905
906 for (bssize_t wallNum = sector[pSprite->sectnum].wallptr; wallNum < endWall; wallNum++)
907 {
908 vec2_t const origin = g_origins[originIdx];
909 vec2_t result;
910 rotatevec(origin, rotateAngle & 2047, &result);
911 dragpoint(wallNum, pSprite->x + result.x, pSprite->y + result.y, 0);
912
913 originIdx++;
914 }
915 }
916
917 // NOTE: T5 is AC_ACTION_ID
918 #define LIGHTRAD_PICOFS(i) (T5(i) ? *(apScript + T5(i)) + (*(apScript + T5(i) + 2)) * AC_CURFRAME(actor[i].t_data) : 0)
919
920 // this is the same crap as in game.c's tspr manipulation. puke.
921 // XXX: may access tilesizy out-of-bounds by bad user code.
922 #define LIGHTRAD(spriteNum, s) (s->yrepeat * tilesiz[s->picnum + LIGHTRAD_PICOFS(spriteNum)].y)
923 #define LIGHTRAD2(spriteNum, s) ((s->yrepeat + ((rand() % s->yrepeat)>>2)) * tilesiz[s->picnum + LIGHTRAD_PICOFS(spriteNum)].y)
924
G_AddGameLight(int lightRadius,int spriteNum,int zOffset,int lightRange,int lightColor,int lightPrio)925 void G_AddGameLight(int lightRadius, int spriteNum, int zOffset, int lightRange, int lightColor, int lightPrio)
926 {
927 #ifdef POLYMER
928 auto const s = &sprite[spriteNum];
929
930 if (videoGetRenderMode() != REND_POLYMER || pr_lighting != 1)
931 return;
932
933 if (practor[spriteNum].lightptr == NULL)
934 {
935 #pragma pack(push, 1)
936 _prlight mylight;
937 #pragma pack(pop)
938 Bmemset(&mylight, 0, sizeof(mylight));
939
940 mylight.sector = s->sectnum;
941 mylight.x = s->x;
942 mylight.y = s->y;
943 mylight.z = s->z - zOffset;
944 mylight.color[0] = lightColor & 255;
945 mylight.color[1] = (lightColor >> 8) & 255;
946 mylight.color[2] = (lightColor >> 16) & 255;
947 mylight.radius = lightRadius;
948 practor[spriteNum].lightmaxrange = mylight.range = lightRange;
949
950 mylight.priority = lightPrio;
951 mylight.tilenum = 0;
952
953 mylight.publicflags.emitshadow = 1;
954 mylight.publicflags.negative = 0;
955
956 practor[spriteNum].lightId = polymer_addlight(&mylight);
957 if (practor[spriteNum].lightId >= 0)
958 practor[spriteNum].lightptr = &prlights[practor[spriteNum].lightId];
959 return;
960 }
961
962 s->z -= zOffset;
963
964 if (lightRange<practor[spriteNum].lightmaxrange>> 1)
965 practor[spriteNum].lightmaxrange = 0;
966
967 if (lightRange > practor[spriteNum].lightmaxrange || lightPrio != practor[spriteNum].lightptr->priority ||
968 Bmemcmp(&sprite[spriteNum], practor[spriteNum].lightptr, sizeof(int32_t) * 3))
969 {
970 if (lightRange > practor[spriteNum].lightmaxrange)
971 practor[spriteNum].lightmaxrange = lightRange;
972
973 Bmemcpy(practor[spriteNum].lightptr, &sprite[spriteNum], sizeof(int32_t) * 3);
974 practor[spriteNum].lightptr->sector = s->sectnum;
975 practor[spriteNum].lightptr->flags.invalidate = 1;
976 }
977
978 practor[spriteNum].lightptr->priority = lightPrio;
979 practor[spriteNum].lightptr->range = lightRange;
980 practor[spriteNum].lightptr->color[0] = lightColor & 255;
981 practor[spriteNum].lightptr->color[1] = (lightColor >> 8) & 255;
982 practor[spriteNum].lightptr->color[2] = (lightColor >> 16) & 255;
983
984 s->z += zOffset;
985
986 #else
987 UNREFERENCED_PARAMETER(lightRadius);
988 UNREFERENCED_PARAMETER(spriteNum);
989 UNREFERENCED_PARAMETER(zOffset);
990 UNREFERENCED_PARAMETER(lightRange);
991 UNREFERENCED_PARAMETER(lightColor);
992 UNREFERENCED_PARAMETER(lightPrio);
993 #endif
994 }
995
A_MaybeAwakenBadGuys(int const spriteNum)996 ACTOR_STATIC void A_MaybeAwakenBadGuys(int const spriteNum)
997 {
998 if (sprite[spriteNum].sectnum == MAXSECTORS)
999 return;
1000
1001 if (A_CheckSpriteFlags(spriteNum, SFLAG_WAKEUPBADGUYS))
1002 {
1003 auto const pSprite = (uspriteptr_t)&sprite[spriteNum];
1004
1005 for (bssize_t nextSprite, SPRITES_OF_STAT_SAFE(STAT_ZOMBIEACTOR, spriteNum, nextSprite))
1006 {
1007 if (A_CheckEnemySprite(&sprite[spriteNum]))
1008 {
1009 if (sprite[spriteNum].sectnum == pSprite->sectnum
1010 || sprite[spriteNum].sectnum == nextsectorneighborz(pSprite->sectnum, sector[pSprite->sectnum].floorz, 1, 1)
1011 || cansee(pSprite->x, pSprite->y, pSprite->z - PHEIGHT, pSprite->sectnum, sprite[spriteNum].x, sprite[spriteNum].y,
1012 sprite[spriteNum].z - PHEIGHT, sprite[spriteNum].sectnum))
1013 {
1014 actor[spriteNum].timetosleep = SLEEPTIME;
1015 A_PlayAlertSound(spriteNum);
1016 changespritestat(spriteNum, STAT_ACTOR);
1017
1018 if (A_CheckSpriteFlags(spriteNum, SFLAG_WAKEUPBADGUYS))
1019 A_MaybeAwakenBadGuys(spriteNum);
1020 }
1021 }
1022 }
1023 }
1024 }
1025
1026
1027 // sleeping monsters, etc
G_MoveZombieActors(void)1028 ACTOR_STATIC void G_MoveZombieActors(void)
1029 {
1030 int spriteNum = headspritestat[STAT_ZOMBIEACTOR], canSeePlayer;
1031
1032 while (spriteNum >= 0)
1033 {
1034 int const nextSprite = nextspritestat[spriteNum];
1035 int32_t playerDist;
1036 auto const pSprite = &sprite[spriteNum];
1037 int const playerNum = A_FindPlayer(pSprite, &playerDist);
1038 auto const pPlayer = g_player[playerNum].ps;
1039
1040 if (sprite[pPlayer->i].extra > 0)
1041 {
1042 if (playerDist < 30000)
1043 {
1044 actor[spriteNum].timetosleep++;
1045 if (actor[spriteNum].timetosleep >= (playerDist>>8))
1046 {
1047 if (pPlayer->newowner == -1 && A_CheckEnemySprite(pSprite))
1048 {
1049 vec3_t const p = { pPlayer->pos.x + 64 - (krand() & 127),
1050 pPlayer->pos.y + 64 - (krand() & 127),
1051 pPlayer->pos.z - (krand() % ZOFFSET5) };
1052
1053 int16_t pSectnum = pPlayer->cursectnum;
1054
1055 updatesector(p.x, p.y, &pSectnum);
1056
1057 if (pSectnum == -1)
1058 {
1059 spriteNum = nextSprite;
1060 continue;
1061 }
1062
1063 vec3_t const s = { pSprite->x + 64 - (krand() & 127),
1064 pSprite->y + 64 - (krand() & 127),
1065 pSprite->z - (krand() % (52 << 8)) };
1066
1067 int16_t sectNum = pSprite->sectnum;
1068
1069 updatesector(s.x, s.y, §Num);
1070
1071 if (sectNum == -1)
1072 {
1073 spriteNum = nextSprite;
1074 continue;
1075 }
1076
1077 canSeePlayer = cansee(s.x, s.y, s.z, sectNum, p.x, p.y, p.z, pSectnum);
1078 }
1079 else
1080 canSeePlayer = cansee(pSprite->x, pSprite->y, pSprite->z - ((krand() & 31) << 8), pSprite->sectnum, pPlayer->opos.x,
1081 pPlayer->opos.y, pPlayer->opos.z - ((krand() & 31) << 8), pPlayer->cursectnum);
1082
1083 if (canSeePlayer)
1084 {
1085 switch (DYNAMICTILEMAP(pSprite->picnum))
1086 {
1087 #ifndef EDUKE32_STANDALONE
1088 case RUBBERCAN__STATIC:
1089 case EXPLODINGBARREL__STATIC:
1090 case WOODENHORSE__STATIC:
1091 case HORSEONSIDE__STATIC:
1092 case CANWITHSOMETHING__STATIC:
1093 case CANWITHSOMETHING2__STATIC:
1094 case CANWITHSOMETHING3__STATIC:
1095 case CANWITHSOMETHING4__STATIC:
1096 case FIREBARREL__STATIC:
1097 case FIREVASE__STATIC:
1098 case NUKEBARREL__STATIC:
1099 case NUKEBARRELDENTED__STATIC:
1100 case NUKEBARRELLEAKED__STATIC:
1101 case TRIPBOMB__STATIC:
1102 if (!FURY)
1103 {
1104 pSprite->shade = ((sector[pSprite->sectnum].ceilingstat & 1) && A_CheckSpriteFlags(spriteNum, SFLAG_NOSHADE) == 0)
1105 ? sector[pSprite->sectnum].ceilingshade
1106 : sector[pSprite->sectnum].floorshade;
1107 actor[spriteNum].timetosleep = 0;
1108 changespritestat(spriteNum, STAT_STANDABLE);
1109 break;
1110 }
1111 fallthrough__;
1112
1113 case RECON__STATIC:
1114 if (!FURY && pSprite->picnum == RECON)
1115 CS(spriteNum) |= 257;
1116 fallthrough__;
1117 #endif
1118 default:
1119 if (A_CheckSpriteFlags(spriteNum, SFLAG_USEACTIVATOR) && sector[sprite[spriteNum].sectnum].lotag & 16384)
1120 break;
1121
1122 actor[spriteNum].timetosleep = 0;
1123 A_PlayAlertSound(spriteNum);
1124 changespritestat(spriteNum, STAT_ACTOR);
1125
1126 if (A_CheckSpriteFlags(spriteNum, SFLAG_WAKEUPBADGUYS))
1127 A_MaybeAwakenBadGuys(spriteNum);
1128
1129 break;
1130 }
1131 }
1132 else
1133 actor[spriteNum].timetosleep = 0;
1134 }
1135 }
1136
1137 if (A_CheckEnemySprite(pSprite) && A_CheckSpriteFlags(spriteNum,SFLAG_NOSHADE) == 0)
1138 {
1139 pSprite->shade = (sector[pSprite->sectnum].ceilingstat & 1)
1140 ? sector[pSprite->sectnum].ceilingshade
1141 : sector[pSprite->sectnum].floorshade;
1142 }
1143 }
1144
1145 spriteNum = nextSprite;
1146 }
1147 }
1148
1149 // stupid name, but it's what the function does.
G_FindExplosionInSector(int const sectNum)1150 static FORCE_INLINE int G_FindExplosionInSector(int const sectNum)
1151 {
1152 for (bssize_t SPRITES_OF(STAT_MISC, i))
1153 if (PN(i) == EXPLOSION2 && sectNum == SECT(i))
1154 return i;
1155
1156 return -1;
1157 }
1158
P_Nudge(int playerNum,int spriteNum,int shiftLeft)1159 static FORCE_INLINE void P_Nudge(int playerNum, int spriteNum, int shiftLeft)
1160 {
1161 g_player[playerNum].ps->vel.x += actor[spriteNum].extra * (sintable[(actor[spriteNum].ang + 512) & 2047]) << shiftLeft;
1162 g_player[playerNum].ps->vel.y += actor[spriteNum].extra * (sintable[actor[spriteNum].ang & 2047]) << shiftLeft;
1163 }
1164
A_IncurDamage(int const spriteNum)1165 int A_IncurDamage(int const spriteNum)
1166 {
1167 auto const pSprite = &sprite[spriteNum];
1168 auto const pActor = &actor[spriteNum];
1169
1170 // dmg->picnum check: safety, since it might have been set to <0 from CON.
1171 if (pActor->extra < 0 || pSprite->extra < 0 || pActor->picnum < 0)
1172 {
1173 pActor->extra = -1;
1174 return -1;
1175 }
1176
1177 if (pSprite->picnum == APLAYER)
1178 {
1179 if (ud.god && pActor->picnum != SHRINKSPARK)
1180 return -1;
1181
1182 int const playerNum = P_GetP(pSprite);
1183
1184 if (pActor->owner >= 0 && (sprite[pActor->owner].picnum == APLAYER))
1185 {
1186 if (
1187 (ud.ffire == 0) &&
1188 (spriteNum != pActor->owner) && // Not damaging self.
1189 ((g_gametypeFlags[ud.coop] & GAMETYPE_PLAYERSFRIENDLY) ||
1190 ((g_gametypeFlags[ud.coop] & GAMETYPE_TDM) && g_player[playerNum].ps->team == g_player[P_Get(pActor->owner)].ps->team))
1191 )
1192 {
1193 // Nullify damage and cancel.
1194 pActor->owner = -1;
1195 pActor->extra = -1;
1196 return -1;
1197 }
1198 }
1199
1200 pSprite->extra -= pActor->extra;
1201
1202 if (pActor->owner >= 0 && pSprite->extra <= 0 && pActor->picnum != FREEZEBLAST)
1203 {
1204 int const damageOwner = pActor->owner;
1205 pSprite->extra = 0;
1206
1207 g_player[playerNum].ps->wackedbyactor = damageOwner;
1208
1209 if (sprite[damageOwner].picnum == APLAYER && playerNum != P_Get(damageOwner))
1210 g_player[playerNum].ps->frag_ps = P_Get(damageOwner);
1211
1212 pActor->owner = g_player[playerNum].ps->i;
1213 }
1214
1215 switch (DYNAMICTILEMAP(pActor->picnum))
1216 {
1217 case RADIUSEXPLOSION__STATIC:
1218 case SEENINE__STATIC:
1219 #ifndef EDUKE32_STANDALONE
1220 case RPG__STATIC:
1221 case HYDRENT__STATIC:
1222 case HEAVYHBOMB__STATIC:
1223 case OOZFILTER__STATIC:
1224 case EXPLODINGBARREL__STATIC:
1225 #endif
1226 P_Nudge(playerNum, spriteNum, 2);
1227 break;
1228
1229 default:
1230 P_Nudge(playerNum, spriteNum, (A_CheckSpriteFlags(pActor->owner, SFLAG_PROJECTILE) &&
1231 (SpriteProjectile[pActor->owner].workslike & PROJECTILE_RPG))
1232 ? 2
1233 : 1);
1234 break;
1235 }
1236
1237 pActor->extra = -1;
1238 return pActor->picnum;
1239 }
1240
1241 if (pActor->extra == 0 && pActor->picnum == SHRINKSPARK && pSprite->xrepeat < 24)
1242 return -1;
1243
1244 pSprite->extra -= pActor->extra;
1245
1246 if (pSprite->picnum != RECON && pSprite->owner >= 0 && sprite[pSprite->owner].statnum < MAXSTATUS)
1247 pSprite->owner = pActor->owner;
1248
1249 pActor->extra = -1;
1250
1251 return pActor->picnum;
1252 }
1253
A_MoveCyclers(void)1254 void A_MoveCyclers(void)
1255 {
1256 for (bssize_t i=g_cyclerCnt-1; i>=0; i--)
1257 {
1258 int16_t *const pCycler = g_cyclers[i];
1259 int const sectNum = pCycler[0];
1260 int spriteShade = pCycler[2];
1261 int const floorShade = pCycler[3];
1262 int sectorShade = clamp(floorShade + (sintable[pCycler[1] & 2047] >> 10), spriteShade, floorShade);
1263
1264 pCycler[1] += sector[sectNum].extra;
1265
1266 if (pCycler[5]) // angle 1536...
1267 {
1268 walltype *pWall = &wall[sector[sectNum].wallptr];
1269
1270 for (bssize_t wallsLeft = sector[sectNum].wallnum; wallsLeft > 0; wallsLeft--, pWall++)
1271 {
1272 if (pWall->hitag != 1)
1273 {
1274 pWall->shade = sectorShade;
1275
1276 if ((pWall->cstat&2) && pWall->nextwall >= 0)
1277 wall[pWall->nextwall].shade = sectorShade;
1278 }
1279 }
1280
1281 sector[sectNum].floorshade = sector[sectNum].ceilingshade = sectorShade;
1282 }
1283 }
1284 }
1285
A_MoveDummyPlayers(void)1286 void A_MoveDummyPlayers(void)
1287 {
1288 int spriteNum = headspritestat[STAT_DUMMYPLAYER];
1289
1290 while (spriteNum >= 0)
1291 {
1292 int const playerNum = P_Get(OW(spriteNum));
1293 auto const pPlayer = g_player[playerNum].ps;
1294 int const nextSprite = nextspritestat[spriteNum];
1295 int const playerSectnum = pPlayer->cursectnum;
1296
1297 if (pPlayer->on_crane >= 0 || (playerSectnum >= 0 && sector[playerSectnum].lotag != ST_1_ABOVE_WATER) || sprite[pPlayer->i].extra <= 0)
1298 {
1299 pPlayer->dummyplayersprite = -1;
1300 DELETE_SPRITE_AND_CONTINUE(spriteNum);
1301 }
1302 else
1303 {
1304 if (pPlayer->on_ground && pPlayer->on_warping_sector == 1 && playerSectnum >= 0 && sector[playerSectnum].lotag == ST_1_ABOVE_WATER)
1305 {
1306 CS(spriteNum) = 257;
1307 SZ(spriteNum) = sector[SECT(spriteNum)].ceilingz+(27<<8);
1308 SA(spriteNum) = fix16_to_int(pPlayer->q16ang);
1309 if (T1(spriteNum) == 8)
1310 T1(spriteNum) = 0;
1311 else T1(spriteNum)++;
1312 }
1313 else
1314 {
1315 if (sector[SECT(spriteNum)].lotag != ST_2_UNDERWATER) SZ(spriteNum) = sector[SECT(spriteNum)].floorz;
1316 CS(spriteNum) = 32768;
1317 }
1318 }
1319
1320 SX(spriteNum) += (pPlayer->pos.x-pPlayer->opos.x);
1321 SY(spriteNum) += (pPlayer->pos.y-pPlayer->opos.y);
1322 setsprite(spriteNum, &sprite[spriteNum].pos);
1323
1324 next_sprite:
1325 spriteNum = nextSprite;
1326 }
1327 }
1328
1329
1330 static int P_Submerge(int, DukePlayer_t *, int, int);
1331 static int P_Emerge(int, DukePlayer_t *, int, int);
1332 static void P_FinishWaterChange(int, DukePlayer_t *, int, int, int);
1333
P_GetQ16AngleDeltaForTic(DukePlayer_t const * pPlayer)1334 static fix16_t P_GetQ16AngleDeltaForTic(DukePlayer_t const *pPlayer)
1335 {
1336 auto oldAngle = pPlayer->oq16ang;
1337 auto newAngle = pPlayer->q16ang;
1338
1339 if (klabs(fix16_sub(oldAngle, newAngle)) < F16(1024))
1340 return fix16_sub(newAngle, oldAngle);
1341
1342 if (newAngle > F16(1024))
1343 newAngle = fix16_sub(newAngle, F16(2048));
1344
1345 if (oldAngle > F16(1024))
1346 oldAngle = fix16_sub(oldAngle, F16(2048));
1347
1348 return fix16_sub(newAngle, oldAngle);
1349 }
1350
G_MovePlayers(void)1351 ACTOR_STATIC void G_MovePlayers(void)
1352 {
1353 int spriteNum = headspritestat[STAT_PLAYER];
1354
1355 while (spriteNum >= 0)
1356 {
1357 int const nextSprite = nextspritestat[spriteNum];
1358 auto const pSprite = &sprite[spriteNum];
1359 int const playerNum = P_GetP(pSprite);
1360 auto & thisPlayer = g_player[playerNum];
1361 auto const pPlayer = thisPlayer.ps;
1362
1363 if (pSprite->owner >= 0)
1364 {
1365 if (pPlayer->newowner >= 0) //Looking thru the camera
1366 {
1367 pSprite->x = pPlayer->opos.x;
1368 pSprite->y = pPlayer->opos.y;
1369 pSprite->z = pPlayer->opos.z + PHEIGHT;
1370 actor[spriteNum].bpos.z = pSprite->z;
1371 pSprite->ang = fix16_to_int(pPlayer->oq16ang);
1372
1373 setsprite(spriteNum, &pSprite->pos);
1374 }
1375 else
1376 {
1377 int32_t otherPlayerDist;
1378 #ifdef YAX_ENABLE
1379 // TROR water submerge/emerge
1380 int const playerSectnum = pSprite->sectnum;
1381 int const sectorLotag = sector[playerSectnum].lotag;
1382 int32_t otherSector;
1383
1384 if (A_CheckNoSE7Water((uspriteptr_t)pSprite, playerSectnum, sectorLotag, &otherSector))
1385 {
1386 // NOTE: Compare with G_MoveTransports().
1387 pPlayer->on_warping_sector = 1;
1388
1389 if ((sectorLotag == ST_1_ABOVE_WATER ?
1390 P_Submerge(P_GetP(pSprite), pPlayer, playerSectnum, otherSector) :
1391 P_Emerge(P_GetP(pSprite), pPlayer, playerSectnum, otherSector)) == 1)
1392 P_FinishWaterChange(spriteNum, pPlayer, sectorLotag, -1, otherSector);
1393 }
1394 #endif
1395 if (g_netServer || ud.multimode > 1)
1396 otherp = P_FindOtherPlayer(P_GetP(pSprite), &otherPlayerDist);
1397 else
1398 {
1399 otherp = P_GetP(pSprite);
1400 otherPlayerDist = 0;
1401 }
1402
1403 if (G_TileHasActor(sprite[spriteNum].picnum))
1404 A_Execute(spriteNum, P_GetP(pSprite), otherPlayerDist);
1405
1406 thisPlayer.smoothcamera = false;
1407
1408 pPlayer->q16angvel = P_GetQ16AngleDeltaForTic(pPlayer);
1409 pPlayer->oq16ang = pPlayer->q16ang;
1410 pPlayer->oq16horiz = pPlayer->q16horiz;
1411 pPlayer->oq16horizoff = pPlayer->q16horizoff;
1412
1413 if (pPlayer->one_eighty_count < 0)
1414 {
1415 thisPlayer.smoothcamera = true;
1416 pPlayer->one_eighty_count += 128;
1417 pPlayer->q16ang += F16(128);
1418 }
1419
1420 if (g_netServer || ud.multimode > 1)
1421 {
1422 if (sprite[g_player[otherp].ps->i].extra > 0)
1423 {
1424 if (pSprite->yrepeat > 32 && sprite[g_player[otherp].ps->i].yrepeat < 32)
1425 {
1426 if (otherPlayerDist < 1400 && pPlayer->knee_incs == 0)
1427 {
1428 // Don't stomp teammates.
1429 if (
1430 ((g_gametypeFlags[ud.coop] & GAMETYPE_TDM) && pPlayer->team != g_player[otherp].ps->team) ||
1431 (!(g_gametypeFlags[ud.coop] & GAMETYPE_PLAYERSFRIENDLY) && !(g_gametypeFlags[ud.coop] & GAMETYPE_TDM))
1432 )
1433 {
1434 pPlayer->knee_incs = 1;
1435 pPlayer->weapon_pos = -1;
1436 pPlayer->actorsqu = g_player[otherp].ps->i;
1437 }
1438 }
1439 }
1440 }
1441 }
1442
1443 if (pPlayer->actorsqu >= 0)
1444 {
1445 thisPlayer.smoothcamera = true;
1446 pPlayer->q16ang += fix16_from_int(
1447 G_GetAngleDelta(fix16_to_int(pPlayer->q16ang), getangle(sprite[pPlayer->actorsqu].x - pPlayer->pos.x, sprite[pPlayer->actorsqu].y - pPlayer->pos.y))
1448 >> 2);
1449 }
1450
1451 if (ud.god)
1452 {
1453 pSprite->extra = pPlayer->max_player_health;
1454 pSprite->cstat = 257;
1455 if (!WW2GI)
1456 pPlayer->inv_amount[GET_JETPACK] = 1599;
1457 }
1458
1459 if (pSprite->extra > 0)
1460 {
1461 #ifndef EDUKE32_STANDALONE
1462 if (!FURY)
1463 {
1464 actor[spriteNum].owner = spriteNum;
1465
1466 if (ud.god == 0)
1467 if (G_CheckForSpaceCeiling(pSprite->sectnum) || G_CheckForSpaceFloor(pSprite->sectnum))
1468 {
1469 OSD_Printf(OSD_ERROR "%s: player killed by space sector!\n", EDUKE32_FUNCTION);
1470 P_QuickKill(pPlayer);
1471 }
1472 }
1473 #endif
1474 }
1475 else
1476 {
1477 pPlayer->pos.x = pSprite->x;
1478 pPlayer->pos.y = pSprite->y;
1479 pPlayer->pos.z = pSprite->z-(20<<8);
1480
1481 pPlayer->newowner = -1;
1482
1483 if (pPlayer->wackedbyactor >= 0 && sprite[pPlayer->wackedbyactor].statnum < MAXSTATUS)
1484 {
1485 thisPlayer.smoothcamera = true;
1486 pPlayer->q16ang += fix16_from_int(G_GetAngleDelta(fix16_to_int(pPlayer->q16ang),
1487 getangle(sprite[pPlayer->wackedbyactor].x - pPlayer->pos.x,
1488 sprite[pPlayer->wackedbyactor].y - pPlayer->pos.y))
1489 >> 1);
1490 pPlayer->q16ang &= 0x7FFFFFF;
1491 }
1492 }
1493
1494 pSprite->ang = fix16_to_int(pPlayer->q16ang);
1495 }
1496 }
1497 else
1498 {
1499 if (pPlayer->holoduke_on == -1)
1500 DELETE_SPRITE_AND_CONTINUE(spriteNum);
1501
1502 actor[spriteNum].bpos = pSprite->pos;
1503 pSprite->cstat = 0;
1504
1505 if (pSprite->xrepeat < 42)
1506 {
1507 pSprite->xrepeat += 4;
1508 pSprite->cstat |= 2;
1509 }
1510 else pSprite->xrepeat = 42;
1511
1512 if (pSprite->yrepeat < 36)
1513 pSprite->yrepeat += 4;
1514 else
1515 {
1516 pSprite->yrepeat = 36;
1517 if (sector[pSprite->sectnum].lotag != ST_2_UNDERWATER)
1518 A_Fall(spriteNum);
1519 if (pSprite->zvel == 0 && sector[pSprite->sectnum].lotag == ST_1_ABOVE_WATER)
1520 pSprite->z += ZOFFSET5;
1521 }
1522
1523 if (pSprite->extra < 8)
1524 {
1525 pSprite->xvel = 128;
1526 pSprite->ang = fix16_to_int(pPlayer->q16ang);
1527 pSprite->extra++;
1528 A_SetSprite(spriteNum,CLIPMASK0);
1529 }
1530 else
1531 {
1532 pSprite->ang = 2047-fix16_to_int(pPlayer->q16ang);
1533 setsprite(spriteNum,&pSprite->pos);
1534 }
1535 }
1536
1537 pSprite->shade =
1538 logapproach(pSprite->shade, (sector[pSprite->sectnum].ceilingstat & 1) ? sector[pSprite->sectnum].ceilingshade
1539 : sector[pSprite->sectnum].floorshade);
1540
1541 next_sprite:
1542 spriteNum = nextSprite;
1543 }
1544 }
1545
G_MoveFX(void)1546 ACTOR_STATIC void G_MoveFX(void)
1547 {
1548 int spriteNum = headspritestat[STAT_FX];
1549
1550 while (spriteNum >= 0)
1551 {
1552 auto const pSprite = &sprite[spriteNum];
1553 int const nextSprite = nextspritestat[spriteNum];
1554
1555 switch (DYNAMICTILEMAP(pSprite->picnum))
1556 {
1557 case RESPAWN__STATIC:
1558 if (pSprite->extra == 66)
1559 {
1560 /*int32_t j =*/ A_Spawn(spriteNum,SHT(spriteNum));
1561 // sprite[j].pal = sprite[i].pal;
1562 DELETE_SPRITE_AND_CONTINUE(spriteNum);
1563 }
1564 else if (pSprite->extra > (66-13))
1565 sprite[spriteNum].extra++;
1566 break;
1567
1568 case MUSICANDSFX__STATIC:
1569 {
1570 int32_t const spriteHitag = (uint16_t)pSprite->hitag;
1571 auto const pPlayer = g_player[screenpeek].ps;
1572
1573 if (T2(spriteNum) != ud.config.SoundToggle)
1574 {
1575 // If sound playback was toggled, restart.
1576 T2(spriteNum) = ud.config.SoundToggle;
1577 T1(spriteNum) = 0;
1578 }
1579
1580 if (pSprite->lotag >= 1000 && pSprite->lotag < 2000)
1581 {
1582 int32_t playerDist = ldist(&sprite[pPlayer->i], pSprite);
1583
1584 #ifdef SPLITSCREEN_MOD_HACKS
1585 if (g_fakeMultiMode==2)
1586 {
1587 // HACK for splitscreen mod
1588 int32_t otherdist = ldist(&sprite[g_player[1].ps->i],pSprite);
1589 playerDist = min(playerDist, otherdist);
1590 }
1591 #endif
1592
1593 if (playerDist < spriteHitag && T1(spriteNum) == 0)
1594 {
1595 FX_SetReverb(pSprite->lotag - 1000);
1596 T1(spriteNum) = 1;
1597 }
1598 else if (playerDist >= spriteHitag && T1(spriteNum) == 1)
1599 {
1600 FX_SetReverb(0);
1601 FX_SetReverbDelay(0);
1602 T1(spriteNum) = 0;
1603 }
1604 }
1605 else if (pSprite->lotag < 999 && (unsigned)sector[pSprite->sectnum].lotag < 9 && // ST_9_SLIDING_ST_DOOR
1606 ud.config.AmbienceToggle && sector[SECT(spriteNum)].floorz != sector[SECT(spriteNum)].ceilingz)
1607 {
1608 if (g_sounds[pSprite->lotag].m & SF_MSFX)
1609 {
1610 int playerDist = dist(&sprite[pPlayer->i], pSprite);
1611
1612 #ifdef SPLITSCREEN_MOD_HACKS
1613 if (g_fakeMultiMode==2)
1614 {
1615 // HACK for splitscreen mod
1616 int32_t otherdist = dist(&sprite[g_player[1].ps->i],pSprite);
1617 playerDist = min(playerDist, otherdist);
1618 }
1619 #endif
1620
1621 if (playerDist < spriteHitag && T1(spriteNum) == 0 && FX_VoiceAvailable(g_sounds[pSprite->lotag].pr-1))
1622 {
1623 // Start playing an ambience sound.
1624
1625 char om = g_sounds[pSprite->lotag].m;
1626 if (g_numEnvSoundsPlaying == ud.config.NumVoices)
1627 {
1628 int32_t j;
1629
1630 for (SPRITES_OF(STAT_FX, j))
1631 if (j != spriteNum && S_IsAmbientSFX(j) && actor[j].t_data[0] == 1 &&
1632 dist(&sprite[j], &sprite[pPlayer->i]) > playerDist)
1633 {
1634 S_StopEnvSound(sprite[j].lotag,j);
1635 break;
1636 }
1637
1638 if (j == -1)
1639 goto next_sprite;
1640 }
1641
1642 g_sounds[pSprite->lotag].m |= SF_LOOP;
1643 A_PlaySound(pSprite->lotag,spriteNum);
1644 g_sounds[pSprite->lotag].m = om;
1645 T1(spriteNum) = 1; // AMBIENT_SFX_PLAYING
1646 }
1647 else if (playerDist >= spriteHitag && T1(spriteNum) == 1)
1648 {
1649 // Stop playing ambience sound because we're out of its range.
1650
1651 // T1 will be reset in sounds.c: CLEAR_SOUND_T0
1652 // T1 = 0;
1653 S_StopEnvSound(pSprite->lotag,spriteNum);
1654 }
1655 }
1656
1657 if ((g_sounds[pSprite->lotag].m & (SF_GLOBAL|SF_DTAG)) == SF_GLOBAL)
1658 {
1659 // Randomly playing global sounds (flyby of planes, screams, ...)
1660
1661 if (T5(spriteNum) > 0)
1662 T5(spriteNum)--;
1663 else
1664 {
1665 for (int TRAVERSE_CONNECT(playerNum))
1666 if (playerNum == myconnectindex && g_player[playerNum].ps->cursectnum == pSprite->sectnum)
1667 {
1668 S_PlaySound(pSprite->lotag + (unsigned)g_globalRandom % (pSprite->hitag+1));
1669 T5(spriteNum) = GAMETICSPERSEC*40 + g_globalRandom%(GAMETICSPERSEC*40);
1670 }
1671 }
1672 }
1673 }
1674 break;
1675 }
1676 }
1677 next_sprite:
1678 spriteNum = nextSprite;
1679 }
1680 }
1681
G_MoveFallers(void)1682 ACTOR_STATIC void G_MoveFallers(void)
1683 {
1684 int spriteNum = headspritestat[STAT_FALLER];
1685
1686 while (spriteNum >= 0)
1687 {
1688 int const nextSprite = nextspritestat[spriteNum];
1689 auto const pSprite = &sprite[spriteNum];
1690 int const sectNum = pSprite->sectnum;
1691
1692 if (T1(spriteNum) == 0)
1693 {
1694 const int16_t oextra = pSprite->extra;
1695 int j;
1696
1697 pSprite->z -= ZOFFSET2;
1698 T2(spriteNum) = pSprite->ang;
1699
1700 if ((j = A_IncurDamage(spriteNum)) >= 0)
1701 {
1702 if (j == FIREEXT || j == RPG || j == RADIUSEXPLOSION || j == SEENINE || j == OOZFILTER)
1703 {
1704 if (pSprite->extra <= 0)
1705 {
1706 T1(spriteNum) = 1;
1707
1708 for (bssize_t SPRITES_OF(STAT_FALLER, j))
1709 {
1710 if (sprite[j].hitag == SHT(spriteNum))
1711 {
1712 actor[j].t_data[0] = 1;
1713 sprite[j].cstat &= (65535-64);
1714 if (sprite[j].picnum == CEILINGSTEAM || sprite[j].picnum == STEAM)
1715 sprite[j].cstat |= 32768;
1716 }
1717 }
1718 }
1719 }
1720 else
1721 {
1722 actor[spriteNum].extra = 0;
1723 pSprite->extra = oextra;
1724 }
1725 }
1726 pSprite->ang = T2(spriteNum);
1727 pSprite->z += ZOFFSET2;
1728 }
1729 else if (T1(spriteNum) == 1)
1730 {
1731 if ((int16_t)pSprite->lotag > 0)
1732 {
1733 pSprite->lotag-=3;
1734 if ((int16_t)pSprite->lotag <= 0)
1735 {
1736 pSprite->xvel = (32+(krand()&63));
1737 pSprite->zvel = -(1024+(krand()&1023));
1738 }
1739 }
1740 else
1741 {
1742 int32_t spriteGravity = g_spriteGravity;
1743
1744 if (pSprite->xvel > 0)
1745 {
1746 pSprite->xvel -= 8;
1747 A_SetSprite(spriteNum,CLIPMASK0);
1748 }
1749
1750 if (EDUKE32_PREDICT_FALSE(G_CheckForSpaceFloor(pSprite->sectnum)))
1751 spriteGravity = 0;
1752 else if (EDUKE32_PREDICT_FALSE(G_CheckForSpaceCeiling(pSprite->sectnum)))
1753 spriteGravity = g_spriteGravity / 6;
1754
1755 if (pSprite->z < (sector[sectNum].floorz-ACTOR_FLOOR_OFFSET))
1756 {
1757 pSprite->zvel += spriteGravity;
1758 if (pSprite->zvel > ACTOR_MAXFALLINGZVEL)
1759 pSprite->zvel = ACTOR_MAXFALLINGZVEL;
1760 pSprite->z += pSprite->zvel;
1761 }
1762
1763 if ((sector[sectNum].floorz-pSprite->z) < ZOFFSET2)
1764 {
1765 #ifndef EDUKE32_STANDALONE
1766 for (int x = 0, x_end = 1+(krand()&7); x < x_end; ++x)
1767 RANDOMSCRAP(pSprite, spriteNum);
1768 #endif
1769 DELETE_SPRITE_AND_CONTINUE(spriteNum);
1770 }
1771 }
1772 }
1773
1774 next_sprite:
1775 spriteNum = nextSprite;
1776 }
1777 }
1778
G_MoveStandables(void)1779 ACTOR_STATIC void G_MoveStandables(void)
1780 {
1781 int spriteNum = headspritestat[STAT_STANDABLE], j, switchPic;
1782
1783 while (spriteNum >= 0)
1784 {
1785 int const nextSprite = nextspritestat[spriteNum];
1786 auto const pData = &actor[spriteNum].t_data[0];
1787 auto const pSprite = &sprite[spriteNum];
1788 int const sectNum = pSprite->sectnum;
1789
1790 if (sectNum < 0)
1791 DELETE_SPRITE_AND_CONTINUE(spriteNum);
1792
1793 // Rotation-fixed sprites in rotating sectors already have bpos* updated.
1794 if ((pData[7]&(0xffff0000))!=ROTFIXSPR_MAGIC)
1795 actor[spriteNum].bpos = pSprite->pos;
1796
1797 #ifndef EDUKE32_STANDALONE
1798 if (!FURY && PN(spriteNum) >= CRANE && PN(spriteNum) <= CRANE+3)
1799 {
1800 int32_t nextj;
1801
1802 //t[0] = state
1803 //t[1] = checking sector number
1804
1805 if (pSprite->xvel) A_GetZLimits(spriteNum);
1806
1807 if (pData[0] == 0) //Waiting to check the sector
1808 {
1809 for (SPRITES_OF_SECT_SAFE(pData[1], j, nextj))
1810 {
1811 switch (sprite[j].statnum)
1812 {
1813 case STAT_ACTOR:
1814 case STAT_ZOMBIEACTOR:
1815 case STAT_STANDABLE:
1816 case STAT_PLAYER:
1817 {
1818 vec3_t vect = { g_origins[pData[4]+1].x, g_origins[pData[4]+1].y, sprite[j].z };
1819
1820 pSprite->ang = getangle(vect.x-pSprite->x, vect.y-pSprite->y);
1821 setsprite(j, &vect);
1822 pData[0]++;
1823 goto next_sprite;
1824 }
1825 }
1826 }
1827 }
1828
1829 else if (pData[0]==1)
1830 {
1831 if (pSprite->xvel < 184)
1832 {
1833 pSprite->picnum = CRANE+1;
1834 pSprite->xvel += 8;
1835 }
1836 A_SetSprite(spriteNum,CLIPMASK0);
1837 if (sectNum == pData[1])
1838 pData[0]++;
1839 }
1840 else if (pData[0]==2 || pData[0]==7)
1841 {
1842 pSprite->z += (1024+512);
1843
1844 if (pData[0]==2)
1845 {
1846 if (sector[sectNum].floorz - pSprite->z < (64<<8))
1847 if (pSprite->picnum > CRANE) pSprite->picnum--;
1848
1849 if (sector[sectNum].floorz - pSprite->z < 4096+1024)
1850 pData[0]++;
1851 }
1852
1853 if (pData[0]==7)
1854 {
1855 if (sector[sectNum].floorz - pSprite->z < (64<<8))
1856 {
1857 if (pSprite->picnum > CRANE) pSprite->picnum--;
1858 else
1859 {
1860 if (pSprite->owner==-2)
1861 {
1862 int32_t p = A_FindPlayer(pSprite, NULL);
1863 A_PlaySound(DUKE_GRUNT,g_player[p].ps->i);
1864 if (g_player[p].ps->on_crane == spriteNum)
1865 g_player[p].ps->on_crane = -1;
1866 }
1867
1868 pData[0]++;
1869 pSprite->owner = -1;
1870 }
1871 }
1872 }
1873 }
1874 else if (pData[0]==3)
1875 {
1876 pSprite->picnum++;
1877 if (pSprite->picnum == CRANE+2)
1878 {
1879 int32_t p = G_GetPlayerInSector(pData[1]);
1880
1881 if (p >= 0 && g_player[p].ps->on_ground)
1882 {
1883 pSprite->owner = -2;
1884 g_player[p].ps->on_crane = spriteNum;
1885 A_PlaySound(DUKE_GRUNT,g_player[p].ps->i);
1886 g_player[p].smoothcamera = true;
1887 g_player[p].ps->q16ang = fix16_from_int(pSprite->ang+1024);
1888 }
1889 else
1890 {
1891 for (SPRITES_OF_SECT(pData[1], j))
1892 {
1893 switch (sprite[j].statnum)
1894 {
1895 case STAT_ACTOR:
1896 case STAT_STANDABLE:
1897 pSprite->owner = j;
1898 break;
1899 }
1900 }
1901 }
1902
1903 pData[0]++;//Grabbed the sprite
1904 pData[2]=0;
1905 goto next_sprite;
1906 }
1907 }
1908 else if (pData[0]==4) //Delay before going up
1909 {
1910 pData[2]++;
1911 if (pData[2] > 10)
1912 pData[0]++;
1913 }
1914 else if (pData[0]==5 || pData[0] == 8)
1915 {
1916 if (pData[0]==8 && pSprite->picnum < (CRANE+2))
1917 if ((sector[sectNum].floorz-pSprite->z) > 8192)
1918 pSprite->picnum++;
1919
1920 if (pSprite->z < g_origins[pData[4]+2].x)
1921 {
1922 pData[0]++;
1923 pSprite->xvel = 0;
1924 }
1925 else
1926 pSprite->z -= (1024+512);
1927 }
1928 else if (pData[0]==6)
1929 {
1930 if (pSprite->xvel < 192)
1931 pSprite->xvel += 8;
1932 pSprite->ang = getangle(g_origins[pData[4]].x - pSprite->x, g_origins[pData[4]].y - pSprite->y);
1933 A_SetSprite(spriteNum,CLIPMASK0);
1934 if (((pSprite->x-g_origins[pData[4]].x)*(pSprite->x-g_origins[pData[4]].x)+(pSprite->y-g_origins[pData[4]].y)*(pSprite->y-g_origins[pData[4]].y)) < (128*128))
1935 pData[0]++;
1936 }
1937
1938 else if (pData[0]==9)
1939 pData[0] = 0;
1940
1941 {
1942 vec3_t vect;
1943 Bmemcpy(&vect,pSprite,sizeof(vec3_t));
1944 vect.z -= (34<<8);
1945 setsprite(g_origins[pData[4]+2].y, &vect);
1946 }
1947
1948
1949 if (pSprite->owner != -1)
1950 {
1951 int32_t p = A_FindPlayer(pSprite, NULL);
1952
1953 if (A_IncurDamage(spriteNum) >= 0)
1954 {
1955 if (pSprite->owner == -2)
1956 if (g_player[p].ps->on_crane == spriteNum)
1957 g_player[p].ps->on_crane = -1;
1958 pSprite->owner = -1;
1959 pSprite->picnum = CRANE;
1960 goto next_sprite;
1961 }
1962
1963 if (pSprite->owner >= 0)
1964 {
1965 setsprite(pSprite->owner,&pSprite->pos);
1966
1967 actor[pSprite->owner].bpos = pSprite->pos;
1968
1969 pSprite->zvel = 0;
1970 }
1971 else if (pSprite->owner == -2)
1972 {
1973 auto const ps = g_player[p].ps;
1974
1975 ps->opos.x = ps->pos.x = pSprite->x-(sintable[(fix16_to_int(ps->q16ang)+512)&2047]>>6);
1976 ps->opos.y = ps->pos.y = pSprite->y-(sintable[fix16_to_int(ps->q16ang)&2047]>>6);
1977 ps->opos.z = ps->pos.z = pSprite->z+(2<<8);
1978
1979 setsprite(ps->i, &ps->pos);
1980 ps->cursectnum = sprite[ps->i].sectnum;
1981 }
1982 }
1983
1984 goto next_sprite;
1985 }
1986 else if (!FURY && PN(spriteNum) >= WATERFOUNTAIN && PN(spriteNum) <= WATERFOUNTAIN+3)
1987 {
1988 if (pData[0] > 0)
1989 {
1990 if (pData[0] < 20)
1991 {
1992 pData[0]++;
1993
1994 pSprite->picnum++;
1995
1996 if (pSprite->picnum == (WATERFOUNTAIN+3))
1997 pSprite->picnum = WATERFOUNTAIN+1;
1998 }
1999 else
2000 {
2001 int32_t playerDist;
2002
2003 A_FindPlayer(pSprite,&playerDist);
2004
2005 if (playerDist > 512)
2006 {
2007 pData[0] = 0;
2008 pSprite->picnum = WATERFOUNTAIN;
2009 }
2010 else pData[0] = 1;
2011 }
2012 }
2013 goto next_sprite;
2014 }
2015 else if (!FURY && AFLAMABLE(pSprite->picnum))
2016 {
2017 if (T1(spriteNum) == 1)
2018 {
2019 if ((++T2(spriteNum)&3) > 0) goto next_sprite;
2020
2021 if (pSprite->picnum == TIRE && T2(spriteNum) == 32)
2022 {
2023 pSprite->cstat = 0;
2024 j = A_Spawn(spriteNum,BLOODPOOL);
2025 sprite[j].shade = 127;
2026 }
2027 else
2028 {
2029 if (pSprite->shade < 64) pSprite->shade++;
2030 else DELETE_SPRITE_AND_CONTINUE(spriteNum);
2031 }
2032
2033 j = pSprite->xrepeat-(krand()&7);
2034 if (j < 10)
2035 DELETE_SPRITE_AND_CONTINUE(spriteNum);
2036
2037 pSprite->xrepeat = j;
2038
2039 j = pSprite->yrepeat-(krand()&7);
2040 if (j < 4)
2041 DELETE_SPRITE_AND_CONTINUE(spriteNum);
2042
2043 pSprite->yrepeat = j;
2044 }
2045 if (pSprite->picnum == BOX)
2046 {
2047 A_Fall(spriteNum);
2048 actor[spriteNum].ceilingz = sector[pSprite->sectnum].ceilingz;
2049 }
2050 goto next_sprite;
2051 }
2052 else if (!FURY && pSprite->picnum == TRIPBOMB)
2053 {
2054 // TIMER_CONTROL
2055 if (actor[spriteNum].t_data[6] == 1)
2056 {
2057
2058 if (actor[spriteNum].t_data[7] >= 1)
2059 {
2060 actor[spriteNum].t_data[7]--;
2061 }
2062
2063 if (actor[spriteNum].t_data[7] <= 0)
2064 {
2065 T3(spriteNum)=16;
2066 actor[spriteNum].t_data[6]=3;
2067 A_PlaySound(LASERTRIP_ARMING,spriteNum);
2068 }
2069 // we're on a timer....
2070 }
2071 if (T3(spriteNum) > 0 && actor[spriteNum].t_data[6] == 3)
2072 {
2073 T3(spriteNum)--;
2074
2075 if (T3(spriteNum) == 8)
2076 {
2077 for (j=0; j<5; j++)
2078 RANDOMSCRAP(pSprite, spriteNum);
2079
2080 int const dmg = pSprite->extra;
2081 A_RadiusDamage(spriteNum, g_tripbombRadius, dmg>>2, dmg>>1, dmg-(dmg>>2), dmg);
2082
2083 j = A_Spawn(spriteNum,EXPLOSION2);
2084 A_PlaySound(LASERTRIP_EXPLODE,j);
2085 sprite[j].ang = pSprite->ang;
2086 sprite[j].xvel = 348;
2087 A_SetSprite(j,CLIPMASK0);
2088
2089 for (SPRITES_OF(STAT_MISC, j))
2090 {
2091 if (sprite[j].picnum == LASERLINE && pSprite->hitag == sprite[j].hitag)
2092 sprite[j].xrepeat = sprite[j].yrepeat = 0;
2093 }
2094
2095 DELETE_SPRITE_AND_CONTINUE(spriteNum);
2096 }
2097 goto next_sprite;
2098 }
2099 else
2100 {
2101 int const oldExtra = pSprite->extra;
2102 int const oldAng = pSprite->ang;
2103
2104 pSprite->extra = 1;
2105 if (A_IncurDamage(spriteNum) >= 0)
2106 {
2107 actor[spriteNum].t_data[6] = 3;
2108 T3(spriteNum) = 16;
2109 }
2110 pSprite->extra = oldExtra;
2111 pSprite->ang = oldAng;
2112 }
2113
2114 switch (T1(spriteNum))
2115 {
2116 default:
2117 {
2118 int32_t playerDist;
2119 A_FindPlayer(pSprite, &playerDist);
2120 if (playerDist > 768 || T1(spriteNum) > 16) T1(spriteNum)++;
2121 break;
2122 }
2123
2124 case 32:
2125 {
2126 int16_t hitSprite;
2127 int const oldAng = pSprite->ang;
2128
2129 pSprite->ang = T6(spriteNum);
2130
2131 T4(spriteNum) = pSprite->x;
2132 T5(spriteNum) = pSprite->y;
2133
2134 pSprite->x += sintable[(T6(spriteNum)+512)&2047]>>9;
2135 pSprite->y += sintable[(T6(spriteNum))&2047]>>9;
2136 pSprite->z -= (3<<8);
2137
2138 int16_t const oldSectNum = pSprite->sectnum;
2139 int16_t curSectNum = pSprite->sectnum;
2140
2141 updatesectorneighbor(pSprite->x, pSprite->y, &curSectNum, 1024, 2048);
2142 changespritesect(spriteNum, curSectNum);
2143
2144 int32_t hitDist = A_CheckHitSprite(spriteNum, &hitSprite);
2145
2146 actor[spriteNum].lastv.x = hitDist;
2147 pSprite->ang = oldAng;
2148
2149 // we're on a trip wire
2150 if (actor[spriteNum].t_data[6] != 1)
2151 {
2152 while (hitDist > 0)
2153 {
2154 j = A_Spawn(spriteNum, LASERLINE);
2155
2156 sprite[j].hitag = pSprite->hitag;
2157 actor[j].t_data[1] = sprite[j].z;
2158
2159 if (hitDist < 1024)
2160 {
2161 sprite[j].xrepeat = hitDist>>5;
2162 break;
2163 }
2164 hitDist -= 1024;
2165
2166 pSprite->x += sintable[(T6(spriteNum)+512)&2047]>>4;
2167 pSprite->y += sintable[(T6(spriteNum))&2047]>>4;
2168
2169 updatesectorneighbor(pSprite->x, pSprite->y, &curSectNum, 1024, 2048);
2170
2171 if (curSectNum == -1)
2172 break;
2173
2174 changespritesect(spriteNum, curSectNum);
2175
2176 // this is a hack to work around the LASERLINE sprite's art tile offset
2177 changespritesect(j, curSectNum);
2178 }
2179 }
2180
2181 T1(spriteNum)++;
2182
2183 pSprite->pos.vec2 = { T4(spriteNum), T5(spriteNum) };
2184 pSprite->z += (3<<8);
2185
2186 changespritesect(spriteNum, oldSectNum);
2187 T4(spriteNum) = T3(spriteNum) = 0;
2188
2189 if (hitSprite >= 0 && actor[spriteNum].t_data[6] != 1)
2190 {
2191 actor[spriteNum].t_data[6] = 3;
2192 T3(spriteNum) = 13;
2193 A_PlaySound(LASERTRIP_ARMING,spriteNum);
2194 }
2195 break;
2196 }
2197
2198 case 33:
2199 {
2200 T2(spriteNum)++;
2201
2202 T4(spriteNum) = pSprite->x;
2203 T5(spriteNum) = pSprite->y;
2204
2205 pSprite->x += sintable[(T6(spriteNum)+512)&2047]>>9;
2206 pSprite->y += sintable[(T6(spriteNum))&2047]>>9;
2207 pSprite->z -= (3<<8);
2208
2209 setsprite(spriteNum, &pSprite->pos);
2210
2211 int32_t const hitDist = A_CheckHitSprite(spriteNum, NULL);
2212
2213 pSprite->pos.vec2 = { T4(spriteNum), T5(spriteNum) };
2214 pSprite->z += (3<<8);
2215 setsprite(spriteNum, &pSprite->pos);
2216
2217 // if( Actor[i].lastvx != x && lTripBombControl & TRIPBOMB_TRIPWIRE)
2218 if (actor[spriteNum].lastv.x != hitDist && actor[spriteNum].t_data[6] != 1)
2219 {
2220 actor[spriteNum].t_data[6] = 3;
2221 T3(spriteNum) = 13;
2222 A_PlaySound(LASERTRIP_ARMING, spriteNum);
2223 }
2224 break;
2225 }
2226 }
2227
2228 goto next_sprite;
2229 }
2230 else if (!FURY && pSprite->picnum >= CRACK1 && pSprite->picnum <= CRACK4)
2231 {
2232 if (pSprite->hitag)
2233 {
2234 pData[0] = pSprite->cstat;
2235 pData[1] = pSprite->ang;
2236
2237 int const dmgTile = A_IncurDamage(spriteNum);
2238
2239 if (dmgTile < 0)
2240 goto crack_default;
2241
2242 switch (DYNAMICTILEMAP(dmgTile))
2243 {
2244 case FIREEXT__STATIC:
2245 case RPG__STATIC:
2246 case RADIUSEXPLOSION__STATIC:
2247 case SEENINE__STATIC:
2248 case OOZFILTER__STATIC:
2249 for (SPRITES_OF(STAT_STANDABLE, j))
2250 {
2251 if (pSprite->hitag == sprite[j].hitag &&
2252 (sprite[j].picnum == OOZFILTER || sprite[j].picnum == SEENINE))
2253 if (sprite[j].shade != -32)
2254 sprite[j].shade = -32;
2255 }
2256
2257 goto DETONATE;
2258
2259 crack_default:
2260 default:
2261 pSprite->cstat = pData[0];
2262 pSprite->ang = pData[1];
2263 pSprite->extra = 0;
2264
2265 goto next_sprite;
2266 }
2267 }
2268 goto next_sprite;
2269 }
2270 else if (!FURY && pSprite->picnum == FIREEXT)
2271 {
2272 if (A_IncurDamage(spriteNum) < 0)
2273 goto next_sprite;
2274
2275 for (int k=0; k<16; k++)
2276 {
2277 j = A_InsertSprite(SECT(spriteNum), SX(spriteNum), SY(spriteNum), SZ(spriteNum) - (krand() % (48 << 8)),
2278 SCRAP3 + (krand() & 3), -8, 48, 48, krand() & 2047, (krand() & 63) + 64,
2279 -(krand() & 4095) - (sprite[spriteNum].zvel >> 2), spriteNum, 5);
2280
2281 sprite[j].pal = 2;
2282 }
2283
2284 j = A_Spawn(spriteNum,EXPLOSION2);
2285 A_PlaySound(PIPEBOMB_EXPLODE,j);
2286 A_PlaySound(GLASS_HEAVYBREAK,j);
2287
2288 if ((int16_t)pSprite->hitag > 0)
2289 {
2290 for (SPRITES_OF(STAT_STANDABLE, j))
2291 {
2292 // XXX: This block seems to be CODEDUP'd a lot of times.
2293 if (pSprite->hitag == sprite[j].hitag && (sprite[j].picnum == OOZFILTER || sprite[j].picnum == SEENINE))
2294 if (sprite[j].shade != -32)
2295 sprite[j].shade = -32;
2296 }
2297
2298 int const dmg = pSprite->extra;
2299 A_RadiusDamage(spriteNum, g_pipebombRadius,dmg>>2, dmg-(dmg>>1),dmg-(dmg>>2), dmg);
2300 j = A_Spawn(spriteNum,EXPLOSION2);
2301 A_PlaySound(PIPEBOMB_EXPLODE,j);
2302
2303 goto DETONATE;
2304 }
2305 else
2306 {
2307 A_RadiusDamage(spriteNum,g_seenineRadius,10,15,20,25);
2308 DELETE_SPRITE_AND_CONTINUE(spriteNum);
2309 }
2310 goto next_sprite;
2311 }
2312 else
2313 #endif
2314 if (pSprite->picnum == OOZFILTER || pSprite->picnum == SEENINE || pSprite->picnum == SEENINEDEAD || pSprite->picnum == SEENINEDEAD+1)
2315 {
2316 if (pSprite->shade != -32 && pSprite->shade != -33)
2317 {
2318 if (pSprite->xrepeat)
2319 j = (A_IncurDamage(spriteNum) >= 0);
2320 else
2321 j = 0;
2322
2323 if (j || pSprite->shade == -31)
2324 {
2325 if (j) pSprite->lotag = 0;
2326
2327 pData[3] = 1;
2328
2329 for (SPRITES_OF(STAT_STANDABLE, j))
2330 {
2331 if (pSprite->hitag == sprite[j].hitag && (sprite[j].picnum == SEENINE || sprite[j].picnum == OOZFILTER))
2332 sprite[j].shade = -32;
2333 }
2334 }
2335 }
2336 else
2337 {
2338 if (pSprite->shade == -32)
2339 {
2340 if ((int16_t)pSprite->lotag > 0)
2341 {
2342 pSprite->lotag -= 3;
2343 if ((int16_t)pSprite->lotag <= 0)
2344 pSprite->lotag = -99;
2345 }
2346 else
2347 pSprite->shade = -33;
2348 }
2349 else
2350 {
2351 if (pSprite->xrepeat > 0)
2352 {
2353 T3(spriteNum)++;
2354 if (T3(spriteNum) == 3)
2355 {
2356 if (pSprite->picnum == OOZFILTER)
2357 {
2358 T3(spriteNum) = 0;
2359 goto DETONATE;
2360 }
2361
2362 if (pSprite->picnum != (SEENINEDEAD+1))
2363 {
2364 T3(spriteNum) = 0;
2365
2366 if (pSprite->picnum == SEENINEDEAD)
2367 pSprite->picnum++;
2368 else if (pSprite->picnum == SEENINE)
2369 pSprite->picnum = SEENINEDEAD;
2370 }
2371 else goto DETONATE;
2372 }
2373 goto next_sprite;
2374 }
2375
2376 DETONATE:
2377 g_earthquakeTime = 16;
2378
2379 for (SPRITES_OF(STAT_EFFECTOR, j))
2380 {
2381 if (pSprite->hitag == sprite[j].hitag)
2382 {
2383 if (sprite[j].lotag == SE_13_EXPLOSIVE)
2384 {
2385 if (actor[j].t_data[2] == 0)
2386 actor[j].t_data[2] = 1;
2387 }
2388 else if (sprite[j].lotag == SE_8_UP_OPEN_DOOR_LIGHTS)
2389 actor[j].t_data[4] = 1;
2390 else if (sprite[j].lotag == SE_18_INCREMENTAL_SECTOR_RISE_FALL)
2391 {
2392 if (actor[j].t_data[0] == 0)
2393 actor[j].t_data[0] = 1;
2394 }
2395 else if (sprite[j].lotag == SE_21_DROP_FLOOR)
2396 actor[j].t_data[0] = 1;
2397 }
2398 }
2399
2400 pSprite->z -= ZOFFSET5;
2401
2402 #ifndef EDUKE32_STANDALONE
2403 if (!FURY && pSprite->xrepeat)
2404 for (int x=0; x<8; x++)
2405 RANDOMSCRAP(pSprite, spriteNum);
2406 #endif
2407
2408 if ((pData[3] == 1 && pSprite->xrepeat) || (int16_t)pSprite->lotag == -99)
2409 {
2410 int const newSprite = A_Spawn(spriteNum,EXPLOSION2);
2411 int const dmg = pSprite->extra;
2412
2413 A_RadiusDamage(spriteNum,g_seenineRadius,dmg>>2, dmg-(dmg>>1),dmg-(dmg>>2), dmg);
2414 A_PlaySound(PIPEBOMB_EXPLODE, newSprite);
2415 }
2416
2417 DELETE_SPRITE_AND_CONTINUE(spriteNum);
2418 }
2419 }
2420 goto next_sprite;
2421 }
2422 else if (pSprite->picnum == MASTERSWITCH)
2423 {
2424 if (pSprite->yvel == 1)
2425 {
2426 if ((int16_t)--pSprite->hitag <= 0)
2427 {
2428 G_OperateSectors(sectNum,spriteNum);
2429
2430 for (SPRITES_OF_SECT(sectNum, j))
2431 {
2432 if (sprite[j].statnum == STAT_EFFECTOR)
2433 {
2434 switch (sprite[j].lotag)
2435 {
2436 case SE_2_EARTHQUAKE:
2437 case SE_21_DROP_FLOOR:
2438 case SE_31_FLOOR_RISE_FALL:
2439 case SE_32_CEILING_RISE_FALL:
2440 case SE_36_PROJ_SHOOTER:
2441 actor[j].t_data[0] = 1;
2442 break;
2443 case SE_3_RANDOM_LIGHTS_AFTER_SHOT_OUT:
2444 actor[j].t_data[4] = 1;
2445 break;
2446 }
2447 }
2448 else if (sprite[j].statnum == STAT_STANDABLE)
2449 {
2450 switch (DYNAMICTILEMAP(sprite[j].picnum))
2451 {
2452 case SEENINE__STATIC:
2453 case OOZFILTER__STATIC:
2454 sprite[j].shade = -31;
2455 break;
2456 }
2457 }
2458 }
2459
2460 DELETE_SPRITE_AND_CONTINUE(spriteNum);
2461 }
2462 }
2463 goto next_sprite;
2464 }
2465 else
2466 {
2467 switchPic = pSprite->picnum;
2468
2469 #ifndef EDUKE32_STANDALONE
2470 if (!FURY)
2471 {
2472 if (switchPic > SIDEBOLT1 && switchPic <= SIDEBOLT1 + 3)
2473 switchPic = SIDEBOLT1;
2474 else if (switchPic > BOLT1 && switchPic <= BOLT1 + 3)
2475 switchPic = BOLT1;
2476 }
2477 #endif
2478 switch (DYNAMICTILEMAP(switchPic))
2479 {
2480 case TOUCHPLATE__STATIC:
2481 if (pData[1] == 1 && (int16_t)pSprite->hitag >= 0) // Move the sector floor
2482 {
2483 int const floorZ = sector[sectNum].floorz;
2484
2485 if (pData[3] == 1)
2486 {
2487 if (floorZ >= pData[2])
2488 {
2489 sector[sectNum].floorz = floorZ;
2490 pData[1] = 0;
2491 }
2492 else
2493 {
2494 sector[sectNum].floorz += sector[sectNum].extra;
2495 int const playerNum = G_GetPlayerInSector(sectNum);
2496 if (playerNum >= 0)
2497 g_player[playerNum].ps->pos.z += sector[sectNum].extra;
2498 }
2499 }
2500 else
2501 {
2502 if (floorZ <= pSprite->z)
2503 {
2504 sector[sectNum].floorz = pSprite->z;
2505 pData[1] = 0;
2506 }
2507 else
2508 {
2509 int32_t p;
2510 sector[sectNum].floorz -= sector[sectNum].extra;
2511 p = G_GetPlayerInSector(sectNum);
2512 if (p >= 0)
2513 g_player[p].ps->pos.z -= sector[sectNum].extra;
2514 }
2515 }
2516 goto next_sprite;
2517 }
2518
2519 if (pData[5] == 1)
2520 goto next_sprite;
2521
2522 {
2523 int32_t p = G_GetPlayerInSector(sectNum);
2524
2525 if (p >= 0 && (g_player[p].ps->on_ground || pSprite->ang == 512))
2526 {
2527 if (pData[0] == 0 && !G_CheckActivatorMotion(pSprite->lotag))
2528 {
2529 pData[0] = 1;
2530 pData[1] = 1;
2531 pData[3] = !pData[3];
2532 G_OperateMasterSwitches(pSprite->lotag);
2533 G_OperateActivators(pSprite->lotag, p);
2534 if ((int16_t)pSprite->hitag > 0)
2535 {
2536 pSprite->hitag--;
2537 if (pSprite->hitag == 0)
2538 pData[5] = 1;
2539 }
2540 }
2541 }
2542 else
2543 pData[0] = 0;
2544 }
2545
2546 if (pData[1] == 1)
2547 {
2548 for (SPRITES_OF(STAT_STANDABLE, j))
2549 {
2550 if (j != spriteNum && sprite[j].picnum == TOUCHPLATE && sprite[j].lotag == pSprite->lotag)
2551 {
2552 actor[j].t_data[1] = 1;
2553 actor[j].t_data[3] = pData[3];
2554 }
2555 }
2556 }
2557 goto next_sprite;
2558
2559 case VIEWSCREEN__STATIC:
2560 case VIEWSCREEN2__STATIC:
2561
2562 if (pSprite->xrepeat == 0)
2563 DELETE_SPRITE_AND_CONTINUE(spriteNum);
2564
2565 {
2566 int32_t playerDist;
2567 int const p = A_FindPlayer(pSprite, &playerDist);
2568 auto const ps = g_player[p].ps;
2569
2570 if (dist(&sprite[ps->i], pSprite) < VIEWSCREEN_ACTIVE_DISTANCE)
2571 {
2572 #if 0
2573 if (sprite[i].yvel == 1) // VIEWSCREEN_YVEL
2574 g_curViewscreen = i;
2575 #endif
2576 }
2577 else if (g_curViewscreen == spriteNum /*&& T1 == 1*/)
2578 {
2579 g_curViewscreen = -1;
2580 sprite[spriteNum].yvel = 0; // VIEWSCREEN_YVEL
2581 T1(spriteNum) = 0;
2582
2583 for (bssize_t ii = 0; ii < VIEWSCREENFACTOR; ii++)
2584 walock[TILE_VIEWSCR - ii] = CACHE1D_UNLOCKED;
2585 }
2586 }
2587
2588 goto next_sprite;
2589 }
2590 #ifndef EDUKE32_STANDALONE
2591 if (!FURY)
2592 switch (DYNAMICTILEMAP(switchPic))
2593 {
2594 case TRASH__STATIC:
2595
2596 if (pSprite->xvel == 0)
2597 pSprite->xvel = 1;
2598 if (A_SetSprite(spriteNum, CLIPMASK0))
2599 {
2600 A_Fall(spriteNum);
2601 if (krand() & 1)
2602 pSprite->zvel -= 256;
2603 if ((pSprite->xvel) < 48)
2604 pSprite->xvel += (krand() & 3);
2605 }
2606 else
2607 DELETE_SPRITE_AND_CONTINUE(spriteNum);
2608 break;
2609
2610 case SIDEBOLT1__STATIC:
2611 // case SIDEBOLT1+1:
2612 // case SIDEBOLT1+2:
2613 // case SIDEBOLT1+3:
2614 {
2615 int32_t playerDist;
2616 A_FindPlayer(pSprite, &playerDist);
2617 if (playerDist > 20480)
2618 goto next_sprite;
2619
2620 CLEAR_THE_BOLT2:
2621 if (pData[2])
2622 {
2623 pData[2]--;
2624 goto next_sprite;
2625 }
2626 if ((pSprite->xrepeat | pSprite->yrepeat) == 0)
2627 {
2628 pSprite->xrepeat = pData[0];
2629 pSprite->yrepeat = pData[1];
2630 }
2631 if ((krand() & 8) == 0)
2632 {
2633 pData[0] = pSprite->xrepeat;
2634 pData[1] = pSprite->yrepeat;
2635 pData[2] = g_globalRandom & 4;
2636 pSprite->xrepeat = pSprite->yrepeat = 0;
2637 goto CLEAR_THE_BOLT2;
2638 }
2639 pSprite->picnum++;
2640
2641 #if 0
2642 // NOTE: Um, this 'l' was assigned to last at the beginning of this function.
2643 // SIDEBOLT1 never gets translucent as a consequence, unlike BOLT1.
2644 if (randomRepeat & 1)
2645 pSprite->cstat ^= 2;
2646 #endif
2647
2648 if ((krand() & 1) && sector[sectNum].floorpicnum == HURTRAIL)
2649 A_PlaySound(SHORT_CIRCUIT, spriteNum);
2650
2651 if (pSprite->picnum == SIDEBOLT1 + 4)
2652 pSprite->picnum = SIDEBOLT1;
2653
2654 goto next_sprite;
2655 }
2656
2657 case BOLT1__STATIC:
2658 // case BOLT1+1:
2659 // case BOLT1+2:
2660 // case BOLT1+3:
2661 {
2662 int32_t playerDist;
2663 A_FindPlayer(pSprite, &playerDist);
2664 if (playerDist > 20480)
2665 goto next_sprite;
2666
2667 if (pData[3] == 0)
2668 pData[3] = sector[sectNum].floorshade;
2669
2670 CLEAR_THE_BOLT:
2671 if (pData[2])
2672 {
2673 pData[2]--;
2674 sector[sectNum].floorshade = 20;
2675 sector[sectNum].ceilingshade = 20;
2676 goto next_sprite;
2677 }
2678 if ((pSprite->xrepeat | pSprite->yrepeat) == 0)
2679 {
2680 pSprite->xrepeat = pData[0];
2681 pSprite->yrepeat = pData[1];
2682 }
2683 else if ((krand() & 8) == 0)
2684 {
2685 pData[0] = pSprite->xrepeat;
2686 pData[1] = pSprite->yrepeat;
2687 pData[2] = g_globalRandom & 4;
2688 pSprite->xrepeat = pSprite->yrepeat = 0;
2689 goto CLEAR_THE_BOLT;
2690 }
2691 pSprite->picnum++;
2692
2693 int const randomRepeat = g_globalRandom & 7;
2694 pSprite->xrepeat = randomRepeat + 8;
2695
2696 if (randomRepeat & 1)
2697 pSprite->cstat ^= 2;
2698
2699 if (pSprite->picnum == (BOLT1 + 1)
2700 && (krand() & 7) == 0 && sector[sectNum].floorpicnum == HURTRAIL)
2701 A_PlaySound(SHORT_CIRCUIT, spriteNum);
2702
2703 if (pSprite->picnum == BOLT1 + 4)
2704 pSprite->picnum = BOLT1;
2705
2706 if (pSprite->picnum & 1)
2707 {
2708 sector[sectNum].floorshade = 0;
2709 sector[sectNum].ceilingshade = 0;
2710 }
2711 else
2712 {
2713 sector[sectNum].floorshade = 20;
2714 sector[sectNum].ceilingshade = 20;
2715 }
2716 goto next_sprite;
2717 }
2718
2719 case WATERDRIP__STATIC:
2720
2721 if (pData[1])
2722 {
2723 if (--pData[1] == 0)
2724 pSprite->cstat &= 32767;
2725 }
2726 else
2727 {
2728 A_Fall(spriteNum);
2729 A_SetSprite(spriteNum, CLIPMASK0);
2730 if (pSprite->xvel > 0)
2731 pSprite->xvel -= 2;
2732
2733 if (pSprite->zvel == 0)
2734 {
2735 pSprite->cstat |= 32768;
2736
2737 if (pSprite->pal != 2 && pSprite->hitag == 0)
2738 A_PlaySound(SOMETHING_DRIPPING, spriteNum);
2739
2740 if (sprite[pSprite->owner].picnum != WATERDRIP)
2741 {
2742 DELETE_SPRITE_AND_CONTINUE(spriteNum);
2743 }
2744 else
2745 {
2746 actor[spriteNum].bpos.z = pSprite->z = pData[0];
2747 pData[1] = 48 + (krand() & 31);
2748 }
2749 }
2750 }
2751
2752
2753 goto next_sprite;
2754
2755 case DOORSHOCK__STATIC:
2756 pSprite->yrepeat = (klabs(sector[sectNum].ceilingz - sector[sectNum].floorz) >> 9) + 4;
2757 pSprite->xrepeat = 16;
2758 pSprite->z = sector[sectNum].floorz;
2759 goto next_sprite;
2760
2761 case CANWITHSOMETHING__STATIC:
2762 case CANWITHSOMETHING2__STATIC:
2763 case CANWITHSOMETHING3__STATIC:
2764 case CANWITHSOMETHING4__STATIC:
2765 A_Fall(spriteNum);
2766 if (A_IncurDamage(spriteNum) >= 0)
2767 {
2768 A_PlaySound(VENT_BUST, spriteNum);
2769
2770 for (j = 9; j >= 0; j--) RANDOMSCRAP(pSprite, spriteNum);
2771
2772 if (pSprite->lotag)
2773 A_Spawn(spriteNum, pSprite->lotag);
2774
2775 DELETE_SPRITE_AND_CONTINUE(spriteNum);
2776 }
2777 goto next_sprite;
2778
2779 case FLOORFLAME__STATIC:
2780 case FIREBARREL__STATIC:
2781 case FIREVASE__STATIC:
2782 case EXPLODINGBARREL__STATIC:
2783 case WOODENHORSE__STATIC:
2784 case HORSEONSIDE__STATIC:
2785 case NUKEBARREL__STATIC:
2786 case NUKEBARRELDENTED__STATIC:
2787 case NUKEBARRELLEAKED__STATIC:
2788 case TOILETWATER__STATIC:
2789 case RUBBERCAN__STATIC:
2790 case STEAM__STATIC:
2791 case CEILINGSTEAM__STATIC:
2792 case WATERBUBBLEMAKER__STATIC:
2793 if (!G_TileHasActor(sprite[spriteNum].picnum))
2794 goto next_sprite;
2795 {
2796 int32_t playerDist;
2797 int const playerNum = A_FindPlayer(pSprite, &playerDist);
2798 A_Execute(spriteNum, playerNum, playerDist);
2799 }
2800 goto next_sprite;
2801 }
2802 #endif
2803 }
2804
2805 next_sprite:
2806 spriteNum = nextSprite;
2807 }
2808 }
2809
A_DoProjectileBounce(int const spriteNum)2810 ACTOR_STATIC void A_DoProjectileBounce(int const spriteNum)
2811 {
2812 auto const pSprite = &sprite[spriteNum];
2813 int32_t const hitSectnum = pSprite->sectnum;
2814 int const firstWall = sector[hitSectnum].wallptr;
2815 int const secondWall = wall[firstWall].point2;
2816 int const wallAngle = getangle(wall[secondWall].x - wall[firstWall].x, wall[secondWall].y - wall[firstWall].y);
2817 vec3_t vect = { mulscale10(pSprite->xvel, sintable[(pSprite->ang + 512) & 2047]),
2818 mulscale10(pSprite->xvel, sintable[pSprite->ang & 2047]), pSprite->zvel };
2819
2820 int k = (pSprite->z<(actor[spriteNum].floorz + actor[spriteNum].ceilingz)>> 1) ? sector[hitSectnum].ceilingheinum
2821 : sector[hitSectnum].floorheinum;
2822
2823 vec3_t const da = { mulscale14(k, sintable[(wallAngle)&2047]),
2824 mulscale14(k, sintable[(wallAngle + 1536) & 2047]), 4096 };
2825
2826 k = vect.x * da.x + vect.y * da.y + vect.z * da.z;
2827 int l = da.x * da.x + da.y * da.y + da.z * da.z;
2828
2829 if ((klabs(k) >> 14) < l)
2830 {
2831 k = divscale17(k, l);
2832 vect.x -= mulscale16(da.x, k);
2833 vect.y -= mulscale16(da.y, k);
2834 vect.z -= mulscale16(da.z, k);
2835 }
2836
2837 pSprite->zvel = vect.z;
2838 pSprite->xvel = ksqrt(dmulscale8(vect.x, vect.x, vect.y, vect.y));
2839 pSprite->ang = getangle(vect.x, vect.y);
2840 }
2841
P_HandleBeingSpitOn(DukePlayer_t * const ps)2842 ACTOR_STATIC void P_HandleBeingSpitOn(DukePlayer_t * const ps)
2843 {
2844 ps->q16horiz += F16(32);
2845 ps->return_to_center = 8;
2846
2847 if (ps->loogcnt)
2848 return;
2849
2850 if (!A_CheckSoundPlaying(ps->i, DUKE_LONGTERM_PAIN))
2851 A_PlaySound(DUKE_LONGTERM_PAIN,ps->i);
2852
2853 int j = 3+(krand()&3);
2854 ps->numloogs = j;
2855 ps->loogcnt = 24*4;
2856 for (bssize_t x=0; x < j; x++)
2857 {
2858 ps->loogiex[x] = krand()%xdim;
2859 ps->loogiey[x] = krand()%ydim;
2860 }
2861 }
2862
A_DoProjectileEffects(int spriteNum,const vec3_t * davect,int radiusDamage)2863 static void A_DoProjectileEffects(int spriteNum, const vec3_t *davect, int radiusDamage)
2864 {
2865 auto const pProj = &SpriteProjectile[spriteNum];
2866
2867 if (pProj->spawns >= 0)
2868 {
2869 int const newSpr = A_Spawn(spriteNum,pProj->spawns);
2870
2871 if (davect)
2872 Bmemcpy(&sprite[newSpr],davect,sizeof(vec3_t));
2873
2874 if (pProj->sxrepeat > 4)
2875 sprite[newSpr].xrepeat=pProj->sxrepeat;
2876 if (pProj->syrepeat > 4)
2877 sprite[newSpr].yrepeat=pProj->syrepeat;
2878 }
2879
2880 if (pProj->isound >= 0)
2881 A_PlaySound(pProj->isound,spriteNum);
2882
2883 if (!radiusDamage)
2884 return;
2885
2886 auto const pSprite = &sprite[spriteNum];
2887 pSprite->extra = Proj_GetDamage(pProj);
2888 int const dmg = pSprite->extra;
2889 A_RadiusDamage(spriteNum, pProj->hitradius, dmg >> 2, dmg >> 1, dmg - (dmg >> 2), dmg);
2890 }
2891
G_WeaponHitCeilingOrFloor(int32_t i,spritetype * s,int * j)2892 static void G_WeaponHitCeilingOrFloor(int32_t i, spritetype *s, int *j)
2893 {
2894 if (actor[i].flags & SFLAG_DIDNOSE7WATER)
2895 {
2896 actor[i].flags &= ~SFLAG_DIDNOSE7WATER;
2897 return;
2898 }
2899
2900 if (s->z < actor[i].ceilingz)
2901 {
2902 *j = 16384|s->sectnum;
2903 s->zvel = -1;
2904 }
2905 else if (s->z > actor[i].floorz + ZOFFSET2*(sector[s->sectnum].lotag == ST_1_ABOVE_WATER))
2906 {
2907 *j = 16384|s->sectnum;
2908
2909 if (sector[s->sectnum].lotag != ST_1_ABOVE_WATER)
2910 s->zvel = 1;
2911 }
2912 }
2913
Proj_BounceOffWall(spritetype * s,int j)2914 static void Proj_BounceOffWall(spritetype *s, int j)
2915 {
2916 int k = getangle(
2917 wall[wall[j].point2].x-wall[j].x,
2918 wall[wall[j].point2].y-wall[j].y);
2919 s->ang = ((k<<1) - s->ang)&2047;
2920 }
2921
2922 #define PROJ_DECAYVELOCITY(s) s->xvel >>= 1, s->zvel >>= 1
2923
2924 // Maybe damage a ceiling or floor as the consequence of projectile impact.
2925 // Returns 1 if sprite <s> should be killed.
2926 // NOTE: Compare with Proj_MaybeDamageCF2() in sector.c
Proj_MaybeDamageCF(int spriteNum)2927 static int Proj_MaybeDamageCF(int spriteNum)
2928 {
2929 auto const s = (uspriteptr_t)&sprite[spriteNum];
2930
2931 if (s->zvel < 0)
2932 {
2933 if ((sector[s->sectnum].ceilingstat&1) && sector[s->sectnum].ceilingpal == 0)
2934 return 1;
2935
2936 Sect_DamageCeiling(spriteNum, s->sectnum);
2937 }
2938 else if (s->zvel > 0)
2939 {
2940 if ((sector[s->sectnum].floorstat&1) && sector[s->sectnum].floorpal == 0)
2941 {
2942 // Keep original Duke3D behavior: pass projectiles through
2943 // parallaxed ceilings, but NOT through such floors.
2944 return 0;
2945 }
2946
2947 Sect_DamageFloor(spriteNum, s->sectnum);
2948 }
2949
2950 return 0;
2951 }
2952
Proj_MoveCustom(int const spriteNum)2953 ACTOR_STATIC void Proj_MoveCustom(int const spriteNum)
2954 {
2955 int projectileMoved = SpriteProjectile[spriteNum].workslike & PROJECTILE_MOVED;
2956 SpriteProjectile[spriteNum].workslike |= PROJECTILE_MOVED;
2957
2958 auto const pProj = &SpriteProjectile[spriteNum];
2959 auto const pSprite = &sprite[spriteNum];
2960 vec3_t davect;
2961 int otherSprite = 0;
2962
2963 switch (pProj->workslike & PROJECTILE_TYPE_MASK)
2964 {
2965 case PROJECTILE_HITSCAN:
2966 {
2967 if (!G_TileHasActor(sprite[spriteNum].picnum))
2968 return;
2969 int32_t playerDist;
2970 int const playerNum = A_FindPlayer(pSprite, &playerDist);
2971 A_Execute(spriteNum, playerNum, playerDist);
2972 return;
2973 }
2974
2975 case PROJECTILE_KNEE:
2976 case PROJECTILE_BLOOD: A_DeleteSprite(spriteNum); return;
2977
2978 default:
2979 case PROJECTILE_RPG:
2980 {
2981 davect = pSprite->pos;
2982
2983 VM_UpdateAnim(spriteNum, &actor[spriteNum].t_data[0]);
2984
2985 if (pProj->flashcolor)
2986 G_AddGameLight(0, spriteNum, ((pSprite->yrepeat * tilesiz[pSprite->picnum].y) << 1), 2048, pProj->flashcolor,
2987 PR_LIGHT_PRIO_LOW_GAME);
2988
2989 if ((pProj->workslike & (PROJECTILE_BOUNCESOFFWALLS | PROJECTILE_EXPLODEONTIMER)) == PROJECTILE_BOUNCESOFFWALLS
2990 && pSprite->yvel < 1)
2991 {
2992 A_DoProjectileEffects(spriteNum, &davect, 1);
2993 A_DeleteSprite(spriteNum);
2994 return;
2995 }
2996
2997 if (pProj->workslike & PROJECTILE_COOLEXPLOSION1 && ++pSprite->shade >= 40)
2998 {
2999 A_DeleteSprite(spriteNum);
3000 return;
3001 }
3002
3003 pSprite->zvel -= pProj->drop;
3004
3005 if (pProj->workslike & PROJECTILE_SPIT && pSprite->zvel < ACTOR_MAXFALLINGZVEL)
3006 pSprite->zvel += g_spriteGravity - 112;
3007
3008 A_GetZLimits(spriteNum);
3009
3010 if (pProj->trail >= 0)
3011 {
3012 for (bssize_t cnt = 0; cnt <= pProj->tnum; cnt++)
3013 {
3014 otherSprite = A_Spawn(spriteNum, pProj->trail);
3015
3016 sprite[otherSprite].z += (pProj->toffset << 8);
3017
3018 if (pProj->txrepeat >= 0)
3019 sprite[otherSprite].xrepeat = pProj->txrepeat;
3020
3021 if (pProj->tyrepeat >= 0)
3022 sprite[otherSprite].yrepeat = pProj->tyrepeat;
3023 }
3024 }
3025
3026 int projMoveCnt = pProj->movecnt;
3027 int projVel = pSprite->xvel;
3028 int projZvel = pSprite->zvel;
3029
3030 if (sector[pSprite->sectnum].lotag == ST_2_UNDERWATER)
3031 {
3032 projVel >>= 1;
3033 projZvel >>= 1;
3034 }
3035
3036 do
3037 {
3038 vec3_t tmpvect = { (projVel * (sintable[(pSprite->ang + 512) & 2047])) >> 14 >> (int)!projectileMoved,
3039 (projVel * (sintable[pSprite->ang & 2047])) >> 14 >> (int)!projectileMoved, projZvel >> (int)!projectileMoved };
3040 Bmemcpy(&davect, pSprite, sizeof(vec3_t));
3041 projectileMoved++;
3042 otherSprite = A_MoveSprite(spriteNum, &tmpvect, (A_CheckSpriteFlags(spriteNum, SFLAG_NOCLIP) ? 0 : CLIPMASK1));
3043 }
3044 while (!otherSprite && --projMoveCnt > 0);
3045
3046 if (!(pProj->workslike & PROJECTILE_BOUNCESOFFWALLS) && // NOT_BOUNCESOFFWALLS_YVEL
3047 (unsigned)pSprite->yvel < MAXSPRITES
3048 && sprite[pSprite->yvel].sectnum != MAXSECTORS)
3049 if (FindDistance2D(pSprite->x - sprite[pSprite->yvel].x, pSprite->y - sprite[pSprite->yvel].y) < 256)
3050 otherSprite = 49152 | pSprite->yvel;
3051
3052 actor[spriteNum].movflag = otherSprite;
3053
3054 if (pSprite->sectnum < 0)
3055 {
3056 A_DeleteSprite(spriteNum);
3057 return;
3058 }
3059
3060 if (pProj->workslike & PROJECTILE_TIMED && pProj->range > 0)
3061 {
3062 if (++actor[spriteNum].t_data[8] > pProj->range)
3063 {
3064 if (pProj->workslike & PROJECTILE_EXPLODEONTIMER)
3065 A_DoProjectileEffects(spriteNum, &davect, 1);
3066
3067 A_DeleteSprite(spriteNum);
3068 return;
3069 }
3070 }
3071
3072 if ((otherSprite & 49152) != 49152 && !(pProj->workslike & PROJECTILE_BOUNCESOFFWALLS))
3073 G_WeaponHitCeilingOrFloor(spriteNum, pSprite, &otherSprite);
3074
3075 if (pProj->workslike & PROJECTILE_WATERBUBBLES && sector[pSprite->sectnum].lotag == ST_2_UNDERWATER && rnd(140))
3076 A_Spawn(spriteNum, WATERBUBBLE);
3077
3078 if (otherSprite != 0)
3079 {
3080 if (pProj->workslike & PROJECTILE_COOLEXPLOSION1)
3081 {
3082 pSprite->xvel = 0;
3083 pSprite->zvel = 0;
3084 }
3085
3086 switch (otherSprite & 49152)
3087 {
3088 case 49152:
3089 otherSprite &= (MAXSPRITES - 1);
3090
3091 if (pProj->workslike & PROJECTILE_BOUNCESOFFSPRITES)
3092 {
3093 pSprite->yvel--;
3094
3095 int const projAngle = getangle(sprite[otherSprite].x - pSprite->x, sprite[otherSprite].y - pSprite->y)
3096 + ((sprite[otherSprite].cstat & 16) ? 0 : 512);
3097 pSprite->ang = ((projAngle << 1) - pSprite->ang) & 2047;
3098
3099 if (pProj->bsound >= 0)
3100 A_PlaySound(pProj->bsound, spriteNum);
3101
3102 if (pProj->workslike & PROJECTILE_LOSESVELOCITY)
3103 PROJ_DECAYVELOCITY(pSprite);
3104
3105 if (!(pProj->workslike & PROJECTILE_FORCEIMPACT))
3106 return;
3107 }
3108
3109 A_DamageObject(otherSprite, spriteNum);
3110
3111 if (sprite[otherSprite].picnum == APLAYER)
3112 {
3113 int playerNum = P_Get(otherSprite);
3114
3115 #ifndef EDUKE32_STANDALONE
3116 if (!FURY)
3117 A_PlaySound(PISTOL_BODYHIT, otherSprite);
3118 #endif
3119 if (pProj->workslike & PROJECTILE_SPIT)
3120 P_HandleBeingSpitOn(g_player[playerNum].ps);
3121 }
3122
3123 if (pProj->workslike & PROJECTILE_RPG_IMPACT)
3124 {
3125 actor[otherSprite].owner = pSprite->owner;
3126 actor[otherSprite].picnum = pSprite->picnum;
3127
3128 if (pProj->workslike & PROJECTILE_RPG_IMPACT_DAMAGE)
3129 actor[otherSprite].extra += pProj->extra;
3130
3131 A_DoProjectileEffects(spriteNum, &davect, 0);
3132
3133 if (!(pProj->workslike & PROJECTILE_FORCEIMPACT))
3134 {
3135 A_DeleteSprite(spriteNum);
3136 return;
3137 }
3138 }
3139
3140 if (pProj->workslike & PROJECTILE_FORCEIMPACT)
3141 return;
3142 break;
3143
3144 case 32768:
3145 otherSprite &= (MAXWALLS - 1);
3146
3147 if (pProj->workslike & PROJECTILE_BOUNCESOFFMIRRORS
3148 && (wall[otherSprite].overpicnum == MIRROR || wall[otherSprite].picnum == MIRROR))
3149 {
3150 Proj_BounceOffWall(pSprite, otherSprite);
3151 pSprite->owner = spriteNum;
3152 #ifndef EDUKE32_STANDALONE
3153 if (!FURY)
3154 A_Spawn(spriteNum, TRANSPORTERSTAR);
3155 #endif
3156 return;
3157 }
3158 else
3159 {
3160 setsprite(spriteNum, &davect);
3161 A_DamageWall(spriteNum, otherSprite, pSprite->pos, pSprite->picnum);
3162
3163 if (pProj->workslike & PROJECTILE_BOUNCESOFFWALLS)
3164 {
3165 if (wall[otherSprite].overpicnum != MIRROR && wall[otherSprite].picnum != MIRROR)
3166 pSprite->yvel--;
3167
3168 Proj_BounceOffWall(pSprite, otherSprite);
3169
3170 if (pProj->bsound >= 0)
3171 A_PlaySound(pProj->bsound, spriteNum);
3172
3173 if (pProj->workslike & PROJECTILE_LOSESVELOCITY)
3174 PROJ_DECAYVELOCITY(pSprite);
3175
3176 return;
3177 }
3178 }
3179 break;
3180
3181 case 16384:
3182 setsprite(spriteNum, &davect);
3183
3184 if (Proj_MaybeDamageCF(spriteNum))
3185 {
3186 A_DeleteSprite(spriteNum);
3187 return;
3188 }
3189
3190 if (pProj->workslike & PROJECTILE_BOUNCESOFFWALLS)
3191 {
3192 A_DoProjectileBounce(spriteNum);
3193 A_SetSprite(spriteNum, CLIPMASK1);
3194
3195 pSprite->yvel--;
3196
3197 if (pProj->bsound >= 0)
3198 A_PlaySound(pProj->bsound, spriteNum);
3199
3200 if (pProj->workslike & PROJECTILE_LOSESVELOCITY)
3201 PROJ_DECAYVELOCITY(pSprite);
3202
3203 return;
3204 }
3205 break;
3206 }
3207
3208 A_DoProjectileEffects(spriteNum, &davect, 1);
3209 A_DeleteSprite(spriteNum);
3210 return;
3211 }
3212 return;
3213 }
3214 }
3215 }
3216
3217 #ifndef EDUKE32_STANDALONE
3218 struct SpriteTracerData
3219 {
3220 int32_t x, y, z;
3221 int32_t xVel, yVel, zVel;
SpriteTracerDataSpriteTracerData3222 SpriteTracerData() : x(0), y(0), z(0), xVel(0), yVel(0), zVel(0) { }
3223 };
3224
3225 std::map<int, SpriteTracerData> tracerData;
3226 #endif
3227
G_MoveWeapons(void)3228 ACTOR_STATIC void G_MoveWeapons(void)
3229 {
3230 int spriteNum = headspritestat[STAT_PROJECTILE];
3231
3232 while (spriteNum >= 0)
3233 {
3234 int const nextSprite = nextspritestat[spriteNum];
3235 auto const pSprite = &sprite[spriteNum];
3236
3237 if (pSprite->sectnum < 0)
3238 DELETE_SPRITE_AND_CONTINUE(spriteNum);
3239
3240 actor[spriteNum].bpos = pSprite->pos;
3241
3242 /* Custom projectiles */
3243 if (A_CheckSpriteFlags(spriteNum, SFLAG_PROJECTILE))
3244 {
3245 Proj_MoveCustom(spriteNum);
3246 goto next_sprite;
3247 }
3248
3249 // hard coded projectiles
3250 switch (DYNAMICTILEMAP(pSprite->picnum))
3251 {
3252 case SHOTSPARK1__STATIC:
3253 {
3254 if (!G_TileHasActor(sprite[spriteNum].picnum))
3255 goto next_sprite;
3256 int32_t playerDist;
3257 int const playerNum = A_FindPlayer(pSprite, &playerDist);
3258 A_Execute(spriteNum, playerNum, playerDist);
3259 goto next_sprite;
3260 }
3261
3262 case RADIUSEXPLOSION__STATIC:
3263 case KNEE__STATIC: DELETE_SPRITE_AND_CONTINUE(spriteNum);
3264 }
3265 #ifndef EDUKE32_STANDALONE
3266 if (!FURY)
3267 switch (DYNAMICTILEMAP(pSprite->picnum))
3268 {
3269 case FREEZEBLAST__STATIC:
3270 if (pSprite->yvel < 1 || pSprite->extra < 2 || (pSprite->xvel | pSprite->zvel) == 0)
3271 {
3272 int const newSprite = A_Spawn(spriteNum, TRANSPORTERSTAR);
3273 sprite[newSprite].pal = 1;
3274 sprite[newSprite].xrepeat = 32;
3275 sprite[newSprite].yrepeat = 32;
3276 DELETE_SPRITE_AND_CONTINUE(spriteNum);
3277 }
3278 fallthrough__;
3279 case FIREBALL__STATIC:
3280 if (!WORLDTOUR && pSprite->picnum == FIREBALL)
3281 break;
3282 fallthrough__;
3283 case SHRINKSPARK__STATIC:
3284 case RPG__STATIC:
3285 case FIRELASER__STATIC:
3286 case SPIT__STATIC:
3287 case COOLEXPLOSION1__STATIC:
3288 {
3289 int const projectileMoved = SpriteProjectile[spriteNum].workslike & PROJECTILE_MOVED;
3290 SpriteProjectile[spriteNum].workslike |= PROJECTILE_MOVED;
3291
3292 if (pSprite->picnum == COOLEXPLOSION1)
3293 if (!S_CheckSoundPlaying(WIERDSHOT_FLY))
3294 A_PlaySound(WIERDSHOT_FLY, spriteNum);
3295
3296 int spriteXvel = pSprite->xvel;
3297 int spriteZvel = pSprite->zvel;
3298
3299 if (pSprite->picnum == RPG && sector[pSprite->sectnum].lotag == ST_2_UNDERWATER)
3300 {
3301 spriteXvel >>= 1;
3302 spriteZvel >>= 1;
3303 }
3304
3305 vec3_t davect = pSprite->pos;
3306
3307 A_GetZLimits(spriteNum);
3308
3309 int const fireball = (WORLDTOUR && pSprite->picnum == FIREBALL && sprite[pSprite->owner].picnum != FIREBALL);
3310
3311 if (pSprite->picnum == RPG && actor[spriteNum].picnum != BOSS2 && pSprite->xrepeat >= 10
3312 && sector[pSprite->sectnum].lotag != ST_2_UNDERWATER
3313 && g_scriptVersion >= 13)
3314 {
3315 int const newSprite = A_Spawn(spriteNum, SMALLSMOKE);
3316 sprite[newSprite].z += (1 << 8);
3317 }
3318 if (pSprite->picnum == FIREBALL)
3319 {
3320 if (sector[pSprite->sectnum].lotag == ST_2_UNDERWATER) { DELETE_SPRITE_AND_CONTINUE(spriteNum); }
3321 if (fireball)
3322 {
3323 if (actor[spriteNum].t_data[0] >= 1 && actor[spriteNum].t_data[0] < 6)
3324 {
3325 float t = 1.f - 0.2 * actor[spriteNum].t_data[0];
3326 int j = A_Spawn(spriteNum, FIREBALL);
3327 spritetype* sj = &sprite[j];
3328 sj->xvel = pSprite->xvel;
3329 sj->yvel = pSprite->yvel;
3330 sj->zvel = pSprite->zvel;
3331 if (actor[spriteNum].t_data[0] > 1)
3332 {
3333 SpriteTracerData t = tracerData[actor[spriteNum].t_data[1]];
3334 sj->x = t.x;
3335 sj->y = t.y;
3336 sj->z = t.z;
3337 sj->xvel = t.xVel;
3338 sj->yvel = t.yVel;
3339 sj->zvel = t.zVel;
3340 }
3341 sj->xrepeat = sj->yrepeat = t * pSprite->xrepeat;
3342 sj->cstat = pSprite->cstat;
3343 sj->extra = 0;
3344 actor[spriteNum].t_data[1] = j;
3345 SpriteTracerData tt;
3346 tt.x = sj->x;
3347 tt.y = sj->y;
3348 tt.z = sj->z;
3349 tt.xVel = sj->xvel;
3350 tt.yVel = sj->yvel;
3351 tt.zVel = sj->zvel;
3352 tracerData[actor[spriteNum].t_data[1]] = tt;
3353 changespritestat(j, 4);
3354 }
3355 actor[spriteNum].t_data[0]++;
3356 }
3357 if (pSprite->zvel < 15000)
3358 pSprite->zvel += 200;
3359 }
3360
3361 vec3_t const tmpvect = { (spriteXvel * (sintable[(pSprite->ang + 512) & 2047])) >> 14 >> (int)!projectileMoved,
3362 (spriteXvel * (sintable[pSprite->ang & 2047])) >> 14 >> (int)!projectileMoved, spriteZvel >> (int)!projectileMoved };
3363
3364 int moveSprite = A_MoveSprite(spriteNum, &tmpvect, (A_CheckSpriteFlags(spriteNum, SFLAG_NOCLIP) ? 0 : CLIPMASK1));
3365
3366 if (pSprite->picnum == RPG && (unsigned) pSprite->yvel < MAXSPRITES) // RPG_YVEL
3367 if (FindDistance2D(pSprite->x - sprite[pSprite->yvel].x, pSprite->y - sprite[pSprite->yvel].y) < 256)
3368 moveSprite = 49152 | pSprite->yvel;
3369
3370 actor[spriteNum].movflag = moveSprite;
3371
3372 if (pSprite->sectnum < 0)
3373 DELETE_SPRITE_AND_CONTINUE(spriteNum);
3374
3375 if ((moveSprite & 49152) != 49152 && pSprite->picnum != FREEZEBLAST)
3376 G_WeaponHitCeilingOrFloor(spriteNum, pSprite, &moveSprite);
3377
3378 if (pSprite->picnum == FIRELASER)
3379 {
3380 for (bssize_t k = -3; k < 2; k++)
3381 {
3382 int const newSprite
3383 = A_InsertSprite(pSprite->sectnum, pSprite->x + ((k * sintable[(pSprite->ang + 512) & 2047]) >> 9),
3384 pSprite->y + ((k * sintable[pSprite->ang & 2047]) >> 9),
3385 pSprite->z + ((k * ksgn(pSprite->zvel)) * klabs(pSprite->zvel / 24)), FIRELASER, -40 + (k << 2),
3386 pSprite->xrepeat, pSprite->yrepeat, 0, 0, 0, pSprite->owner, 5);
3387
3388 sprite[newSprite].cstat = 128;
3389 sprite[newSprite].pal = pSprite->pal;
3390 }
3391 }
3392 else if (pSprite->picnum == SPIT)
3393 if (pSprite->zvel < ACTOR_MAXFALLINGZVEL)
3394 pSprite->zvel += g_spriteGravity - 112;
3395
3396 if (moveSprite != 0)
3397 {
3398 if (pSprite->picnum == COOLEXPLOSION1)
3399 {
3400 if ((moveSprite & 49152) == 49152 && sprite[moveSprite & (MAXSPRITES - 1)].picnum != APLAYER)
3401 goto COOLEXPLOSION;
3402 pSprite->xvel = 0;
3403 pSprite->zvel = 0;
3404 }
3405
3406 switch (moveSprite & 49152)
3407 {
3408 case 49152:
3409 moveSprite &= (MAXSPRITES - 1);
3410
3411 if (pSprite->picnum == FREEZEBLAST && sprite[moveSprite].pal == 1)
3412 if (A_CheckEnemySprite(&sprite[moveSprite]) || sprite[moveSprite].picnum == APLAYER)
3413 {
3414 int const newSprite = A_Spawn(spriteNum, TRANSPORTERSTAR);
3415 sprite[newSprite].pal = 1;
3416 sprite[newSprite].xrepeat = 32;
3417 sprite[newSprite].yrepeat = 32;
3418
3419 DELETE_SPRITE_AND_CONTINUE(spriteNum);
3420 }
3421
3422 if (!WORLDTOUR || pSprite->picnum != FIREBALL || fireball)
3423 A_DamageObject(moveSprite, spriteNum);
3424
3425 if (sprite[moveSprite].picnum == APLAYER)
3426 {
3427 int const playerNum = P_Get(moveSprite);
3428 A_PlaySound(PISTOL_BODYHIT, moveSprite);
3429
3430 if (pSprite->picnum == SPIT)
3431 P_HandleBeingSpitOn(g_player[playerNum].ps);
3432 }
3433 break;
3434
3435 case 32768:
3436 moveSprite &= (MAXWALLS - 1);
3437
3438 if (pSprite->picnum != RPG && pSprite->picnum != FREEZEBLAST && pSprite->picnum != SPIT
3439 && (!WORLDTOUR || pSprite->picnum != FIREBALL)
3440 && (wall[moveSprite].overpicnum == MIRROR || wall[moveSprite].picnum == MIRROR))
3441 {
3442 Proj_BounceOffWall(pSprite, moveSprite);
3443 pSprite->owner = spriteNum;
3444 A_Spawn(spriteNum, TRANSPORTERSTAR);
3445 goto next_sprite;
3446 }
3447 else
3448 {
3449 setsprite(spriteNum, &davect);
3450 A_DamageWall(spriteNum, moveSprite, pSprite->pos, pSprite->picnum);
3451
3452 if (pSprite->picnum == FREEZEBLAST)
3453 {
3454 if (wall[moveSprite].overpicnum != MIRROR && wall[moveSprite].picnum != MIRROR)
3455 {
3456 pSprite->extra >>= 1;
3457 pSprite->yvel--;
3458 }
3459
3460 Proj_BounceOffWall(pSprite, moveSprite);
3461 goto next_sprite;
3462 }
3463 }
3464 break;
3465
3466 case 16384:
3467 setsprite(spriteNum, &davect);
3468
3469 if (Proj_MaybeDamageCF(spriteNum))
3470 DELETE_SPRITE_AND_CONTINUE(spriteNum);
3471
3472 if (pSprite->picnum == FREEZEBLAST)
3473 {
3474 A_DoProjectileBounce(spriteNum);
3475 A_SetSprite(spriteNum, CLIPMASK1);
3476
3477 pSprite->extra >>= 1;
3478 pSprite->yvel--;
3479
3480 if (pSprite->xrepeat > 8)
3481 {
3482 pSprite->xrepeat -= 2;
3483
3484 if (pSprite->yrepeat > 8)
3485 pSprite->yrepeat -= 2;
3486 }
3487
3488 goto next_sprite;
3489 }
3490
3491 if (pSprite->zvel >= 0 && fireball)
3492 {
3493 int lp = A_Spawn(spriteNum, LAVAPOOL);
3494 sprite[lp].owner = sprite[spriteNum].owner;
3495 sprite[lp].yvel = sprite[spriteNum].yvel;
3496 actor[lp].owner = sprite[spriteNum].owner;
3497 DELETE_SPRITE_AND_CONTINUE(spriteNum);
3498 }
3499 break;
3500 default: break;
3501 }
3502
3503 switch (DYNAMICTILEMAP(pSprite->picnum))
3504 {
3505 case SPIT__STATIC:
3506 case COOLEXPLOSION1__STATIC:
3507 case FREEZEBLAST__STATIC:
3508 case FIRELASER__STATIC:
3509 break;
3510
3511 case RPG__STATIC:
3512 {
3513 int const newSprite = A_Spawn(spriteNum, EXPLOSION2);
3514 A_PlaySound(RPG_EXPLODE, newSprite);
3515 Bmemcpy(&sprite[newSprite], &davect, sizeof(vec3_t));
3516
3517 if (pSprite->xrepeat < 10)
3518 {
3519 sprite[newSprite].xrepeat = 6;
3520 sprite[newSprite].yrepeat = 6;
3521 }
3522 else if ((moveSprite & 49152) == 16384)
3523 {
3524 if (pSprite->zvel > 0)
3525 A_Spawn(spriteNum, EXPLOSION2BOT);
3526 else
3527 {
3528 sprite[newSprite].cstat |= 8;
3529 sprite[newSprite].z += (48 << 8);
3530 }
3531 }
3532
3533 if (pSprite->xrepeat >= 10)
3534 {
3535 int const x = pSprite->extra;
3536 A_RadiusDamage(spriteNum, g_rpgRadius, x >> 2, x >> 1, x - (x >> 2), x);
3537 }
3538 else
3539 {
3540 int const x = pSprite->extra + (g_globalRandom & 3);
3541 A_RadiusDamage(spriteNum, (g_rpgRadius >> 1), x >> 2, x >> 1, x - (x >> 2), x);
3542 }
3543 break;
3544 }
3545
3546 case SHRINKSPARK__STATIC:
3547 A_Spawn(spriteNum, SHRINKEREXPLOSION);
3548 A_PlaySound(SHRINKER_HIT, spriteNum);
3549 A_RadiusDamage(spriteNum, g_shrinkerRadius, 0, 0, 0, 0);
3550 break;
3551
3552 case FIREBALL__STATIC:
3553 if (WORLDTOUR)
3554 break;
3555 fallthrough__;
3556 default:
3557 {
3558 int const newSprite = A_Spawn(spriteNum, EXPLOSION2);
3559 sprite[newSprite].xrepeat = sprite[newSprite].yrepeat = pSprite->xrepeat >> 1;
3560 if ((moveSprite & 49152) == 16384)
3561 {
3562 if (pSprite->zvel < 0)
3563 {
3564 sprite[newSprite].cstat |= 8;
3565 sprite[newSprite].z += (72 << 8);
3566 }
3567 }
3568 break;
3569 }
3570 }
3571
3572 if (fireball)
3573 {
3574 int ex = A_Spawn(spriteNum, EXPLOSION2);
3575 sprite[ex].xrepeat = sprite[ex].yrepeat = pSprite->xrepeat >> 1;
3576 }
3577
3578 if (pSprite->picnum != COOLEXPLOSION1)
3579 DELETE_SPRITE_AND_CONTINUE(spriteNum);
3580 }
3581
3582 if (pSprite->picnum == COOLEXPLOSION1)
3583 {
3584 COOLEXPLOSION:
3585 pSprite->shade++;
3586 if (pSprite->shade >= 40)
3587 DELETE_SPRITE_AND_CONTINUE(spriteNum);
3588 }
3589 else if (pSprite->picnum == RPG && sector[pSprite->sectnum].lotag == ST_2_UNDERWATER && pSprite->xrepeat >= 10 && rnd(140))
3590 A_Spawn(spriteNum, WATERBUBBLE);
3591
3592 goto next_sprite;
3593 }
3594 }
3595 #endif
3596 next_sprite:
3597 spriteNum = nextSprite;
3598 }
3599 }
3600
3601
P_Submerge(int const playerNum,DukePlayer_t * const pPlayer,int const sectNum,int const otherSect)3602 static int P_Submerge(int const playerNum, DukePlayer_t * const pPlayer, int const sectNum, int const otherSect)
3603 {
3604 if (pPlayer->on_ground && pPlayer->pos.z >= sector[sectNum].floorz
3605 && (TEST_SYNC_KEY(g_player[playerNum].input.bits, SK_CROUCH) || pPlayer->vel.z > 2048))
3606 // if( onfloorz && sectlotag == 1 && ps->pos.z > (sector[sect].floorz-(6<<8)) )
3607 {
3608 if (screenpeek == playerNum)
3609 {
3610 FX_StopAllSounds();
3611 S_ClearSoundLocks();
3612 }
3613
3614 #ifndef EDUKE32_STANDALONE
3615 if (!FURY && sprite[pPlayer->i].extra > 0)
3616 A_PlaySound(DUKE_UNDERWATER, pPlayer->i);
3617 #endif
3618
3619 pPlayer->opos.z = pPlayer->pos.z = sector[otherSect].ceilingz;
3620
3621 if (TEST_SYNC_KEY(g_player[playerNum].input.bits, SK_CROUCH))
3622 pPlayer->vel.z += 512;
3623
3624 return 1;
3625 }
3626
3627 return 0;
3628 }
3629
P_Emerge(int const playerNum,DukePlayer_t * const pPlayer,int const sectNum,int const otherSect)3630 static int P_Emerge(int const playerNum, DukePlayer_t * const pPlayer, int const sectNum, int const otherSect)
3631 {
3632 // r1449-:
3633 if (pPlayer->pos.z < (sector[sectNum].ceilingz+1080) && pPlayer->vel.z <= 0)
3634 // r1450+, breaks submergible slime in bobsp2:
3635 // if (onfloorz && sectlotag == 2 && ps->pos.z <= sector[sect].ceilingz /*&& ps->vel.z == 0*/)
3636 {
3637 // if( sprite[j].extra <= 0) break;
3638 if (screenpeek == playerNum)
3639 {
3640 FX_StopAllSounds();
3641 S_ClearSoundLocks();
3642 }
3643
3644 #ifndef EDUKE32_STANDALONE
3645 if (!FURY)
3646 A_PlaySound(DUKE_GASP, pPlayer->i);
3647 #endif
3648
3649 pPlayer->opos.z = pPlayer->pos.z = sector[otherSect].floorz;
3650 pPlayer->vel.z = 0;
3651 // ps->vel.z += 1024;
3652
3653 pPlayer->jumping_toggle = 1;
3654 pPlayer->jumping_counter = 0;
3655
3656 return 1;
3657 }
3658
3659 return 0;
3660 }
3661
P_FinishWaterChange(int const playerNum,DukePlayer_t * const pPlayer,int const sectLotag,int const spriteOwner,int const newSector)3662 static void P_FinishWaterChange(int const playerNum, DukePlayer_t * const pPlayer, int const sectLotag, int const spriteOwner, int const newSector)
3663 {
3664 pPlayer->bobpos.x = pPlayer->opos.x = pPlayer->pos.x;
3665 pPlayer->bobpos.y = pPlayer->opos.y = pPlayer->pos.y;
3666
3667 if (spriteOwner < 0 || sprite[spriteOwner].owner != spriteOwner)
3668 pPlayer->transporter_hold = -2;
3669
3670 pPlayer->cursectnum = newSector;
3671 changespritesect(playerNum, newSector);
3672
3673 vec3_t vect = pPlayer->pos;
3674 vect.z += PHEIGHT;
3675 setsprite(pPlayer->i, &vect);
3676
3677 P_UpdateScreenPal(pPlayer);
3678
3679 if ((krand()&255) < 32)
3680 A_Spawn(playerNum, WATERSPLASH2);
3681
3682 if (sectLotag == ST_1_ABOVE_WATER)
3683 {
3684 for (bssize_t l = 0; l < 9; l++)
3685 sprite[A_Spawn(pPlayer->i, WATERBUBBLE)].z += krand() & 16383;
3686 }
3687 }
3688
3689 // Check prevention of teleportation *when alive*. For example, commanders and
3690 // octabrains would be transported by SE7 (both water and normal) only if dead.
A_CheckNonTeleporting(int const spriteNum)3691 static int A_CheckNonTeleporting(int const spriteNum)
3692 {
3693 int const tileNum = sprite[spriteNum].picnum;
3694 return !!(A_CheckSpriteFlags(spriteNum, SFLAG_NOTELEPORT) || tileNum == SHARK || tileNum == COMMANDER || tileNum == OCTABRAIN
3695 || (tileNum >= GREENSLIME && tileNum <= GREENSLIME + 7));
3696 }
3697
G_MoveTransports(void)3698 ACTOR_STATIC void G_MoveTransports(void)
3699 {
3700 int spriteNum = headspritestat[STAT_TRANSPORT];
3701
3702 while (spriteNum >= 0)
3703 {
3704 int const nextSprite = nextspritestat[spriteNum];
3705
3706 if (OW(spriteNum) == spriteNum)
3707 {
3708 spriteNum = nextSprite;
3709 continue;
3710 }
3711
3712 int const sectNum = SECT(spriteNum);
3713 int const sectLotag = sector[sectNum].lotag;
3714 int const onFloor = T5(spriteNum); // ONFLOORZ
3715
3716 if (T1(spriteNum) > 0)
3717 T1(spriteNum)--;
3718
3719 int sectSprite = headspritesect[sectNum];
3720 while (sectSprite >= 0)
3721 {
3722 int const nextSectSprite = nextspritesect[sectSprite];
3723
3724 switch (sprite[sectSprite].statnum)
3725 {
3726 case STAT_PLAYER:
3727 if (sprite[sectSprite].owner != -1)
3728 {
3729 int const playerNum = P_Get(sectSprite);
3730 auto & thisPlayer = g_player[playerNum];
3731 auto const pPlayer = thisPlayer.ps;
3732
3733 pPlayer->on_warping_sector = 1;
3734
3735 if (pPlayer->transporter_hold == 0 && pPlayer->jumping_counter == 0)
3736 {
3737 if (pPlayer->on_ground && sectLotag == 0 && onFloor && pPlayer->jetpack_on == 0)
3738 {
3739 #ifndef EDUKE32_STANDALONE
3740 if (!FURY && sprite[spriteNum].pal == 0)
3741 {
3742 A_Spawn(spriteNum, TRANSPORTERBEAM);
3743 A_PlaySound(TELEPORTER, spriteNum);
3744 }
3745 #endif
3746 for (int TRAVERSE_CONNECT(otherPlayer))
3747 {
3748 if (g_player[otherPlayer].ps->cursectnum == sprite[OW(spriteNum)].sectnum)
3749 {
3750 g_player[otherPlayer].ps->frag_ps = playerNum;
3751 sprite[g_player[otherPlayer].ps->i].extra = 0;
3752 }
3753 }
3754
3755 thisPlayer.smoothcamera = true;
3756 pPlayer->q16ang = fix16_from_int(sprite[OW(spriteNum)].ang);
3757
3758 if (sprite[OW(spriteNum)].owner != OW(spriteNum))
3759 {
3760 T1(spriteNum) = 13;
3761 actor[OW(spriteNum)].t_data[0] = 13;
3762 pPlayer->transporter_hold = 13;
3763 }
3764
3765 pPlayer->pos = sprite[OW(spriteNum)].pos;
3766 pPlayer->pos.z -= PHEIGHT;
3767 pPlayer->opos = pPlayer->pos;
3768 pPlayer->bobpos = pPlayer->pos.vec2;
3769
3770 changespritesect(sectSprite, sprite[OW(spriteNum)].sectnum);
3771 pPlayer->cursectnum = sprite[sectSprite].sectnum;
3772
3773 #ifndef EDUKE32_STANDALONE
3774 if (!FURY && sprite[spriteNum].pal == 0)
3775 {
3776 int const newSprite = A_Spawn(OW(spriteNum), TRANSPORTERBEAM);
3777 A_PlaySound(TELEPORTER, newSprite);
3778 }
3779 #endif
3780 break;
3781 }
3782
3783 if (onFloor == 0 && klabs(SZ(spriteNum) - pPlayer->pos.z) < 6144)
3784 if (!pPlayer->jetpack_on || TEST_SYNC_KEY(thisPlayer.input.bits, SK_JUMP)
3785 || TEST_SYNC_KEY(thisPlayer.input.bits, SK_CROUCH))
3786 {
3787 pPlayer->pos.x += sprite[OW(spriteNum)].x - SX(spriteNum);
3788 pPlayer->pos.y += sprite[OW(spriteNum)].y - SY(spriteNum);
3789 pPlayer->pos.z = (pPlayer->jetpack_on && (TEST_SYNC_KEY(thisPlayer.input.bits, SK_JUMP)
3790 || pPlayer->jetpack_on < 11))
3791 ? sprite[OW(spriteNum)].z - 6144
3792 : sprite[OW(spriteNum)].z + 6144;
3793
3794 actor[pPlayer->i].bpos = pPlayer->pos;
3795 pPlayer->opos = pPlayer->pos;
3796 pPlayer->bobpos = pPlayer->pos.vec2;
3797
3798 changespritesect(sectSprite, sprite[OW(spriteNum)].sectnum);
3799 pPlayer->cursectnum = sprite[OW(spriteNum)].sectnum;
3800
3801 break;
3802 }
3803
3804 int doWater = 0;
3805
3806 if (onFloor)
3807 {
3808 if (sectLotag == ST_1_ABOVE_WATER)
3809 doWater = P_Submerge(playerNum, pPlayer, sectNum, sprite[OW(spriteNum)].sectnum);
3810 else if (sectLotag == ST_2_UNDERWATER)
3811 doWater = P_Emerge(playerNum, pPlayer, sectNum, sprite[OW(spriteNum)].sectnum);
3812
3813 if (doWater == 1)
3814 {
3815 pPlayer->pos.x += sprite[OW(spriteNum)].x - SX(spriteNum);
3816 pPlayer->pos.y += sprite[OW(spriteNum)].y - SY(spriteNum);
3817
3818 P_FinishWaterChange(sectSprite, pPlayer, sectLotag, OW(spriteNum), sprite[OW(spriteNum)].sectnum);
3819 }
3820 }
3821 }
3822 else if (!(sectLotag == ST_1_ABOVE_WATER && pPlayer->on_ground == 1))
3823 break;
3824 }
3825 break;
3826
3827
3828 ////////// Non-player teleportation //////////
3829
3830 case STAT_PROJECTILE:
3831 // SE7_PROJECTILE, PROJECTILE_CHSECT.
3832 // comment out to make RPGs pass through water: (r1450 breaks this)
3833 // if (sectlotag != 0) goto JBOLT;
3834 case STAT_ACTOR:
3835 if (sprite[sectSprite].extra > 0 && A_CheckNonTeleporting(sectSprite))
3836 goto JBOLT;
3837 fallthrough__;
3838 case STAT_MISC:
3839 case STAT_FALLER:
3840 case STAT_DUMMYPLAYER:
3841 {
3842 if (((int32_t) totalclock & UINT8_MAX) != actor[sectSprite].lasttransport)
3843 {
3844 int const zvel = sprite[sectSprite].zvel;
3845 int const absZvel = klabs(zvel);
3846 int doWarp = 0;
3847
3848 if (absZvel != 0)
3849 {
3850 if (sectLotag == ST_2_UNDERWATER && sprite[sectSprite].z < (sector[sectNum].ceilingz + absZvel) && zvel < 0)
3851 doWarp = 1;
3852 if (sectLotag == ST_1_ABOVE_WATER && sprite[sectSprite].z > (sector[sectNum].floorz - absZvel) && zvel > 0)
3853 doWarp = 1;
3854 }
3855
3856 if (sectLotag == 0 && (onFloor || klabs(sprite[sectSprite].z - SZ(spriteNum)) < 4096))
3857 {
3858 if (sprite[OW(spriteNum)].owner != OW(spriteNum) && onFloor && T1(spriteNum) > 0
3859 && sprite[sectSprite].statnum != STAT_MISC)
3860 {
3861 T1(spriteNum)++;
3862 goto next_sprite;
3863 }
3864 doWarp = 1;
3865 }
3866
3867 if (doWarp)
3868 {
3869 if (A_CheckSpriteFlags(sectSprite, SFLAG_DECAL))
3870 goto JBOLT;
3871
3872 #ifndef EDUKE32_STANDALONE
3873 if (!FURY)
3874 switch (DYNAMICTILEMAP(sprite[sectSprite].picnum))
3875 {
3876 case TRANSPORTERSTAR__STATIC:
3877 case TRANSPORTERBEAM__STATIC:
3878 case TRIPBOMB__STATIC:
3879 case BULLETHOLE__STATIC:
3880 case WATERSPLASH2__STATIC:
3881 case BURNING__STATIC:
3882 case BURNING2__STATIC:
3883 case FIRE__STATIC:
3884 case FIRE2__STATIC:
3885 case TOILETWATER__STATIC:
3886 case LASERLINE__STATIC: goto JBOLT;
3887 }
3888 #endif
3889 switch (DYNAMICTILEMAP(sprite[sectSprite].picnum))
3890 {
3891 case PLAYERONWATER__STATIC:
3892 if (sectLotag == ST_2_UNDERWATER)
3893 {
3894 sprite[sectSprite].cstat &= 32768;
3895 break;
3896 }
3897 fallthrough__;
3898 default:
3899 if (sprite[sectSprite].statnum == STAT_MISC && !(sectLotag == ST_1_ABOVE_WATER || sectLotag == ST_2_UNDERWATER))
3900 break;
3901 fallthrough__;
3902 case WATERBUBBLE__STATIC:
3903 // if( rnd(192) && sprite[j].picnum == WATERBUBBLE)
3904 // break;
3905
3906 if (sectLotag > 0)
3907 {
3908 // Water SE7 teleportation.
3909 int const osect = sprite[OW(spriteNum)].sectnum;
3910
3911 Bassert(sectLotag == ST_1_ABOVE_WATER || sectLotag == ST_2_UNDERWATER);
3912 #ifndef EDUKE32_STANDALONE
3913 if (!FURY)
3914 {
3915 int const newSprite = A_Spawn(sectSprite, WATERSPLASH2);
3916
3917 if (sectLotag == ST_1_ABOVE_WATER && sprite[sectSprite].statnum == STAT_PROJECTILE)
3918 {
3919 sprite[newSprite].xvel = sprite[sectSprite].xvel >> 1;
3920 sprite[newSprite].ang = sprite[sectSprite].ang;
3921 A_SetSprite(newSprite, CLIPMASK0);
3922 }
3923 }
3924 #endif
3925 actor[sectSprite].lasttransport = ((int32_t) totalclock & UINT8_MAX);
3926
3927 sprite[sectSprite].x += sprite[OW(spriteNum)].x - SX(spriteNum);
3928 sprite[sectSprite].y += sprite[OW(spriteNum)].y - SY(spriteNum);
3929 sprite[sectSprite].z = (sectLotag == ST_1_ABOVE_WATER) ? sector[osect].ceilingz : sector[osect].floorz;
3930
3931 actor[sectSprite].bpos = sprite[sectSprite].pos;
3932
3933 changespritesect(sectSprite, sprite[OW(spriteNum)].sectnum);
3934 }
3935 else if (Bassert(sectLotag == 0), 1)
3936 {
3937 // Non-water SE7 teleportation.
3938
3939 if (onFloor)
3940 {
3941 if (sprite[sectSprite].statnum == STAT_PROJECTILE
3942 || (G_GetPlayerInSector(sectNum) == -1
3943 && G_GetPlayerInSector(sprite[OW(spriteNum)].sectnum) == -1))
3944 {
3945 sprite[sectSprite].x += (sprite[OW(spriteNum)].x - SX(spriteNum));
3946 sprite[sectSprite].y += (sprite[OW(spriteNum)].y - SY(spriteNum));
3947 sprite[sectSprite].z -= SZ(spriteNum) - sector[sprite[OW(spriteNum)].sectnum].floorz;
3948
3949 sprite[sectSprite].ang = sprite[OW(spriteNum)].ang;
3950 actor[sectSprite].bpos = sprite[sectSprite].pos;
3951 #ifndef EDUKE32_STANDALONE
3952 if (!FURY && sprite[spriteNum].pal == 0)
3953 {
3954 int newSprite = A_Spawn(spriteNum, TRANSPORTERBEAM);
3955 A_PlaySound(TELEPORTER, newSprite);
3956
3957 newSprite = A_Spawn(OW(spriteNum), TRANSPORTERBEAM);
3958 A_PlaySound(TELEPORTER, newSprite);
3959 }
3960 #endif
3961 if (sprite[OW(spriteNum)].owner != OW(spriteNum))
3962 {
3963 T1(spriteNum) = 13;
3964 actor[OW(spriteNum)].t_data[0] = 13;
3965 }
3966
3967 changespritesect(sectSprite, sprite[OW(spriteNum)].sectnum);
3968 }
3969 }
3970 else
3971 {
3972 sprite[sectSprite].x += (sprite[OW(spriteNum)].x - SX(spriteNum));
3973 sprite[sectSprite].y += (sprite[OW(spriteNum)].y - SY(spriteNum));
3974 sprite[sectSprite].z = sprite[OW(spriteNum)].z + 4096;
3975
3976 actor[sectSprite].bpos = sprite[sectSprite].pos;
3977
3978 changespritesect(sectSprite, sprite[OW(spriteNum)].sectnum);
3979 }
3980 }
3981
3982 break;
3983 } // switch (DYNAMICTILEMAP(sprite[j].picnum))
3984 } // if (doWarp)
3985 } // if (totalclock > actor[j].lasttransport)
3986
3987 break;
3988 } // five cases
3989
3990 } // switch (sprite[j].statnum)
3991 JBOLT:
3992 sectSprite = nextSectSprite;
3993 }
3994 next_sprite:
3995 spriteNum = nextSprite;
3996 }
3997 }
3998
A_FindLocator(int const tag,int const sectNum)3999 static int A_FindLocator(int const tag, int const sectNum)
4000 {
4001 for (bssize_t SPRITES_OF(STAT_LOCATOR, spriteNum))
4002 {
4003 if ((sectNum == -1 || sectNum == SECT(spriteNum)) && tag == SLT(spriteNum))
4004 return spriteNum;
4005 }
4006
4007 return -1;
4008 }
4009
A_FindLocatorWithHiLoTags(int const hitag,int const tag,int const sectNum)4010 static int A_FindLocatorWithHiLoTags(int const hitag, int const tag, int const sectNum)
4011 {
4012 for (bssize_t SPRITES_OF(STAT_LOCATOR, spriteNum))
4013 {
4014 if ((sectNum == -1 || sectNum == SECT(spriteNum)) && tag == SLT(spriteNum) && hitag == SHT(spriteNum))
4015 return spriteNum;
4016 }
4017
4018 return -1;
4019 }
4020
G_MoveActors(void)4021 ACTOR_STATIC void G_MoveActors(void)
4022 {
4023 int spriteNum = headspritestat[STAT_ACTOR];
4024
4025 while (spriteNum >= 0)
4026 {
4027 int const nextSprite = nextspritestat[spriteNum];
4028 auto const pSprite = &sprite[spriteNum];
4029 int const sectNum = pSprite->sectnum;
4030 auto const pData = actor[spriteNum].t_data;
4031
4032 int switchPic;
4033
4034 if (pSprite->xrepeat == 0 || sectNum < 0 || sectNum >= MAXSECTORS)
4035 DELETE_SPRITE_AND_CONTINUE(spriteNum);
4036
4037 actor[spriteNum].bpos = pSprite->pos;
4038
4039 switchPic = pSprite->picnum;
4040
4041 #ifndef EDUKE32_STANDALONE
4042 if (!FURY && pSprite->picnum > GREENSLIME && pSprite->picnum <= GREENSLIME+7)
4043 switchPic = GREENSLIME;
4044 #endif
4045
4046 switch (DYNAMICTILEMAP(switchPic))
4047 {
4048 case OOZ__STATIC:
4049 case OOZ2__STATIC:
4050 {
4051 A_GetZLimits(spriteNum);
4052
4053 int const yrepeat = clamp((actor[spriteNum].floorz - actor[spriteNum].ceilingz) >> 9, 8, 255);
4054 int const xrepeat = clamp(25 - (yrepeat >> 1), 8, 48);
4055
4056 pSprite->yrepeat = yrepeat;
4057 pSprite->xrepeat = xrepeat;
4058 pSprite->z = actor[spriteNum].floorz;
4059
4060 goto next_sprite;
4061 }
4062 case CAMERA1__STATIC:
4063 if (pData[0] == 0)
4064 {
4065 pData[1]+=8;
4066 if (g_damageCameras)
4067 {
4068 if (A_IncurDamage(spriteNum) >= 0)
4069 {
4070 pData[0] = 1; // static
4071 pSprite->cstat = 32768;
4072
4073 #ifndef EDUKE32_STANDALONE
4074 if (!FURY)
4075 {
4076 for (bssize_t x = 0; x < 5; x++)
4077 RANDOMSCRAP(pSprite, spriteNum);
4078 }
4079 #endif
4080 goto next_sprite;
4081 }
4082 }
4083
4084 if (pSprite->hitag > 0)
4085 {
4086 if (pData[1] < pSprite->hitag) pSprite->ang += 8;
4087 else if (pData[1] < pSprite->hitag * 3) pSprite->ang -= 8;
4088 else if (pData[1] < (pSprite->hitag << 2)) pSprite->ang += 8;
4089 else
4090 {
4091 pData[1] = 8;
4092 pSprite->ang += 16;
4093 }
4094 }
4095 }
4096 goto next_sprite;
4097 }
4098 #ifndef EDUKE32_STANDALONE
4099 switch (DYNAMICTILEMAP(switchPic))
4100 {
4101 case FLAMETHROWERFLAME__STATIC:
4102 {
4103 if (!WORLDTOUR)
4104 goto next_sprite;
4105
4106 if (G_TileHasActor(sprite[spriteNum].picnum))
4107 {
4108 int32_t playerDist;
4109 int const playerNum = A_FindPlayer(pSprite, &playerDist);
4110 A_Execute(spriteNum, playerNum, playerDist);
4111 }
4112
4113 actor[spriteNum].t_data[0]++;
4114 if (sector[pSprite->sectnum].lotag == ST_2_UNDERWATER)
4115 {
4116 int const newSprite = A_Spawn(spriteNum, EXPLOSION2);
4117 sprite[newSprite].shade = 127;
4118 DELETE_SPRITE_AND_CONTINUE(spriteNum);
4119 }
4120 int spriteXvel = pSprite->xvel;
4121 int spriteZvel = pSprite->zvel;
4122
4123 vec3_t davect = pSprite->pos;
4124
4125 A_GetZLimits(spriteNum);
4126
4127 if (pSprite->xrepeat < 80)
4128 {
4129 pSprite->xrepeat += actor[spriteNum].t_data[0] / 6;
4130 pSprite->yrepeat += actor[spriteNum].t_data[0] / 6;
4131 }
4132 pSprite->clipdist += actor[spriteNum].t_data[0] / 6;
4133 if (actor[spriteNum].t_data[0] < 2)
4134 actor[spriteNum].t_data[3] = krand() % 10;
4135 if (actor[spriteNum].t_data[0] > 30)
4136 {
4137 int const newSprite = A_Spawn(spriteNum, EXPLOSION2);
4138 sprite[newSprite].shade = 127;
4139 DELETE_SPRITE_AND_CONTINUE(spriteNum);
4140 }
4141
4142 vec3_t const tmpvect = { (spriteXvel * (sintable[(pSprite->ang + 512) & 2047])) >> 14,
4143 (spriteXvel * (sintable[pSprite->ang & 2047])) >> 14, spriteZvel };
4144
4145 int moveSprite = A_MoveSprite(spriteNum, &tmpvect, CLIPMASK1);
4146
4147 actor[spriteNum].movflag = moveSprite;
4148
4149 if (pSprite->sectnum < 0)
4150 DELETE_SPRITE_AND_CONTINUE(spriteNum);
4151
4152 if ((moveSprite & 49152) != 49152 && pSprite->picnum != FREEZEBLAST)
4153 G_WeaponHitCeilingOrFloor(spriteNum, pSprite, &moveSprite);
4154
4155 if (moveSprite != 0)
4156 {
4157 switch (moveSprite & 49152)
4158 {
4159 case 49152:
4160 moveSprite &= (MAXSPRITES - 1);
4161
4162 A_DamageObject(moveSprite, spriteNum);
4163
4164 if (sprite[moveSprite].picnum == APLAYER)
4165 {
4166 A_PlaySound(PISTOL_BODYHIT, moveSprite);
4167 }
4168 break;
4169
4170 case 32768:
4171 moveSprite &= (MAXWALLS - 1);
4172
4173 setsprite(spriteNum, &davect);
4174 A_DamageWall(spriteNum, moveSprite, pSprite->pos, pSprite->picnum);
4175
4176 break;
4177
4178 case 16384:
4179 setsprite(spriteNum, &davect);
4180
4181 if (pSprite->zvel < 0)
4182 Sect_DamageCeiling(spriteNum, pSprite->sectnum);
4183 else if (pSprite->zvel > 0)
4184 Sect_DamageFloor(spriteNum, pSprite->sectnum);
4185 break;
4186 default: break;
4187 }
4188 }
4189
4190 if (pSprite->xrepeat >= 10)
4191 {
4192 int const x = pSprite->extra;
4193 A_RadiusDamage(spriteNum, g_rpgRadius, x >> 2, x >> 1, x - (x >> 2), x);
4194 }
4195 else
4196 {
4197 int const x = pSprite->extra + (g_globalRandom & 3);
4198 A_RadiusDamage(spriteNum, (g_rpgRadius >> 1), x >> 2, x >> 1, x - (x >> 2), x);
4199 }
4200
4201 goto next_sprite;
4202 }
4203 case DUCK__STATIC:
4204 case TARGET__STATIC:
4205 if (pSprite->cstat&32)
4206 {
4207 pData[0]++;
4208 if (pData[0] > 60)
4209 {
4210 pData[0] = 0;
4211 pSprite->cstat = 128+257+16;
4212 pSprite->extra = 1;
4213 }
4214 }
4215 else
4216 {
4217 if (A_IncurDamage(spriteNum) >= 0)
4218 {
4219 int doEffects = 1;
4220
4221 pSprite->cstat = 32+128;
4222
4223 for (bssize_t SPRITES_OF(STAT_ACTOR, actorNum))
4224 {
4225 if ((sprite[actorNum].lotag == pSprite->lotag && sprite[actorNum].picnum == pSprite->picnum)
4226 && ((sprite[actorNum].hitag != 0) ^ ((sprite[actorNum].cstat & 32) != 0)))
4227 {
4228 doEffects = 0;
4229 break;
4230 }
4231 }
4232
4233 if (doEffects == 1)
4234 {
4235 G_OperateActivators(pSprite->lotag, -1);
4236 G_OperateForceFields(spriteNum, pSprite->lotag);
4237 G_OperateMasterSwitches(pSprite->lotag);
4238 }
4239 }
4240 }
4241 goto next_sprite;
4242
4243 case RESPAWNMARKERRED__STATIC:
4244 case RESPAWNMARKERYELLOW__STATIC:
4245 case RESPAWNMARKERGREEN__STATIC:
4246 if (++T1(spriteNum) > g_itemRespawnTime)
4247 DELETE_SPRITE_AND_CONTINUE(spriteNum);
4248
4249 if (T1(spriteNum) >= (g_itemRespawnTime>>1) && T1(spriteNum) < ((g_itemRespawnTime>>1)+(g_itemRespawnTime>>2)))
4250 PN(spriteNum) = RESPAWNMARKERYELLOW;
4251 else if (T1(spriteNum) > ((g_itemRespawnTime>>1)+(g_itemRespawnTime>>2)))
4252 PN(spriteNum) = RESPAWNMARKERGREEN;
4253
4254 A_Fall(spriteNum);
4255 break;
4256
4257 case HELECOPT__STATIC:
4258 case DUKECAR__STATIC:
4259 pSprite->z += pSprite->zvel;
4260 pData[0]++;
4261
4262 if (pData[0] == 4)
4263 A_PlaySound(WAR_AMBIENCE2,spriteNum);
4264
4265 if (pData[0] > (GAMETICSPERSEC*8))
4266 {
4267 g_earthquakeTime = 16;
4268 S_PlaySound(RPG_EXPLODE);
4269
4270 for (bssize_t j = 0; j < 32; j++)
4271 RANDOMSCRAP(pSprite, spriteNum);
4272
4273 DELETE_SPRITE_AND_CONTINUE(spriteNum);
4274 }
4275 else if ((pData[0]&3) == 0)
4276 A_Spawn(spriteNum,EXPLOSION2);
4277
4278 A_SetSprite(spriteNum,CLIPMASK0);
4279 break;
4280
4281 case RAT__STATIC:
4282 A_Fall(spriteNum);
4283 if (A_SetSprite(spriteNum, CLIPMASK0))
4284 {
4285 if ((krand()&255) < 3) A_PlaySound(RATTY,spriteNum);
4286 pSprite->ang += (krand()&31)-15+(sintable[(pData[0]<<8)&2047]>>11);
4287 }
4288 else
4289 {
4290 T1(spriteNum)++;
4291 if (T1(spriteNum) > 1)
4292 {
4293 DELETE_SPRITE_AND_CONTINUE(spriteNum);
4294 }
4295 else pSprite->ang = (krand()&2047);
4296 }
4297 if (pSprite->xvel < 128)
4298 pSprite->xvel+=2;
4299 pSprite->ang += (krand()&3)-6;
4300 break;
4301
4302 case QUEBALL__STATIC:
4303 case STRIPEBALL__STATIC:
4304 if (pSprite->xvel)
4305 {
4306 for (bssize_t SPRITES_OF(STAT_DEFAULT, hitObject))
4307 if (sprite[hitObject].picnum == POCKET && ldist(&sprite[hitObject],pSprite) < 52)
4308 DELETE_SPRITE_AND_CONTINUE(spriteNum);
4309
4310 int hitObject = clipmove(&pSprite->pos, &pSprite->sectnum,
4311 (((pSprite->xvel * (sintable[(pSprite->ang + 512) & 2047])) >> 14) * TICSPERFRAME) << 11,
4312 (((pSprite->xvel * (sintable[pSprite->ang & 2047])) >> 14) * TICSPERFRAME) << 11, 24L, ZOFFSET6,
4313 ZOFFSET6, CLIPMASK1);
4314
4315 if (hitObject & 49152)
4316 {
4317 if ((hitObject & 49152) == 32768)
4318 {
4319 hitObject &= (MAXWALLS - 1);
4320 Proj_BounceOffWall(pSprite, hitObject);
4321 }
4322 else if ((hitObject & 49152) == 49152)
4323 {
4324 hitObject &= (MAXSPRITES - 1);
4325 A_DamageObject(spriteNum, hitObject);
4326 }
4327 }
4328
4329 if (--pSprite->xvel < 0)
4330 pSprite->xvel = 0;
4331
4332 if (pSprite->picnum == STRIPEBALL)
4333 {
4334 pSprite->cstat = 257;
4335 pSprite->cstat |= (4 & pSprite->xvel) | (8 & pSprite->xvel);
4336 }
4337 }
4338 else
4339 {
4340 int32_t playerDist;
4341 int const playerNum = A_FindPlayer(pSprite, &playerDist);
4342 auto const pPlayer = g_player[playerNum].ps;
4343
4344 // I'm 50/50 on this being either a typo or a stupid hack
4345 if (playerDist < 1596)
4346 {
4347 int const angDiff = G_GetAngleDelta(fix16_to_int(pPlayer->q16ang),getangle(pSprite->x-pPlayer->pos.x,pSprite->y-pPlayer->pos.y));
4348
4349 if (angDiff > -64 && angDiff < 64 && TEST_SYNC_KEY(g_player[playerNum].input.bits, SK_OPEN)
4350 && pPlayer->toggle_key_flag == 1)
4351 {
4352 int ballSprite;
4353
4354 for (SPRITES_OF(STAT_ACTOR, ballSprite))
4355 {
4356 if (sprite[ballSprite].picnum == QUEBALL || sprite[ballSprite].picnum == STRIPEBALL)
4357 {
4358 int const angDiff2 = G_GetAngleDelta(
4359 fix16_to_int(pPlayer->q16ang), getangle(sprite[ballSprite].x - pPlayer->pos.x, sprite[ballSprite].y - pPlayer->pos.y));
4360
4361 if (angDiff2 > -64 && angDiff2 < 64)
4362 {
4363 int32_t ballDist;
4364 A_FindPlayer(&sprite[ballSprite], &ballDist);
4365
4366 if (playerDist > ballDist)
4367 break;
4368 }
4369 }
4370 }
4371
4372 if (ballSprite == -1)
4373 {
4374 pSprite->xvel = (pSprite->pal == 12) ? 164 : 140;
4375 pSprite->ang = fix16_to_int(pPlayer->q16ang);
4376
4377 pPlayer->toggle_key_flag = 2;
4378 }
4379 }
4380 }
4381
4382 if (playerDist < 512 && pSprite->sectnum == pPlayer->cursectnum)
4383 {
4384 pSprite->ang = getangle(pSprite->x-pPlayer->pos.x,pSprite->y-pPlayer->pos.y);
4385 pSprite->xvel = 48;
4386 }
4387 }
4388
4389 break;
4390
4391 case FORCESPHERE__STATIC:
4392 if (pSprite->yvel == 0)
4393 {
4394 pSprite->yvel = 1;
4395
4396 for (bssize_t l = 512; l < (2048 - 512); l += 128)
4397 {
4398 for (bssize_t j = 0; j < 2048; j += 128)
4399 {
4400 int const newSprite = A_Spawn(spriteNum, FORCESPHERE);
4401 sprite[newSprite].cstat = 257 + 128;
4402 sprite[newSprite].clipdist = 64;
4403 sprite[newSprite].ang = j;
4404 sprite[newSprite].zvel = sintable[l & 2047] >> 5;
4405 sprite[newSprite].xvel = sintable[(l + 512) & 2047] >> 9;
4406 sprite[newSprite].owner = spriteNum;
4407 }
4408 }
4409 }
4410
4411 if (pData[3] > 0)
4412 {
4413 if (pSprite->zvel < ACTOR_MAXFALLINGZVEL)
4414 pSprite->zvel += 192;
4415
4416 pSprite->z += pSprite->zvel;
4417
4418 if (pSprite->z > sector[sectNum].floorz)
4419 pSprite->z = sector[sectNum].floorz;
4420
4421 if (--pData[3] == 0)
4422 DELETE_SPRITE_AND_CONTINUE(spriteNum);
4423 }
4424 else if (pData[2] > 10)
4425 {
4426 for (bssize_t SPRITES_OF(STAT_MISC, miscSprite))
4427 {
4428 if (sprite[miscSprite].owner == spriteNum && sprite[miscSprite].picnum == FORCESPHERE)
4429 actor[miscSprite].t_data[1] = 1 + (krand() & 63);
4430 }
4431
4432 pData[3] = 64;
4433 }
4434
4435 goto next_sprite;
4436
4437 case RECON__STATIC:
4438 {
4439 int playerNum;
4440 DukePlayer_t *pPlayer;
4441
4442 A_GetZLimits(spriteNum);
4443
4444 pSprite->shade += (sector[pSprite->sectnum].ceilingstat & 1) ? (sector[pSprite->sectnum].ceilingshade - pSprite->shade) >> 1
4445 : (sector[pSprite->sectnum].floorshade - pSprite->shade) >> 1;
4446
4447 if (pSprite->z < sector[sectNum].ceilingz + ZOFFSET5)
4448 pSprite->z = sector[sectNum].ceilingz + ZOFFSET5;
4449
4450 #if 0 //def POLYMER
4451 gamelights[gamelightcount&(PR_MAXLIGHTS-1)].sector = s->sectnum;
4452 gamelights[gamelightcount&(PR_MAXLIGHTS-1)].x = s->x;
4453 gamelights[gamelightcount&(PR_MAXLIGHTS-1)].y = s->y;
4454 gamelights[gamelightcount&(PR_MAXLIGHTS-1)].z = s->z + 10248;
4455 gamelights[gamelightcount&(PR_MAXLIGHTS-1)].range = 8192;
4456
4457 gamelights[gamelightcount&(PR_MAXLIGHTS-1)].angle = s->ang;
4458 gamelights[gamelightcount&(PR_MAXLIGHTS-1)].horiz = 100;
4459 gamelights[gamelightcount&(PR_MAXLIGHTS-1)].radius = 256;
4460 gamelights[gamelightcount&(PR_MAXLIGHTS-1)].faderadius = 200;
4461
4462 gamelights[gamelightcount&(PR_MAXLIGHTS-1)].color[0] = 255;
4463 gamelights[gamelightcount&(PR_MAXLIGHTS-1)].color[1] = 255;
4464 gamelights[gamelightcount&(PR_MAXLIGHTS-1)].color[2] = 255;
4465
4466 gamelights[gamelightcount&(PR_MAXLIGHTS-1)].priority = PR_LIGHT_PRIO_MAX_GAME;
4467
4468 if (gamelightcount < PR_MAXLIGHTS)
4469 gamelightcount++;
4470 #endif
4471
4472 if (!g_netServer && ud.multimode < 2)
4473 {
4474 if (g_noEnemies == 1)
4475 {
4476 pSprite->cstat = 32768;
4477 goto next_sprite;
4478 }
4479 else if (g_noEnemies == 2) pSprite->cstat = 257;
4480 }
4481 if (A_IncurDamage(spriteNum) >= 0)
4482 {
4483 if (pSprite->extra < 0 && pData[0] != -1)
4484 {
4485 pData[0] = -1;
4486 pSprite->extra = 0;
4487 }
4488
4489 A_PlaySound(RECO_PAIN,spriteNum);
4490 RANDOMSCRAP(pSprite, spriteNum);
4491 }
4492
4493 if (pData[0] == -1)
4494 {
4495 pSprite->z += 1024;
4496 pData[2]++;
4497
4498 if ((pData[2]&3) == 0)
4499 A_Spawn(spriteNum,EXPLOSION2);
4500
4501 A_GetZLimits(spriteNum);
4502 pSprite->ang += 96;
4503 pSprite->xvel = 128;
4504
4505 if (!A_SetSprite(spriteNum, CLIPMASK0) || pSprite->z > actor[spriteNum].floorz)
4506 {
4507 for (bssize_t l = 0; l < 16; l++)
4508 RANDOMSCRAP(pSprite, spriteNum);
4509
4510 int const newSprite = A_Spawn(spriteNum, EXPLOSION2);
4511 A_PlaySound(LASERTRIP_EXPLODE, newSprite);
4512 A_Spawn(spriteNum, PIGCOP);
4513 P_AddKills(g_player[myconnectindex].ps, 1);
4514 DELETE_SPRITE_AND_CONTINUE(spriteNum);
4515 }
4516
4517 goto next_sprite;
4518 }
4519 else
4520 {
4521 if (pSprite->z > actor[spriteNum].floorz-(48<<8))
4522 pSprite->z = actor[spriteNum].floorz-(48<<8);
4523 }
4524
4525 int32_t playerDist;
4526 playerNum = A_FindPlayer(pSprite, &playerDist);
4527 pPlayer = g_player[playerNum].ps;
4528
4529 int const spriteOwner = pSprite->owner;
4530
4531 // 3 = findplayerz, 4 = shoot
4532
4533 if (pData[0] >= 4)
4534 {
4535 if ((++pData[2] & 15) == 0)
4536 {
4537 int const saveAng = pSprite->ang;
4538 pSprite->ang = actor[spriteNum].tempang;
4539 A_PlaySound(RECO_ATTACK, spriteNum);
4540 A_Shoot(spriteNum, FIRELASER);
4541 pSprite->ang = saveAng;
4542 }
4543 if (pData[2] > (GAMETICSPERSEC * 3)
4544 || !cansee(pSprite->x, pSprite->y, pSprite->z - ZOFFSET2, pSprite->sectnum, pPlayer->pos.x, pPlayer->pos.y,
4545 pPlayer->pos.z, pPlayer->cursectnum))
4546 {
4547 pData[0] = 0;
4548 pData[2] = 0;
4549 }
4550 else actor[spriteNum].tempang += G_GetAngleDelta(actor[spriteNum].tempang,
4551 getangle(pPlayer->pos.x - pSprite->x,
4552 pPlayer->pos.y - pSprite->y)) / 3;
4553 }
4554 else if (pData[0] == 2 || pData[0] == 3)
4555 {
4556 pData[3] = 0;
4557 pSprite->xvel = (pSprite->xvel > 0) ? pSprite->xvel - 16 : 0;
4558
4559 if (pData[0] == 2)
4560 {
4561 int const zDiff = pPlayer->pos.z - pSprite->z;
4562
4563 if (klabs(zDiff) < (48 << 8))
4564 pData[0] = 3;
4565 else
4566 pSprite->z += ksgn(pPlayer->pos.z - pSprite->z) << 10;
4567 }
4568 else
4569 {
4570 pData[2]++;
4571 if (pData[2] > (GAMETICSPERSEC*3) ||
4572 !cansee(pSprite->x,pSprite->y,pSprite->z-ZOFFSET2,pSprite->sectnum, pPlayer->pos.x,pPlayer->pos.y,pPlayer->pos.z,pPlayer->cursectnum))
4573 {
4574 pData[0] = 1;
4575 pData[2] = 0;
4576 }
4577 else if ((pData[2]&15) == 0)
4578 {
4579 A_PlaySound(RECO_ATTACK,spriteNum);
4580 A_Shoot(spriteNum,FIRELASER);
4581 }
4582 }
4583 pSprite->ang += G_GetAngleDelta(pSprite->ang, getangle(pPlayer->pos.x - pSprite->x, pPlayer->pos.y - pSprite->y)) >> 2;
4584 }
4585
4586 if (pData[0] != 2 && pData[0] != 3)
4587 {
4588 int newAngle;
4589 int locatorDist = ldist(&sprite[spriteOwner], pSprite);
4590 if (locatorDist <= 1524)
4591 {
4592 newAngle = pSprite->ang;
4593 pSprite->xvel >>= 1;
4594 }
4595 else newAngle = getangle(sprite[spriteOwner].x - pSprite->x, sprite[spriteOwner].y - pSprite->y);
4596
4597 if (pData[0] == 1 || pData[0] == 4) // Found a locator and going with it
4598 {
4599 locatorDist = dist(&sprite[spriteOwner], pSprite);
4600
4601 if (locatorDist <= 1524)
4602 {
4603 pData[0] = (pData[0] == 1) ? 0 : 5;
4604 }
4605 else
4606 {
4607 // Control speed here
4608 if (pSprite->xvel < 256) pSprite->xvel += 32;
4609 }
4610
4611 if (pData[0] < 2) pData[2]++;
4612
4613 if (playerDist < 6144 && pData[0] < 2 && pData[2] > (GAMETICSPERSEC*4))
4614 {
4615 pData[0] = 2+(krand()&2);
4616 pData[2] = 0;
4617 actor[spriteNum].tempang = pSprite->ang;
4618 }
4619 }
4620
4621 int locatorSprite = pSprite->owner;
4622
4623 if (pData[0] == 0 || pData[0] == 5)
4624 {
4625 pData[0] = (pData[0] == 0) ? 1 : 4;
4626 pSprite->owner = A_FindLocator(pSprite->hitag, -1);
4627 locatorSprite = pSprite->owner;
4628
4629 if (locatorSprite == -1)
4630 {
4631 locatorSprite = actor[spriteNum].t_data[5];
4632 pSprite->hitag = locatorSprite;
4633 pSprite->owner = A_FindLocator(locatorSprite, -1);
4634 locatorSprite = pSprite->owner;
4635
4636 if (locatorSprite == -1)
4637 DELETE_SPRITE_AND_CONTINUE(spriteNum);
4638 }
4639 else pSprite->hitag++;
4640 }
4641
4642 // RECON_T4
4643 pData[3] = G_GetAngleDelta(pSprite->ang,newAngle);
4644 pSprite->ang += pData[3]>>3;
4645
4646 if (pSprite->z < sprite[locatorSprite].z - 512)
4647 pSprite->z += 512;
4648 else if (pSprite->z > sprite[locatorSprite].z + 512)
4649 pSprite->z -= 512;
4650 else
4651 pSprite->z = sprite[locatorSprite].z;
4652 }
4653
4654 if (!A_CheckSoundPlaying(spriteNum,RECO_ROAM))
4655 A_PlaySound(RECO_ROAM,spriteNum);
4656
4657 A_SetSprite(spriteNum,CLIPMASK0);
4658
4659 goto next_sprite;
4660 }
4661
4662 case GREENSLIME__STATIC:
4663 {
4664 // #ifndef VOLUMEONE
4665 if (!g_netServer && ud.multimode < 2)
4666 {
4667 if (g_noEnemies == 1)
4668 {
4669 pSprite->cstat = 32768;
4670 goto next_sprite;
4671 }
4672 else if (g_noEnemies == 2) pSprite->cstat = 257;
4673 }
4674 // #endif
4675
4676 pData[1]+=128;
4677
4678 if (sector[sectNum].floorstat&1)
4679 DELETE_SPRITE_AND_CONTINUE(spriteNum);
4680
4681 int32_t playerDist;
4682 int const playerNum = A_FindPlayer(pSprite, &playerDist);
4683 auto const pPlayer = g_player[playerNum].ps;
4684
4685 if (playerDist > 20480)
4686 {
4687 if (++actor[spriteNum].timetosleep > SLEEPTIME)
4688 {
4689 actor[spriteNum].timetosleep = 0;
4690 changespritestat(spriteNum, STAT_ZOMBIEACTOR);
4691 goto next_sprite;
4692 }
4693 }
4694
4695 enum
4696 {
4697 GREENSLIME_FROZEN = -5,
4698 GREENSLIME_ONPLAYER,
4699 GREENSLIME_DEAD, // set but not checked anywhere...
4700 GREENSLIME_EATINGACTOR,
4701 GREENSLIME_DONEEATING,
4702 GREENSLIME_ONFLOOR,
4703 GREENSLIME_TOCEILING,
4704 GREENSLIME_ONCEILING,
4705 GREENSLIME_TOFLOOR,
4706 };
4707
4708 if (pData[0] == GREENSLIME_FROZEN)
4709 {
4710 pData[3]++;
4711 if (pData[3] > 280)
4712 {
4713 pSprite->pal = 0;
4714 pData[0] = GREENSLIME_ONFLOOR;
4715 pData[3] = 0;
4716 goto next_sprite;
4717 }
4718 A_Fall(spriteNum);
4719
4720 pSprite->cstat = 257;
4721 pSprite->picnum = GREENSLIME + 2;
4722 pSprite->extra = 1;
4723 pSprite->pal = 1;
4724
4725 int const damageTile = A_IncurDamage(spriteNum);
4726 if (damageTile >= 0)
4727 {
4728 if (damageTile == FREEZEBLAST)
4729 goto next_sprite;
4730
4731 P_AddKills(pPlayer, 1);
4732
4733 for (bssize_t j = 16; j >= 0; --j)
4734 {
4735 int32_t newSprite = A_InsertSprite(SECT(spriteNum), SX(spriteNum), SY(spriteNum), SZ(spriteNum),
4736 GLASSPIECES + (j % 3), -32, 36, 36, krand() & 2047, 32 + (krand() & 63),
4737 1024 - (krand() & 1023), spriteNum, 5);
4738 sprite[newSprite].pal = 1;
4739 }
4740
4741 A_PlaySound(GLASS_BREAKING, spriteNum);
4742 DELETE_SPRITE_AND_CONTINUE(spriteNum);
4743 }
4744 else if (playerDist < 1024 && pPlayer->quick_kick == 0)
4745 {
4746 int const angDiff = G_GetAngleDelta(fix16_to_int(pPlayer->q16ang), getangle(SX(spriteNum) - pPlayer->pos.x,
4747 SY(spriteNum) - pPlayer->pos.y));
4748
4749 if (angDiff > -128 && angDiff < 128)
4750 pPlayer->quick_kick = 14;
4751 }
4752
4753 goto next_sprite;
4754 }
4755
4756 pSprite->cstat = (playerDist < 1596) ? 0 : 257;
4757
4758 if (pData[0] == GREENSLIME_ONPLAYER)
4759 {
4760 if (sprite[pPlayer->i].extra < 1 && pPlayer->somethingonplayer == spriteNum)
4761 {
4762 pPlayer->somethingonplayer = -1;
4763 pData[0] = GREENSLIME_TOFLOOR;
4764 goto next_sprite;
4765 }
4766
4767 setsprite(spriteNum,&pSprite->pos);
4768
4769 pSprite->ang = fix16_to_int(pPlayer->q16ang);
4770
4771 if ((TEST_SYNC_KEY(g_player[playerNum].input.bits, SK_FIRE) || (pPlayer->quick_kick > 0)) && sprite[pPlayer->i].extra > 0)
4772 if (pPlayer->quick_kick > 0 ||
4773 (PWEAPON(playerNum, pPlayer->curr_weapon, WorksLike) != HANDREMOTE_WEAPON && PWEAPON(playerNum, pPlayer->curr_weapon, WorksLike) != HANDBOMB_WEAPON &&
4774 PWEAPON(playerNum, pPlayer->curr_weapon, WorksLike) != TRIPBOMB_WEAPON && pPlayer->ammo_amount[pPlayer->curr_weapon] >= 0))
4775 {
4776 for (bssize_t x = 0; x < 8; ++x)
4777 {
4778 int const j
4779 = A_InsertSprite(sectNum, pSprite->x, pSprite->y, pSprite->z - ZOFFSET3, SCRAP3 + (krand() & 3), -8, 48, 48,
4780 krand() & 2047, (krand() & 63) + 64, -(krand() & 4095) - (pSprite->zvel >> 2), spriteNum, 5);
4781 sprite[j].pal = 6;
4782 }
4783
4784 A_PlaySound(SLIM_DYING,spriteNum);
4785 A_PlaySound(SQUISHED,spriteNum);
4786
4787 if ((krand()&255) < 32)
4788 {
4789 int const j = A_Spawn(spriteNum,BLOODPOOL);
4790 sprite[j].pal = 0;
4791 }
4792
4793 P_AddKills(pPlayer, 1);
4794 pData[0] = GREENSLIME_DEAD;
4795
4796 if (pPlayer->somethingonplayer == spriteNum)
4797 pPlayer->somethingonplayer = -1;
4798
4799 DELETE_SPRITE_AND_CONTINUE(spriteNum);
4800 }
4801
4802 pSprite->z = pPlayer->pos.z + pPlayer->pyoff - pData[2] + ZOFFSET3 + (fix16_to_int(F16(100) - pPlayer->q16horiz) << 4);
4803
4804 if (pData[2] > 512)
4805 pData[2] -= 128;
4806
4807 if (pData[2] < 348)
4808 pData[2] += 128;
4809
4810 if (pPlayer->newowner >= 0)
4811 G_ClearCameraView(pPlayer);
4812
4813 if (pData[3] > 0)
4814 {
4815 static const char slimeFrames[] = { 5, 5, 6, 6, 7, 7, 6, 5 };
4816
4817 Bassert(pData[3] < ARRAY_SSIZE(slimeFrames));
4818
4819 pSprite->picnum = GREENSLIME + slimeFrames[pData[3]];
4820
4821 if (pData[3] == 5)
4822 {
4823 sprite[pPlayer->i].extra += -(5 + (krand() & 3));
4824 A_PlaySound(SLIM_ATTACK, spriteNum);
4825 }
4826
4827 if (pData[3] < 7)
4828 pData[3]++;
4829 else
4830 pData[3] = 0;
4831 }
4832 else
4833 {
4834 pSprite->picnum = GREENSLIME + 5;
4835 if (rnd(32))
4836 pData[3] = 1;
4837 }
4838
4839 pSprite->xrepeat = 20 + (sintable[pData[1] & 2047] >> 13);
4840 pSprite->yrepeat = 15 + (sintable[pData[1] & 2047] >> 13);
4841 pSprite->x = pPlayer->pos.x + (sintable[(fix16_to_int(pPlayer->q16ang) + 512) & 2047] >> 7);
4842 pSprite->y = pPlayer->pos.y + (sintable[fix16_to_int(pPlayer->q16ang) & 2047] >> 7);
4843
4844 goto next_sprite;
4845 }
4846 else if (pSprite->xvel < 64 && playerDist < 768)
4847 {
4848 if (pPlayer->somethingonplayer == -1 && sprite[pPlayer->i].extra > 0)
4849 {
4850 pPlayer->somethingonplayer = spriteNum;
4851
4852 if (pData[0] == GREENSLIME_TOFLOOR || pData[0] == GREENSLIME_ONCEILING) // Falling downward
4853 pData[2] = (12 << 8);
4854 else
4855 pData[2] = -(13 << 8); // Climbing up player
4856
4857 pData[0] = GREENSLIME_ONPLAYER;
4858 }
4859 }
4860
4861 int const damageTile = A_IncurDamage(spriteNum);
4862 if (damageTile >= 0)
4863 {
4864 A_PlaySound(SLIM_DYING,spriteNum);
4865
4866 if (pPlayer->somethingonplayer == spriteNum)
4867 pPlayer->somethingonplayer = -1;
4868
4869 if (damageTile == FREEZEBLAST)
4870 {
4871 A_PlaySound(SOMETHINGFROZE, spriteNum);
4872 pData[0] = GREENSLIME_FROZEN;
4873 pData[3] = 0;
4874 goto next_sprite;
4875 }
4876
4877 P_AddKills(pPlayer, 1);
4878
4879 if ((krand()&255) < 32)
4880 {
4881 int const j = A_Spawn(spriteNum,BLOODPOOL);
4882 sprite[j].pal = 0;
4883 }
4884
4885 for (bssize_t x=0; x<8; x++)
4886 {
4887 int const j = A_InsertSprite(sectNum, pSprite->x, pSprite->y, pSprite->z - ZOFFSET3, SCRAP3 + (krand() & 3), -8,
4888 48, 48, krand() & 2047, (krand() & 63) + 64, -(krand() & 4095) - (pSprite->zvel >> 2),
4889 spriteNum, 5);
4890 sprite[j].pal = 6;
4891 }
4892 pData[0] = GREENSLIME_DEAD;
4893 DELETE_SPRITE_AND_CONTINUE(spriteNum);
4894 }
4895 // All weap
4896 if (pData[0] == GREENSLIME_DONEEATING)
4897 {
4898 A_Fall(spriteNum);
4899
4900 pSprite->cstat &= 65535-8;
4901 pSprite->picnum = GREENSLIME+4;
4902
4903 if (pSprite->xrepeat > 32) pSprite->xrepeat -= krand()&7;
4904 if (pSprite->yrepeat > 16) pSprite->yrepeat -= krand()&7;
4905 else
4906 {
4907 pSprite->xrepeat = 40;
4908 pSprite->yrepeat = 16;
4909 pData[5] = -1;
4910 pData[0] = GREENSLIME_ONFLOOR;
4911 }
4912
4913 goto next_sprite;
4914 }
4915 else if (pData[0] != GREENSLIME_EATINGACTOR) A_GetZLimits(spriteNum);
4916
4917 if (pData[0] == GREENSLIME_EATINGACTOR) //On top of somebody
4918 {
4919 A_Fall(spriteNum);
4920 sprite[pData[5]].xvel = 0;
4921
4922 int const ang = sprite[pData[5]].ang;
4923 pSprite->x = sprite[pData[5]].x + (sintable[(ang + 512) & 2047] >> 11);
4924 pSprite->y = sprite[pData[5]].y + (sintable[ang & 2047] >> 11);
4925 pSprite->z = sprite[pData[5]].z;
4926
4927 pSprite->picnum = GREENSLIME + 2 + (g_globalRandom & 1);
4928
4929 if (pSprite->yrepeat < 64)
4930 pSprite->yrepeat += 2;
4931 else
4932 {
4933 if (pSprite->xrepeat < 32)
4934 pSprite->xrepeat += 4;
4935 else
4936 {
4937 pData[0] = GREENSLIME_DONEEATING;
4938 playerDist = ldist(pSprite, &sprite[pData[5]]);
4939
4940 if (playerDist < 768)
4941 {
4942 sprite[pData[5]].xrepeat = 0;
4943
4944 // JBF 20041129: a slimer eating another enemy really ought
4945 // to decrease the maximum kill count by one.
4946 if (sprite[pData[5]].extra > 0)
4947 g_player[myconnectindex].ps->max_actors_killed--;
4948 }
4949 }
4950 }
4951
4952 goto next_sprite;
4953 }
4954
4955 //Check randomly to see of there is an actor near
4956 if (rnd(32))
4957 {
4958 for (bssize_t SPRITES_OF_SECT(sectNum, j))
4959 {
4960 if (A_CheckSpriteFlags(j, SFLAG_GREENSLIMEFOOD))
4961 {
4962 if (ldist(pSprite, &sprite[j]) < 768 && (klabs(pSprite->z - sprite[j].z) < 8192)) // Gulp them
4963 {
4964 pData[5] = j;
4965 pData[0] = GREENSLIME_EATINGACTOR;
4966 pData[1] = 0;
4967 goto next_sprite;
4968 }
4969 }
4970 }
4971 }
4972
4973 //Moving on the ground or ceiling
4974
4975 if (pData[0] == GREENSLIME_ONFLOOR || pData[0] == GREENSLIME_ONCEILING)
4976 {
4977 pSprite->picnum = GREENSLIME;
4978
4979 if ((krand()&511) == 0)
4980 A_PlaySound(SLIM_ROAM,spriteNum);
4981
4982 if (pData[0]==GREENSLIME_ONCEILING)
4983 {
4984 pSprite->zvel = 0;
4985 pSprite->cstat &= (65535-8);
4986
4987 if ((sector[sectNum].ceilingstat&1) || (actor[spriteNum].ceilingz+6144) < pSprite->z)
4988 {
4989 pSprite->z += 2048;
4990 pData[0] = GREENSLIME_TOFLOOR;
4991 goto next_sprite;
4992 }
4993 }
4994 else
4995 {
4996 pSprite->cstat |= 8;
4997 A_Fall(spriteNum);
4998 }
4999
5000 if (everyothertime&1) A_SetSprite(spriteNum,CLIPMASK0);
5001
5002 if (pSprite->xvel > 96)
5003 {
5004 pSprite->xvel -= 2;
5005 goto next_sprite;
5006 }
5007 else
5008 {
5009 pSprite->xvel = 64 - (sintable[(pData[1]+512)&2047]>>9);
5010
5011 pSprite->ang += G_GetAngleDelta(pSprite->ang,
5012 getangle(pPlayer->pos.x-pSprite->x,pPlayer->pos.y-pSprite->y))>>3;
5013 // TJR
5014 }
5015
5016 pSprite->xrepeat = 36 + (sintable[(pData[1]+512)&2047]>>11);
5017 pSprite->yrepeat = 16 + (sintable[pData[1]&2047]>>13);
5018
5019 if (rnd(4) && (sector[sectNum].ceilingstat&1) == 0 &&
5020 klabs(actor[spriteNum].floorz-actor[spriteNum].ceilingz)
5021 < (192<<8))
5022 {
5023 pSprite->zvel = 0;
5024 pData[0]++;
5025 }
5026
5027 }
5028
5029 if (pData[0]==GREENSLIME_TOCEILING)
5030 {
5031 pSprite->picnum = GREENSLIME;
5032 if (pSprite->yrepeat < 40) pSprite->yrepeat+=8;
5033 if (pSprite->xrepeat > 8) pSprite->xrepeat-=4;
5034 if (pSprite->zvel > -(2048+1024))
5035 pSprite->zvel -= 348;
5036 pSprite->z += pSprite->zvel;
5037 if (pSprite->z < actor[spriteNum].ceilingz+4096)
5038 {
5039 pSprite->z = actor[spriteNum].ceilingz+4096;
5040 pSprite->xvel = 0;
5041 pData[0] = GREENSLIME_ONCEILING;
5042 }
5043 }
5044
5045 if (pData[0]==GREENSLIME_TOFLOOR)
5046 {
5047 pSprite->picnum = GREENSLIME+1;
5048
5049 A_Fall(spriteNum);
5050
5051 if (pSprite->z > actor[spriteNum].floorz-ZOFFSET3)
5052 {
5053 pSprite->yrepeat-=4;
5054 pSprite->xrepeat+=2;
5055 }
5056 else
5057 {
5058 if (pSprite->yrepeat < (40-4)) pSprite->yrepeat+=8;
5059 if (pSprite->xrepeat > 8) pSprite->xrepeat-=4;
5060 }
5061
5062 if (pSprite->z > actor[spriteNum].floorz-2048)
5063 {
5064 pSprite->z = actor[spriteNum].floorz-2048;
5065 pData[0] = GREENSLIME_ONFLOOR;
5066 pSprite->xvel = 0;
5067 }
5068 }
5069 goto next_sprite;
5070 }
5071
5072 case BOUNCEMINE__STATIC:
5073 if (pSprite->xvel != 0)
5074 case MORTER__STATIC:
5075 {
5076 int const j = A_Spawn(spriteNum, (PLUTOPAK ? FRAMEEFFECT1 : FRAMEEFFECT1_13));
5077 actor[j].t_data[0] = 3;
5078 }
5079 fallthrough__;
5080 case HEAVYHBOMB__STATIC:
5081 {
5082 int playerNum;
5083 DukePlayer_t *pPlayer;
5084 int detonatePlayer;
5085
5086 if ((pSprite->cstat&32768))
5087 {
5088 if (--pData[2] <= 0)
5089 {
5090 A_PlaySound(TELEPORTER, spriteNum);
5091 A_Spawn(spriteNum, TRANSPORTERSTAR);
5092 pSprite->cstat = 257;
5093 }
5094 goto next_sprite;
5095 }
5096
5097 int32_t playerDist;
5098 playerNum = A_FindPlayer(pSprite, &playerDist);
5099 pPlayer = g_player[playerNum].ps;
5100
5101 if (playerDist < 1220)
5102 pSprite->cstat &= ~257;
5103 else
5104 pSprite->cstat |= 257;
5105
5106 if (pData[3] == 0)
5107 {
5108 if (A_IncurDamage(spriteNum) >= 0)
5109 {
5110 pData[3] = 1;
5111 pData[2] = 0;
5112 detonatePlayer = 0;
5113 pSprite->xvel = 0;
5114 goto DETONATEB;
5115 }
5116 }
5117
5118 if (pSprite->picnum != BOUNCEMINE)
5119 {
5120 A_Fall(spriteNum);
5121
5122 if ((sector[sectNum].lotag != ST_1_ABOVE_WATER || actor[spriteNum].floorz != sector[sectNum].floorz) && pSprite->z >= actor[spriteNum].floorz-(ACTOR_FLOOR_OFFSET) && pSprite->yvel < 3)
5123 {
5124 if (pSprite->yvel > 0 || (pSprite->yvel == 0 && actor[spriteNum].floorz == sector[sectNum].floorz))
5125 A_PlaySound(PIPEBOMB_BOUNCE,spriteNum);
5126 pSprite->zvel = -((4-pSprite->yvel)<<8);
5127 if (sector[pSprite->sectnum].lotag == ST_2_UNDERWATER)
5128 pSprite->zvel >>= 2;
5129 pSprite->yvel++;
5130 }
5131 if (pSprite->z < actor[spriteNum].ceilingz) // && sector[sect].lotag != ST_2_UNDERWATER )
5132 {
5133 pSprite->z = actor[spriteNum].ceilingz+(3<<8);
5134 pSprite->zvel = 0;
5135 }
5136 }
5137
5138 // can't initialize this because of the goto above
5139 vec3_t tmpvect;
5140 tmpvect.x = (pSprite->xvel * (sintable[(pSprite->ang + 512) & 2047])) >> 14;
5141 tmpvect.y = (pSprite->xvel * (sintable[pSprite->ang & 2047])) >> 14;
5142 tmpvect.z = pSprite->zvel;
5143
5144 int moveSprite;
5145 moveSprite = A_MoveSprite(spriteNum, &tmpvect, CLIPMASK0);
5146
5147 actor[spriteNum].movflag = moveSprite;
5148
5149 if (sector[SECT(spriteNum)].lotag == ST_1_ABOVE_WATER && pSprite->zvel == 0 && actor[spriteNum].floorz == sector[sectNum].floorz)
5150 {
5151 pSprite->z += ZOFFSET5;
5152 if (pData[5] == 0)
5153 {
5154 pData[5] = 1;
5155 A_Spawn(spriteNum,WATERSPLASH2);
5156 }
5157 }
5158 else pData[5] = 0;
5159
5160 if (pData[3] == 0 && (pSprite->picnum == BOUNCEMINE || pSprite->picnum == MORTER) && (moveSprite || playerDist < 844))
5161 {
5162 pData[3] = 1;
5163 pData[2] = 0;
5164 detonatePlayer = 0;
5165 pSprite->xvel = 0;
5166 goto DETONATEB;
5167 }
5168
5169 if (sprite[pSprite->owner].picnum == APLAYER)
5170 detonatePlayer = P_Get(pSprite->owner);
5171 else detonatePlayer = -1;
5172
5173 if (pSprite->xvel > 0)
5174 {
5175 pSprite->xvel -= 5;
5176 if (sector[sectNum].lotag == ST_2_UNDERWATER)
5177 pSprite->xvel -= 10;
5178
5179 if (pSprite->xvel < 0)
5180 pSprite->xvel = 0;
5181 if (pSprite->xvel&8) pSprite->cstat ^= 4;
5182 }
5183
5184 if ((moveSprite&49152) == 32768)
5185 {
5186 moveSprite &= (MAXWALLS - 1);
5187 A_DamageWall(spriteNum, moveSprite, pSprite->pos, pSprite->picnum);
5188 Proj_BounceOffWall(pSprite, moveSprite);
5189 pSprite->xvel >>= 1;
5190 }
5191
5192 DETONATEB:
5193 // Pipebomb control set to timer? (see player.c)
5194 // TIMER_CONTROL
5195 if (pSprite->picnum == HEAVYHBOMB && pData[6] == 1)
5196 {
5197 if (pData[7] >= 1)
5198 pData[7]--;
5199
5200 if (pData[7] <= 0)
5201 pData[6] = 3;
5202 }
5203
5204 if ((detonatePlayer >= 0 && g_player[detonatePlayer].ps->hbomb_on == 0 && pData[6] == 2) || pData[3] == 1)
5205 pData[6] = 3;
5206
5207 if (pData[6] == 3)
5208 {
5209 pData[2]++;
5210
5211 if (pData[2] == 2)
5212 {
5213 int const x = pSprite->extra;
5214 int radius = 0;
5215
5216 switch (DYNAMICTILEMAP(pSprite->picnum))
5217 {
5218 case HEAVYHBOMB__STATIC: radius = g_pipebombRadius; break;
5219 case MORTER__STATIC: radius = g_morterRadius; break;
5220 case BOUNCEMINE__STATIC: radius = g_bouncemineRadius; break;
5221 }
5222
5223 A_RadiusDamage(spriteNum, radius, x >> 2, x >> 1, x - (x >> 2), x);
5224
5225 int const j = A_Spawn(spriteNum, EXPLOSION2);
5226 A_PlaySound(PIPEBOMB_EXPLODE, j);
5227
5228 if (pSprite->zvel == 0)
5229 A_Spawn(spriteNum,EXPLOSION2BOT);
5230
5231 for (bssize_t x = 0; x < 8; ++x)
5232 RANDOMSCRAP(pSprite, spriteNum);
5233 }
5234
5235 if (pSprite->yrepeat)
5236 {
5237 pSprite->yrepeat = 0;
5238 goto next_sprite;
5239 }
5240
5241 if (pData[2] > 20)
5242 {
5243 if (pSprite->owner != spriteNum || ud.respawn_items == 0)
5244 {
5245 DELETE_SPRITE_AND_CONTINUE(spriteNum);
5246 }
5247 else
5248 {
5249 pData[2] = g_itemRespawnTime;
5250 A_Spawn(spriteNum,RESPAWNMARKERRED);
5251 pSprite->cstat = 32768;
5252 pSprite->yrepeat = 9;
5253 goto next_sprite;
5254 }
5255 }
5256 }
5257 else if (pSprite->picnum == HEAVYHBOMB && playerDist < 788 && pData[0] > 7 && pSprite->xvel == 0)
5258 {
5259 if (cansee(pSprite->x, pSprite->y, pSprite->z - ZOFFSET3, pSprite->sectnum,
5260 pPlayer->pos.x, pPlayer->pos.y, pPlayer->pos.z, pPlayer->cursectnum))
5261 {
5262 if (pPlayer->ammo_amount[HANDBOMB_WEAPON] < pPlayer->max_ammo_amount[HANDBOMB_WEAPON])
5263 {
5264 if ((g_gametypeFlags[ud.coop] & GAMETYPE_WEAPSTAY) && pSprite->owner == spriteNum)
5265 {
5266 for (bssize_t j = 0; j < pPlayer->weapreccnt; j++)
5267 {
5268 if (pPlayer->weaprecs[j] == pSprite->picnum)
5269 goto next_sprite;
5270 }
5271
5272 if (pPlayer->weapreccnt < MAX_WEAPONS)
5273 pPlayer->weaprecs[pPlayer->weapreccnt++] = pSprite->picnum;
5274 }
5275
5276 P_AddAmmo(pPlayer, HANDBOMB_WEAPON, 1);
5277 A_PlaySound(DUKE_GET, pPlayer->i);
5278
5279 if ((pPlayer->gotweapon & (1<<HANDBOMB_WEAPON)) == 0 || pSprite->owner == pPlayer->i)
5280 {
5281 int doSwitch = ((pPlayer->weaponswitch & 1) ||
5282 PWEAPON(playerNum, pPlayer->curr_weapon, WorksLike) == HANDREMOTE_WEAPON);
5283 P_AddWeapon(pPlayer, HANDBOMB_WEAPON, doSwitch);
5284 }
5285
5286 if (sprite[pSprite->owner].picnum != APLAYER)
5287 P_PalFrom(pPlayer, 32, 0, 32, 0);
5288
5289 if (pSprite->owner != spriteNum || ud.respawn_items == 0)
5290 {
5291 if (pSprite->owner == spriteNum && (g_gametypeFlags[ud.coop] & GAMETYPE_WEAPSTAY))
5292 goto next_sprite;
5293 DELETE_SPRITE_AND_CONTINUE(spriteNum);
5294 }
5295 else
5296 {
5297 pData[2] = g_itemRespawnTime;
5298 A_Spawn(spriteNum, RESPAWNMARKERRED);
5299 pSprite->cstat = 32768;
5300 }
5301 }
5302 }
5303 }
5304
5305 if (pData[0] < 8)
5306 pData[0]++;
5307
5308 goto next_sprite;
5309 }
5310
5311 case REACTORBURNT__STATIC:
5312 case REACTOR2BURNT__STATIC:
5313 goto next_sprite;
5314
5315 case REACTOR__STATIC:
5316 case REACTOR2__STATIC:
5317 {
5318 if (pData[4] == 1)
5319 {
5320 for (bssize_t SPRITES_OF_SECT(sectNum, j))
5321 {
5322 switch (DYNAMICTILEMAP(sprite[j].picnum))
5323 {
5324 case SECTOREFFECTOR__STATIC:
5325 if (sprite[j].lotag == 1)
5326 {
5327 sprite[j].lotag = 65535u;
5328 sprite[j].hitag = 65535u;
5329 }
5330 break;
5331 case REACTOR__STATIC:
5332 sprite[j].picnum = REACTORBURNT;
5333 break;
5334 case REACTOR2__STATIC:
5335 sprite[j].picnum = REACTOR2BURNT;
5336 break;
5337 case REACTORSPARK__STATIC:
5338 case REACTOR2SPARK__STATIC:
5339 sprite[j].cstat = 32768;
5340 break;
5341 }
5342 }
5343
5344 goto next_sprite;
5345 }
5346
5347 if (pData[1] >= 20)
5348 {
5349 pData[4] = 1;
5350 goto next_sprite;
5351 }
5352
5353 int32_t playerDist;
5354 int playerNum = A_FindPlayer(pSprite, &playerDist);
5355 auto const pPlayer = g_player[playerNum].ps;
5356
5357 if (++pData[2] == 4)
5358 pData[2] = 0;
5359
5360 if (playerDist < 4096)
5361 {
5362 if ((krand() & 255) < 16)
5363 {
5364 if (!A_CheckSoundPlaying(pPlayer->i, DUKE_LONGTERM_PAIN))
5365 A_PlaySound(DUKE_LONGTERM_PAIN, pPlayer->i);
5366
5367 A_PlaySound(SHORT_CIRCUIT, spriteNum);
5368 sprite[pPlayer->i].extra--;
5369 P_PalFrom(pPlayer, 32, 32, 0, 0);
5370 }
5371
5372 pData[0] += 128;
5373
5374 if (pData[3] == 0)
5375 pData[3] = 1;
5376 }
5377 else pData[3] = 0;
5378
5379 if (pData[1])
5380 {
5381 pData[1]++;
5382 pData[4] = pSprite->z;
5383 pSprite->z = sector[sectNum].floorz - (krand() % (sector[sectNum].floorz - sector[sectNum].ceilingz));
5384
5385 switch (pData[1])
5386 {
5387 case 3:
5388 // Turn on all of those flashing sectoreffector.
5389 A_RadiusDamage(spriteNum, 4096, g_impactDamage << 2, g_impactDamage << 2, g_impactDamage << 2, g_impactDamage << 2);
5390
5391 for (bssize_t SPRITES_OF(STAT_STANDABLE, j))
5392 {
5393 if (sprite[j].picnum == MASTERSWITCH && sprite[j].hitag == pSprite->hitag && sprite[j].yvel == 0)
5394 sprite[j].yvel = 1;
5395 }
5396 break;
5397
5398 case 4:
5399 case 7:
5400 case 10:
5401 case 15:
5402 for (bssize_t SPRITES_OF_SECT(sectNum, j))
5403 {
5404 if (j != spriteNum)
5405 {
5406 A_DeleteSprite(j);
5407 break;
5408 }
5409 }
5410
5411 break;
5412 }
5413
5414 for (bssize_t x = 0; x < 16; x++)
5415 RANDOMSCRAP(pSprite, spriteNum);
5416
5417 pSprite->z = pData[4];
5418 pData[4] = 0;
5419 }
5420 else if (A_IncurDamage(spriteNum) >= 0)
5421 {
5422 for (bssize_t x = 0; x < 32; x++)
5423 RANDOMSCRAP(pSprite, spriteNum);
5424
5425 if (pSprite->extra < 0)
5426 pData[1] = 1;
5427 }
5428 goto next_sprite;
5429 }
5430 }
5431 #endif // EDUKE32_STANDALONE
5432
5433 if (!g_netServer && ud.multimode < 2 && A_CheckEnemySprite(pSprite))
5434 {
5435 if (g_noEnemies == 1)
5436 {
5437 pSprite->cstat = 32768;
5438 goto next_sprite;
5439 }
5440 else if (g_noEnemies == 2)
5441 {
5442 pSprite->cstat = 0;
5443 if (pSprite->extra)
5444 pSprite->cstat = 257;
5445 }
5446 }
5447
5448 if (G_TileHasActor(sprite[spriteNum].picnum))
5449 {
5450 int32_t playerDist;
5451 int const playerNum = A_FindPlayer(pSprite, &playerDist);
5452 A_Execute(spriteNum, playerNum, playerDist);
5453 }
5454 next_sprite:
5455 A_MaybeAwakenBadGuys(spriteNum);
5456 spriteNum = nextSprite;
5457 }
5458 }
5459
G_MoveMisc(void)5460 ACTOR_STATIC void G_MoveMisc(void) // STATNUM 5
5461 {
5462 int spriteNum = headspritestat[STAT_MISC];
5463
5464 while (spriteNum >= 0)
5465 {
5466 int const nextSprite = nextspritestat[spriteNum];
5467 int32_t playerDist;
5468 auto const pData = actor[spriteNum].t_data;
5469 auto const pSprite = &sprite[spriteNum];
5470 int sectNum = pSprite->sectnum; // XXX: not const
5471 int switchPic;
5472
5473 if (sectNum < 0 || pSprite->xrepeat == 0)
5474 DELETE_SPRITE_AND_CONTINUE(spriteNum);
5475
5476 actor[spriteNum].bpos = pSprite->pos;
5477
5478 switchPic = pSprite->picnum;
5479
5480 #ifndef EDUKE32_STANDALONE
5481 if (pSprite->picnum > NUKEBUTTON && pSprite->picnum <= NUKEBUTTON+3)
5482 switchPic = NUKEBUTTON;
5483
5484 if (pSprite->picnum > GLASSPIECES && pSprite->picnum <= GLASSPIECES+2)
5485 switchPic = GLASSPIECES;
5486
5487 if (pSprite->picnum == INNERJAW+1)
5488 switchPic--;
5489
5490 if ((pSprite->picnum == MONEY+1) || (pSprite->picnum == MAIL+1) || (pSprite->picnum == PAPER+1))
5491 actor[spriteNum].floorz = pSprite->z = getflorzofslope(pSprite->sectnum,pSprite->x,pSprite->y);
5492 else
5493 #endif
5494 {
5495 switch (DYNAMICTILEMAP(switchPic))
5496 {
5497 case APLAYER__STATIC: pSprite->cstat = 32768; goto next_sprite;
5498 case FRAMEEFFECT1_13__STATIC:
5499 if (PLUTOPAK) goto next_sprite; // JBF: ideally this should never happen...
5500 fallthrough__;
5501 case FRAMEEFFECT1__STATIC:
5502
5503 if (pSprite->owner >= 0)
5504 {
5505 pData[0]++;
5506
5507 if (pData[0] > 7)
5508 {
5509 DELETE_SPRITE_AND_CONTINUE(spriteNum);
5510 }
5511 else if (pData[0] > 4)
5512 pSprite->cstat |= 512+2;
5513 else if (pData[0] > 2)
5514 pSprite->cstat |= 2;
5515 pSprite->xoffset = sprite[pSprite->owner].xoffset;
5516 pSprite->yoffset = sprite[pSprite->owner].yoffset;
5517 }
5518 goto next_sprite;
5519
5520 #ifndef EDUKE32_STANDALONE
5521 case ONFIRESMOKE__STATIC:
5522 case ONFIRE__STATIC:
5523 case BURNEDCORPSE__STATIC:
5524 case LAVAPOOLBUBBLE__STATIC:
5525 case WHISPYSMOKE__STATIC:
5526 case LAVAPOOL__STATIC:
5527 if (!WORLDTOUR)
5528 goto next_sprite;
5529 fallthrough__;
5530 #endif
5531 case EXPLOSION2__STATIC:
5532 case EXPLOSION2BOT__STATIC:
5533 case FORCERIPPLE__STATIC:
5534 case TRANSPORTERSTAR__STATIC:
5535 case TRANSPORTERBEAM__STATIC:
5536 case SMALLSMOKE__STATIC:
5537 #ifndef EDUKE32_STANDALONE
5538 case WATERBUBBLE__STATIC:
5539 case BURNING__STATIC:
5540 case BURNING2__STATIC:
5541 case FECES__STATIC:
5542 case SHRINKEREXPLOSION__STATIC:
5543 case BLOOD__STATIC:
5544 case LASERSITE__STATIC:
5545 #endif
5546 {
5547 if (!G_TileHasActor(sprite[spriteNum].picnum))
5548 goto next_sprite;
5549 int const playerNum = A_FindPlayer(pSprite, &playerDist);
5550 A_Execute(spriteNum, playerNum, playerDist);
5551 goto next_sprite;
5552 }
5553 }
5554
5555 #ifndef EDUKE32_STANDALONE
5556 if (!FURY)
5557 switch (DYNAMICTILEMAP(switchPic))
5558 {
5559 case NEON1__STATIC:
5560 case NEON2__STATIC:
5561 case NEON3__STATIC:
5562 case NEON4__STATIC:
5563 case NEON5__STATIC:
5564 case NEON6__STATIC:
5565 pSprite->shade = ((tabledivide32_noinline(g_globalRandom, pSprite->lotag + 1) & 31) > 4) ? -127 : 127;
5566 goto next_sprite;
5567
5568 case BLOODSPLAT1__STATIC:
5569 case BLOODSPLAT2__STATIC:
5570 case BLOODSPLAT3__STATIC:
5571 case BLOODSPLAT4__STATIC:
5572 if (pData[0] == 3 * GAMETICSPERSEC)
5573 goto next_sprite;
5574
5575 actor[spriteNum].bpos.z -= pSprite->z;
5576
5577 if ((++pData[0] % 9) == 0)
5578 {
5579 pSprite->yrepeat++;
5580 pSprite->z += (tilesiz[pSprite->picnum].y * pSprite->yrepeat) >> 2;
5581 }
5582 else
5583 pSprite->z += 16 + (krand() & 15);
5584
5585 actor[spriteNum].bpos.z += pSprite->z;
5586 goto next_sprite;
5587
5588 case NUKEBUTTON__STATIC:
5589 // case NUKEBUTTON+1:
5590 // case NUKEBUTTON+2:
5591 // case NUKEBUTTON+3:
5592
5593 if (pData[0])
5594 {
5595 pData[0]++;
5596 if (pData[0] == 8)
5597 pSprite->picnum = NUKEBUTTON + 1;
5598 else if (pData[0] == 16)
5599 {
5600 pSprite->picnum = NUKEBUTTON + 2;
5601 g_player[P_Get(pSprite->owner)].ps->fist_incs = 1;
5602 }
5603 if (g_player[P_Get(pSprite->owner)].ps->fist_incs == GAMETICSPERSEC)
5604 pSprite->picnum = NUKEBUTTON + 3;
5605 }
5606 goto next_sprite;
5607
5608 case FORCESPHERE__STATIC:
5609 {
5610 int forceRepeat = pSprite->xrepeat;
5611 if (pData[1] > 0)
5612 {
5613 pData[1]--;
5614 if (pData[1] == 0)
5615 DELETE_SPRITE_AND_CONTINUE(spriteNum);
5616 }
5617 if (actor[pSprite->owner].t_data[1] == 0)
5618 {
5619 if (pData[0] < 64)
5620 {
5621 pData[0]++;
5622 forceRepeat += 3;
5623 }
5624 }
5625 else if (pData[0] > 64)
5626 {
5627 pData[0]--;
5628 forceRepeat -= 3;
5629 }
5630
5631 pSprite->pos = sprite[pSprite->owner].pos;
5632 pSprite->ang += actor[pSprite->owner].t_data[0];
5633
5634 forceRepeat = clamp2(forceRepeat, 1, 64);
5635 pSprite->xrepeat = forceRepeat;
5636 pSprite->yrepeat = forceRepeat;
5637 pSprite->shade = (forceRepeat >> 1) - 48;
5638
5639 for (int j = pData[0]; j > 0; j--)
5640 A_SetSprite(spriteNum, CLIPMASK0);
5641 goto next_sprite;
5642 }
5643
5644 case WATERSPLASH2__STATIC:
5645 pData[0]++;
5646 if (pData[0] == 1)
5647 {
5648 if (sector[sectNum].lotag != ST_1_ABOVE_WATER && sector[sectNum].lotag != ST_2_UNDERWATER)
5649 DELETE_SPRITE_AND_CONTINUE(spriteNum);
5650 /*
5651 else
5652 {
5653 l = getflorzofslope(sect,s->x,s->y)-s->z;
5654 if( l > ZOFFSET2 ) KILLIT(i);
5655 }
5656 else
5657 */
5658 if (!S_CheckSoundPlaying(ITEM_SPLASH))
5659 A_PlaySound(ITEM_SPLASH,spriteNum);
5660 }
5661 if (pData[0] == 3)
5662 {
5663 pData[0] = 0;
5664 pData[1]++; // WATERSPLASH_T2
5665 }
5666 if (pData[1] == 5)
5667 A_DeleteSprite(spriteNum);
5668 goto next_sprite;
5669 case INNERJAW__STATIC:
5670 {
5671 // case INNERJAW+1:
5672 int32_t playerDist, playerNum = A_FindPlayer(pSprite,&playerDist);
5673
5674 if (playerDist < 512)
5675 {
5676 P_PalFrom(g_player[playerNum].ps, 32, 32,0,0);
5677 sprite[g_player[playerNum].ps->i].extra -= 4;
5678 }
5679 }
5680 fallthrough__;
5681 case FIRELASER__STATIC:
5682 if (pSprite->extra != 5)
5683 pSprite->extra = 5;
5684 else DELETE_SPRITE_AND_CONTINUE(spriteNum);
5685 break;
5686 case TONGUE__STATIC:
5687 DELETE_SPRITE_AND_CONTINUE(spriteNum);
5688
5689 case MONEY__STATIC:
5690 case MAIL__STATIC:
5691 case PAPER__STATIC:
5692 {
5693 pSprite->xvel = (krand()&7)+(sintable[T1(spriteNum)&2047]>>9);
5694 T1(spriteNum) += (krand()&63);
5695 if ((T1(spriteNum)&2047) > 512 && (T1(spriteNum)&2047) < 1536)
5696 {
5697 if (sector[sectNum].lotag == ST_2_UNDERWATER)
5698 {
5699 if (pSprite->zvel < 64)
5700 pSprite->zvel += (g_spriteGravity>>5)+(krand()&7);
5701 }
5702 else if (pSprite->zvel < 144)
5703 pSprite->zvel += (g_spriteGravity>>5)+(krand()&7);
5704 }
5705
5706 A_SetSprite(spriteNum, CLIPMASK0);
5707
5708 if ((krand()&3) == 0)
5709 setsprite(spriteNum, &pSprite->pos);
5710
5711 if (pSprite->sectnum == -1)
5712 DELETE_SPRITE_AND_CONTINUE(spriteNum);
5713
5714 int const floorZ = getflorzofslope(pSprite->sectnum, pSprite->x, pSprite->y);
5715
5716 if (pSprite->z > floorZ)
5717 {
5718 pSprite->z = floorZ;
5719 A_AddToDeleteQueue(spriteNum);
5720 PN(spriteNum)++;
5721
5722 for (bssize_t SPRITES_OF(STAT_MISC, j))
5723 {
5724 if (sprite[j].picnum == BLOODPOOL && ldist(pSprite, &sprite[j]) < 348)
5725 {
5726 pSprite->pal = 2;
5727 break;
5728 }
5729 }
5730 }
5731
5732 break;
5733 }
5734
5735 case JIBS1__STATIC:
5736 case JIBS2__STATIC:
5737 case JIBS3__STATIC:
5738 case JIBS4__STATIC:
5739 case JIBS5__STATIC:
5740 case JIBS6__STATIC:
5741 case HEADJIB1__STATIC:
5742 case ARMJIB1__STATIC:
5743 case LEGJIB1__STATIC:
5744 case LIZMANHEAD1__STATIC:
5745 case LIZMANARM1__STATIC:
5746 case LIZMANLEG1__STATIC:
5747 case DUKETORSO__STATIC:
5748 case DUKEGUN__STATIC:
5749 case DUKELEG__STATIC:
5750 {
5751 pSprite->xvel = (pSprite->xvel > 0) ? pSprite->xvel - 1 : 0;
5752
5753 if (++pData[5] == (30*10))
5754 DELETE_SPRITE_AND_CONTINUE(spriteNum);
5755
5756 if (pSprite->zvel > 1024 && pSprite->zvel < 1280)
5757 {
5758 setsprite(spriteNum, &pSprite->pos);
5759 sectNum = pSprite->sectnum;
5760 }
5761
5762 int32_t floorZ, ceilZ;
5763 getzsofslope(sectNum, pSprite->x, pSprite->y, &ceilZ, &floorZ);
5764
5765 if (ceilZ == floorZ || sectNum < 0 || sectNum >= MAXSECTORS)
5766 DELETE_SPRITE_AND_CONTINUE(spriteNum);
5767
5768 if (pSprite->z < floorZ-(2<<8))
5769 {
5770 if (pData[1] < 2) pData[1]++;
5771 else if (sector[sectNum].lotag != ST_2_UNDERWATER)
5772 {
5773 pData[1] = 0;
5774
5775 if (pSprite->picnum == DUKELEG || pSprite->picnum == DUKETORSO || pSprite->picnum == DUKEGUN)
5776 {
5777 pData[0] = (pData[0] > 6) ? 0 : pData[0] + 1;
5778 }
5779 else
5780 {
5781 pData[0] = (pData[0] > 2) ? 0 : pData[0] + 1;
5782 }
5783 }
5784
5785 if (pSprite->zvel < ACTOR_MAXFALLINGZVEL)
5786 {
5787 if (sector[sectNum].lotag == ST_2_UNDERWATER)
5788 {
5789 if (pSprite->zvel < 1024)
5790 pSprite->zvel += 48;
5791 else pSprite->zvel = 1024;
5792 }
5793 else pSprite->zvel += g_spriteGravity-50;
5794 }
5795
5796 pSprite->x += (pSprite->xvel*sintable[(pSprite->ang+512)&2047])>>14;
5797 pSprite->y += (pSprite->xvel*sintable[pSprite->ang&2047])>>14;
5798 pSprite->z += pSprite->zvel;
5799 }
5800 else
5801 {
5802 if (pData[2] == 0)
5803 {
5804 if (pSprite->sectnum == -1)
5805 DELETE_SPRITE_AND_CONTINUE(spriteNum);
5806
5807 if ((sector[pSprite->sectnum].floorstat&2))
5808 DELETE_SPRITE_AND_CONTINUE(spriteNum);
5809
5810 pData[2]++;
5811 }
5812
5813 floorZ = getflorzofslope(pSprite->sectnum, pSprite->x, pSprite->y);
5814 pSprite->z = floorZ - (2 << 8);
5815 pSprite->xvel = 0;
5816
5817 if (pSprite->picnum == JIBS6)
5818 {
5819 pData[1]++;
5820
5821 if ((pData[1]&3) == 0 && pData[0] < 7)
5822 pData[0]++;
5823
5824 if (pData[1] > 20)
5825 DELETE_SPRITE_AND_CONTINUE(spriteNum);
5826 }
5827 else
5828 {
5829 pSprite->picnum = JIBS6;
5830 pData[0] = 0;
5831 pData[1] = 0;
5832 }
5833 }
5834 goto next_sprite;
5835 }
5836
5837 case BLOODPOOL__STATIC:
5838 case PUKE__STATIC:
5839 {
5840 if (pData[0] == 0)
5841 {
5842 pData[0] = 1;
5843 if (sector[sectNum].floorstat&2)
5844 {
5845 DELETE_SPRITE_AND_CONTINUE(spriteNum);
5846 }
5847 else A_AddToDeleteQueue(spriteNum);
5848 }
5849
5850 A_Fall(spriteNum);
5851
5852 int32_t playerDist;
5853 int const playerNum = A_FindPlayer(pSprite, &playerDist);
5854
5855 pSprite->z = actor[spriteNum].floorz - 1;
5856
5857 auto const pPlayer = g_player[playerNum].ps;
5858
5859 if (pData[2] < 32)
5860 {
5861 pData[2]++;
5862
5863 if (actor[spriteNum].picnum == TIRE)
5864 {
5865 if (pSprite->xrepeat < 64 && pSprite->yrepeat < 64)
5866 {
5867 pSprite->xrepeat += krand()&3;
5868 pSprite->yrepeat += krand()&3;
5869 }
5870 }
5871 else
5872 {
5873 if (pSprite->xrepeat < 32 && pSprite->yrepeat < 32)
5874 {
5875 pSprite->xrepeat += krand()&3;
5876 pSprite->yrepeat += krand()&3;
5877 }
5878 }
5879 }
5880
5881 if (playerDist < 844 && pSprite->xrepeat > 6 && pSprite->yrepeat > 6)
5882 {
5883 if (pSprite->pal == 0 && pSprite->picnum != PUKE && (krand()&255) < 16)
5884 {
5885 if (pPlayer->inv_amount[GET_BOOTS] > 0)
5886 pPlayer->inv_amount[GET_BOOTS]--;
5887 else
5888 {
5889 if (!A_CheckSoundPlaying(pPlayer->i,DUKE_LONGTERM_PAIN))
5890 A_PlaySound(DUKE_LONGTERM_PAIN,pPlayer->i);
5891
5892 sprite[pPlayer->i].extra --;
5893
5894 P_PalFrom(pPlayer, 32, 16,0,0);
5895 }
5896 }
5897
5898 if (pData[1] == 1) goto next_sprite;
5899
5900 pData[1] = 1;
5901
5902 pPlayer->footprintcount = (actor[spriteNum].picnum == TIRE) ? 10 : 3;
5903 pPlayer->footprintpal = pSprite->pal;
5904 pPlayer->footprintshade = pSprite->shade;
5905
5906 if (pData[2] == 32)
5907 {
5908 pSprite->xrepeat -= 6;
5909 pSprite->yrepeat -= 6;
5910 }
5911 }
5912 else pData[1] = 0;
5913 goto next_sprite;
5914 }
5915
5916 case SHELL__STATIC:
5917 case SHOTGUNSHELL__STATIC:
5918
5919 A_SetSprite(spriteNum,CLIPMASK0);
5920
5921 if (sectNum < 0 || (sector[sectNum].floorz + 256) < pSprite->z)
5922 DELETE_SPRITE_AND_CONTINUE(spriteNum);
5923
5924 if (sector[sectNum].lotag == ST_2_UNDERWATER)
5925 {
5926 pData[1]++;
5927 if (pData[1] > 8)
5928 {
5929 pData[1] = 0;
5930 pData[0]++;
5931 pData[0] &= 3;
5932 }
5933 if (pSprite->zvel < 128) pSprite->zvel += (g_spriteGravity/13); // 8
5934 else pSprite->zvel -= 64;
5935 if (pSprite->xvel > 0)
5936 pSprite->xvel -= 4;
5937 else pSprite->xvel = 0;
5938 }
5939 else
5940 {
5941 pData[1]++;
5942 if (pData[1] > 3)
5943 {
5944 pData[1] = 0;
5945 pData[0]++;
5946 pData[0] &= 3;
5947 }
5948 if (pSprite->zvel < 512) pSprite->zvel += (g_spriteGravity/3); // 52;
5949 if (pSprite->xvel > 0)
5950 pSprite->xvel --;
5951 // else KILLIT(i);
5952 }
5953
5954 goto next_sprite;
5955
5956 case GLASSPIECES__STATIC:
5957 // case GLASSPIECES+1:
5958 // case GLASSPIECES+2:
5959
5960 A_Fall(spriteNum);
5961
5962 if (pSprite->zvel > 4096) pSprite->zvel = 4096;
5963 if (sectNum < 0)
5964 DELETE_SPRITE_AND_CONTINUE(spriteNum);
5965
5966 if (pSprite->z == actor[spriteNum].floorz-(ACTOR_FLOOR_OFFSET) && pData[0] < 3)
5967 {
5968 pSprite->zvel = -((3-pData[0])<<8)-(krand()&511);
5969 if (sector[sectNum].lotag == ST_2_UNDERWATER)
5970 pSprite->zvel >>= 1;
5971 pSprite->xrepeat >>= 1;
5972 pSprite->yrepeat >>= 1;
5973 if (rnd(96))
5974 setsprite(spriteNum,&pSprite->pos);
5975 pData[0]++;//Number of bounces
5976 }
5977 else if (pData[0] == 3)
5978 DELETE_SPRITE_AND_CONTINUE(spriteNum);
5979
5980 if (pSprite->xvel > 0)
5981 {
5982 pSprite->xvel -= 2;
5983 pSprite->cstat = ((pSprite->xvel&3)<<2);
5984 }
5985 else pSprite->xvel = 0;
5986
5987 A_SetSprite(spriteNum,CLIPMASK0);
5988
5989 goto next_sprite;
5990
5991 case FIREFLYFLYINGEFFECT__STATIC:
5992 if (WORLDTOUR && G_TileHasActor(sprite[spriteNum].picnum))
5993 {
5994 int playerDist;
5995 int const playerNum = A_FindPlayer(pSprite, &playerDist);
5996 A_Execute(spriteNum, playerNum, playerDist);
5997 spritetype *pPlayer = &sprite[g_player[playerNum].ps->i];
5998 spritetype* pOwner = &sprite[pSprite->owner];
5999 if (pOwner->picnum != FIREFLY) DELETE_SPRITE_AND_CONTINUE(spriteNum);
6000 if (pOwner->xrepeat < 24 && pOwner->pal != 1)
6001 pSprite->cstat &= ~32768;
6002 else
6003 pSprite->cstat |= 32768;
6004 float dx = pOwner->x-pPlayer->x;
6005 float dy = pOwner->y-pPlayer->y;
6006 float dn = sqrt(dx*dx+dy*dy);
6007 if (dn > 0.f)
6008 {
6009 dx /= dn;
6010 dy /= dn;
6011 }
6012 pSprite->x = pOwner->x-int(dx*-10.f);
6013 pSprite->y = pOwner->y-int(dy*-10.f);
6014 pSprite->z = pOwner->z+0x800;
6015 if (pOwner->extra <= 0) DELETE_SPRITE_AND_CONTINUE(spriteNum);
6016 }
6017 goto next_sprite;
6018 }
6019 #endif
6020 }
6021
6022 #ifndef EDUKE32_STANDALONE
6023 if (!FURY && PN(spriteNum) >= SCRAP6 && PN(spriteNum) <= SCRAP5+3)
6024 {
6025 if (pSprite->xvel > 0)
6026 pSprite->xvel--;
6027 else pSprite->xvel = 0;
6028
6029 if (pSprite->zvel > 1024 && pSprite->zvel < 1280)
6030 {
6031 setsprite(spriteNum,&pSprite->pos);
6032 sectNum = pSprite->sectnum;
6033 }
6034
6035 if (pSprite->z < sector[sectNum].floorz-(2<<8))
6036 {
6037 if (pData[1] < 1) pData[1]++;
6038 else
6039 {
6040 pData[1] = 0;
6041
6042 if (pSprite->picnum < SCRAP6 + 8)
6043 pData[0] = (pData[0] > 6) ? 0 : pData[0] + 1;
6044 else
6045 pData[0] = (pData[0] > 2) ? 0 : pData[0] + 1;
6046 }
6047 if (pSprite->zvel < 4096)
6048 pSprite->zvel += g_spriteGravity - 50;
6049 pSprite->x += (pSprite->xvel*sintable[(pSprite->ang+512)&2047])>>14;
6050 pSprite->y += (pSprite->xvel*sintable[pSprite->ang&2047])>>14;
6051 pSprite->z += pSprite->zvel;
6052 }
6053 else
6054 {
6055 if (pSprite->picnum == SCRAP1 && pSprite->yvel > 0 && pSprite->yvel < MAXUSERTILES)
6056 {
6057 int32_t j = A_Spawn(spriteNum, pSprite->yvel);
6058
6059 setsprite(j,&pSprite->pos);
6060 A_GetZLimits(j);
6061 sprite[j].hitag = sprite[j].lotag = 0;
6062 }
6063
6064 DELETE_SPRITE_AND_CONTINUE(spriteNum);
6065 }
6066 goto next_sprite;
6067 }
6068 #endif
6069
6070 next_sprite:
6071 spriteNum = nextSprite;
6072 }
6073 }
6074
6075
6076 // i: SE spritenum
HandleSE31(int spriteNum,int setFloorZ,int spriteZ,int SEdir,int zDifference)6077 static void HandleSE31(int spriteNum, int setFloorZ, int spriteZ, int SEdir, int zDifference)
6078 {
6079 auto const pSprite = &sprite[spriteNum];
6080 auto const pSector = §or[sprite[spriteNum].sectnum];
6081 auto const pData = actor[spriteNum].t_data;
6082
6083 if (klabs(pSector->floorz - spriteZ) < SP(spriteNum))
6084 {
6085 if (setFloorZ)
6086 pSector->floorz = spriteZ;
6087
6088 pData[2] = SEdir;
6089 pData[0] = 0;
6090 pData[3] = pSprite->hitag;
6091
6092 for (bssize_t SPRITES_OF_SECT(pSprite->sectnum, j))
6093 {
6094 if (sprite[j].zvel == 0 && sprite[j].statnum != STAT_EFFECTOR && sprite[j].statnum != STAT_PROJECTILE)
6095 {
6096 actor[j].bpos.z = sprite[j].z;
6097 actor[j].floorz = pSector->floorz;
6098 }
6099 }
6100
6101 A_CallSound(pSprite->sectnum, spriteNum);
6102 }
6103 else
6104 {
6105 int const zChange = ksgn(zDifference) * SP(spriteNum);
6106
6107 pSector->floorz += zChange;
6108
6109 for (bssize_t SPRITES_OF_SECT(pSprite->sectnum, j))
6110 {
6111 if (sprite[j].picnum == APLAYER && sprite[j].owner >= 0)
6112 {
6113 int const playerNum = P_Get(j);
6114
6115 if (g_player[playerNum].ps->on_ground == 1)
6116 g_player[playerNum].ps->pos.z += zChange;
6117 }
6118
6119 if (sprite[j].zvel == 0 && sprite[j].statnum != STAT_EFFECTOR && sprite[j].statnum != STAT_PROJECTILE)
6120 {
6121 actor[j].bpos.z = sprite[j].z;
6122 sprite[j].z += zChange;
6123 actor[j].floorz = pSector->floorz;
6124 }
6125 }
6126 }
6127 }
6128
6129 // s: SE sprite
MaybeTrainKillPlayer(const spritetype * pSprite,int const setOPos)6130 static void MaybeTrainKillPlayer(const spritetype *pSprite, int const setOPos)
6131 {
6132 for (bssize_t TRAVERSE_CONNECT(playerNum))
6133 {
6134 auto const pPlayer = g_player[playerNum].ps;
6135
6136 if (sprite[pPlayer->i].extra > 0)
6137 {
6138 int16_t playerSectnum = pPlayer->cursectnum;
6139
6140 updatesector(pPlayer->pos.x, pPlayer->pos.y, &playerSectnum);
6141
6142 if (pPlayer->cursectnum != pSprite->sectnum && (playerSectnum == -1 || playerSectnum == pSprite->sectnum))
6143 {
6144 pPlayer->pos.vec2 = pSprite->pos.vec2;
6145
6146 if (setOPos)
6147 pPlayer->opos.vec2 = pPlayer->pos.vec2;
6148
6149 pPlayer->cursectnum = pSprite->sectnum;
6150
6151 setsprite(pPlayer->i, (vec3_t const *)pSprite);
6152 P_QuickKill(pPlayer);
6153 }
6154 }
6155 }
6156 }
6157
6158 // i: SE spritenum
MaybeTrainKillEnemies(int const spriteNum)6159 static void MaybeTrainKillEnemies(int const spriteNum)
6160 {
6161 int findSprite = headspritesect[sprite[OW(spriteNum)].sectnum];
6162
6163 do
6164 {
6165 int const nextSprite = nextspritesect[findSprite];
6166
6167 if (sprite[findSprite].extra >= 0 && sprite[findSprite].statnum == STAT_ACTOR && A_CheckEnemySprite(&sprite[findSprite]))
6168 {
6169 int16_t sectNum = sprite[findSprite].sectnum;
6170
6171 updatesector(sprite[findSprite].x,sprite[findSprite].y,§Num);
6172
6173 if (sectNum == sprite[spriteNum].sectnum || sectNum == -1)
6174 {
6175 actor[findSprite].picnum = RADIUSEXPLOSION;
6176 actor[findSprite].extra = g_impactDamage << 10;
6177 actor[findSprite].owner = spriteNum;
6178 }
6179 }
6180
6181 findSprite = nextSprite;
6182 }
6183 while (findSprite >= 0);
6184 }
6185
G_MoveEffectors(void)6186 ACTOR_STATIC void G_MoveEffectors(void) //STATNUM 3
6187 {
6188 int32_t q = 0, j, k, l, m, x;
6189 int spriteNum = headspritestat[STAT_EFFECTOR];
6190
6191 #ifndef EDUKE32_STANDALONE
6192 if (!FURY)
6193 {
6194 for (native_t TRAVERSE_CONNECT(playerNum))
6195 {
6196 vec2_t & fric = g_player[playerNum].ps->fric;
6197 fric.x = fric.y = 0;
6198 }
6199 }
6200 #endif
6201 while (spriteNum >= 0)
6202 {
6203 int const nextSprite = nextspritestat[spriteNum];
6204 auto const pSprite = &sprite[spriteNum];
6205 int32_t playerDist;
6206 int playerNum = A_FindPlayer(pSprite, &playerDist);
6207 auto const pPlayer = g_player[playerNum].ps;
6208
6209 if (VM_OnEvent(EVENT_MOVEEFFECTORS, spriteNum, playerNum, playerDist, 0))
6210 {
6211 spriteNum = nextSprite;
6212 continue;
6213 }
6214
6215 sectortype *const pSector = §or[pSprite->sectnum];
6216 int const spriteLotag = pSprite->lotag;
6217 int const spriteHitag = pSprite->hitag;
6218 int32_t *const pData = &actor[spriteNum].t_data[0];
6219
6220 switch (spriteLotag)
6221 {
6222 case SE_0_ROTATING_SECTOR:
6223 {
6224 int32_t zchange = 0;
6225
6226 j = pSprite->owner;
6227
6228 if ((uint16_t)sprite[j].lotag == UINT16_MAX)
6229 DELETE_SPRITE_AND_CONTINUE(spriteNum);
6230
6231 q = pSector->extra>>3;
6232 l = 0;
6233
6234 if (pSector->lotag == ST_30_ROTATE_RISE_BRIDGE)
6235 {
6236 q >>= 2;
6237
6238 if (sprite[spriteNum].extra == 1)
6239 {
6240 if (actor[spriteNum].tempang < 256)
6241 {
6242 actor[spriteNum].tempang += 4;
6243 if (actor[spriteNum].tempang >= 256)
6244 A_CallSound(pSprite->sectnum,spriteNum);
6245 if (pSprite->clipdist) l = 1;
6246 else l = -1;
6247 }
6248 else actor[spriteNum].tempang = 256;
6249
6250 if (pSector->floorz > pSprite->z) //z's are touching
6251 {
6252 pSector->floorz -= 512;
6253 zchange = -512;
6254 if (pSector->floorz < pSprite->z)
6255 pSector->floorz = pSprite->z;
6256 }
6257 else if (pSector->floorz < pSprite->z) //z's are touching
6258 {
6259 pSector->floorz += 512;
6260 zchange = 512;
6261 if (pSector->floorz > pSprite->z)
6262 pSector->floorz = pSprite->z;
6263 }
6264 }
6265 else if (sprite[spriteNum].extra == 3)
6266 {
6267 if (actor[spriteNum].tempang > 0)
6268 {
6269 actor[spriteNum].tempang -= 4;
6270 if (actor[spriteNum].tempang <= 0)
6271 A_CallSound(pSprite->sectnum,spriteNum);
6272 if (pSprite->clipdist) l = -1;
6273 else l = 1;
6274 }
6275 else actor[spriteNum].tempang = 0;
6276
6277 if (pSector->floorz > T4(spriteNum)) //z's are touching
6278 {
6279 pSector->floorz -= 512;
6280 zchange = -512;
6281 if (pSector->floorz < T4(spriteNum))
6282 pSector->floorz = T4(spriteNum);
6283 }
6284 else if (pSector->floorz < T4(spriteNum)) //z's are touching
6285 {
6286 pSector->floorz += 512;
6287 zchange = 512;
6288 if (pSector->floorz > T4(spriteNum))
6289 pSector->floorz = T4(spriteNum);
6290 }
6291 }
6292 }
6293 else
6294 {
6295 if (actor[j].t_data[0] == 0) break;
6296 if (actor[j].t_data[0] == 2) DELETE_SPRITE_AND_CONTINUE(spriteNum);
6297
6298 l = (sprite[j].ang > 1024) ? -1 : 1;
6299
6300 if (pData[3] == 0)
6301 pData[3] = ldist(pSprite,&sprite[j]);
6302 pSprite->xvel = pData[3];
6303 pSprite->x = sprite[j].x;
6304 pSprite->y = sprite[j].y;
6305 }
6306
6307 pSprite->ang += (l*q);
6308 pData[2] += (l*q);
6309
6310 if (l && (pSector->floorstat&64))
6311 {
6312 for (TRAVERSE_CONNECT(playerNum))
6313 {
6314 auto const pPlayer = g_player[playerNum].ps;
6315
6316 if (pPlayer->cursectnum == pSprite->sectnum && pPlayer->on_ground == 1)
6317 {
6318 g_player[playerNum].smoothcamera = true;
6319 pPlayer->q16ang += fix16_from_int(l*q);
6320 pPlayer->q16ang &= 0x7FFFFFF;
6321
6322 pPlayer->pos.z += zchange;
6323
6324 vec2_t r;
6325 rotatepoint(sprite[j].pos.vec2,pPlayer->pos.vec2,(q*l),&r);
6326
6327 pPlayer->bobpos.x += r.x-pPlayer->pos.x;
6328 pPlayer->bobpos.y += r.y-pPlayer->pos.y;
6329
6330 pPlayer->pos.vec2 = r;
6331
6332 if (sprite[pPlayer->i].extra <= 0)
6333 sprite[pPlayer->i].pos.vec2 = r;
6334 }
6335 }
6336
6337 for (bssize_t SPRITES_OF_SECT(pSprite->sectnum, p))
6338 {
6339 // KEEPINSYNC1
6340 if (sprite[p].statnum != STAT_EFFECTOR && sprite[p].statnum != STAT_PROJECTILE)
6341 if (sprite[p].picnum != LASERLINE)
6342 {
6343 if (sprite[p].picnum == APLAYER && sprite[p].owner >= 0)
6344 continue;
6345
6346 sprite[p].ang += (l*q);
6347 sprite[p].ang &= 2047;
6348
6349 sprite[p].z += zchange;
6350
6351 // interpolation fix
6352 actor[p].bpos.vec2 = sprite[p].pos.vec2;
6353
6354 if (move_rotfixed_sprite(p, j, pData[2]))
6355 rotatepoint(sprite[j].pos.vec2, sprite[p].pos.vec2, (q * l), &sprite[p].pos.vec2);
6356 }
6357 }
6358
6359 }
6360 else if (l==0 && (pSector->floorstat&64))
6361 {
6362 // fix for jittering of sprites in halted rotating sectors
6363 for (bssize_t SPRITES_OF_SECT(pSprite->sectnum, p))
6364 {
6365 // KEEPINSYNC1
6366 if (sprite[p].statnum != STAT_EFFECTOR && sprite[p].statnum != STAT_PROJECTILE)
6367 if (sprite[p].picnum != LASERLINE)
6368 {
6369 if (sprite[p].picnum == APLAYER && sprite[p].owner >= 0)
6370 continue;
6371
6372 actor[p].bpos.vec2 = sprite[p].pos.vec2;
6373 }
6374 }
6375 }
6376
6377 A_MoveSector(spriteNum);
6378 }
6379 break;
6380
6381 case SE_1_PIVOT: //Nothing for now used as the pivot
6382 if (pSprite->owner == -1) //Init
6383 {
6384 pSprite->owner = spriteNum;
6385
6386 for (SPRITES_OF(STAT_EFFECTOR, j))
6387 {
6388 if (sprite[j].lotag == SE_19_EXPLOSION_LOWERS_CEILING && sprite[j].hitag == spriteHitag)
6389 {
6390 pData[0] = 0;
6391 break;
6392 }
6393 }
6394 }
6395 break;
6396
6397 case SE_6_SUBWAY:
6398 k = pSector->extra;
6399
6400 if (pData[4] > 0)
6401 {
6402 pData[4]--;
6403 if (pData[4] >= (k-(k>>3)))
6404 pSprite->xvel -= (k>>5);
6405 if (pData[4] > ((k>>1)-1) && pData[4] < (k-(k>>3)))
6406 pSprite->xvel = 0;
6407 if (pData[4] < (k>>1))
6408 pSprite->xvel += (k>>5);
6409 if (pData[4] < ((k>>1)-(k>>3)))
6410 {
6411 pData[4] = 0;
6412 pSprite->xvel = k;
6413 }
6414 }
6415 else pSprite->xvel = k;
6416
6417 for (SPRITES_OF(STAT_EFFECTOR, j))
6418 {
6419 if (sprite[j].lotag == SE_14_SUBWAY_CAR && spriteHitag == sprite[j].hitag && actor[j].t_data[0] == pData[0])
6420 {
6421 sprite[j].xvel = pSprite->xvel;
6422 // if( t[4] == 1 )
6423 {
6424 if (actor[j].t_data[5] == 0)
6425 actor[j].t_data[5] = dist(&sprite[j],pSprite);
6426 x = ksgn(dist(&sprite[j],pSprite)-actor[j].t_data[5]);
6427 if (sprite[j].extra)
6428 x = -x;
6429 pSprite->xvel += x;
6430 }
6431 actor[j].t_data[4] = pData[4];
6432 }
6433 }
6434 x = 0; // XXX: This assignment is dead?
6435 fallthrough__;
6436
6437 case SE_14_SUBWAY_CAR:
6438 if (pSprite->owner==-1)
6439 pSprite->owner = A_FindLocator((int16_t)pData[3],(int16_t)pData[0]);
6440
6441 if (pSprite->owner == -1)
6442 {
6443 // debugging subway cars (mapping-wise) is freakin annoying
6444 // let's at least have a helpful message...
6445 Bsprintf(tempbuf,"Could not find any locators in sector %d"
6446 " for SE# 6 or 14 with hitag %d.\n", (int)pData[0], (int)pData[3]);
6447 G_GameExit(tempbuf);
6448 }
6449
6450 j = ldist(&sprite[pSprite->owner],pSprite);
6451
6452 if (j < 1024L)
6453 {
6454 if (spriteLotag==SE_6_SUBWAY)
6455 if (sprite[pSprite->owner].hitag&1)
6456 pData[4]=pSector->extra; //Slow it down
6457 pData[3]++;
6458 pSprite->owner = A_FindLocator(pData[3],pData[0]);
6459 if (pSprite->owner==-1)
6460 {
6461 pData[3]=0;
6462 pSprite->owner = A_FindLocator(0,pData[0]);
6463 }
6464 }
6465
6466 if (pSprite->xvel)
6467 {
6468 #ifdef YAX_ENABLE
6469 int32_t firstrun = 1;
6470 #endif
6471 x = getangle(sprite[pSprite->owner].x-pSprite->x,sprite[pSprite->owner].y-pSprite->y);
6472 q = G_GetAngleDelta(pSprite->ang,x)>>3;
6473
6474 pData[2] += q;
6475 pSprite->ang += q;
6476
6477 if (pSprite->xvel == pSector->extra)
6478 {
6479 if ((pSector->floorstat&1) == 0 && (pSector->ceilingstat&1) == 0)
6480 {
6481 if (!S_CheckSoundPlaying(actor[spriteNum].lastv.x))
6482 A_PlaySound(actor[spriteNum].lastv.x,spriteNum);
6483 }
6484 else if (ud.monsters_off == 0 && pSector->floorpal == 0 && (pSector->floorstat&1) && rnd(8))
6485 {
6486 if (playerDist < 20480)
6487 {
6488 j = pSprite->ang;
6489 pSprite->ang = getangle(pSprite->x-g_player[playerNum].ps->pos.x,pSprite->y-g_player[playerNum].ps->pos.y);
6490 A_Shoot(spriteNum,RPG);
6491 pSprite->ang = j;
6492 }
6493 }
6494 }
6495
6496 if (pSprite->xvel <= 64 && (pSector->floorstat&1) == 0 && (pSector->ceilingstat&1) == 0)
6497 S_StopEnvSound(actor[spriteNum].lastv.x,spriteNum);
6498
6499 if ((pSector->floorz-pSector->ceilingz) < (108<<8))
6500 {
6501 if (ud.noclip == 0 && pSprite->xvel >= 192)
6502 MaybeTrainKillPlayer(pSprite, 0);
6503 }
6504
6505 m = (pSprite->xvel*sintable[(pSprite->ang+512)&2047])>>14;
6506 x = (pSprite->xvel*sintable[pSprite->ang&2047])>>14;
6507
6508 if (sector[pSprite->sectnum].lotag != ST_2_UNDERWATER)
6509 {
6510 // Move player spawns with sector.
6511 for (int spawnNum = 0; spawnNum < g_playerSpawnCnt; spawnNum++)
6512 {
6513 if (g_playerSpawnPoints[spawnNum].sect == pSprite->sectnum)
6514 {
6515 g_playerSpawnPoints[spawnNum].pos.x += m;
6516 g_playerSpawnPoints[spawnNum].pos.y += x;
6517 }
6518 }
6519
6520 for (TRAVERSE_CONNECT(playerNum))
6521 {
6522 auto const pPlayer = g_player[playerNum].ps;
6523
6524 // might happen when squished into void space
6525 if (pPlayer->cursectnum < 0)
6526 break;
6527
6528 if (pSprite->sectnum == pPlayer->cursectnum
6529 #ifdef YAX_ENABLE
6530 || (pData[9] >= 0 && pData[9] == pPlayer->cursectnum)
6531 #endif
6532 )
6533 {
6534 rotatepoint(pSprite->pos.vec2, pPlayer->pos.vec2, q, &pPlayer->pos.vec2);
6535
6536 pPlayer->pos.x += m;
6537 pPlayer->pos.y += x;
6538
6539 pPlayer->bobpos.x += m;
6540 pPlayer->bobpos.y += x;
6541
6542 g_player[playerNum].smoothcamera = true;
6543
6544 pPlayer->q16ang += fix16_from_int(q);
6545 pPlayer->q16ang &= 0x7FFFFFF;
6546
6547 if (sprite[pPlayer->i].extra <= 0)
6548 sprite[pPlayer->i].pos.vec2 = pPlayer->pos.vec2;
6549 }
6550 }
6551
6552 // NOTE: special loop handling
6553 j = headspritesect[pSprite->sectnum];
6554 while (j >= 0)
6555 {
6556 if ((sprite[j].picnum != SECTOREFFECTOR || (sprite[j].lotag == SE_49_POINT_LIGHT || sprite[j].lotag == SE_50_SPOT_LIGHT))
6557 && sprite[j].picnum != LOCATORS)
6558 {
6559 if (move_rotfixed_sprite(j, pSprite - sprite, pData[2]))
6560 rotatepoint(pSprite->pos.vec2, sprite[j].pos.vec2, q, &sprite[j].pos.vec2);
6561
6562 sprite[j].x += m;
6563 sprite[j].y += x;
6564
6565 sprite[j].ang += q;
6566 }
6567 j = nextspritesect[j];
6568 #ifdef YAX_ENABLE
6569 if (j < 0)
6570 {
6571 if (pData[9] >= 0 && firstrun)
6572 {
6573 firstrun = 0;
6574 j = headspritesect[pData[9]];
6575 }
6576 }
6577 #endif
6578 }
6579 }
6580
6581 A_MoveSector(spriteNum);
6582 setsprite(spriteNum,&pSprite->pos);
6583
6584 if ((pSector->floorz-pSector->ceilingz) < (108<<8))
6585 {
6586 if (ud.noclip == 0 && pSprite->xvel >= 192)
6587 MaybeTrainKillPlayer(pSprite, 1);
6588
6589 MaybeTrainKillEnemies(spriteNum);
6590 }
6591 }
6592
6593 break;
6594
6595 case SE_30_TWO_WAY_TRAIN:
6596 if (pSprite->owner == -1)
6597 {
6598 pData[3] = !pData[3];
6599 pSprite->owner = A_FindLocator(pData[3],pData[0]);
6600 }
6601 else
6602 {
6603
6604 if (pData[4] == 1) // Starting to go
6605 {
6606 if (ldist(&sprite[pSprite->owner],pSprite) < (2048-128))
6607 pData[4] = 2;
6608 else
6609 {
6610 if (pSprite->xvel == 0)
6611 G_OperateActivators(pSprite->hitag+(!pData[3]),-1);
6612 if (pSprite->xvel < 256)
6613 pSprite->xvel += 16;
6614 }
6615 }
6616 if (pData[4] == 2)
6617 {
6618 l = FindDistance2D(sprite[pSprite->owner].x-pSprite->x,sprite[pSprite->owner].y-pSprite->y);
6619
6620 if (l <= 128)
6621 pSprite->xvel = 0;
6622
6623 if (pSprite->xvel > 0)
6624 pSprite->xvel -= 16;
6625 else
6626 {
6627 pSprite->xvel = 0;
6628 G_OperateActivators(pSprite->hitag+(int16_t)pData[3],-1);
6629 pSprite->owner = -1;
6630 pSprite->ang += 1024;
6631 pData[4] = 0;
6632 G_OperateForceFields(spriteNum,pSprite->hitag);
6633
6634 for (SPRITES_OF_SECT(pSprite->sectnum, j))
6635 {
6636 if (sprite[j].picnum != SECTOREFFECTOR && sprite[j].picnum != LOCATORS)
6637 actor[j].bpos.vec2 = sprite[j].pos.vec2;
6638 }
6639
6640 }
6641 }
6642 }
6643
6644 if (pSprite->xvel)
6645 {
6646 l = (pSprite->xvel*sintable[(pSprite->ang+512)&2047])>>14;
6647 x = (pSprite->xvel*sintable[pSprite->ang&2047])>>14;
6648
6649 if ((pSector->floorz-pSector->ceilingz) < (108<<8))
6650 if (ud.noclip == 0)
6651 MaybeTrainKillPlayer(pSprite, 0);
6652
6653 // Move player spawns with sector.
6654 for (int spawnNum = 0; spawnNum < g_playerSpawnCnt; spawnNum++)
6655 {
6656 if (g_playerSpawnPoints[spawnNum].sect == pSprite->sectnum)
6657 {
6658 g_playerSpawnPoints[spawnNum].pos.x += l;
6659 g_playerSpawnPoints[spawnNum].pos.y += x;
6660 }
6661 }
6662
6663 for (int TRAVERSE_CONNECT(playerNum))
6664 {
6665 auto const pPlayer = g_player[playerNum].ps;
6666
6667 if (pPlayer->cursectnum == pSprite->sectnum)
6668 {
6669 pPlayer->pos.x += l;
6670 pPlayer->pos.y += x;
6671
6672 if (g_netServer || numplayers > 1)
6673 pPlayer->opos.vec2 = pPlayer->pos.vec2;
6674
6675 pPlayer->bobpos.x += l;
6676 pPlayer->bobpos.y += x;
6677 }
6678 }
6679
6680 for (SPRITES_OF_SECT(pSprite->sectnum, j))
6681 {
6682 // TODO: replace some checks for SE 49/50 with statnum LIGHT instead?
6683 if ((sprite[j].picnum != SECTOREFFECTOR || sprite[j].lotag==SE_49_POINT_LIGHT || sprite[j].lotag==SE_50_SPOT_LIGHT)
6684 && sprite[j].picnum != LOCATORS)
6685 {
6686 if (numplayers < 2 && !g_netServer)
6687 actor[j].bpos.vec2 = sprite[j].pos.vec2;
6688
6689 sprite[j].x += l;
6690 sprite[j].y += x;
6691
6692 if (g_netServer || numplayers > 1)
6693 actor[j].bpos.vec2 = sprite[j].pos.vec2;
6694 }
6695 }
6696
6697 A_MoveSector(spriteNum);
6698 setsprite(spriteNum,&pSprite->pos);
6699
6700 if (pSector->floorz-pSector->ceilingz < (108<<8))
6701 {
6702 if (ud.noclip == 0)
6703 MaybeTrainKillPlayer(pSprite, 1);
6704
6705 MaybeTrainKillEnemies(spriteNum);
6706 }
6707 }
6708
6709 break;
6710
6711
6712 case SE_2_EARTHQUAKE://Quakes
6713 if (pData[4] > 0 && pData[0] == 0)
6714 {
6715 if (pData[4] < spriteHitag)
6716 pData[4]++;
6717 else pData[0] = 1;
6718 }
6719
6720 if (pData[0] > 0)
6721 {
6722 pData[0]++;
6723
6724 pSprite->xvel = 3;
6725
6726 if (pData[0] > 96)
6727 {
6728 pData[0] = -1; //Stop the quake
6729 pData[4] = -1;
6730 DELETE_SPRITE_AND_CONTINUE(spriteNum);
6731 }
6732 else
6733 {
6734 if ((pData[0]&31) == 8)
6735 {
6736 g_earthquakeTime = 48;
6737 A_PlaySound(EARTHQUAKE,g_player[screenpeek].ps->i);
6738 }
6739
6740 pSector->floorheinum = (klabs(pSector->floorheinum - pData[5]) < 8)
6741 ? pData[5]
6742 : pSector->floorheinum + (ksgn(pData[5] - pSector->floorheinum) << 4);
6743 }
6744
6745 vec2_t const vect = { (pSprite->xvel * sintable[(pSprite->ang + 512) & 2047]) >> 14,
6746 (pSprite->xvel * sintable[pSprite->ang & 2047]) >> 14 };
6747
6748 for (TRAVERSE_CONNECT(playerNum))
6749 {
6750 auto const pPlayer = g_player[playerNum].ps;
6751
6752 if (pPlayer->cursectnum == pSprite->sectnum && pPlayer->on_ground)
6753 {
6754 pPlayer->pos.x += vect.x;
6755 pPlayer->pos.y += vect.y;
6756
6757 pPlayer->bobpos.x += vect.x;
6758 pPlayer->bobpos.y += vect.y;
6759 }
6760 }
6761
6762 for (bssize_t nextSprite, SPRITES_OF_SECT_SAFE(pSprite->sectnum, sectSprite, nextSprite))
6763 {
6764 if (sprite[sectSprite].picnum != SECTOREFFECTOR)
6765 {
6766 sprite[sectSprite].x+=vect.x;
6767 sprite[sectSprite].y+=vect.y;
6768 setsprite(sectSprite,&sprite[sectSprite].pos);
6769 }
6770 }
6771
6772 A_MoveSector(spriteNum);
6773 setsprite(spriteNum,&pSprite->pos);
6774 }
6775 break;
6776
6777 //Flashing sector lights after reactor EXPLOSION2
6778
6779 case SE_3_RANDOM_LIGHTS_AFTER_SHOT_OUT:
6780 {
6781 if (pData[4] == 0) break;
6782
6783 // if(t[5] > 0) { t[5]--; break; }
6784
6785 if ((tabledivide32_noinline(g_globalRandom, spriteHitag+1)&31) < 4 && !pData[2])
6786 {
6787 // t[5] = 4+(g_globalRandom&7);
6788 pSector->ceilingpal = pSprite->owner >> 8;
6789 pSector->floorpal = pSprite->owner & 0xff;
6790 pData[0] = pSprite->shade + (g_globalRandom & 15);
6791 }
6792 else
6793 {
6794 // t[5] = 4+(g_globalRandom&3);
6795 pSector->ceilingpal = pSprite->pal;
6796 pSector->floorpal = pSprite->pal;
6797 pData[0] = pData[3];
6798 }
6799
6800 pSector->ceilingshade = pData[0];
6801 pSector->floorshade = pData[0];
6802
6803 walltype *pWall = &wall[pSector->wallptr];
6804
6805 for (x=pSector->wallnum; x > 0; x--,pWall++)
6806 {
6807 if (pWall->hitag != 1)
6808 {
6809 pWall->shade = pData[0];
6810
6811 if ((pWall->cstat & 2) && pWall->nextwall >= 0)
6812 wall[pWall->nextwall].shade = pWall->shade;
6813 }
6814 }
6815
6816 break;
6817 }
6818
6819 case SE_4_RANDOM_LIGHTS:
6820 {
6821 // See A_Spawn():
6822 // s->owner: original ((ceilingpal<<8) | floorpal)
6823 // t[2]: original floor shade
6824 // t[3]: max wall shade
6825 int lightFlag;
6826
6827 if ((tabledivide32_noinline(g_globalRandom, spriteHitag+1)&31) < 4)
6828 {
6829 pData[1] = pSprite->shade + (g_globalRandom & 15); // Got really bright
6830 pData[0] = pSprite->shade + (g_globalRandom & 15);
6831 pSector->ceilingpal = pSprite->owner >> 8;
6832 pSector->floorpal = pSprite->owner & 0xff;
6833 lightFlag = 1;
6834 }
6835 else
6836 {
6837 pData[1] = pData[2];
6838 pData[0] = pData[3];
6839
6840 pSector->ceilingpal = pSprite->pal;
6841 pSector->floorpal = pSprite->pal;
6842
6843 lightFlag = 0;
6844 }
6845
6846 pSector->floorshade = pData[1];
6847 pSector->ceilingshade = pData[1];
6848
6849 walltype *pWall = &wall[pSector->wallptr];
6850
6851 for (x=pSector->wallnum; x > 0; x--,pWall++)
6852 {
6853 if (lightFlag) pWall->pal = (pSprite->owner&0xff);
6854 else pWall->pal = pSprite->pal;
6855
6856 if (pWall->hitag != 1)
6857 {
6858 pWall->shade = pData[0];
6859 if ((pWall->cstat&2) && pWall->nextwall >= 0)
6860 wall[pWall->nextwall].shade = pWall->shade;
6861 }
6862 }
6863
6864 for (bssize_t SPRITES_OF_SECT(SECT(spriteNum), sectSprite))
6865 {
6866 if (sprite[sectSprite].cstat&16 && A_CheckSpriteFlags(sectSprite,SFLAG_NOSHADE) == 0)
6867 sprite[sectSprite].shade = (pSector->ceilingstat & 1) ? pSector->ceilingshade : pSector->floorshade;
6868 }
6869
6870 if (pData[4])
6871 DELETE_SPRITE_AND_CONTINUE(spriteNum);
6872
6873 break;
6874 }
6875
6876 //BOSS
6877 case SE_5:
6878 {
6879 if (playerDist < 8192)
6880 {
6881 int const saveAng = pSprite->ang;
6882 pSprite->ang = getangle(pSprite->x - pPlayer->pos.x, pSprite->y - pPlayer->pos.y);
6883 A_Shoot(spriteNum, FIRELASER);
6884 pSprite->ang = saveAng;
6885 }
6886
6887 if (pSprite->owner==-1) //Start search
6888 {
6889 pData[4] = 0;
6890 int closestLocatorDist = INT32_MAX;
6891 int closestLocator = pSprite->owner;
6892
6893 //Find the shortest dist
6894 do
6895 {
6896 pSprite->owner = A_FindLocator((int16_t)pData[4], -1); // t[0] hold sectnum
6897
6898 if (pSprite->owner == -1)
6899 break;
6900
6901 int const locatorDist = ldist(&sprite[pPlayer->i],&sprite[pSprite->owner]);
6902
6903 if (closestLocatorDist > locatorDist)
6904 {
6905 closestLocator = pSprite->owner;
6906 closestLocatorDist = locatorDist;
6907 }
6908
6909 pData[4]++;
6910 }
6911 while (1);
6912
6913 pSprite->owner = closestLocator;
6914 pSprite->zvel = ksgn(sprite[closestLocator].z - pSprite->z) << 4;
6915 }
6916
6917 if (ldist(&sprite[pSprite->owner],pSprite) < 1024)
6918 {
6919 pSprite->owner = -1;
6920 goto next_sprite;
6921 }
6922 else pSprite->xvel=256;
6923
6924 int const angInc = G_GetAngleDelta(pSprite->ang, getangle(sprite[pSprite->owner].x-pSprite->x,
6925 sprite[pSprite->owner].y-pSprite->y))>>3;
6926 pSprite->ang += angInc;
6927
6928 if (rnd(32))
6929 {
6930 pData[2] += angInc;
6931 pSector->ceilingshade = 127;
6932 }
6933 else
6934 {
6935 pData[2] += G_GetAngleDelta(pData[2] + 512, getangle(pPlayer->pos.x - pSprite->x, pPlayer->pos.y - pSprite->y)) >> 2;
6936 pSector->ceilingshade = 0;
6937 }
6938
6939 if (A_IncurDamage(spriteNum) >= 0)
6940 {
6941 if (++pData[3] == 5)
6942 {
6943 pSprite->zvel += 1024;
6944 P_DoQuote(QUOTE_WASTED, g_player[myconnectindex].ps);
6945 }
6946 }
6947
6948 pSprite->z += pSprite->zvel;
6949 pSector->ceilingz += pSprite->zvel;
6950 sector[pData[0]].ceilingz += pSprite->zvel;
6951
6952 A_MoveSector(spriteNum);
6953 setsprite(spriteNum, &pSprite->pos);
6954 break;
6955 }
6956
6957 case SE_8_UP_OPEN_DOOR_LIGHTS:
6958 case SE_9_DOWN_OPEN_DOOR_LIGHTS:
6959 {
6960
6961 // work only if its moving
6962
6963 int animGoal = -1;
6964
6965 if (actor[spriteNum].t_data[4])
6966 {
6967 if (++actor[spriteNum].t_data[4] > 8)
6968 DELETE_SPRITE_AND_CONTINUE(spriteNum);
6969
6970 animGoal = 1;
6971 }
6972 else animGoal = GetAnimationGoal(&pSector->ceilingz);
6973
6974 if (animGoal >= 0)
6975 {
6976 int shadeInc = ((pSector->lotag & 0x8000u) || actor[spriteNum].t_data[4]) ? -pData[3] : pData[3];
6977
6978 if (spriteLotag == SE_9_DOWN_OPEN_DOOR_LIGHTS)
6979 shadeInc = -shadeInc;
6980
6981 for (bssize_t SPRITES_OF(STAT_EFFECTOR, sectorEffector))
6982 {
6983 if (sprite[sectorEffector].lotag == spriteLotag && sprite[sectorEffector].hitag == spriteHitag)
6984 {
6985 int const sectNum = sprite[sectorEffector].sectnum;
6986 int const spriteShade = sprite[sectorEffector].shade;
6987
6988 walltype *pWall = &wall[sector[sectNum].wallptr];
6989
6990 for (int l=sector[sectNum].wallnum; l>0; l--, pWall++)
6991 {
6992 if (pWall->hitag == 1)
6993 continue;
6994
6995 pWall->shade += shadeInc;
6996
6997 if (pWall->shade < spriteShade)
6998 pWall->shade = spriteShade;
6999 else if (pWall->shade > actor[sectorEffector].t_data[2])
7000 pWall->shade = actor[sectorEffector].t_data[2];
7001
7002 if (pWall->nextwall >= 0 && wall[pWall->nextwall].hitag != 1)
7003 wall[pWall->nextwall].shade = pWall->shade;
7004 }
7005
7006 sector[sectNum].floorshade += shadeInc;
7007 sector[sectNum].ceilingshade += shadeInc;
7008
7009 if (sector[sectNum].floorshade < spriteShade)
7010 sector[sectNum].floorshade = spriteShade;
7011 else if (sector[sectNum].floorshade > actor[sectorEffector].t_data[0])
7012 sector[sectNum].floorshade = actor[sectorEffector].t_data[0];
7013
7014 if (sector[sectNum].ceilingshade < spriteShade)
7015 sector[sectNum].ceilingshade = spriteShade;
7016 else if (sector[sectNum].ceilingshade > actor[sectorEffector].t_data[1])
7017 sector[sectNum].ceilingshade = actor[sectorEffector].t_data[1];
7018 }
7019 }
7020 }
7021 break;
7022 }
7023
7024 case SE_10_DOOR_AUTO_CLOSE:
7025 // XXX: 32791, what the hell?
7026 if ((pSector->lotag&0xff) == ST_27_STRETCH_BRIDGE || (pSector->floorz > pSector->ceilingz && (pSector->lotag&0xff) != ST_23_SWINGING_DOOR) || pSector->lotag == (int16_t)32791u)
7027 {
7028 j = 1;
7029
7030 if ((pSector->lotag&0xff) != ST_27_STRETCH_BRIDGE)
7031 for (bssize_t TRAVERSE_CONNECT(playerNum))
7032 if (pSector->lotag != ST_30_ROTATE_RISE_BRIDGE && pSector->lotag != ST_31_TWO_WAY_TRAIN && pSector->lotag != 0
7033 && pSprite->sectnum == pPlayer->cursectnum)
7034 j = 0;
7035
7036 if (j == 1)
7037 {
7038 if (pData[0] > spriteHitag)
7039 switch (sector[pSprite->sectnum].lotag)
7040 {
7041 case ST_20_CEILING_DOOR:
7042 case ST_21_FLOOR_DOOR:
7043 case ST_22_SPLITTING_DOOR:
7044 case ST_26_SPLITTING_ST_DOOR:
7045 if (GetAnimationGoal(§or[pSprite->sectnum].ceilingz) >= 0)
7046 break;
7047 fallthrough__;
7048 default:
7049 G_ActivateBySector(pSprite->sectnum,spriteNum);
7050 pData[0] = 0;
7051 break;
7052 }
7053 else pData[0]++;
7054 }
7055 }
7056 else pData[0]=0;
7057 break;
7058
7059 case SE_11_SWINGING_DOOR:
7060 if (pData[5] > 0)
7061 {
7062 pData[5]--;
7063 break;
7064 }
7065
7066 if (pData[4])
7067 {
7068 auto dukeLivesMatter = [&](vec2_t const *const pos, int const w, int const clipdist)
7069 {
7070 if (clipinsidebox(pos, w, clipdist))
7071 {
7072 uint16_t const tag = sector[pSprite->sectnum].lotag & 0x8000u;
7073
7074 for (auto SPRITES_OF(STAT_EFFECTOR, i))
7075 {
7076 if (tag == (sector[SECT(i)].lotag & 0x8000u) && SLT(i) == SE_11_SWINGING_DOOR && pSprite->hitag == SHT(i))
7077 {
7078 actor[i].t_data[5] = 2; // delay
7079 actor[i].t_data[2] -= l;
7080 actor[i].t_data[4] -= l;
7081 A_MoveSector(i);
7082
7083 actor[i].t_data[3] = -actor[i].t_data[3];
7084 if (actor[i].t_data[4] < 0)
7085 actor[i].t_data[4] += 512;
7086 else
7087 actor[i].t_data[4] -= 512;
7088
7089 if (sector[SECT(i)].lotag & 0x8000u) sector[SECT(i)].lotag &= 0x7fff;
7090 else sector[SECT(i)].lotag |= 0x8000u;
7091 }
7092 }
7093
7094 A_CallSound(pSprite->sectnum, spriteNum);
7095
7096 return true;
7097 }
7098
7099 return false;
7100 };
7101
7102 int const endWall = pSector->wallptr+pSector->wallnum;
7103
7104 l = (SP(spriteNum) >> 3) * pData[3];
7105 pData[2] += l;
7106 pData[4] += l;
7107
7108 A_MoveSector(spriteNum);
7109
7110 for (auto SPRITES_OF(STAT_ACTOR, spr))
7111 {
7112 auto const foundSprite = (uspriteptr_t)&sprite[spr];
7113
7114 int32_t floorZ, ceilZ;
7115 getcorrectzsofslope(pSprite->sectnum, foundSprite->pos.x, foundSprite->pos.y, &ceilZ, &floorZ);
7116
7117 if ((foundSprite->pos.z > floorZ || foundSprite->pos.z - ((foundSprite->yrepeat * tilesiz[foundSprite->picnum].y) << 2) < ceilZ)
7118 && foundSprite->extra > 0 && A_CheckEnemySprite(foundSprite))
7119 {
7120 auto const clipdist = A_GetClipdist(spr, -1);
7121
7122 for (int w = pSector->wallptr; w < endWall; w++)
7123 {
7124 if (dukeLivesMatter(&foundSprite->pos.vec2, w, clipdist))
7125 break;
7126 }
7127 }
7128 }
7129
7130 for (auto TRAVERSE_CONNECT(plr))
7131 {
7132 auto const foundPlayer = g_player[plr].ps;
7133 auto const foundPlayerSprite = &sprite[foundPlayer->i];
7134
7135 int32_t floorZ, ceilZ;
7136 getcorrectzsofslope(pSprite->sectnum, foundPlayer->pos.x, foundPlayer->pos.y, &ceilZ, &floorZ);
7137
7138 for (int w = pSector->wallptr; w < endWall; w++)
7139 {
7140 if ((foundPlayerSprite->pos.z > floorZ || foundPlayer->pos.z < ceilZ) && foundPlayerSprite->extra > 0
7141 && dukeLivesMatter(&foundPlayer->pos.vec2, w, foundPlayer->clipdist))
7142 break;
7143 }
7144 }
7145
7146 if (pData[4] <= -511 || pData[4] >= 512)
7147 {
7148 pData[4] = 0;
7149 pData[2] &= 0xffffff00;
7150 A_MoveSector(spriteNum);
7151 }
7152 }
7153 break;
7154
7155 case SE_12_LIGHT_SWITCH:
7156 if (pData[0] == 3 || pData[3] == 1) //Lights going off
7157 {
7158 pSector->floorpal = 0;
7159 pSector->ceilingpal = 0;
7160
7161 walltype *pWall = &wall[pSector->wallptr];
7162
7163 for (j = pSector->wallnum; j > 0; j--, pWall++)
7164 {
7165 if (pWall->hitag != 1)
7166 {
7167 pWall->shade = pData[1];
7168 pWall->pal = 0;
7169 }
7170 }
7171
7172 pSector->floorshade = pData[1];
7173 pSector->ceilingshade = pData[2];
7174 pData[0] = 0;
7175
7176 for (SPRITES_OF_SECT(SECT(spriteNum), j))
7177 {
7178 if ((sprite[j].cstat & 16) && (A_CheckSpriteFlags(j, SFLAG_NOSHADE) == 0))
7179 sprite[j].shade = (pSector->ceilingstat & 1) ? pSector->ceilingshade : pSector->floorshade;
7180 }
7181
7182 if (pData[3] == 1)
7183 DELETE_SPRITE_AND_CONTINUE(spriteNum);
7184 }
7185
7186 if (pData[0] == 1) //Lights flickering on
7187 {
7188 if (pSector->floorshade > pSprite->shade)
7189 {
7190 pSector->floorpal = pSprite->pal;
7191 pSector->ceilingpal = pSprite->pal;
7192
7193 pSector->floorshade -= 2;
7194 pSector->ceilingshade -= 2;
7195
7196 walltype *pWall = &wall[pSector->wallptr];
7197 for (j = pSector->wallnum; j > 0; j--, pWall++)
7198 {
7199 if (pWall->hitag != 1)
7200 {
7201 pWall->pal = pSprite->pal;
7202 pWall->shade -= 2;
7203 }
7204 }
7205 }
7206 else pData[0] = 2;
7207
7208 for (SPRITES_OF_SECT(SECT(spriteNum), j))
7209 {
7210 if ((sprite[j].cstat & 16) && (A_CheckSpriteFlags(j, SFLAG_NOSHADE) == 0))
7211 sprite[j].shade = (pSector->ceilingstat & 1) ? pSector->ceilingshade : pSector->floorshade;
7212 }
7213 }
7214 break;
7215
7216
7217 case SE_13_EXPLOSIVE:
7218 if (pData[2])
7219 {
7220 // t[0]: ceiling z
7221 // t[1]: floor z
7222 // s->owner: 1 if affect ceiling, 0 if affect floor
7223 // t[3]: 1 if ceiling was parallaxed at premap, 0 else
7224
7225 j = (SP(spriteNum)<<5)|1;
7226
7227 if (pSprite->ang == 512)
7228 {
7229 if (pSprite->owner)
7230 {
7231 pSector->ceilingz = (klabs(pData[0] - pSector->ceilingz) >= j)
7232 ? pSector->ceilingz + ksgn(pData[0] - pSector->ceilingz) * j
7233 : pData[0];
7234 }
7235 else
7236 {
7237 pSector->floorz = (klabs(pData[1] - pSector->floorz) >= j)
7238 ? pSector->floorz + ksgn(pData[1] - pSector->floorz) * j
7239 : pData[1];
7240 }
7241 }
7242 else
7243 {
7244 pSector->floorz = (klabs(pData[1] - pSector->floorz) >= j)
7245 ? pSector->floorz + ksgn(pData[1] - pSector->floorz) * j
7246 : pData[1];
7247
7248 pSector->ceilingz = (klabs(pData[0] - pSector->ceilingz) >= j)
7249 ? pSector->ceilingz + ksgn(pData[0] - pSector->ceilingz) * j
7250 : pData[0];
7251 }
7252 #ifdef YAX_ENABLE
7253 if (pSprite->ang == 512)
7254 {
7255 int16_t cf=!pSprite->owner, bn=yax_getbunch(pSector-sector, cf);
7256 int32_t jj, daz=SECTORFLD(pSector-sector,z, cf);
7257
7258 if (bn >= 0)
7259 {
7260 for (SECTORS_OF_BUNCH(bn, cf, jj))
7261 {
7262 SECTORFLD(jj,z, cf) = daz;
7263 SECTORFLD(jj,stat, cf) &= ~(128+256 + 512+2048);
7264 }
7265 for (SECTORS_OF_BUNCH(bn, !cf, jj))
7266 {
7267 SECTORFLD(jj,z, !cf) = daz;
7268 SECTORFLD(jj,stat, !cf) &= ~(128+256 + 512+2048);
7269 }
7270 }
7271 }
7272 #endif
7273 if (pData[3] == 1)
7274 {
7275 //Change the shades
7276
7277 pData[3]++;
7278 pSector->ceilingstat ^= 1;
7279
7280 if (pSprite->ang == 512)
7281 {
7282 walltype *pWall = &wall[pSector->wallptr];
7283
7284 for (j = pSector->wallnum; j > 0; j--, pWall++)
7285 pWall->shade = pSprite->shade;
7286
7287 pSector->floorshade = pSprite->shade;
7288
7289 if (g_player[0].ps->parallax_sectnum >= 0)
7290 {
7291 pSector->ceilingpicnum = sector[g_player[0].ps->parallax_sectnum].ceilingpicnum;
7292 pSector->ceilingshade = sector[g_player[0].ps->parallax_sectnum].ceilingshade;
7293 }
7294 }
7295 }
7296
7297 for (int SPRITES_OF_SECT(pSprite->sectnum, p))
7298 if (sprite[p].statnum >= STAT_DEFAULT && sprite[p].statnum <= STAT_ZOMBIEACTOR)
7299 A_GetZLimits(p);
7300
7301 if (++pData[2] > 256)
7302 DELETE_SPRITE_AND_CONTINUE(spriteNum);
7303 }
7304
7305 #ifndef EDUKE32_STANDALONE
7306 if (!FURY && pData[2] == 4 && pSprite->ang != 512)
7307 for (x=0; x<7; x++) RANDOMSCRAP(pSprite, spriteNum);
7308 #endif
7309 break;
7310
7311
7312 case SE_15_SLIDING_DOOR:
7313
7314 if (pData[4])
7315 {
7316 pSprite->xvel = 16;
7317
7318 if (pData[4] == 1) //Opening
7319 {
7320 if (pData[3] >= (SP(spriteNum)>>3))
7321 {
7322 pData[4] = 0; //Turn off the sliders
7323 A_CallSound(pSprite->sectnum,spriteNum);
7324 break;
7325 }
7326 pData[3]++;
7327 }
7328 else if (pData[4] == 2)
7329 {
7330 if (pData[3]<1)
7331 {
7332 pData[4] = 0;
7333 A_CallSound(pSprite->sectnum,spriteNum);
7334 break;
7335 }
7336 pData[3]--;
7337 }
7338
7339 A_MoveSector(spriteNum);
7340 setsprite(spriteNum,&pSprite->pos);
7341 }
7342 break;
7343
7344 case SE_16_REACTOR: //Reactor
7345
7346 pData[2]+=32;
7347
7348 if (pSector->floorz < pSector->ceilingz)
7349 pSprite->shade = 0;
7350 else if (pSector->ceilingz < pData[3])
7351 {
7352 //The following code check to see if
7353 //there is any other sprites in the sector.
7354 //If there isn't, then kill this sectoreffector
7355 //itself.....
7356
7357 for (SPRITES_OF_SECT(pSprite->sectnum, j))
7358 {
7359 if (sprite[j].picnum == REACTOR || sprite[j].picnum == REACTOR2)
7360 break;
7361 }
7362
7363 if (j == -1)
7364 DELETE_SPRITE_AND_CONTINUE(spriteNum);
7365
7366 pSprite->shade = 1;
7367 }
7368
7369 pSector->ceilingz = (pSprite->shade)
7370 ? pSector->ceilingz + 1024
7371 : pSector->ceilingz - 512;
7372
7373 A_MoveSector(spriteNum);
7374 setsprite(spriteNum,&pSprite->pos);
7375
7376 break;
7377
7378 case SE_17_WARP_ELEVATOR:
7379 {
7380 q = pData[0]*(SP(spriteNum)<<2);
7381
7382 pSector->ceilingz += q;
7383 pSector->floorz += q;
7384
7385 for (SPRITES_OF_SECT(pSprite->sectnum, j))
7386 {
7387 if (sprite[j].statnum == STAT_PLAYER && sprite[j].owner >= 0)
7388 {
7389 int const warpPlayer = P_Get(j);
7390 auto const pPlayer = g_player[warpPlayer].ps;
7391
7392 if (numplayers < 2 && !g_netServer)
7393 pPlayer->opos.z = pPlayer->pos.z;
7394
7395 pPlayer->pos.z += q;
7396 pPlayer->truefz += q;
7397 pPlayer->truecz += q;
7398
7399 if (g_netServer || numplayers > 1)
7400 pPlayer->opos.z = pPlayer->pos.z;
7401 }
7402
7403 if (sprite[j].statnum != STAT_EFFECTOR)
7404 {
7405 actor[j].bpos.z = sprite[j].z;
7406 sprite[j].z += q;
7407 }
7408
7409 actor[j].floorz = pSector->floorz;
7410 actor[j].ceilingz = pSector->ceilingz;
7411 }
7412
7413 if (pData[0]) //If in motion
7414 {
7415 if (klabs(pSector->floorz-pData[2]) <= SP(spriteNum))
7416 {
7417 G_ActivateWarpElevators(spriteNum,0);
7418 break;
7419 }
7420
7421 // If we still see the opening, we can't yet teleport.
7422 if (pData[0]==-1)
7423 {
7424 if (pSector->floorz > pData[3])
7425 break;
7426 }
7427 else if (pSector->ceilingz < pData[4]) break;
7428
7429 if (pData[1] == 0) break;
7430 pData[1] = 0;
7431
7432 for (SPRITES_OF(STAT_EFFECTOR, j))
7433 {
7434 if (spriteNum != j && sprite[j].lotag == SE_17_WARP_ELEVATOR)
7435 if (pSector->hitag-pData[0] == sector[sprite[j].sectnum].hitag
7436 && spriteHitag == sprite[j].hitag)
7437 break;
7438 }
7439
7440 if (j == -1) break;
7441
7442 int32_t nextk;
7443
7444 for (SPRITES_OF_SECT_SAFE(pSprite->sectnum, k, nextk))
7445 {
7446 if (sprite[k].statnum == STAT_PLAYER && sprite[k].owner >= 0)
7447 {
7448 int const warpPlayer = P_Get(k);
7449 auto const pPlayer = g_player[warpPlayer].ps;
7450
7451 pPlayer->pos.x += sprite[j].x - pSprite->x;
7452 pPlayer->pos.y += sprite[j].y - pSprite->y;
7453 pPlayer->opos.z -= pPlayer->pos.z;
7454 pPlayer->pos.z = sector[sprite[j].sectnum].floorz - (pSector->floorz - pPlayer->pos.z);
7455 pPlayer->opos.z += pPlayer->pos.z;
7456
7457 actor[k].floorz = sector[sprite[j].sectnum].floorz;
7458 actor[k].ceilingz = sector[sprite[j].sectnum].ceilingz;
7459 pPlayer->opos.vec2 = pPlayer->pos.vec2;
7460 pPlayer->bobpos = pPlayer->pos.vec2;
7461 pPlayer->truefz = actor[k].floorz;
7462 pPlayer->truecz = actor[k].ceilingz;
7463 pPlayer->bobcounter = 0;
7464
7465 changespritesect(k, sprite[j].sectnum);
7466 pPlayer->cursectnum = sprite[j].sectnum;
7467 }
7468 else if (sprite[k].statnum != STAT_EFFECTOR)
7469 {
7470 sprite[k].x += sprite[j].x-pSprite->x;
7471 sprite[k].y += sprite[j].y-pSprite->y;
7472
7473 actor[k].bpos.vec2 = sprite[k].pos.vec2;
7474
7475 actor[k].bpos.z -= sprite[k].z;
7476 sprite[k].z = sector[sprite[j].sectnum].floorz - (pSector->floorz - sprite[k].z);
7477 actor[k].bpos.z += sprite[k].z;
7478
7479 changespritesect(k,sprite[j].sectnum);
7480 setsprite(k,&sprite[k].pos);
7481
7482 actor[k].floorz = sector[sprite[j].sectnum].floorz;
7483 actor[k].ceilingz = sector[sprite[j].sectnum].ceilingz;
7484 }
7485 }
7486 }
7487 break;
7488 }
7489
7490 case SE_18_INCREMENTAL_SECTOR_RISE_FALL:
7491 if (pData[0])
7492 {
7493 if (pSprite->pal)
7494 {
7495 if (pSprite->ang == 512)
7496 {
7497 pSector->ceilingz -= pSector->extra;
7498 if (pSector->ceilingz <= pData[1])
7499 {
7500 pSector->ceilingz = pData[1];
7501 DELETE_SPRITE_AND_CONTINUE(spriteNum);
7502 }
7503 }
7504 else
7505 {
7506 pSector->floorz += pSector->extra;
7507
7508 for (bssize_t SPRITES_OF_SECT(pSprite->sectnum, sectSprite))
7509 {
7510 if (sprite[sectSprite].picnum == APLAYER && sprite[sectSprite].owner >= 0 && g_player[P_Get(sectSprite)].ps->on_ground == 1)
7511 g_player[P_Get(sectSprite)].ps->pos.z += pSector->extra;
7512
7513 if (sprite[sectSprite].zvel == 0 && sprite[sectSprite].statnum != STAT_EFFECTOR && sprite[sectSprite].statnum != STAT_PROJECTILE)
7514 {
7515 actor[sectSprite].bpos.z = sprite[sectSprite].z += pSector->extra;
7516 actor[sectSprite].floorz = pSector->floorz;
7517 }
7518 }
7519
7520 if (pSector->floorz >= pData[1])
7521 {
7522 pSector->floorz = pData[1];
7523 DELETE_SPRITE_AND_CONTINUE(spriteNum);
7524 }
7525 }
7526 }
7527 else
7528 {
7529 if (pSprite->ang == 512)
7530 {
7531 pSector->ceilingz += pSector->extra;
7532 if (pSector->ceilingz >= pSprite->z)
7533 {
7534 pSector->ceilingz = pSprite->z;
7535 DELETE_SPRITE_AND_CONTINUE(spriteNum);
7536 }
7537 }
7538 else
7539 {
7540 pSector->floorz -= pSector->extra;
7541
7542 for (bssize_t SPRITES_OF_SECT(pSprite->sectnum, sectSprite))
7543 {
7544 if (sprite[sectSprite].picnum == APLAYER && sprite[sectSprite].owner >= 0 &&g_player[P_Get(sectSprite)].ps->on_ground == 1)
7545 g_player[P_Get(sectSprite)].ps->pos.z -= pSector->extra;
7546
7547 if (sprite[sectSprite].zvel == 0 && sprite[sectSprite].statnum != STAT_EFFECTOR && sprite[sectSprite].statnum != STAT_PROJECTILE)
7548 {
7549 actor[sectSprite].bpos.z = sprite[sectSprite].z -= pSector->extra;
7550 actor[sectSprite].floorz = pSector->floorz;
7551 }
7552 }
7553
7554 if (pSector->floorz <= pSprite->z)
7555 {
7556 pSector->floorz = pSprite->z;
7557 DELETE_SPRITE_AND_CONTINUE(spriteNum);
7558 }
7559 }
7560 }
7561
7562 if (++pData[2] >= pSprite->hitag)
7563 {
7564 pData[2] = 0;
7565 pData[0] = 0;
7566 }
7567 }
7568 break;
7569
7570 case SE_19_EXPLOSION_LOWERS_CEILING: //Battlestar galactia shields
7571
7572 if (pData[0])
7573 {
7574 if (pData[0] == 1)
7575 {
7576 pData[0]++;
7577 x = pSector->wallptr;
7578 q = x+pSector->wallnum;
7579
7580 for (j=x; j<q; j++)
7581 {
7582 if (wall[j].overpicnum == BIGFORCE)
7583 {
7584 wall[j].cstat &= (128+32+8+4+2);
7585 wall[j].overpicnum = 0;
7586
7587 if (wall[j].nextwall >= 0)
7588 {
7589 wall[wall[j].nextwall].overpicnum = 0;
7590 wall[wall[j].nextwall].cstat &= (128+32+8+4+2);
7591 }
7592 }
7593 }
7594 }
7595
7596 if (pSector->ceilingz < pSector->floorz)
7597 pSector->ceilingz += SP(spriteNum);
7598 else
7599 {
7600 pSector->ceilingz = pSector->floorz;
7601
7602 for (SPRITES_OF(STAT_EFFECTOR, j))
7603 {
7604 if (sprite[j].lotag == SE_0_ROTATING_SECTOR && sprite[j].hitag==spriteHitag)
7605 {
7606 sectortype *const pSector = §or[sprite[j].sectnum];
7607 int const ownerSector = sprite[sprite[j].owner].sectnum;
7608
7609 pSector->ceilingpal = sector[ownerSector].floorpal;
7610 pSector->floorpal = pSector->ceilingpal;
7611 pSector->ceilingshade = sector[ownerSector].floorshade;
7612 pSector->floorshade = pSector->ceilingshade;
7613
7614 actor[sprite[j].owner].t_data[0] = 2;
7615 }
7616 }
7617
7618 DELETE_SPRITE_AND_CONTINUE(spriteNum);
7619 }
7620 }
7621 else //Not hit yet
7622 {
7623 if (G_FindExplosionInSector(pSprite->sectnum) >= 0)
7624 {
7625 P_DoQuote(QUOTE_UNLOCKED, g_player[myconnectindex].ps);
7626
7627 for (SPRITES_OF(STAT_EFFECTOR, l))
7628 {
7629 switch (sprite[l].lotag & 0x7fff)
7630 {
7631 case SE_0_ROTATING_SECTOR:
7632 if (sprite[l].hitag == spriteHitag)
7633 {
7634 int const spriteOwner = sprite[l].owner;
7635 int const sectNum = sprite[l].sectnum;
7636
7637 sector[sectNum].ceilingshade = sprite[spriteOwner].shade;
7638 sector[sectNum].floorshade = sector[sectNum].ceilingshade;
7639 sector[sectNum].ceilingpal = sprite[spriteOwner].pal;
7640 sector[sectNum].floorpal = sector[sectNum].ceilingpal;
7641 }
7642 break;
7643
7644 case SE_1_PIVOT:
7645 case SE_12_LIGHT_SWITCH:
7646 // case SE_18_INCREMENTAL_SECTOR_RISE_FALL:
7647 case SE_19_EXPLOSION_LOWERS_CEILING:
7648 if (spriteHitag == sprite[l].hitag)
7649 if (actor[l].t_data[0] == 0)
7650 {
7651 actor[l].t_data[0] = 1; // Shut them all on
7652 sprite[l].owner = spriteNum;
7653 }
7654
7655 break;
7656 }
7657 }
7658 }
7659 }
7660
7661 break;
7662
7663 case SE_20_STRETCH_BRIDGE: //Extend-o-bridge
7664 if (pData[0] == 0) break;
7665 pSprite->xvel = (pData[0] == 1) ? 8 : -8;
7666
7667 if (pSprite->xvel) //Moving
7668 {
7669 vec2_t const vect = { (pSprite->xvel * sintable[(pSprite->ang + 512) & 2047]) >> 14,
7670 (pSprite->xvel * sintable[pSprite->ang & 2047]) >> 14 };
7671
7672 pData[3] += pSprite->xvel;
7673
7674 pSprite->x += vect.x;
7675 pSprite->y += vect.y;
7676
7677 if (pData[3] <= 0 || (pData[3] >> 6) >= (SP(spriteNum) >> 6))
7678 {
7679 pSprite->x -= vect.x;
7680 pSprite->y -= vect.y;
7681 pData[0] = 0;
7682 A_CallSound(pSprite->sectnum, spriteNum);
7683 break;
7684 }
7685
7686 for (bssize_t nextSprite, SPRITES_OF_SECT_SAFE(pSprite->sectnum, sectSprite, nextSprite))
7687 {
7688 if (sprite[sectSprite].statnum != STAT_EFFECTOR && sprite[sectSprite].zvel == 0)
7689 {
7690 sprite[sectSprite].x += vect.x;
7691 sprite[sectSprite].y += vect.y;
7692
7693 setsprite(sectSprite, &sprite[sectSprite].pos);
7694
7695 if (sector[sprite[sectSprite].sectnum].floorstat & 2 && sprite[sectSprite].statnum == STAT_ZOMBIEACTOR)
7696 A_Fall(sectSprite);
7697 }
7698 }
7699
7700 dragpoint((int16_t)pData[1], wall[pData[1]].x + vect.x, wall[pData[1]].y + vect.y, 0);
7701 dragpoint((int16_t)pData[2], wall[pData[2]].x + vect.x, wall[pData[2]].y + vect.y, 0);
7702
7703 for (bssize_t TRAVERSE_CONNECT(playerNum))
7704 {
7705 auto const pPlayer = g_player[playerNum].ps;
7706
7707 if (pPlayer->cursectnum == pSprite->sectnum && pPlayer->on_ground)
7708 {
7709 pPlayer->pos.x += vect.x;
7710 pPlayer->pos.y += vect.y;
7711
7712 pPlayer->opos.x = pPlayer->pos.x;
7713 pPlayer->opos.y = pPlayer->pos.y;
7714
7715 pPlayer->pos.z += PHEIGHT;
7716 setsprite(pPlayer->i, &pPlayer->pos);
7717 pPlayer->pos.z -= PHEIGHT;
7718 }
7719 }
7720
7721 pSector->floorxpanning -= vect.x >> 3;
7722 pSector->floorypanning -= vect.y >> 3;
7723
7724 pSector->ceilingxpanning -= vect.x >> 3;
7725 pSector->ceilingypanning -= vect.y >> 3;
7726 }
7727
7728 break;
7729
7730 case SE_21_DROP_FLOOR: // Cascading effect
7731 {
7732 if (pData[0] == 0) break;
7733
7734 int32_t *zptr = (pSprite->ang == 1536) ? &pSector->ceilingz : &pSector->floorz;
7735
7736 if (pData[0] == 1) //Decide if the s->sectnum should go up or down
7737 {
7738 pSprite->zvel = ksgn(pSprite->z-*zptr) * (SP(spriteNum)<<4);
7739 pData[0]++;
7740 }
7741
7742 if (pSector->extra == 0)
7743 {
7744 *zptr += pSprite->zvel;
7745
7746 if (klabs(*zptr-pSprite->z) < 1024)
7747 {
7748 *zptr = pSprite->z;
7749 DELETE_SPRITE_AND_CONTINUE(spriteNum); //All done // SE_21_KILLIT, see sector.c
7750 }
7751 }
7752 else pSector->extra--;
7753 break;
7754 }
7755
7756 case SE_22_TEETH_DOOR:
7757 if (pData[1])
7758 {
7759 if (GetAnimationGoal(§or[pData[0]].ceilingz) >= 0)
7760 pSector->ceilingz += pSector->extra*9;
7761 else pData[1] = 0;
7762 }
7763 break;
7764
7765 case SE_24_CONVEYOR:
7766 case SE_34:
7767 {
7768 if (pData[4])
7769 break;
7770
7771 vec2_t const vect = { (SP(spriteNum) * sintable[(pSprite->ang + 512) & 2047]) >> 18,
7772 (SP(spriteNum) * sintable[pSprite->ang & 2047]) >> 18 };
7773
7774 k = 0;
7775
7776 for (bssize_t nextSprite, SPRITES_OF_SECT_SAFE(pSprite->sectnum, sectSprite, nextSprite))
7777 {
7778 if (sprite[sectSprite].zvel < 0)
7779 continue;
7780
7781 switch (sprite[sectSprite].statnum)
7782 {
7783 case STAT_MISC:
7784 switch (DYNAMICTILEMAP(sprite[sectSprite].picnum))
7785 {
7786 case BLOODPOOL__STATIC:
7787 case PUKE__STATIC:
7788 case FOOTPRINTS__STATIC:
7789 case FOOTPRINTS2__STATIC:
7790 case FOOTPRINTS3__STATIC:
7791 case FOOTPRINTS4__STATIC:
7792 case BULLETHOLE__STATIC:
7793 case BLOODSPLAT1__STATIC:
7794 case BLOODSPLAT2__STATIC:
7795 case BLOODSPLAT3__STATIC:
7796 case BLOODSPLAT4__STATIC: sprite[sectSprite].xrepeat = sprite[sectSprite].yrepeat = 0; continue;
7797
7798 case LASERLINE__STATIC: continue;
7799 }
7800 fallthrough__;
7801 case STAT_STANDABLE:
7802 if (sprite[sectSprite].picnum == TRIPBOMB)
7803 break;
7804 fallthrough__;
7805 case STAT_ACTOR:
7806 case STAT_DEFAULT:
7807 if (sprite[sectSprite].picnum == BOLT1
7808 || sprite[sectSprite].picnum == BOLT1 + 1
7809 || sprite[sectSprite].picnum == BOLT1 + 2
7810 || sprite[sectSprite].picnum == BOLT1 + 3
7811 || sprite[sectSprite].picnum == SIDEBOLT1
7812 || sprite[sectSprite].picnum == SIDEBOLT1 + 1
7813 || sprite[sectSprite].picnum == SIDEBOLT1 + 2
7814 || sprite[sectSprite].picnum == SIDEBOLT1 + 3
7815 || A_CheckSwitchTile(sectSprite))
7816 break;
7817
7818 if (!(sprite[sectSprite].picnum >= CRANE && sprite[sectSprite].picnum <= CRANE + 3))
7819 {
7820 if (sprite[sectSprite].z > actor[sectSprite].floorz - ZOFFSET2)
7821 {
7822 actor[sectSprite].bpos.vec2 = sprite[sectSprite].pos.vec2;
7823
7824 sprite[sectSprite].x += vect.x >> 2;
7825 sprite[sectSprite].y += vect.y >> 2;
7826
7827 setsprite(sectSprite, &sprite[sectSprite].pos);
7828
7829 if (sector[sprite[sectSprite].sectnum].floorstat & 2)
7830 if (sprite[sectSprite].statnum == STAT_ZOMBIEACTOR)
7831 A_Fall(sectSprite);
7832 }
7833 }
7834 break;
7835 }
7836 }
7837
7838 for (bssize_t TRAVERSE_CONNECT(playerNum))
7839 {
7840 auto const pPlayer = g_player[playerNum].ps;
7841
7842 if (pPlayer->cursectnum == pSprite->sectnum && pPlayer->on_ground)
7843 {
7844 if (klabs(pPlayer->pos.z - pPlayer->truefz) < PHEIGHT + (9 << 8))
7845 {
7846 pPlayer->fric.x += vect.x << 3;
7847 pPlayer->fric.y += vect.y << 3;
7848 }
7849 }
7850 }
7851 pSector->floorxpanning += SP(spriteNum)>>7;
7852
7853 break;
7854 }
7855
7856 case SE_35:
7857 if (pSector->ceilingz > pSprite->z)
7858 {
7859 for (j = 0; j < 8; j++)
7860 {
7861 pSprite->ang += krand()&511;
7862 k = A_Spawn(spriteNum, SMALLSMOKE);
7863 sprite[k].xvel = 96+(krand()&127);
7864 A_SetSprite(k, CLIPMASK0);
7865 setsprite(k, &sprite[k].pos);
7866 if (rnd(16))
7867 A_Spawn(spriteNum, EXPLOSION2);
7868 }
7869
7870 }
7871 switch (pData[0])
7872 {
7873 case 0:
7874 pSector->ceilingz += pSprite->yvel;
7875 if (pSector->ceilingz > pSector->floorz)
7876 pSector->floorz = pSector->ceilingz;
7877 if (pSector->ceilingz > pSprite->z+ZOFFSET5)
7878 pData[0]++;
7879 break;
7880 case 1:
7881 pSector->ceilingz-=(pSprite->yvel<<2);
7882 if (pSector->ceilingz < pData[4])
7883 {
7884 pSector->ceilingz = pData[4];
7885 pData[0] = 0;
7886 }
7887 break;
7888 }
7889 break;
7890
7891 case SE_25_PISTON: //PISTONS
7892 if (pData[4] == 0) break;
7893
7894 if (pSector->floorz <= pSector->ceilingz)
7895 pSprite->shade = 0;
7896 else if (pSector->ceilingz <= pData[3])
7897 pSprite->shade = 1;
7898
7899 if (pSprite->shade)
7900 {
7901 pSector->ceilingz += SP(spriteNum)<<4;
7902 if (pSector->ceilingz > pSector->floorz)
7903 pSector->ceilingz = pSector->floorz;
7904 }
7905 else
7906 {
7907 pSector->ceilingz -= SP(spriteNum)<<4;
7908 if (pSector->ceilingz < pData[3])
7909 pSector->ceilingz = pData[3];
7910 }
7911
7912 break;
7913
7914 case SE_26:
7915 {
7916 int32_t p, nextj;
7917
7918 pSprite->xvel = pSector->extra != 0 ? pSector->extra : 32;
7919 l = (pSprite->xvel*sintable[(pSprite->ang+512)&2047])>>14;
7920 x = (pSprite->xvel*sintable[pSprite->ang&2047])>>14;
7921
7922 pSprite->shade++;
7923 if (pSprite->shade > 7)
7924 {
7925 pSprite->x = pData[3];
7926 pSprite->y = pData[4];
7927 pSector->floorz -= ((pSprite->zvel*pSprite->shade)-pSprite->zvel);
7928 pSprite->shade = 0;
7929 }
7930 else
7931 pSector->floorz += pSprite->zvel;
7932
7933 for (SPRITES_OF_SECT_SAFE(pSprite->sectnum, j, nextj))
7934 {
7935 if (sprite[j].statnum != STAT_EFFECTOR && sprite[j].statnum != STAT_PLAYER && sprite[j].statnum != STAT_PROJECTILE)
7936 {
7937 actor[j].bpos.vec2 = sprite[j].pos.vec2;
7938
7939 sprite[j].x += l;
7940 sprite[j].y += x;
7941 sprite[j].z += pSprite->zvel;
7942
7943 setsprite(j, &sprite[j].pos);
7944 }
7945 }
7946
7947 for (TRAVERSE_CONNECT(p))
7948 {
7949 auto const pPlayer = g_player[p].ps;
7950
7951 if (pSprite->sectnum == pPlayer->cursectnum && pPlayer->on_ground)
7952 {
7953 pPlayer->pos.x += l;
7954 pPlayer->pos.y += x;
7955 pPlayer->pos.z += pSprite->zvel;
7956
7957 updatesector(pPlayer->pos.x, pPlayer->pos.y, &pPlayer->cursectnum);
7958 changespritesect(pPlayer->i, pPlayer->cursectnum);
7959
7960 pPlayer->bobpos.x += l;
7961 pPlayer->bobpos.y += x;
7962
7963 if (g_netServer || numplayers > 1)
7964 pPlayer->opos.vec2 = pPlayer->pos.vec2;
7965
7966 if (sprite[pPlayer->i].extra <= 0)
7967 sprite[pPlayer->i].pos.vec2 = pPlayer->pos.vec2;
7968 }
7969 }
7970
7971 A_MoveSector(spriteNum);
7972 setsprite(spriteNum,&pSprite->pos);
7973
7974 break;
7975 }
7976
7977 case SE_27_DEMO_CAM:
7978 {
7979 if (pSprite->extra < 1 && (ud.recstat == 0 || !ud.democams) && g_BenchmarkMode == BENCHMARKMODE_OFF) break;
7980
7981 if (klabs(pSprite->extra) == 2)
7982 {
7983 actor[spriteNum].tempang = pSprite->ang;
7984 if (ud.camerasprite != spriteNum)
7985 {
7986 //level the camera out by default (yvel stores the up/down angle)
7987 pSprite->yvel = 100;
7988 ud.camerasprite = spriteNum;
7989 }
7990
7991 findCameraDestination:
7992 if (pSprite->owner == spriteNum)
7993 {
7994 pSprite->owner = A_FindLocatorWithHiLoTags(pSprite->hitag, pData[0], -1);
7995
7996 //reset our elapsed time since reaching a locator
7997 pData[1] = 0;
7998 //store our starting point
7999 pData[2] = pSprite->x;
8000 pData[3] = pSprite->y;
8001 pData[4] = pSprite->z;
8002 pData[5] = pSprite->ang;
8003 pData[6] = pSprite->yvel;
8004 if (pSprite->owner != -1)
8005 {
8006 spritetype* const destLocator = &sprite[pSprite->owner];
8007 int32_t subjectLocatorIndex = A_FindLocatorWithHiLoTags(pSprite->hitag, destLocator->owner, -1);
8008 pData[7] = G_GetAngleDelta(pData[5], destLocator->ang);
8009 //level the camera out by default (pData[8] stores our destination up/down angle)
8010 pData[8] = 100;
8011 if (subjectLocatorIndex != -1)
8012 {
8013 spritetype* const subjectLocator = &sprite[subjectLocatorIndex];
8014 const vec3_t cameraDirection = {subjectLocator->x - destLocator->x,
8015 subjectLocator->y - destLocator->y,
8016 subjectLocator->z - destLocator->z};
8017 pData[7] = G_GetAngleDelta(pData[5], getangle(cameraDirection.x,
8018 cameraDirection.y));
8019 pData[8] = (((int32_t) getangle(-ksqrt(cameraDirection.x*cameraDirection.x+cameraDirection.y*cameraDirection.y), cameraDirection.z)*(400.f/1024.f)))-300;
8020 }
8021 }
8022
8023 //if we are benchmarking, take a screenshot at each waypoint (camera start point/locator)
8024 benchmarkScreenshot = g_BenchmarkMode == BENCHMARKMODE_GENERATE_REFERENCE;
8025 }
8026 if (pSprite->owner == -1)
8027 {
8028 break;
8029 }
8030
8031 spritetype* const destLocator = &sprite[pSprite->owner];
8032 if (pData[1] == destLocator->extra)
8033 {
8034 pSprite->owner = spriteNum;
8035 ++pData[0];
8036 goto findCameraDestination;
8037 }
8038
8039 //smoothstep to the new location and camera direction over the duration (in ticks) stored in the destLocator's extra value
8040 const vec3_t heading = {destLocator->x-pData[2],
8041 destLocator->y-pData[3],
8042 destLocator->z-pData[4]};
8043 float interpolation = (pData[1]/(float) destLocator->extra);
8044 interpolation = interpolation*interpolation*(3-2*interpolation);
8045 pSprite->x = pData[2]+interpolation*heading.x;
8046 pSprite->y = pData[3]+interpolation*heading.y;
8047 pSprite->z = pData[4]+interpolation*heading.z;
8048 pSprite->ang = pData[5]+interpolation*pData[7];
8049 pSprite->yvel = (pData[6]+((int32_t) interpolation*pData[8])+100)%400-100;
8050
8051 //increment elapsed time
8052 ++pData[1];
8053 }
8054 else
8055 {
8056 actor[spriteNum].tempang = pSprite->ang;
8057
8058 int const p = A_FindPlayer(pSprite, &x);
8059 auto const ps = g_player[p].ps;
8060
8061 if (sprite[ps->i].extra > 0 && myconnectindex == screenpeek)
8062 {
8063 if (pData[0] < 0)
8064 {
8065 ud.camerasprite = spriteNum;
8066 pData[0]++;
8067 }
8068 else if (ud.recstat == 2 && ps->newowner == -1)
8069 {
8070 if (cansee(pSprite->x,pSprite->y,pSprite->z,SECT(spriteNum),ps->pos.x,ps->pos.y,ps->pos.z,ps->cursectnum))
8071 {
8072 if (x < (int32_t)((unsigned)spriteHitag))
8073 {
8074 ud.camerasprite = spriteNum;
8075 pData[0] = 999;
8076 pSprite->ang += G_GetAngleDelta(pSprite->ang,getangle(ps->pos.x-pSprite->x,ps->pos.y-pSprite->y))>>3;
8077 SP(spriteNum) = 100+((pSprite->z-ps->pos.z)/257);
8078
8079 }
8080 else if (pData[0] == 999)
8081 {
8082 if (ud.camerasprite == spriteNum)
8083 pData[0] = 0;
8084 else pData[0] = -10;
8085 ud.camerasprite = spriteNum;
8086
8087 }
8088 }
8089 else
8090 {
8091 pSprite->ang = getangle(ps->pos.x-pSprite->x,ps->pos.y-pSprite->y);
8092
8093 if (pData[0] == 999)
8094 {
8095 if (ud.camerasprite == spriteNum)
8096 pData[0] = 0;
8097 else pData[0] = -20;
8098 ud.camerasprite = spriteNum;
8099 }
8100 }
8101 }
8102 }
8103 }
8104 break;
8105 }
8106
8107 case SE_28_LIGHTNING:
8108 {
8109 if (pData[5] > 0)
8110 {
8111 pData[5]--;
8112 break;
8113 }
8114
8115 if (T1(spriteNum) == 0)
8116 {
8117 A_FindPlayer(pSprite,&x);
8118 if (x > 15500)
8119 break;
8120 T1(spriteNum) = 1;
8121 T2(spriteNum) = 64 + (krand()&511);
8122 T3(spriteNum) = 0;
8123 }
8124 else
8125 {
8126 T3(spriteNum)++;
8127 if (T3(spriteNum) > T2(spriteNum))
8128 {
8129 T1(spriteNum) = 0;
8130 g_player[screenpeek].ps->visibility = ud.const_visibility;
8131 break;
8132 }
8133 else if (T3(spriteNum) == (T2(spriteNum)>>1))
8134 A_PlaySound(THUNDER,spriteNum);
8135 else if (T3(spriteNum) == (T2(spriteNum)>>3))
8136 A_PlaySound(LIGHTNING_SLAP,spriteNum);
8137 else if (T3(spriteNum) == (T2(spriteNum)>>2))
8138 {
8139 for (SPRITES_OF(STAT_DEFAULT, j))
8140 if (sprite[j].picnum == NATURALLIGHTNING && sprite[j].hitag == pSprite->hitag)
8141 sprite[j].cstat |= 32768;
8142 }
8143 else if (T3(spriteNum) > (T2(spriteNum)>>3) && T3(spriteNum) < (T2(spriteNum)>>2))
8144 {
8145 if (cansee(pSprite->x,pSprite->y,pSprite->z,pSprite->sectnum,g_player[screenpeek].ps->pos.x,g_player[screenpeek].ps->pos.y,g_player[screenpeek].ps->pos.z,g_player[screenpeek].ps->cursectnum))
8146 j = 1;
8147 else j = 0;
8148
8149 if (rnd(192) && (T3(spriteNum)&1))
8150 {
8151 if (j)
8152 g_player[screenpeek].ps->visibility = 0;
8153 }
8154 else if (j)
8155 g_player[screenpeek].ps->visibility = ud.const_visibility;
8156
8157 for (SPRITES_OF(STAT_DEFAULT, j))
8158 {
8159 if (sprite[j].picnum == NATURALLIGHTNING && sprite[j].hitag == pSprite->hitag)
8160 {
8161 if (rnd(32) && (T3(spriteNum)&1))
8162 {
8163 int32_t p;
8164 DukePlayer_t *ps;
8165
8166 sprite[j].cstat &= 32767;
8167 A_Spawn(j,SMALLSMOKE);
8168
8169 p = A_FindPlayer(pSprite, NULL);
8170 ps = g_player[p].ps;
8171
8172 x = ldist(&sprite[ps->i], &sprite[j]);
8173 if (x < 768)
8174 {
8175 if (!A_CheckSoundPlaying(ps->i,DUKE_LONGTERM_PAIN))
8176 A_PlaySound(DUKE_LONGTERM_PAIN,ps->i);
8177 A_PlaySound(SHORT_CIRCUIT,ps->i);
8178 sprite[ps->i].extra -= 8+(krand()&7);
8179
8180 P_PalFrom(ps, 32, 16,0,0);
8181 }
8182 break;
8183 }
8184 else sprite[j].cstat |= 32768;
8185 }
8186 }
8187 }
8188 }
8189 break;
8190 }
8191
8192 case SE_29_WAVES:
8193 pSprite->hitag += 64;
8194 l = mulscale12((int32_t)pSprite->yvel,sintable[pSprite->hitag&2047]);
8195 pSector->floorz = pSprite->z + l;
8196 break;
8197
8198 case SE_31_FLOOR_RISE_FALL: // True Drop Floor
8199 if (pData[0] == 1)
8200 {
8201 // Choose dir
8202
8203 if (pData[3] > 0)
8204 {
8205 pData[3]--;
8206 break;
8207 }
8208
8209 if (pData[2] == 1) // Retract
8210 {
8211 if (SA(spriteNum) != 1536)
8212 HandleSE31(spriteNum, 1, pSprite->z, 0, pSprite->z-pSector->floorz);
8213 else
8214 HandleSE31(spriteNum, 1, pData[1], 0, pData[1]-pSector->floorz);
8215
8216 Yax_SetBunchZs(pSector-sector, YAX_FLOOR, pSector->floorz);
8217
8218 break;
8219 }
8220
8221 if ((pSprite->ang&2047) == 1536)
8222 HandleSE31(spriteNum, 0, pSprite->z, 1, pSprite->z-pSector->floorz);
8223 else
8224 HandleSE31(spriteNum, 0, pData[1], 1, pData[1]-pSprite->z);
8225
8226 Yax_SetBunchZs(pSector-sector, YAX_FLOOR, pSector->floorz);
8227 }
8228 break;
8229
8230 case SE_32_CEILING_RISE_FALL: // True Drop Ceiling
8231 if (pData[0] == 1)
8232 {
8233 // Choose dir
8234
8235 if (pData[2] == 1) // Retract
8236 {
8237 if (SA(spriteNum) != 1536)
8238 {
8239 if (klabs(pSector->ceilingz - pSprite->z) < (SP(spriteNum)<<1))
8240 {
8241 pSector->ceilingz = pSprite->z;
8242 A_CallSound(pSprite->sectnum,spriteNum);
8243 pData[2] = 0;
8244 pData[0] = 0;
8245 }
8246 else pSector->ceilingz += ksgn(pSprite->z-pSector->ceilingz)*SP(spriteNum);
8247 }
8248 else
8249 {
8250 if (klabs(pSector->ceilingz - pData[1]) < (SP(spriteNum)<<1))
8251 {
8252 pSector->ceilingz = pData[1];
8253 A_CallSound(pSprite->sectnum,spriteNum);
8254 pData[2] = 0;
8255 pData[0] = 0;
8256 }
8257 else pSector->ceilingz += ksgn(pData[1]-pSector->ceilingz)*SP(spriteNum);
8258 }
8259
8260 Yax_SetBunchZs(pSector-sector, YAX_CEILING, pSector->ceilingz);
8261
8262 break;
8263 }
8264
8265 if ((pSprite->ang&2047) == 1536)
8266 {
8267 if (klabs(pSector->ceilingz-pSprite->z) < (SP(spriteNum)<<1))
8268 {
8269 pData[0] = 0;
8270 pData[2] = !pData[2];
8271 A_CallSound(pSprite->sectnum,spriteNum);
8272 pSector->ceilingz = pSprite->z;
8273 }
8274 else pSector->ceilingz += ksgn(pSprite->z-pSector->ceilingz)*SP(spriteNum);
8275 }
8276 else
8277 {
8278 if (klabs(pSector->ceilingz-pData[1]) < (SP(spriteNum)<<1))
8279 {
8280 pData[0] = 0;
8281 pData[2] = !pData[2];
8282 A_CallSound(pSprite->sectnum,spriteNum);
8283 }
8284 else pSector->ceilingz -= ksgn(pSprite->z-pData[1])*SP(spriteNum);
8285 }
8286
8287 Yax_SetBunchZs(pSector-sector, YAX_CEILING, pSector->ceilingz);
8288 }
8289 break;
8290
8291 #ifndef EDUKE32_STANDALONE
8292 case SE_33_QUAKE_DEBRIS:
8293 if (!FURY && g_earthquakeTime > 0 && (krand()&7) == 0)
8294 RANDOMSCRAP(pSprite, spriteNum);
8295 break;
8296 #endif
8297 case SE_36_PROJ_SHOOTER:
8298 if (pData[0])
8299 {
8300 if (pData[0] == 1)
8301 A_Shoot(spriteNum,pSector->extra);
8302 else if (pData[0] == GAMETICSPERSEC*5)
8303 pData[0] = 0;
8304 pData[0]++;
8305 }
8306 break;
8307
8308 case 128: //SE to control glass breakage
8309 {
8310 walltype *pWall = &wall[pData[2]];
8311
8312 pWall->cstat &= (255-32);
8313 pWall->cstat |= 16;
8314 if (pWall->nextwall >= 0)
8315 {
8316 wall[pWall->nextwall].cstat &= (255-32);
8317 wall[pWall->nextwall].cstat |= 16;
8318 }
8319
8320 pWall->overpicnum++;
8321 if (pWall->nextwall >= 0)
8322 wall[pWall->nextwall].overpicnum++;
8323
8324 if (pData[0] < pData[1]) pData[0]++;
8325 else
8326 {
8327 pWall->cstat &= (128+32+8+4+2);
8328 if (pWall->nextwall >= 0)
8329 wall[pWall->nextwall].cstat &= (128+32+8+4+2);
8330 DELETE_SPRITE_AND_CONTINUE(spriteNum);
8331 }
8332 }
8333 break;
8334
8335 case SE_130:
8336 if (pData[0] > 80)
8337 {
8338 DELETE_SPRITE_AND_CONTINUE(spriteNum);
8339 }
8340 else pData[0]++;
8341
8342 x = pSector->floorz-pSector->ceilingz;
8343
8344 if (rnd(64))
8345 {
8346 k = A_Spawn(spriteNum,EXPLOSION2);
8347 sprite[k].xrepeat = sprite[k].yrepeat = 2+(krand()&7);
8348 sprite[k].z = pSector->floorz-(krand()%x);
8349 sprite[k].ang += 256-(krand()%511);
8350 sprite[k].xvel = krand()&127;
8351 A_SetSprite(k,CLIPMASK0);
8352 }
8353 break;
8354
8355 case SE_131:
8356 if (pData[0] > 40)
8357 {
8358 DELETE_SPRITE_AND_CONTINUE(spriteNum);
8359 }
8360 else pData[0]++;
8361
8362 x = pSector->floorz-pSector->ceilingz;
8363
8364 if (rnd(32))
8365 {
8366 k = A_Spawn(spriteNum,EXPLOSION2);
8367 sprite[k].xrepeat = sprite[k].yrepeat = 2+(krand()&3);
8368 sprite[k].z = pSector->floorz-(krand()%x);
8369 sprite[k].ang += 256-(krand()%511);
8370 sprite[k].xvel = krand()&127;
8371 A_SetSprite(k,CLIPMASK0);
8372 }
8373 break;
8374
8375 case SE_49_POINT_LIGHT:
8376 case SE_50_SPOT_LIGHT:
8377 changespritestat(spriteNum, STAT_LIGHT);
8378 break;
8379 }
8380 next_sprite:
8381 spriteNum = nextSprite;
8382 }
8383
8384 //Sloped sin-wave floors!
8385 for (SPRITES_OF(STAT_EFFECTOR, spriteNum))
8386 {
8387 auto const s = &sprite[spriteNum];
8388
8389 if (s->lotag == SE_29_WAVES)
8390 {
8391 auto const sc = (usectorptr_t)§or[s->sectnum];
8392
8393 if (sc->wallnum == 4)
8394 {
8395 auto const pWall = &wall[sc->wallptr+2];
8396 if (pWall->nextsector >= 0)
8397 alignflorslope(s->sectnum, pWall->x,pWall->y, sector[pWall->nextsector].floorz);
8398 }
8399 }
8400 }
8401 }
8402
G_DoEffectorLights(void)8403 static void G_DoEffectorLights(void) // STATNUM 14
8404 {
8405 int32_t i;
8406
8407 for (SPRITES_OF(STAT_LIGHT, i))
8408 {
8409 switch (sprite[i].lotag)
8410 {
8411 #ifdef POLYMER
8412 case SE_49_POINT_LIGHT:
8413 {
8414 if (!A_CheckSpriteFlags(i, SFLAG_NOLIGHT) && videoGetRenderMode() == REND_POLYMER &&
8415 !(A_CheckSpriteFlags(i, SFLAG_USEACTIVATOR) && sector[sprite[i].sectnum].lotag & 16384))
8416 {
8417 if (practor[i].lightptr == NULL)
8418 {
8419 #pragma pack(push,1)
8420 _prlight mylight;
8421 #pragma pack(pop)
8422 mylight.sector = SECT(i);
8423 Bmemcpy(&mylight, &sprite[i], sizeof(int32_t) * 3);
8424 mylight.range = SHT(i);
8425 mylight.color[0] = sprite[i].xvel;
8426 mylight.color[1] = sprite[i].yvel;
8427 mylight.color[2] = sprite[i].zvel;
8428 mylight.radius = 0;
8429 mylight.angle = SA(i);
8430 mylight.horiz = SH(i);
8431 mylight.minshade = sprite[i].xoffset;
8432 mylight.maxshade = sprite[i].yoffset;
8433 mylight.tilenum = 0;
8434 mylight.publicflags.emitshadow = 0;
8435 mylight.publicflags.negative = !!(CS(i) & 128);
8436
8437 if (CS(i) & 2)
8438 {
8439 if (CS(i) & 512)
8440 mylight.priority = PR_LIGHT_PRIO_LOW;
8441 else
8442 mylight.priority = PR_LIGHT_PRIO_HIGH;
8443 }
8444 else
8445 mylight.priority = PR_LIGHT_PRIO_MAX;
8446
8447 practor[i].lightId = polymer_addlight(&mylight);
8448 if (practor[i].lightId >= 0)
8449 practor[i].lightptr = &prlights[practor[i].lightId];
8450 break;
8451 }
8452
8453 if (Bmemcmp(&sprite[i], practor[i].lightptr, sizeof(int32_t) * 3))
8454 {
8455 Bmemcpy(practor[i].lightptr, &sprite[i], sizeof(int32_t) * 3);
8456 practor[i].lightptr->sector = sprite[i].sectnum;
8457 practor[i].lightptr->flags.invalidate = 1;
8458 }
8459 if (SHT(i) != practor[i].lightptr->range)
8460 {
8461 practor[i].lightptr->range = SHT(i);
8462 practor[i].lightptr->flags.invalidate = 1;
8463 }
8464 if ((sprite[i].xvel != practor[i].lightptr->color[0]) ||
8465 (sprite[i].yvel != practor[i].lightptr->color[1]) ||
8466 (sprite[i].zvel != practor[i].lightptr->color[2]))
8467 {
8468 practor[i].lightptr->color[0] = sprite[i].xvel;
8469 practor[i].lightptr->color[1] = sprite[i].yvel;
8470 practor[i].lightptr->color[2] = sprite[i].zvel;
8471 }
8472 if ((int)!!(CS(i) & 128) != practor[i].lightptr->publicflags.negative) {
8473 practor[i].lightptr->publicflags.negative = !!(CS(i) & 128);
8474 }
8475 }
8476 break;
8477 }
8478 case SE_50_SPOT_LIGHT:
8479 {
8480 if (!A_CheckSpriteFlags(i, SFLAG_NOLIGHT) && videoGetRenderMode() == REND_POLYMER &&
8481 !(A_CheckSpriteFlags(i, SFLAG_USEACTIVATOR) && sector[sprite[i].sectnum].lotag & 16384))
8482 {
8483 if (practor[i].lightptr == NULL)
8484 {
8485 #pragma pack(push,1)
8486 _prlight mylight;
8487 #pragma pack(pop)
8488
8489 mylight.sector = SECT(i);
8490 Bmemcpy(&mylight, &sprite[i], sizeof(int32_t) * 3);
8491 mylight.range = SHT(i);
8492 mylight.color[0] = sprite[i].xvel;
8493 mylight.color[1] = sprite[i].yvel;
8494 mylight.color[2] = sprite[i].zvel;
8495 mylight.radius = (256-(SS(i)+128))<<1;
8496 mylight.faderadius = (int16_t)(mylight.radius * 0.75f);
8497 mylight.angle = SA(i);
8498 mylight.horiz = SH(i);
8499 mylight.minshade = sprite[i].xoffset;
8500 mylight.maxshade = sprite[i].yoffset;
8501 mylight.tilenum = actor[i].picnum;
8502 mylight.publicflags.emitshadow = !(CS(i) & 64);
8503 mylight.publicflags.negative = !!(CS(i) & 128);
8504
8505 if (CS(i) & 2)
8506 {
8507 if (CS(i) & 512)
8508 mylight.priority = PR_LIGHT_PRIO_LOW;
8509 else
8510 mylight.priority = PR_LIGHT_PRIO_HIGH;
8511 }
8512 else
8513 mylight.priority = PR_LIGHT_PRIO_MAX;
8514
8515 practor[i].lightId = polymer_addlight(&mylight);
8516 if (practor[i].lightId >= 0)
8517 {
8518 practor[i].lightptr = &prlights[practor[i].lightId];
8519
8520 // Hack in case polymer_addlight tweaked the horiz value
8521 if (practor[i].lightptr->horiz != SH(i))
8522 SH(i) = practor[i].lightptr->horiz;
8523 }
8524 break;
8525 }
8526
8527 if (Bmemcmp(&sprite[i], practor[i].lightptr, sizeof(int32_t) * 3))
8528 {
8529 Bmemcpy(practor[i].lightptr, &sprite[i], sizeof(int32_t) * 3);
8530 practor[i].lightptr->sector = sprite[i].sectnum;
8531 practor[i].lightptr->flags.invalidate = 1;
8532 }
8533 if (SHT(i) != practor[i].lightptr->range)
8534 {
8535 practor[i].lightptr->range = SHT(i);
8536 practor[i].lightptr->flags.invalidate = 1;
8537 }
8538 if ((sprite[i].xvel != practor[i].lightptr->color[0]) ||
8539 (sprite[i].yvel != practor[i].lightptr->color[1]) ||
8540 (sprite[i].zvel != practor[i].lightptr->color[2]))
8541 {
8542 practor[i].lightptr->color[0] = sprite[i].xvel;
8543 practor[i].lightptr->color[1] = sprite[i].yvel;
8544 practor[i].lightptr->color[2] = sprite[i].zvel;
8545 }
8546 if (((256-(SS(i)+128))<<1) != practor[i].lightptr->radius)
8547 {
8548 practor[i].lightptr->radius = (256-(SS(i)+128))<<1;
8549 practor[i].lightptr->faderadius = (int16_t)(practor[i].lightptr->radius * 0.75f);
8550 practor[i].lightptr->flags.invalidate = 1;
8551 }
8552 if (SA(i) != practor[i].lightptr->angle)
8553 {
8554 practor[i].lightptr->angle = SA(i);
8555 practor[i].lightptr->flags.invalidate = 1;
8556 }
8557 if (SH(i) != practor[i].lightptr->horiz)
8558 {
8559 practor[i].lightptr->horiz = SH(i);
8560 practor[i].lightptr->flags.invalidate = 1;
8561 }
8562 if ((int)!(CS(i) & 64) != practor[i].lightptr->publicflags.emitshadow) {
8563 practor[i].lightptr->publicflags.emitshadow = !(CS(i) & 64);
8564 }
8565 if ((int)!!(CS(i) & 128) != practor[i].lightptr->publicflags.negative) {
8566 practor[i].lightptr->publicflags.negative = !!(CS(i) & 128);
8567 }
8568 practor[i].lightptr->tilenum = actor[i].picnum;
8569 }
8570
8571 break;
8572 }
8573 #endif // POLYMER
8574 }
8575 }
8576 }
8577
8578 #ifdef POLYMER
A_DoLight(int spriteNum)8579 static void A_DoLight(int spriteNum)
8580 {
8581 auto const pSprite = &sprite[spriteNum];
8582 int savedFires = 0;
8583
8584 if (((sector[pSprite->sectnum].floorz - sector[pSprite->sectnum].ceilingz) < 16) || pSprite->z > sector[pSprite->sectnum].floorz || pSprite->z > actor[spriteNum].floorz ||
8585 (pSprite->picnum != SECTOREFFECTOR && ((pSprite->cstat & 32768) || pSprite->yrepeat < 4)) ||
8586 A_CheckSpriteFlags(spriteNum, SFLAG_NOLIGHT) || (A_CheckSpriteFlags(spriteNum, SFLAG_USEACTIVATOR) && sector[pSprite->sectnum].lotag & 16384))
8587 {
8588 if (practor[spriteNum].lightptr != NULL)
8589 A_DeleteLight(spriteNum);
8590 }
8591 else
8592 {
8593 if (practor[spriteNum].lightptr != NULL && practor[spriteNum].lightcount)
8594 {
8595 if (!(--practor[spriteNum].lightcount))
8596 A_DeleteLight(spriteNum);
8597 }
8598
8599 if (pr_lighting != 1)
8600 return;
8601
8602 #ifndef EDUKE32_STANDALONE
8603 if (FURY)
8604 return;
8605
8606 for (int ii=0; ii<2; ii++)
8607 {
8608 if (pSprite->picnum <= 0) // oob safety
8609 break;
8610
8611 switch (DYNAMICTILEMAP(pSprite->picnum-1+ii))
8612 {
8613 case DIPSWITCH__STATIC:
8614 case DIPSWITCH2__STATIC:
8615 case DIPSWITCH3__STATIC:
8616 case PULLSWITCH__STATIC:
8617 case SLOTDOOR__STATIC:
8618 case LIGHTSWITCH__STATIC:
8619 case SPACELIGHTSWITCH__STATIC:
8620 case SPACEDOORSWITCH__STATIC:
8621 case FRANKENSTINESWITCH__STATIC:
8622 case POWERSWITCH1__STATIC:
8623 case LOCKSWITCH1__STATIC:
8624 case POWERSWITCH2__STATIC:
8625 case TECHSWITCH__STATIC:
8626 case ACCESSSWITCH__STATIC:
8627 case ACCESSSWITCH2__STATIC:
8628 {
8629 if ((pSprite->cstat & 32768) || A_CheckSpriteFlags(spriteNum, SFLAG_NOLIGHT))
8630 {
8631 if (practor[spriteNum].lightptr != NULL)
8632 A_DeleteLight(spriteNum);
8633 break;
8634 }
8635
8636 vec2_t const d = { sintable[(pSprite->ang+512)&2047]>>7, sintable[(pSprite->ang)&2047]>>7 };
8637
8638 pSprite->x += d.x;
8639 pSprite->y += d.y;
8640
8641 int16_t sectnum = pSprite->sectnum;
8642 updatesector(pSprite->x, pSprite->y, §num);
8643
8644 if ((unsigned) sectnum >= MAXSECTORS || pSprite->z > sector[sectnum].floorz || pSprite->z < sector[sectnum].ceilingz)
8645 goto POOP;
8646
8647 G_AddGameLight(0, spriteNum, (pSprite->yrepeat*tilesiz[pSprite->picnum].y)<<1, 512-ii*128,
8648 ii==0 ? (172+(200<<8)+(104<<16)) : 216+(52<<8)+(20<<16), PR_LIGHT_PRIO_LOW);
8649
8650 POOP:
8651 pSprite->x -= d.x;
8652 pSprite->y -= d.y;
8653 }
8654 break;
8655 }
8656 }
8657
8658 switch (DYNAMICTILEMAP(pSprite->picnum))
8659 {
8660 case ATOMICHEALTH__STATIC:
8661 G_AddGameLight(0, spriteNum, ((pSprite->yrepeat*tilesiz[pSprite->picnum].y)<<1), LIGHTRAD2(spriteNum, pSprite), 128+(128<<8)+(255<<16),PR_LIGHT_PRIO_HIGH_GAME);
8662 break;
8663
8664 case FIRE__STATIC:
8665 case FIRE2__STATIC:
8666 case BURNING__STATIC:
8667 case BURNING2__STATIC:
8668 {
8669 uint32_t color;
8670 int32_t jj;
8671
8672 static int32_t savedfires[32][4]; // sectnum x y z
8673
8674 /*
8675 if (Actor[i].floorz - Actor[i].ceilingz < 128) break;
8676 if (s->z > Actor[i].floorz+2048) break;
8677 */
8678
8679 switch (pSprite->pal)
8680 {
8681 case 1: color = 128+(128<<8)+(255<<16); break;
8682 case 2: color = 255+(48<<8)+(48<<16); break;
8683 case 8: color = 48+(255<<8)+(48<<16); break;
8684 default: color = 240+(160<<8)+(80<<16); break;
8685 }
8686
8687 for (jj=savedFires-1; jj>=0; jj--)
8688 if (savedfires[jj][0]==pSprite->sectnum && savedfires[jj][1]==(pSprite->x>>3) &&
8689 savedfires[jj][2]==(pSprite->y>>3) && savedfires[jj][3]==(pSprite->z>>7))
8690 break;
8691
8692 if (jj==-1 && savedFires<32)
8693 {
8694 jj = savedFires;
8695 G_AddGameLight(0, spriteNum, ((pSprite->yrepeat*tilesiz[pSprite->picnum].y)<<1), LIGHTRAD2(spriteNum, pSprite), color, PR_LIGHT_PRIO_HIGH_GAME);
8696 savedfires[jj][0] = pSprite->sectnum;
8697 savedfires[jj][1] = pSprite->x>>3;
8698 savedfires[jj][2] = pSprite->y>>3;
8699 savedfires[jj][3] = pSprite->z>>7;
8700 savedFires++;
8701 }
8702 }
8703 break;
8704
8705 case OOZFILTER__STATIC:
8706 if (pSprite->xrepeat > 4)
8707 G_AddGameLight(0, spriteNum, ((pSprite->yrepeat*tilesiz[pSprite->picnum].y)<<1), 4096, 176+(252<<8)+(120<<16),PR_LIGHT_PRIO_HIGH_GAME);
8708 break;
8709 case FLOORFLAME__STATIC:
8710 case FIREBARREL__STATIC:
8711 case FIREVASE__STATIC:
8712 G_AddGameLight(0, spriteNum, ((pSprite->yrepeat*tilesiz[pSprite->picnum].y)<<2), LIGHTRAD2(spriteNum, pSprite)>>1, 255+(95<<8),PR_LIGHT_PRIO_HIGH_GAME);
8713 break;
8714
8715 case EXPLOSION2__STATIC:
8716 if (!practor[spriteNum].lightcount)
8717 {
8718 // XXX: This block gets CODEDUP'd too much.
8719 int32_t x = ((sintable[(pSprite->ang+512)&2047])>>6);
8720 int32_t y = ((sintable[(pSprite->ang)&2047])>>6);
8721
8722 pSprite->x -= x;
8723 pSprite->y -= y;
8724
8725 G_AddGameLight(0, spriteNum, ((pSprite->yrepeat*tilesiz[pSprite->picnum].y)<<1), LIGHTRAD(spriteNum, pSprite), 240+(160<<8)+(80<<16),
8726 pSprite->yrepeat > 32 ? PR_LIGHT_PRIO_HIGH_GAME : PR_LIGHT_PRIO_LOW_GAME);
8727
8728 pSprite->x += x;
8729 pSprite->y += y;
8730 }
8731 break;
8732 case FORCERIPPLE__STATIC:
8733 case TRANSPORTERBEAM__STATIC:
8734 G_AddGameLight(0, spriteNum, ((pSprite->yrepeat*tilesiz[pSprite->picnum].y)<<1), LIGHTRAD(spriteNum, pSprite), 80+(80<<8)+(255<<16),PR_LIGHT_PRIO_LOW_GAME);
8735 break;
8736 case GROWSPARK__STATIC:
8737 {
8738 int32_t x = ((sintable[(pSprite->ang+512)&2047])>>6);
8739 int32_t y = ((sintable[(pSprite->ang)&2047])>>6);
8740
8741 pSprite->x -= x;
8742 pSprite->y -= y;
8743
8744 G_AddGameLight(0, spriteNum, ((pSprite->yrepeat*tilesiz[pSprite->picnum].y)<<1), 1024, 216+(52<<8)+(20<<16),PR_LIGHT_PRIO_HIGH_GAME);
8745
8746 pSprite->x += x;
8747 pSprite->y += y;
8748 }
8749 break;
8750 case SHRINKEREXPLOSION__STATIC:
8751 {
8752 int32_t x = ((sintable[(pSprite->ang+512)&2047])>>6);
8753 int32_t y = ((sintable[(pSprite->ang)&2047])>>6);
8754
8755 pSprite->x -= x;
8756 pSprite->y -= y;
8757
8758 G_AddGameLight(0, spriteNum, ((pSprite->yrepeat*tilesiz[pSprite->picnum].y)<<1), 2048, 176+(252<<8)+(120<<16),PR_LIGHT_PRIO_HIGH_GAME);
8759
8760 pSprite->x += x;
8761 pSprite->y += y;
8762 }
8763 break;
8764 case FREEZEBLAST__STATIC:
8765 G_AddGameLight(0, spriteNum, ((pSprite->yrepeat*tilesiz[pSprite->picnum].y)<<1), LIGHTRAD(spriteNum, pSprite)<<2, 72+(88<<8)+(140<<16),PR_LIGHT_PRIO_HIGH_GAME);
8766 break;
8767 case COOLEXPLOSION1__STATIC:
8768 G_AddGameLight(0, spriteNum, ((pSprite->yrepeat*tilesiz[pSprite->picnum].y)<<1), LIGHTRAD(spriteNum, pSprite)<<2, 128+(0<<8)+(255<<16),PR_LIGHT_PRIO_HIGH_GAME);
8769 break;
8770 case SHRINKSPARK__STATIC:
8771 G_AddGameLight(0, spriteNum, ((pSprite->yrepeat*tilesiz[pSprite->picnum].y)<<1), LIGHTRAD(spriteNum, pSprite), 176+(252<<8)+(120<<16),PR_LIGHT_PRIO_HIGH_GAME);
8772 break;
8773 case FIRELASER__STATIC:
8774 if (pSprite->statnum == STAT_PROJECTILE)
8775 G_AddGameLight(0, spriteNum, ((pSprite->yrepeat*tilesiz[pSprite->picnum].y)<<1), 64 * pSprite->yrepeat, 255+(95<<8),PR_LIGHT_PRIO_LOW_GAME);
8776 break;
8777 case RPG__STATIC:
8778 G_AddGameLight(0, spriteNum, ((pSprite->yrepeat*tilesiz[pSprite->picnum].y)<<1), 128 * pSprite->yrepeat, 255+(95<<8),PR_LIGHT_PRIO_LOW_GAME);
8779 break;
8780 case SHOTSPARK1__STATIC:
8781 if (actor[spriteNum].t_data[2] == 0) // check for first frame of action
8782 {
8783 int32_t x = ((sintable[(pSprite->ang+512)&2047])>>7);
8784 int32_t y = ((sintable[(pSprite->ang)&2047])>>7);
8785
8786 pSprite->x -= x;
8787 pSprite->y -= y;
8788
8789 G_AddGameLight(0, spriteNum, ((pSprite->yrepeat*tilesiz[pSprite->picnum].y)<<1), 8 * pSprite->yrepeat, 240+(160<<8)+(80<<16),PR_LIGHT_PRIO_LOW_GAME);
8790 practor[spriteNum].lightcount = 1;
8791
8792 pSprite->x += x;
8793 pSprite->y += y;
8794 }
8795 break;
8796 }
8797 #endif
8798 }
8799 }
8800 #endif // POLYMER
8801
A_PlayAlertSound(int spriteNum)8802 void A_PlayAlertSound(int spriteNum)
8803 {
8804 if (sprite[spriteNum].extra > 0)
8805 {
8806 if ((VM_OnEventWithReturn(EVENT_RECOGSOUND, spriteNum, screenpeek, 0)) != 0)
8807 return;
8808
8809 #ifndef EDUKE32_STANDALONE
8810 if (FURY)
8811 return;
8812
8813 switch (DYNAMICTILEMAP(PN(spriteNum)))
8814 {
8815 case LIZTROOPONTOILET__STATIC:
8816 case LIZTROOPJUSTSIT__STATIC:
8817 case LIZTROOPSHOOT__STATIC:
8818 case LIZTROOPJETPACK__STATIC:
8819 case LIZTROOPDUCKING__STATIC:
8820 case LIZTROOPRUNNING__STATIC:
8821 case LIZTROOP__STATIC: A_PlaySound(PRED_RECOG, spriteNum); break;
8822 case LIZMAN__STATIC:
8823 case LIZMANSPITTING__STATIC:
8824 case LIZMANFEEDING__STATIC:
8825 case LIZMANJUMP__STATIC: A_PlaySound(CAPT_RECOG, spriteNum); break;
8826 case PIGCOP__STATIC:
8827 case PIGCOPDIVE__STATIC: A_PlaySound(PIG_RECOG, spriteNum); break;
8828 case RECON__STATIC: A_PlaySound(RECO_RECOG, spriteNum); break;
8829 case DRONE__STATIC: A_PlaySound(DRON_RECOG, spriteNum); break;
8830 case COMMANDER__STATIC:
8831 case COMMANDERSTAYPUT__STATIC: A_PlaySound(COMM_RECOG, spriteNum); break;
8832 case ORGANTIC__STATIC: A_PlaySound(TURR_RECOG, spriteNum); break;
8833 case OCTABRAIN__STATIC:
8834 case OCTABRAINSTAYPUT__STATIC: A_PlaySound(OCTA_RECOG, spriteNum); break;
8835 case BOSS1__STATIC:
8836 case BOSS1STAYPUT__STATIC: S_PlaySound(BOS1_RECOG); break;
8837 case BOSS2__STATIC: S_PlaySound((sprite[spriteNum].pal != 0) ? BOS2_RECOG : WHIPYOURASS); break;
8838 case BOSS3__STATIC: S_PlaySound((sprite[spriteNum].pal != 0) ? BOS3_RECOG : RIPHEADNECK); break;
8839 case BOSS4__STATIC:
8840 case BOSS4STAYPUT__STATIC: S_PlaySound((sprite[spriteNum].pal != 0) ? BOS4_RECOG : BOSS4_FIRSTSEE); break;
8841 case GREENSLIME__STATIC: A_PlaySound(SLIM_RECOG, spriteNum); break;
8842 }
8843 #endif
8844 }
8845 }
8846
A_CheckSwitchTile(int spriteNum)8847 int A_CheckSwitchTile(int spriteNum)
8848 {
8849 UNREFERENCED_PARAMETER(spriteNum);
8850
8851 #ifndef EDUKE32_STANDALONE
8852 if (FURY)
8853 return 0;
8854
8855 // picnum 0 would oob in the switch below,
8856
8857 if (PN(spriteNum) <= 0)
8858 return 0;
8859
8860 // MULTISWITCH has 4 states so deal with it separately,
8861 // ACCESSSWITCH and ACCESSSWITCH2 are only active in one state so deal with
8862 // them separately.
8863
8864 if ((PN(spriteNum) >= MULTISWITCH && PN(spriteNum) <= MULTISWITCH + 3) || (PN(spriteNum) == ACCESSSWITCH || PN(spriteNum) == ACCESSSWITCH2))
8865 return 1;
8866
8867 // Loop to catch both states of switches.
8868 for (bssize_t j=1; j>=0; j--)
8869 {
8870 switch (DYNAMICTILEMAP(PN(spriteNum)-j))
8871 {
8872 case HANDPRINTSWITCH__STATIC:
8873 case ALIENSWITCH__STATIC:
8874 case MULTISWITCH__STATIC:
8875 case PULLSWITCH__STATIC:
8876 case HANDSWITCH__STATIC:
8877 case SLOTDOOR__STATIC:
8878 case LIGHTSWITCH__STATIC:
8879 case SPACELIGHTSWITCH__STATIC:
8880 case SPACEDOORSWITCH__STATIC:
8881 case FRANKENSTINESWITCH__STATIC:
8882 case LIGHTSWITCH2__STATIC:
8883 case POWERSWITCH1__STATIC:
8884 case LOCKSWITCH1__STATIC:
8885 case POWERSWITCH2__STATIC:
8886 case DIPSWITCH__STATIC:
8887 case DIPSWITCH2__STATIC:
8888 case TECHSWITCH__STATIC:
8889 case DIPSWITCH3__STATIC:
8890 return 1;
8891 }
8892 }
8893 #endif
8894 return 0;
8895 }
8896
G_RefreshLights(void)8897 void G_RefreshLights(void)
8898 {
8899 #ifdef POLYMER
8900 if (Numsprites && videoGetRenderMode() == REND_POLYMER)
8901 {
8902 int statNum = 0;
8903
8904 do
8905 {
8906 int spriteNum = headspritestat[statNum++];
8907
8908 while (spriteNum >= 0)
8909 {
8910 A_DoLight(spriteNum);
8911 spriteNum = nextspritestat[spriteNum];
8912 }
8913 }
8914 while (statNum < MAXSTATUS);
8915 }
8916 #endif
8917 }
8918
G_RecordOldSpritePos(void)8919 static void G_RecordOldSpritePos(void)
8920 {
8921 int statNum = 0;
8922 do
8923 {
8924 int spriteNum = headspritestat[statNum++];
8925
8926 while (spriteNum >= 0)
8927 {
8928 int const nextSprite = nextspritestat[spriteNum];
8929 actor[spriteNum].bpos = sprite[spriteNum].pos;
8930
8931 spriteNum = nextSprite;
8932 }
8933 }
8934 while (statNum < MAXSTATUS);
8935 }
8936
G_DoEventGame(int const nEventID)8937 static void G_DoEventGame(int const nEventID)
8938 {
8939 if (VM_HaveEvent(nEventID))
8940 {
8941 int statNum = 0;
8942
8943 do
8944 {
8945 int spriteNum = headspritestat[statNum++];
8946
8947 while (spriteNum >= 0)
8948 {
8949 int const nextSprite = nextspritestat[spriteNum];
8950
8951 if (A_CheckSpriteFlags(spriteNum, SFLAG_NOEVENTCODE))
8952 {
8953 spriteNum = nextSprite;
8954 continue;
8955 }
8956
8957 int32_t playerDist;
8958 int const playerNum = A_FindPlayer(&sprite[spriteNum], &playerDist);
8959 VM_ExecuteEvent(nEventID, spriteNum, playerNum, playerDist);
8960
8961 spriteNum = nextSprite;
8962 }
8963 }
8964 while (statNum < MAXSTATUS);
8965 }
8966 }
8967
G_MoveWorld(void)8968 void G_MoveWorld(void)
8969 {
8970 extern double g_moveActorsTime, g_moveWorldTime;
8971 const double worldTime = timerGetHiTicks();
8972
8973 MICROPROFILE_SCOPEI("Game", "MoveWorld", MP_YELLOW);
8974
8975 VM_OnEvent(EVENT_PREWORLD);
8976 G_DoEventGame(EVENT_PREGAME);
8977 G_RecordOldSpritePos();
8978
8979 {
8980 MICROPROFILE_SCOPEI("MoveWorld", "MoveZombieActors", MP_YELLOW2);
8981 G_MoveZombieActors(); //ST 2
8982 }
8983
8984 {
8985 MICROPROFILE_SCOPEI("MoveWorld", "MoveWeapons", MP_YELLOW3);
8986 G_MoveWeapons(); //ST 4
8987 }
8988
8989 {
8990 MICROPROFILE_SCOPEI("MoveWorld", "MoveTransports", MP_YELLOW4);
8991 G_MoveTransports(); //ST 9
8992 }
8993
8994 {
8995 MICROPROFILE_SCOPEI("MoveWorld", "MovePlayers", MP_YELLOW);
8996 G_MovePlayers(); //ST 10
8997 }
8998
8999 {
9000 MICROPROFILE_SCOPEI("MoveWorld", "MoveFallers", MP_YELLOW2);
9001 G_MoveFallers(); //ST 12
9002 }
9003
9004 {
9005 MICROPROFILE_SCOPEI("MoveWorld", "MoveMisc", MP_YELLOW3);
9006 G_MoveMisc(); //ST 5
9007 }
9008
9009 const double actorsTime = timerGetHiTicks();
9010
9011 {
9012 MICROPROFILE_SCOPEI("MoveWorld", "MoveActors", MP_YELLOW4);
9013 G_MoveActors(); //ST 1
9014 }
9015
9016 g_moveActorsTime = (1-0.033)*g_moveActorsTime + 0.033*(timerGetHiTicks()-actorsTime);
9017
9018 // XXX: Has to be before effectors, in particular movers?
9019 // TODO: lights in moving sectors ought to be interpolated
9020 G_DoEffectorLights();
9021
9022 {
9023 MICROPROFILE_SCOPEI("MoveWorld", "MoveEffectors", MP_YELLOW);
9024 G_MoveEffectors(); //ST 3
9025 }
9026
9027 {
9028 MICROPROFILE_SCOPEI("MoveWorld", "MoveStandables", MP_YELLOW2);
9029 G_MoveStandables(); //ST 6
9030 }
9031
9032
9033 VM_OnEvent(EVENT_WORLD);
9034
9035 G_DoEventGame(EVENT_GAME);
9036
9037 G_RefreshLights();
9038 G_DoSectorAnimations();
9039
9040 {
9041 MICROPROFILE_SCOPEI("MoveWorld", "MoveFX", MP_YELLOW3);
9042 G_MoveFX(); //ST 11
9043 }
9044
9045 g_moveWorldTime = (1-0.033)*g_moveWorldTime + 0.033*(timerGetHiTicks()-worldTime);
9046 }
9047