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, &sectNum);
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 = &sector[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,&sectNum);
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     = &sector[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(&sector[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     = &sector[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(&sector[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)&sector[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, &sectnum);
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