1 //-------------------------------------------------------------------------
2 /*
3 Copyright (C) 2010 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 sector_c_
24 
25 #include "duke3d.h"
26 #include "input.h"
27 
28 // PRIMITIVE
29 
30 static int g_haltSoundHack = 0;
31 
S_FindMusicSFX(int sectNum,int * sndptr)32 int S_FindMusicSFX(int sectNum, int *sndptr)
33 {
34     for (bssize_t SPRITES_OF_SECT(sectNum, spriteNum))
35     {
36         const int32_t snd = sprite[spriteNum].lotag;
37         EDUKE32_STATIC_ASSERT(MAXSOUNDS >= 1000);
38 
39         if (PN(spriteNum) == MUSICANDSFX && (unsigned)snd < 1000)  // XXX: in other places, 999
40         {
41             *sndptr = snd;
42             return spriteNum;
43         }
44     }
45 
46     *sndptr = -1;
47     return -1;
48 }
49 
50 // this function activates a sector's MUSICANDSFX sprite
A_CallSound(int sectNum,int spriteNum)51 int A_CallSound(int sectNum, int spriteNum)
52 {
53     if (g_haltSoundHack)
54     {
55         g_haltSoundHack = 0;
56         return -1;
57     }
58 
59     int soundNum;
60     int const SFXsprite = S_FindMusicSFX(sectNum, &soundNum);
61 
62     if (SFXsprite >= 0)
63     {
64         if (spriteNum == -1)
65             spriteNum = SFXsprite;
66 
67         if (T1(SFXsprite) == 0)
68         {
69             if ((g_sounds[soundNum].m & (SF_GLOBAL|SF_DTAG)) != SF_GLOBAL)
70             {
71                 if (soundNum)
72                 {
73                     A_PlaySound(soundNum, spriteNum);
74 
75                     if (SHT(SFXsprite) && soundNum != SHT(SFXsprite) && SHT(SFXsprite) < MAXSOUNDS)
76                         S_StopEnvSound(SHT(SFXsprite),T6(SFXsprite));
77 
78                     T6(SFXsprite) = spriteNum;
79                 }
80 
81                 if ((sector[SECT(SFXsprite)].lotag&0xff) != ST_22_SPLITTING_DOOR)
82                     T1(SFXsprite) = 1;
83             }
84         }
85         else if (SHT(SFXsprite) < MAXSOUNDS)
86         {
87             if (SHT(SFXsprite))
88                 A_PlaySound(SHT(SFXsprite), spriteNum);
89 
90             if ((g_sounds[soundNum].m & SF_LOOP) || (SHT(SFXsprite) && SHT(SFXsprite) != soundNum))
91                 S_StopEnvSound(soundNum, T6(SFXsprite));
92 
93             T6(SFXsprite) = spriteNum;
94             T1(SFXsprite) = 0;
95         }
96 
97         return soundNum;
98     }
99 
100     return -1;
101 }
102 
G_CheckActivatorMotion(int lotag)103 int G_CheckActivatorMotion(int lotag)
104 {
105     int spriteNum = headspritestat[STAT_ACTIVATOR];
106 
107     while (spriteNum >= 0)
108     {
109         if (sprite[spriteNum].lotag == lotag)
110         {
111             auto const pSprite = &sprite[spriteNum];
112 
113             for (bssize_t j = g_animateCnt - 1; j >= 0; j--)
114                 if (pSprite->sectnum == g_animateSect[j])
115                     return 1;
116 
117             int sectorEffector = headspritestat[STAT_EFFECTOR];
118 
119             while (sectorEffector >= 0)
120             {
121                 if (pSprite->sectnum == sprite[sectorEffector].sectnum)
122                 {
123                     switch (sprite[sectorEffector].lotag)
124                     {
125                         case SE_11_SWINGING_DOOR:
126                         case SE_30_TWO_WAY_TRAIN:
127                             if (actor[sectorEffector].t_data[4])
128                                 return 1;
129                             break;
130                         case SE_20_STRETCH_BRIDGE:
131                         case SE_31_FLOOR_RISE_FALL:
132                         case SE_32_CEILING_RISE_FALL:
133                         case SE_18_INCREMENTAL_SECTOR_RISE_FALL:
134                             if (actor[sectorEffector].t_data[0])
135                                 return 1;
136                             break;
137                     }
138                 }
139 
140                 sectorEffector = nextspritestat[sectorEffector];
141             }
142         }
143         spriteNum = nextspritestat[spriteNum];
144     }
145     return 0;
146 }
147 
CheckDoorTile(int tileNum)148 int CheckDoorTile(int tileNum)
149 {
150 #ifndef EDUKE32_STANDALONE
151     switch (DYNAMICTILEMAP(tileNum))
152     {
153         case DOORTILE1__STATIC:
154         case DOORTILE2__STATIC:
155         case DOORTILE3__STATIC:
156         case DOORTILE4__STATIC:
157         case DOORTILE5__STATIC:
158         case DOORTILE6__STATIC:
159         case DOORTILE7__STATIC:
160         case DOORTILE8__STATIC:
161         case DOORTILE9__STATIC:
162         case DOORTILE10__STATIC:
163         case DOORTILE11__STATIC:
164         case DOORTILE12__STATIC:
165         case DOORTILE14__STATIC:
166         case DOORTILE15__STATIC:
167         case DOORTILE16__STATIC:
168         case DOORTILE17__STATIC:
169         case DOORTILE18__STATIC:
170         case DOORTILE19__STATIC:
171         case DOORTILE20__STATIC:
172         case DOORTILE21__STATIC:
173         case DOORTILE22__STATIC:
174         case DOORTILE23__STATIC:
175             return 1;
176     }
177 #else
178     UNREFERENCED_PARAMETER(tileNum);
179 #endif
180     return 0;
181 }
182 
isanunderoperator(int lotag)183 int isanunderoperator(int lotag)
184 {
185     switch (lotag & 0xff)
186     {
187         case ST_15_WARP_ELEVATOR:
188         case ST_16_PLATFORM_DOWN:
189         case ST_17_PLATFORM_UP:
190         case ST_18_ELEVATOR_DOWN:
191         case ST_19_ELEVATOR_UP:
192         case ST_22_SPLITTING_DOOR:
193         case ST_26_SPLITTING_ST_DOOR:
194             return 1;
195     }
196     return 0;
197 }
198 
isanearoperator(int lotag)199 int isanearoperator(int lotag)
200 {
201     switch (lotag & 0xff)
202     {
203         case ST_9_SLIDING_ST_DOOR:
204         case ST_15_WARP_ELEVATOR:
205         case ST_16_PLATFORM_DOWN:
206         case ST_17_PLATFORM_UP:
207         case ST_18_ELEVATOR_DOWN:
208         case ST_19_ELEVATOR_UP:
209         case ST_20_CEILING_DOOR:
210         case ST_21_FLOOR_DOOR:
211         case ST_22_SPLITTING_DOOR:
212         case ST_23_SWINGING_DOOR:
213         case ST_25_SLIDING_DOOR:
214         case ST_26_SPLITTING_ST_DOOR:
215         case ST_29_TEETH_DOOR:
216             return 1;
217     }
218     return 0;
219 }
220 
A_FP_ManhattanDist(const DukePlayer_t * pPlayer,const spritetype * pSprite)221 static inline int32_t A_FP_ManhattanDist(const DukePlayer_t *pPlayer, const spritetype *pSprite)
222 {
223     return klabs(pPlayer->opos.x - pSprite->x)
224            + klabs(pPlayer->opos.y - pSprite->y)
225            + ((klabs(pPlayer->opos.z - pSprite->z + (28 << 8))) >> 4);
226 }
227 
A_FindPlayer(const spritetype * pSprite,int32_t * dist)228 int __fastcall A_FindPlayer(const spritetype *pSprite, int32_t *dist)
229 {
230     if (!g_netServer && ud.multimode < 2)
231     {
232         auto const pPlayer = g_player[myconnectindex].ps;
233 
234         if (dist)
235             *dist = A_FP_ManhattanDist(pPlayer, pSprite);
236 
237         return myconnectindex;
238     }
239 
240     int     closestPlayer     = 0;
241     int32_t closestPlayerDist = INT32_MAX;
242 
243     for (bssize_t TRAVERSE_CONNECT(j))
244     {
245         auto const pPlayer    = g_player[j].ps;
246         int32_t             playerDist = A_FP_ManhattanDist(pPlayer, pSprite);
247 
248         if (playerDist < closestPlayerDist && sprite[pPlayer->i].extra > 0)
249         {
250             closestPlayer     = j;
251             closestPlayerDist = playerDist;
252         }
253     }
254 
255     if (dist)
256         *dist = closestPlayerDist;
257 
258     return closestPlayer;
259 }
260 
G_DoSectorAnimations(void)261 void G_DoSectorAnimations(void)
262 {
263     for (bssize_t animNum=g_animateCnt-1; animNum>=0; animNum--)
264     {
265         int       animPos  = *g_animatePtr[animNum];
266         int const animVel  = g_animateVel[animNum] * TICSPERFRAME;
267         int const animSect = g_animateSect[animNum];
268 
269         if (animPos == g_animateGoal[animNum])
270         {
271             G_StopInterpolation(g_animatePtr[animNum]);
272 
273             // This fixes a bug where wall or floor sprites contained in
274             // elevator sectors (ST 16-19) would jitter vertically after the
275             // elevator had stopped.
276             if (g_animatePtr[animNum] == &sector[g_animateSect[animNum]].floorz)
277             {
278                 for (bssize_t j=headspritesect[animSect]; j>=0; j=nextspritesect[j])
279                 {
280                     if (sprite[j].statnum != STAT_EFFECTOR)
281                         actor[j].bpos.z = sprite[j].z;
282                 }
283             }
284 
285             g_animateCnt--;
286 
287             g_animatePtr[animNum]  = g_animatePtr[g_animateCnt];
288             g_animateGoal[animNum] = g_animateGoal[g_animateCnt];
289             g_animateVel[animNum]  = g_animateVel[g_animateCnt];
290             g_animateSect[animNum] = g_animateSect[g_animateCnt];
291 
292             if ((sector[g_animateSect[animNum]].lotag == ST_18_ELEVATOR_DOWN || sector[g_animateSect[animNum]].lotag == ST_19_ELEVATOR_UP)
293                 && (g_animatePtr[animNum] == &sector[g_animateSect[animNum]].ceilingz))
294                 continue;
295 
296             if ((sector[animSect].lotag&0xff) != ST_22_SPLITTING_DOOR)
297                 A_CallSound(animSect,-1);
298 
299             continue;
300         }
301 
302         animPos = (animVel > 0) ? min(animPos + animVel, g_animateGoal[animNum])
303                                 : max(animPos + animVel, g_animateGoal[animNum]);
304 
305         if (g_animatePtr[animNum] == &sector[g_animateSect[animNum]].floorz)
306         {
307             for (bssize_t TRAVERSE_CONNECT(playerNum))
308             {
309                 if ((g_player[playerNum].ps->cursectnum == animSect)
310                     && ((sector[animSect].floorz - g_player[playerNum].ps->pos.z) < (64 << 8))
311                     && (sprite[g_player[playerNum].ps->i].owner >= 0))
312                 {
313                     g_player[playerNum].ps->pos.z += animVel;
314                     g_player[playerNum].ps->vel.z = 0;
315                     /*
316                                                 if (p == myconnectindex)
317                                                 {
318                                                     my.z += v;
319                                                     myvel.z = 0;
320                                                 }
321                     */
322                 }
323             }
324 
325             for (bssize_t j=headspritesect[animSect]; j>=0; j=nextspritesect[j])
326             {
327                 if (sprite[j].statnum != STAT_EFFECTOR)
328                 {
329                     actor[j].bpos.z = sprite[j].z;
330                     sprite[j].z += animVel;
331                     actor[j].floorz = sector[animSect].floorz+animVel;
332                 }
333             }
334         }
335 
336         *g_animatePtr[animNum] = animPos;
337     }
338 }
339 
GetAnimationGoal(const int32_t * animPtr)340 int GetAnimationGoal(const int32_t *animPtr)
341 {
342     for (bssize_t i = 0; i < g_animateCnt; i++)
343         if (animPtr == g_animatePtr[i])
344             return i;
345     return -1;
346 }
347 
SetAnimation(int sectNum,int32_t * animPtr,int goalVal,int animVel)348 int SetAnimation(int sectNum, int32_t *animPtr, int goalVal, int animVel)
349 {
350     if (g_animateCnt >= MAXANIMATES)
351         return -1;
352 
353     int animNum = g_animateCnt;
354 
355     for (bssize_t i = 0; i < g_animateCnt; i++)
356     {
357         if (animPtr == g_animatePtr[i])
358         {
359             animNum = i;
360             break;
361         }
362     }
363 
364     g_animateSect[animNum] = sectNum;
365     g_animatePtr[animNum]  = animPtr;
366     g_animateGoal[animNum] = goalVal;
367     g_animateVel[animNum]  = (goalVal >= *animPtr) ? animVel : -animVel;
368 
369     if (animNum == g_animateCnt)
370         g_animateCnt++;
371 
372     G_SetInterpolation(animPtr);
373 
374     return animNum;
375 }
376 
G_SetupCamTile(int spriteNum,int tileNum,int smoothRatio)377 static void G_SetupCamTile(int spriteNum, int tileNum, int smoothRatio)
378 {
379     int const playerNum = screenpeek;
380 
381     vec3_t const camera     = G_GetCameraPosition(spriteNum, smoothRatio);
382     int const    saveMirror = display_mirror;
383 
384     //if (waloff[wn] == 0) loadtile(wn);
385     renderSetTarget(tileNum, tilesiz[tileNum].y, tilesiz[tileNum].x);
386 
387     int const noDraw = VM_OnEventWithReturn(EVENT_DISPLAYROOMSCAMERATILE, spriteNum, playerNum, 0);
388 
389     if (noDraw == 1)
390         goto finishTileSetup;
391 #ifdef DEBUGGINGAIDS
392     else if (EDUKE32_PREDICT_FALSE(noDraw != 0)) // event return values other than 0 and 1 are reserved
393         OSD_Printf(OSD_ERROR "ERROR: EVENT_DISPLAYROOMSCAMERATILE return value must be 0 or 1, "
394                    "other values are reserved.\n");
395 #endif
396 
397     yax_preparedrawrooms();
398     drawrooms(camera.x, camera.y, camera.z, SA(spriteNum), 100 + sprite[spriteNum].shade, SECT(spriteNum));
399     yax_drawrooms(G_DoSpriteAnimations, SECT(spriteNum), 0, smoothRatio);
400 
401     display_mirror = 3;
402     G_DoSpriteAnimations(camera.x, camera.y, camera.z, SA(spriteNum), smoothRatio);
403     display_mirror = saveMirror;
404     renderDrawMasks();
405 
406 finishTileSetup:
407     renderRestoreTarget();
408     squarerotatetile(tileNum);
409     tileInvalidate(tileNum, -1, 255);
410 }
411 
G_AnimateCamSprite(int smoothRatio)412 void G_AnimateCamSprite(int smoothRatio)
413 {
414 #ifdef DEBUG_VALGRIND_NO_SMC
415     return;
416 #endif
417 
418     if (g_curViewscreen < 0)
419         return;
420 
421     int const spriteNum = g_curViewscreen;
422 
423     if (totalclock >= T1(spriteNum) + ud.camera_time)
424     {
425         auto const pPlayer = g_player[screenpeek].ps;
426 
427         if (pPlayer->newowner >= 0)
428             OW(spriteNum) = pPlayer->newowner;
429 
430         if (OW(spriteNum) >= 0 && dist(&sprite[pPlayer->i], &sprite[spriteNum]) < VIEWSCREEN_ACTIVE_DISTANCE)
431         {
432             int const viewscrShift = G_GetViewscreenSizeShift((uspriteptr_t)&sprite[spriteNum]);
433             int const viewscrTile  = TILE_VIEWSCR - viewscrShift;
434 
435             if (waloff[viewscrTile] == 0)
436                 tileCreate(viewscrTile, tilesiz[PN(spriteNum)].x << viewscrShift, tilesiz[PN(spriteNum)].y << viewscrShift);
437             else
438                 walock[viewscrTile] = CACHE1D_UNLOCKED;
439 
440             G_SetupCamTile(OW(spriteNum), viewscrTile, smoothRatio);
441 #ifdef POLYMER
442             // Force texture update on viewscreen sprite in Polymer!
443             if (videoGetRenderMode() == REND_POLYMER)
444                 polymer_invalidatesprite(spriteNum);
445 #endif
446         }
447 
448         T1(spriteNum) = (int32_t) totalclock;
449     }
450 }
451 
G_AnimateWalls(void)452 void G_AnimateWalls(void)
453 {
454     for (bssize_t animwallNum = g_animWallCnt-1; animwallNum>=0; animwallNum--)
455     {
456         int const wallNum = animwall[animwallNum].wallnum;
457 
458         switch (DYNAMICTILEMAP(wall[wallNum].picnum))
459         {
460         case SCREENBREAK1__STATIC:
461         case SCREENBREAK2__STATIC:
462         case SCREENBREAK3__STATIC:
463         case SCREENBREAK4__STATIC:
464         case SCREENBREAK5__STATIC:
465 
466         case SCREENBREAK9__STATIC:
467         case SCREENBREAK10__STATIC:
468         case SCREENBREAK11__STATIC:
469         case SCREENBREAK12__STATIC:
470         case SCREENBREAK13__STATIC:
471         case SCREENBREAK14__STATIC:
472         case SCREENBREAK15__STATIC:
473         case SCREENBREAK16__STATIC:
474         case SCREENBREAK17__STATIC:
475         case SCREENBREAK18__STATIC:
476         case SCREENBREAK19__STATIC:
477             if ((krand()&255) < 16)
478             {
479                 animwall[animwallNum].tag = wall[wallNum].picnum;
480                 wall[wallNum].picnum = SCREENBREAK6;
481             }
482             continue;
483 
484         case SCREENBREAK6__STATIC:
485         case SCREENBREAK7__STATIC:
486         case SCREENBREAK8__STATIC:
487             if (animwall[animwallNum].tag >= 0 && wall[wallNum].extra != FEMPIC2 && wall[wallNum].extra != FEMPIC3)
488                 wall[wallNum].picnum = animwall[animwallNum].tag;
489             else
490             {
491                 wall[wallNum].picnum++;
492                 if (wall[wallNum].picnum == (SCREENBREAK6+3))
493                     wall[wallNum].picnum = SCREENBREAK6;
494             }
495             continue;
496         }
497 
498         if ((wall[wallNum].cstat&16) && G_GetForcefieldPicnum(wallNum)==W_FORCEFIELD)
499         {
500             int const wallTag = animwall[animwallNum].tag;
501 
502             if (wall[wallNum].cstat&254)
503             {
504                 wall[wallNum].xpanning -= wallTag>>10; // sintable[(t+512)&2047]>>12;
505                 wall[wallNum].ypanning -= wallTag>>10; // sintable[t&2047]>>12;
506 
507                 if (wall[wallNum].extra == 1)
508                 {
509                     wall[wallNum].extra = 0;
510                     animwall[animwallNum].tag     = 0;
511                 }
512                 else animwall[animwallNum].tag += 128;
513 
514                 if (animwall[animwallNum].tag < (128 << 4))
515                 {
516                     wall[wallNum].overpicnum = (animwall[animwallNum].tag & 128) ? W_FORCEFIELD : W_FORCEFIELD + 1;
517                 }
518                 else
519                 {
520                     if ((krand()&255) < 32)
521                         animwall[animwallNum].tag = 128<<(krand()&3);
522                     else wall[wallNum].overpicnum = W_FORCEFIELD+1;
523                 }
524             }
525         }
526     }
527 }
528 
G_ActivateWarpElevators(int spriteNum,int warpDir)529 int G_ActivateWarpElevators(int spriteNum, int warpDir)
530 {
531     bssize_t i;
532     int const sectNum = sprite[spriteNum].sectnum;
533 
534     for (SPRITES_OF(STAT_EFFECTOR, i))
535         if (SLT(i) == SE_17_WARP_ELEVATOR && SHT(i) == sprite[spriteNum].hitag)
536         {
537             if (klabs(sector[sectNum].floorz - actor[spriteNum].t_data[2]) > SP(i) ||
538                     sector[SECT(i)].hitag == sector[sectNum].hitag - warpDir)
539                 break;
540         }
541 
542     if (i == -1)
543         return 1; // No find
544 
545     A_PlaySound(warpDir ? ELEVATOR_ON : ELEVATOR_OFF, spriteNum);
546 
547     for (SPRITES_OF(STAT_EFFECTOR, i))
548         if (SLT(i) == SE_17_WARP_ELEVATOR && SHT(i) == sprite[spriteNum].hitag)
549             T1(i) = T2(i) = warpDir; //Make all check warp
550 
551     return 0;
552 }
553 
G_OperateSectors(int sectNum,int spriteNum)554 void G_OperateSectors(int sectNum, int spriteNum)
555 {
556     int32_t j=0;
557     int32_t i;
558     sectortype *const pSector = &sector[sectNum];
559 
560     switch (pSector->lotag & (uint16_t)~49152u)
561     {
562     case ST_30_ROTATE_RISE_BRIDGE:
563         j = sector[sectNum].hitag;
564 
565         if (E_SpriteIsValid(j))
566         {
567             if (actor[j].tempang == 0 || actor[j].tempang == 256)
568                 A_CallSound(sectNum,spriteNum);
569             sprite[j].extra = (sprite[j].extra == 1) ? 3 : 1;
570         }
571         break;
572 
573     case ST_31_TWO_WAY_TRAIN:
574         j = sector[sectNum].hitag;
575 
576         if (E_SpriteIsValid(j))
577         {
578             if (actor[j].t_data[4] == 0)
579                 actor[j].t_data[4] = 1;
580 
581             A_CallSound(sectNum,spriteNum);
582         }
583         break;
584 
585     case ST_26_SPLITTING_ST_DOOR: //The split doors
586         if (GetAnimationGoal(&pSector->ceilingz) == -1) //if the door has stopped
587         {
588             g_haltSoundHack = 1;
589             pSector->lotag &= 0xFF00u;
590             pSector->lotag |= ST_22_SPLITTING_DOOR;
591             G_OperateSectors(sectNum,spriteNum);
592             pSector->lotag &= 0xFF00u;
593             pSector->lotag |= ST_9_SLIDING_ST_DOOR;
594             G_OperateSectors(sectNum,spriteNum);
595             pSector->lotag &= 0xFF00u;
596             pSector->lotag |= ST_26_SPLITTING_ST_DOOR;
597         }
598         return;
599 
600     case ST_9_SLIDING_ST_DOOR:
601     {
602         int const startWall = pSector->wallptr;
603         int const endWall   = startWall + pSector->wallnum - 1;
604         int const doorSpeed = pSector->extra >> 4;
605 
606         //first find center point by averaging all points
607 
608         vec2_t vect = { 0, 0 };
609 
610         for (i = startWall; i <= endWall; i++)
611         {
612             vect.x += wall[i].x;
613             vect.y += wall[i].y;
614         }
615 
616         vect.x = tabledivide32_noinline(vect.x, (endWall-startWall+1));
617         vect.y = tabledivide32_noinline(vect.y, (endWall-startWall+1));
618 
619         //find any points with either same x or same y coordinate
620         //  as center (dax, day) - should be 2 points found.
621 
622         int wallfind[2] = { -1, -1 };
623 
624         for (i = startWall; i <= endWall; i++)
625         {
626             if (wall[i].x == vect.x || wall[i].y == vect.y)
627             {
628                 if (wallfind[0] == -1)
629                     wallfind[0] = i;
630                 else
631                     wallfind[1] = i;
632             }
633         }
634 
635         if (wallfind[1] == -1)
636             return;
637 
638         for (j=0; j<2; j++)
639         {
640             int const foundWall = wallfind[j];
641 
642             i = foundWall - 1;
643 
644             if (i < startWall)
645                 i = endWall;
646 
647             vec2_t vect2 = { ((wall[i].x + wall[wall[foundWall].point2].x) >> 1) - wall[foundWall].x,
648                              ((wall[i].y + wall[wall[foundWall].point2].y) >> 1) - wall[foundWall].y };
649 
650             if (wall[foundWall].x == vect.x && wall[foundWall].y == vect.y)
651             {
652                 //find what direction door should open by averaging the
653                 //  2 neighboring points of wallfind[0] & wallfind[1].
654                 if (vect2.x != 0)
655                 {
656                     vect2.x = wall[wall[wall[foundWall].point2].point2].x;
657                     vect2.x -= wall[wall[foundWall].point2].x;
658                     SetAnimation(sectNum, &wall[foundWall].x, wall[foundWall].x + vect2.x, doorSpeed);
659                     SetAnimation(sectNum, &wall[i].x, wall[i].x + vect2.x, doorSpeed);
660                     SetAnimation(sectNum, &wall[wall[foundWall].point2].x, wall[wall[foundWall].point2].x + vect2.x, doorSpeed);
661                     A_CallSound(sectNum, spriteNum);
662                 }
663                 else if (vect2.y != 0)
664                 {
665                     vect2.y = wall[wall[wall[foundWall].point2].point2].y;
666                     vect2.y -= wall[wall[foundWall].point2].y;
667                     SetAnimation(sectNum, &wall[foundWall].y, wall[foundWall].y + vect2.y, doorSpeed);
668                     SetAnimation(sectNum, &wall[i].y, wall[i].y + vect2.y, doorSpeed);
669                     SetAnimation(sectNum, &wall[wall[foundWall].point2].y, wall[wall[foundWall].point2].y + vect2.y, doorSpeed);
670                     A_CallSound(sectNum, spriteNum);
671                 }
672             }
673             else
674             {
675                 if (vect2.x != 0)
676                 {
677                     SetAnimation(sectNum, &wall[foundWall].x, vect.x, doorSpeed);
678                     SetAnimation(sectNum, &wall[i].x, vect.x + vect2.x, doorSpeed);
679                     SetAnimation(sectNum, &wall[wall[foundWall].point2].x, vect.x + vect2.x, doorSpeed);
680                     A_CallSound(sectNum, spriteNum);
681                 }
682                 else if (vect2.y != 0)
683                 {
684                     SetAnimation(sectNum, &wall[foundWall].y, vect.y, doorSpeed);
685                     SetAnimation(sectNum, &wall[i].y, vect.y + vect2.y, doorSpeed);
686                     SetAnimation(sectNum, &wall[wall[foundWall].point2].y, vect.y + vect2.y, doorSpeed);
687                     A_CallSound(sectNum, spriteNum);
688                 }
689             }
690         }
691     }
692     return;
693 
694     case ST_15_WARP_ELEVATOR://Warping elevators
695 
696         if (sprite[spriteNum].picnum != APLAYER)
697             return;
698 
699         for (SPRITES_OF_SECT(sectNum, i))
700             if (PN(i)==SECTOREFFECTOR && SLT(i) == SE_17_WARP_ELEVATOR)
701                 break;
702 
703         if (i < 0)
704             return;
705 
706         if (sprite[spriteNum].sectnum == sectNum)
707         {
708             if (G_ActivateWarpElevators(i,-1))
709                 G_ActivateWarpElevators(i,1);
710             else if (G_ActivateWarpElevators(i,1))
711                 G_ActivateWarpElevators(i,-1);
712         }
713         else
714         {
715             if (pSector->floorz > SZ(i))
716                 G_ActivateWarpElevators(i,-1);
717             else
718                 G_ActivateWarpElevators(i,1);
719         }
720 
721         return;
722 
723     case ST_16_PLATFORM_DOWN:
724     case ST_17_PLATFORM_UP:
725 
726         i = GetAnimationGoal(&pSector->floorz);
727 
728         if (i == -1)
729         {
730             i = nextsectorneighborz(sectNum,pSector->floorz,1,1);
731             if (i == -1)
732             {
733                 i = nextsectorneighborz(sectNum,pSector->floorz,1,-1);
734                 if (i == -1)
735                 {
736                     OSD_Printf("ST_16_PLATFORM_DOWN/ST_17_PLATFORM_UP: bad neighbor for sector %d!\n", sectNum);
737                     return;
738                 }
739                 j = sector[i].floorz;
740                 SetAnimation(sectNum,&pSector->floorz,j,pSector->extra);
741             }
742             else
743             {
744                 j = sector[i].floorz;
745                 SetAnimation(sectNum,&pSector->floorz,j,pSector->extra);
746             }
747             A_CallSound(sectNum,spriteNum);
748         }
749 
750         return;
751 
752     case ST_18_ELEVATOR_DOWN:
753     case ST_19_ELEVATOR_UP:
754 
755         i = GetAnimationGoal(&pSector->floorz);
756 
757         if (i==-1)
758         {
759             i = nextsectorneighborz(sectNum, pSector->floorz, 1, -1);
760 
761             if (i == -1)
762                 i = nextsectorneighborz(sectNum, pSector->floorz, 1, 1);
763 
764             if (i == -1)
765             {
766                 OSD_Printf("ST_18_ELEVATOR_DOWN/ST_19_ELEVATOR_UP: bad neighbor for sector %d!\n", sectNum);
767                 return;
768             }
769 
770             j = sector[i].floorz;
771 
772             int const sectorExtra = pSector->extra;
773             int const zDiff       = pSector->ceilingz - pSector->floorz;
774 
775             SetAnimation(sectNum, &pSector->floorz, j, sectorExtra);
776             SetAnimation(sectNum, &pSector->ceilingz, j + zDiff, sectorExtra);
777             A_CallSound(sectNum, spriteNum);
778         }
779         return;
780 
781     case ST_29_TEETH_DOOR:
782 
783         for (SPRITES_OF(STAT_EFFECTOR, i))
784             if (SLT(i) == SE_22_TEETH_DOOR && SHT(i) == pSector->hitag)
785             {
786                 sector[SECT(i)].extra = -sector[SECT(i)].extra;
787 
788                 T1(i) = sectNum;
789                 T2(i) = 1;
790             }
791 
792         A_CallSound(sectNum, spriteNum);
793 
794         pSector->lotag ^= 0x8000u;
795 
796         if (pSector->lotag & 0x8000u)
797         {
798             j = nextsectorneighborz(sectNum,pSector->ceilingz,-1,-1);
799             if (j == -1) j = nextsectorneighborz(sectNum,pSector->ceilingz,1,1);
800             if (j == -1)
801             {
802                 OSD_Printf("ST_29_TEETH_DOOR: bad neighbor for sector %d!\n", sectNum);
803                 return;
804             }
805             j = sector[j].ceilingz;
806         }
807         else
808         {
809             j = nextsectorneighborz(sectNum,pSector->ceilingz,1,1);
810             if (j == -1) j = nextsectorneighborz(sectNum,pSector->ceilingz,-1,-1);
811             if (j == -1)
812             {
813                 OSD_Printf("ST_29_TEETH_DOOR: bad neighbor for sector %d!\n", sectNum);
814                 return;
815             }
816             j = sector[j].floorz;
817         }
818 
819         SetAnimation(sectNum,&pSector->ceilingz,j,pSector->extra);
820 
821         return;
822 
823     case ST_20_CEILING_DOOR:
824 REDODOOR:
825 
826         if (pSector->lotag & 0x8000u)
827         {
828             for (SPRITES_OF_SECT(sectNum, i))
829                 if (sprite[i].statnum == STAT_EFFECTOR && SLT(i)==SE_9_DOWN_OPEN_DOOR_LIGHTS)
830                 {
831                     j = SZ(i);
832                     break;
833                 }
834 
835             if (i==-1)
836                 j = pSector->floorz;
837         }
838         else
839         {
840             j = nextsectorneighborz(sectNum,pSector->ceilingz,-1,-1);
841 
842             if (j >= 0) j = sector[j].ceilingz;
843             else
844             {
845                 pSector->lotag |= 32768u;
846                 goto REDODOOR;
847             }
848         }
849 
850         pSector->lotag ^= 0x8000u;
851 
852         SetAnimation(sectNum,&pSector->ceilingz,j,pSector->extra);
853         A_CallSound(sectNum,spriteNum);
854 
855         return;
856 
857     case ST_21_FLOOR_DOOR:
858         i = GetAnimationGoal(&pSector->floorz);
859         if (i >= 0)
860         {
861             if (g_animateGoal[sectNum] == pSector->ceilingz)
862             {
863                 j = nextsectorneighborz(sectNum, pSector->ceilingz, 1, 1);
864                 if (j == -1)
865                 {
866                     OSD_Printf("ST_21_FLOOR_DOOR: bad neighbor for sector %d!\n", sectNum);
867                     return;
868                 }
869                 g_animateGoal[i] = sector[j].floorz;
870             }
871             else g_animateGoal[i] = pSector->ceilingz;
872         }
873         else
874         {
875             if (pSector->ceilingz == pSector->floorz)
876             {
877                 i = nextsectorneighborz(sectNum, pSector->ceilingz, 1, 1);
878                 if (i == -1)
879                 {
880                     OSD_Printf("ST_21_FLOOR_DOOR: bad neighbor for sector %d!\n", sectNum);
881                     return;
882                 }
883                 j = sector[i].floorz;
884             }
885             else j = pSector->ceilingz;
886 
887             pSector->lotag ^= 0x8000u;
888 
889             if (SetAnimation(sectNum,&pSector->floorz,j,pSector->extra) >= 0)
890                 A_CallSound(sectNum,spriteNum);
891         }
892         return;
893 
894     case ST_22_SPLITTING_DOOR:
895 
896         if (pSector->lotag & 0x8000u)
897         {
898             int const q = (pSector->ceilingz + pSector->floorz) >> 1;
899             SetAnimation(sectNum, &pSector->floorz, q, pSector->extra);
900             SetAnimation(sectNum, &pSector->ceilingz, q, pSector->extra);
901         }
902         else
903         {
904             int const floorNeighbor   = nextsectorneighborz(sectNum, pSector->floorz, 1, 1);
905             int const ceilingNeighbor = nextsectorneighborz(sectNum, pSector->ceilingz, -1, -1);
906 
907             if (floorNeighbor>=0 && ceilingNeighbor>=0)
908             {
909                 SetAnimation(sectNum, &pSector->floorz, sector[floorNeighbor].floorz, pSector->extra);
910                 SetAnimation(sectNum, &pSector->ceilingz, sector[ceilingNeighbor].ceilingz, pSector->extra);
911             }
912             else
913             {
914                 OSD_Printf("ST_22_SPLITTING_DOOR: bad neighbor for sector %d; floor neighbor=%d, ceiling neighbor=%d!\n", sectNum, floorNeighbor, ceilingNeighbor);
915                 pSector->lotag ^= 0x8000u;
916             }
917         }
918 
919         pSector->lotag ^= 0x8000u;
920 
921         A_CallSound(sectNum,spriteNum);
922 
923         return;
924 
925     case ST_23_SWINGING_DOOR: //Swingdoor
926         {
927             j = -1;
928 
929             for (SPRITES_OF(STAT_EFFECTOR, i))
930                 if (SLT(i) == SE_11_SWINGING_DOOR && SECT(i) == sectNum && !T5(i))
931                 {
932                     j = i;
933                     break;
934                 }
935 
936             if (i < 0)
937                 return;
938 
939             uint16_t const tag = sector[SECT(i)].lotag & 0x8000u;
940 
941             if (j >= 0)
942             {
943                 for (SPRITES_OF(STAT_EFFECTOR, i))
944                     if (tag == (sector[SECT(i)].lotag & 0x8000u) && SLT(i) == SE_11_SWINGING_DOOR && sprite[j].hitag == SHT(i) && (T5(i)||T6(i)))
945                         return;
946 
947                 int soundPlayed = 0;
948 
949                 for (SPRITES_OF(STAT_EFFECTOR, i))
950                 {
951                     if (tag == (sector[SECT(i)].lotag & 0x8000u) && SLT(i) == SE_11_SWINGING_DOOR && sprite[j].hitag == SHT(i))
952                     {
953                         if (sector[SECT(i)].lotag & 0x8000u) sector[SECT(i)].lotag &= 0x7fff;
954                         else sector[SECT(i)].lotag |= 0x8000u;
955 
956                         T5(i) = 1;
957                         T4(i) = -T4(i);
958 
959                         if (!soundPlayed)
960                         {
961                             A_CallSound(sectNum, i);
962                             soundPlayed = 1;
963                         }
964                     }
965                 }
966             }
967         }
968         return;
969 
970     case ST_25_SLIDING_DOOR: //Subway type sliding doors
971 
972         for (SPRITES_OF(STAT_EFFECTOR, j))
973             if (sprite[j].lotag == SE_15_SLIDING_DOOR && sprite[j].sectnum == sectNum)
974                 break; //Found the sectoreffector.
975 
976         if (j < 0)
977             return;
978 
979         for (SPRITES_OF(STAT_EFFECTOR, i))
980             if (SHT(i)==sprite[j].hitag)
981             {
982                 if (SLT(i) == SE_15_SLIDING_DOOR)
983                 {
984                     sector[SECT(i)].lotag ^= 0x8000u; // Toggle the open or close
985                     SA(i) += 1024;
986 
987                     if (T5(i))
988                         A_CallSound(SECT(i),i);
989                     A_CallSound(SECT(i),i);
990 
991                     T5(i) = (sector[SECT(i)].lotag & 0x8000u) ? 1 : 2;
992                 }
993             }
994 
995         return;
996 
997     case ST_27_STRETCH_BRIDGE:  //Extended bridge
998 
999         for (SPRITES_OF(STAT_EFFECTOR, j))
1000             if ((sprite[j].lotag&0xff)==SE_20_STRETCH_BRIDGE && sprite[j].sectnum == sectNum)  //Bridge
1001             {
1002                 sector[sectNum].lotag ^= 0x8000u;
1003                 // Highest bit now set means we're opening.
1004 
1005                 actor[j].t_data[0] = (sector[sectNum].lotag & 0x8000u) ? 1 : 2;
1006                 A_CallSound(sectNum,spriteNum);
1007                 break;
1008             }
1009 
1010         return;
1011 
1012     case ST_28_DROP_FLOOR:
1013         //activate the rest of them
1014 
1015         for (SPRITES_OF_SECT(sectNum, j))
1016             if (sprite[j].statnum==STAT_EFFECTOR && (sprite[j].lotag&0xff)==SE_21_DROP_FLOOR)
1017                 break;
1018 
1019         if (j >= 0)  // PK: The matching SE21 might have gone, see SE_21_KILLIT in actors.c
1020         {
1021             j = sprite[j].hitag;
1022 
1023             for (bssize_t SPRITES_OF(STAT_EFFECTOR, l))
1024             {
1025                 if ((sprite[l].lotag&0xff)==SE_21_DROP_FLOOR && !actor[l].t_data[0] &&
1026                         sprite[l].hitag == j)
1027                     actor[l].t_data[0] = 1;
1028             }
1029 
1030             A_CallSound(sectNum,spriteNum);
1031         }
1032 
1033         return;
1034     }
1035 }
1036 
G_OperateRespawns(int lotag)1037 void G_OperateRespawns(int lotag)
1038 {
1039     for (bssize_t nextSprite, SPRITES_OF_STAT_SAFE(STAT_FX, spriteNum, nextSprite))
1040     {
1041         auto const pSprite = &sprite[spriteNum];
1042 
1043         if (pSprite->lotag == lotag && pSprite->picnum == RESPAWN)
1044         {
1045             if (!ud.monsters_off || !A_CheckEnemyTile(pSprite->hitag))
1046             {
1047 #ifndef EDUKE32_STANDALONE
1048                 if (!FURY)
1049                 {
1050                     int const j = A_Spawn(spriteNum, TRANSPORTERSTAR);
1051                     sprite[j].z -= ZOFFSET5;
1052                 }
1053 #endif
1054                 // Just a way to killit (see G_MoveFX(): RESPAWN__STATIC)
1055                 pSprite->extra = 66-12;
1056             }
1057         }
1058     }
1059 }
1060 
G_OperateActivators(int lotag,int playerNum)1061 void G_OperateActivators(int lotag, int playerNum)
1062 {
1063     for (bssize_t spriteNum=g_cyclerCnt-1; spriteNum>=0; spriteNum--)
1064     {
1065         int16_t *const pCycler = &g_cyclers[spriteNum][0];
1066 
1067         if (pCycler[4] == lotag)
1068         {
1069             pCycler[5]                      = !pCycler[5];
1070             sector[pCycler[0]].floorshade   = pCycler[3];
1071             sector[pCycler[0]].ceilingshade = pCycler[3];
1072             walltype *pWall                 = &wall[sector[pCycler[0]].wallptr];
1073 
1074             for (int j = sector[pCycler[0]].wallnum; j > 0; j--, pWall++)
1075                 pWall->shade = pCycler[3];
1076         }
1077     }
1078 
1079     int soundPlayed = -1;
1080 
1081     for (bssize_t nextSprite, SPRITES_OF_STAT_SAFE(STAT_ACTIVATOR, spriteNum, nextSprite))
1082     {
1083         if (sprite[spriteNum].lotag == lotag)
1084         {
1085             if (sprite[spriteNum].picnum == ACTIVATORLOCKED)
1086             {
1087                 sector[SECT(spriteNum)].lotag ^= 16384;
1088 
1089                 if (playerNum >= 0 && playerNum < ud.multimode)
1090                     P_DoQuote((sector[SECT(spriteNum)].lotag & 16384) ? QUOTE_LOCKED : QUOTE_UNLOCKED, g_player[playerNum].ps);
1091             }
1092             else
1093             {
1094                 switch (SHT(spriteNum))
1095                 {
1096                     case 1:
1097                         if (sector[SECT(spriteNum)].floorz != sector[SECT(spriteNum)].ceilingz)
1098                             continue;
1099                         break;
1100                     case 2:
1101                         if (sector[SECT(spriteNum)].floorz == sector[SECT(spriteNum)].ceilingz)
1102                             continue;
1103                         break;
1104                 }
1105 
1106                 // ST_2_UNDERWATER
1107                 if (sector[sprite[spriteNum].sectnum].lotag < 3)
1108                 {
1109                     for (bssize_t SPRITES_OF_SECT(sprite[spriteNum].sectnum, foundSprite))
1110                     {
1111                         if (sprite[foundSprite].statnum == STAT_EFFECTOR)
1112                         {
1113                             switch (sprite[foundSprite].lotag)
1114                             {
1115                                 case SE_36_PROJ_SHOOTER:
1116                                 case SE_31_FLOOR_RISE_FALL:
1117                                 case SE_32_CEILING_RISE_FALL:
1118                                 case SE_18_INCREMENTAL_SECTOR_RISE_FALL:
1119                                     actor[foundSprite].t_data[0] = 1 - actor[foundSprite].t_data[0];
1120                                     A_CallSound(SECT(spriteNum), foundSprite);
1121                                     break;
1122                             }
1123                         }
1124                     }
1125                 }
1126 
1127                 if (soundPlayed == -1 && (sector[SECT(spriteNum)].lotag&0xff) == ST_22_SPLITTING_DOOR)
1128                     soundPlayed = A_CallSound(SECT(spriteNum),spriteNum);
1129 
1130                 G_OperateSectors(SECT(spriteNum),spriteNum);
1131             }
1132         }
1133     }
1134 
1135     G_OperateRespawns(lotag);
1136 }
1137 
G_OperateMasterSwitches(int lotag)1138 void G_OperateMasterSwitches(int lotag)
1139 {
1140     for (bssize_t SPRITES_OF(STAT_STANDABLE, i))
1141         if (PN(i) == MASTERSWITCH && SLT(i) == lotag && SP(i) == 0)
1142             SP(i) = 1;
1143 }
1144 
G_OperateForceFields(int spriteNum,int wallTag)1145 void G_OperateForceFields(int spriteNum, int wallTag)
1146 {
1147     for (bssize_t animwallNum = 0; animwallNum < g_animWallCnt; ++animwallNum)
1148     {
1149         int const wallNum = animwall[animwallNum].wallnum;
1150 
1151         if ((wallTag == wall[wallNum].lotag || wallTag == -1)
1152             && (G_GetForcefieldPicnum(wallNum) == W_FORCEFIELD || (wall[wallNum].overpicnum == BIGFORCE)))
1153         {
1154             animwall[animwallNum].tag = 0;
1155 
1156             if (wall[wallNum].cstat)
1157             {
1158                 wall[wallNum].cstat = 0;
1159 
1160                 if (spriteNum >= 0 && sprite[spriteNum].picnum == SECTOREFFECTOR && sprite[spriteNum].lotag == SE_30_TWO_WAY_TRAIN)
1161                     wall[wallNum].lotag = 0;
1162             }
1163             else
1164                 wall[wallNum].cstat = FORCEFIELD_CSTAT;
1165         }
1166     }
1167 }
1168 
1169 // List of switches that function like dip (combination lock) switches.
1170 #define DIPSWITCH_LIKE_CASES                                                                                                \
1171     DIPSWITCH__STATIC:                                                                                                      \
1172     case TECHSWITCH__STATIC:                                                                                                \
1173     case ALIENSWITCH__STATIC
1174 
1175 // List of access switches.
1176 #define ACCESSSWITCH_CASES                                                                                                  \
1177     ACCESSSWITCH__STATIC:                                                                                                   \
1178     case ACCESSSWITCH2__STATIC
1179 
1180 // List of switches that don't fit the two preceding categories, and are not
1181 // the MULTISWITCH. 13 cases.
1182 #define REST_SWITCH_CASES                                                                                                   \
1183     DIPSWITCH2__STATIC:                                                                                                     \
1184     case DIPSWITCH3__STATIC:                                                                                                \
1185     case FRANKENSTINESWITCH__STATIC:                                                                                        \
1186     case HANDSWITCH__STATIC:                                                                                                \
1187     case LIGHTSWITCH2__STATIC:                                                                                              \
1188     case LIGHTSWITCH__STATIC:                                                                                               \
1189     case LOCKSWITCH1__STATIC:                                                                                               \
1190     case POWERSWITCH1__STATIC:                                                                                              \
1191     case POWERSWITCH2__STATIC:                                                                                              \
1192     case PULLSWITCH__STATIC:                                                                                                \
1193     case SLOTDOOR__STATIC:                                                                                                  \
1194     case SPACEDOORSWITCH__STATIC:                                                                                           \
1195     case SPACELIGHTSWITCH__STATIC
1196 
1197 // Returns:
1198 //  0: is not a dipswitch-like switch
1199 //  1: is one, off
1200 //  2: is one, on
G_IsLikeDipswitch(int switchPic)1201 static int G_IsLikeDipswitch(int switchPic)
1202 {
1203     for (bssize_t i=0; i<2; i++)
1204         if (switchPic == DIPSWITCH+i || switchPic == TECHSWITCH+i || switchPic == ALIENSWITCH+i)
1205             return 1+i;
1206 
1207     return 0;
1208 }
1209 
1210 // Get base (unpressed) tile number for switch.
G_GetBaseSwitch(int switchPic)1211 static int G_GetBaseSwitch(int switchPic)
1212 {
1213     if (switchPic > MULTISWITCH && switchPic <= MULTISWITCH+3)
1214         return MULTISWITCH;
1215 
1216     return (switchPic == DIPSWITCH + 1      || switchPic == DIPSWITCH2 + 1   || switchPic == DIPSWITCH3 + 1 ||
1217         switchPic == TECHSWITCH + 1         || switchPic == ALIENSWITCH + 1  || switchPic == PULLSWITCH + 1 ||
1218         switchPic == HANDSWITCH + 1         || switchPic == SLOTDOOR + 1     || switchPic == SPACEDOORSWITCH + 1 ||
1219         switchPic == SPACELIGHTSWITCH + 1   || switchPic == LIGHTSWITCH + 1  || switchPic == LIGHTSWITCH2 + 1 ||
1220         switchPic == FRANKENSTINESWITCH + 1 || switchPic == POWERSWITCH1 + 1 || switchPic == POWERSWITCH2 + 1 ||
1221         switchPic == LOCKSWITCH1 + 1) ?
1222         switchPic-1 : switchPic;
1223 }
1224 
1225 enum { SWITCH_WALL, SWITCH_SPRITE };
1226 
P_ActivateSwitch(int playerNum,int wallOrSprite,int switchType)1227 int P_ActivateSwitch(int playerNum, int wallOrSprite, int switchType)
1228 {
1229     if (wallOrSprite < 0)
1230         return 0;
1231 
1232     vec3_t davector;
1233     int16_t lotag, hitag;
1234     int16_t nSwitchPicnum;
1235     uint8_t nSwitchPal;
1236 
1237     if (switchType == SWITCH_SPRITE) // A wall sprite
1238     {
1239         if (actor[wallOrSprite].lasttransport == ((int32_t) totalclock & UINT8_MAX))
1240             return 0;
1241 
1242         actor[wallOrSprite].lasttransport = ((int32_t) totalclock & UINT8_MAX);
1243 
1244         if (sprite[wallOrSprite].lotag == 0)
1245             return 0;
1246 
1247         lotag         = sprite[wallOrSprite].lotag;
1248         hitag         = sprite[wallOrSprite].hitag;
1249         davector      = sprite[wallOrSprite].pos;
1250         nSwitchPicnum = sprite[wallOrSprite].picnum;
1251         nSwitchPal    = sprite[wallOrSprite].pal;
1252     }
1253     else
1254     {
1255         if (wall[wallOrSprite].lotag == 0)
1256             return 0;
1257 
1258         lotag         = wall[wallOrSprite].lotag;
1259         hitag         = wall[wallOrSprite].hitag;
1260         davector      = { wall[wallOrSprite].x, wall[wallOrSprite].y, g_player[playerNum].ps->pos.z };
1261         nSwitchPicnum = wall[wallOrSprite].picnum;
1262         nSwitchPal    = wall[wallOrSprite].pal;
1263     }
1264 
1265     //    initprintf("P_ActivateSwitch called picnum=%i switchissprite=%i\n",picnum,switchissprite);
1266 
1267     int basePicnum   = G_GetBaseSwitch(nSwitchPicnum);
1268     int correctDips  = 1;
1269     int numDips      = 0;
1270 
1271     switch (DYNAMICTILEMAP(basePicnum))
1272     {
1273     case DIPSWITCH_LIKE_CASES:
1274         break;
1275 
1276     case ACCESSSWITCH_CASES:
1277         if (g_player[playerNum].ps->access_incs == 0)
1278         {
1279             static const int32_t key_switchpal[3]  = { 0, 21, 23 };
1280             static const int32_t need_key_quote[3] = { QUOTE_NEED_BLUE_KEY, QUOTE_NEED_RED_KEY, QUOTE_NEED_YELLOW_KEY };
1281 
1282             for (bssize_t nKeyPal = 0; nKeyPal < 3; nKeyPal++)
1283             {
1284                 if (nSwitchPal == key_switchpal[nKeyPal])
1285                 {
1286                     if (g_player[playerNum].ps->got_access & (1 << nKeyPal))
1287                         g_player[playerNum].ps->access_incs = 1;
1288                     else
1289                         P_DoQuote(need_key_quote[nKeyPal], g_player[playerNum].ps);
1290 
1291                     break;
1292                 }
1293             }
1294 
1295             if (g_player[playerNum].ps->access_incs == 1)
1296             {
1297                 if (switchType == SWITCH_WALL)
1298                     g_player[playerNum].ps->access_wallnum = wallOrSprite;
1299                 else
1300                     g_player[playerNum].ps->access_spritenum = wallOrSprite;
1301             }
1302 
1303             return 0;
1304         }
1305         fallthrough__;
1306     case MULTISWITCH__STATIC:
1307     case REST_SWITCH_CASES:
1308         if (G_CheckActivatorMotion(lotag))
1309             return 0;
1310         break;
1311 
1312     default:
1313         if (CheckDoorTile(nSwitchPicnum) == 0)
1314             return 0;
1315         break;
1316     }
1317 
1318     for (bssize_t SPRITES_OF(STAT_DEFAULT, spriteNum))
1319     {
1320         if (lotag == SLT(spriteNum))
1321         {
1322             // Put the tile number into a variable so later switches don't
1323             // trigger on the result of changes:
1324             int const spritePic = PN(spriteNum);
1325 
1326             if (spritePic >= MULTISWITCH && spritePic <= MULTISWITCH+3)
1327             {
1328                 sprite[spriteNum].picnum++;
1329                 if (sprite[spriteNum].picnum > MULTISWITCH+3)
1330                     sprite[spriteNum].picnum = MULTISWITCH;
1331             }
1332 
1333             switch (DYNAMICTILEMAP(spritePic))
1334             {
1335             case DIPSWITCH_LIKE_CASES:
1336                 if (switchType == SWITCH_SPRITE && wallOrSprite == spriteNum)
1337                     PN(spriteNum)++;
1338                 else if (SHT(spriteNum) == 0)
1339                     correctDips++;
1340                 numDips++;
1341                 break;
1342 
1343             case ACCESSSWITCH_CASES:
1344             case REST_SWITCH_CASES:
1345                 sprite[spriteNum].picnum++;
1346                 break;
1347 
1348             default:
1349                 if (spritePic <= 0)  // oob safety
1350                     break;
1351 
1352                 switch (DYNAMICTILEMAP(spritePic - 1))
1353                 {
1354                     case DIPSWITCH_LIKE_CASES:
1355                         if (switchType == SWITCH_SPRITE && wallOrSprite == spriteNum)
1356                             PN(spriteNum)--;
1357                         else if (SHT(spriteNum) == 1)
1358                             correctDips++;
1359                         numDips++;
1360                         break;
1361 
1362                     case REST_SWITCH_CASES:
1363                         sprite[spriteNum].picnum--;
1364                         break;
1365                 }
1366                 break;
1367             }
1368         }
1369     }
1370 
1371     for (bssize_t wallNum=numwalls-1; wallNum>=0; wallNum--)
1372     {
1373         if (lotag == wall[wallNum].lotag)
1374         {
1375             if (wall[wallNum].picnum >= MULTISWITCH && wall[wallNum].picnum <= MULTISWITCH+3)
1376             {
1377                 wall[wallNum].picnum++;
1378                 if (wall[wallNum].picnum > MULTISWITCH+3)
1379                     wall[wallNum].picnum = MULTISWITCH;
1380             }
1381 
1382             switch (DYNAMICTILEMAP(wall[wallNum].picnum))
1383             {
1384                 case DIPSWITCH_LIKE_CASES:
1385                     if (switchType == SWITCH_WALL && wallNum == wallOrSprite)
1386                         wall[wallNum].picnum++;
1387                     else if (wall[wallNum].hitag == 0)
1388                         correctDips++;
1389                     numDips++;
1390                     break;
1391 
1392                 case ACCESSSWITCH_CASES:
1393                 case REST_SWITCH_CASES:
1394                     wall[wallNum].picnum++;
1395                     break;
1396 
1397                 default:
1398                     if (wall[wallNum].picnum <= 0)  // oob safety
1399                         break;
1400 
1401                     switch (DYNAMICTILEMAP(wall[wallNum].picnum - 1))
1402                     {
1403                         case DIPSWITCH_LIKE_CASES:
1404                             if (switchType == SWITCH_WALL && wallNum == wallOrSprite)
1405                                 wall[wallNum].picnum--;
1406                             else if (wall[wallNum].hitag == 1)
1407                                 correctDips++;
1408                             numDips++;
1409                             break;
1410 
1411                         case REST_SWITCH_CASES:
1412                             wall[wallNum].picnum--;
1413                             break;
1414                     }
1415                     break;
1416             }
1417         }
1418     }
1419 
1420     if ((uint16_t)lotag == UINT16_MAX)
1421     {
1422         P_EndLevel();
1423         return 1;
1424     }
1425 
1426     basePicnum = G_GetBaseSwitch(nSwitchPicnum);
1427 
1428     switch (DYNAMICTILEMAP(basePicnum))
1429     {
1430         default:
1431             if (CheckDoorTile(nSwitchPicnum) == 0)
1432                 break;
1433             fallthrough__;
1434         case DIPSWITCH_LIKE_CASES:
1435             if (G_IsLikeDipswitch(nSwitchPicnum))
1436             {
1437                 S_PlaySound3D((nSwitchPicnum == ALIENSWITCH || nSwitchPicnum == ALIENSWITCH + 1) ? ALIEN_SWITCH1 : SWITCH_ON,
1438                               (switchType == SWITCH_SPRITE) ? wallOrSprite : g_player[playerNum].ps->i, &davector);
1439 
1440                 if (numDips != correctDips)
1441                     break;
1442 
1443                 S_PlaySound3D(END_OF_LEVEL_WARN, g_player[playerNum].ps->i, &davector);
1444             }
1445             fallthrough__;
1446         case ACCESSSWITCH_CASES:
1447         case MULTISWITCH__STATIC:
1448         case REST_SWITCH_CASES:
1449         {
1450             if (nSwitchPicnum >= MULTISWITCH && nSwitchPicnum <= MULTISWITCH + 3)
1451                 lotag += nSwitchPicnum - MULTISWITCH;
1452 
1453             for (bssize_t SPRITES_OF(STAT_EFFECTOR, spriteNum))
1454             {
1455                 if (sprite[spriteNum].hitag == lotag)
1456                 {
1457                     switch (sprite[spriteNum].lotag)
1458                     {
1459                         case SE_12_LIGHT_SWITCH:
1460                             sector[sprite[spriteNum].sectnum].floorpal = 0;
1461                             actor[spriteNum].t_data[0]++;
1462                             if (actor[spriteNum].t_data[0] == 2)
1463                                 actor[spriteNum].t_data[0]++;
1464                             break;
1465 
1466                         case SE_24_CONVEYOR:
1467                         case SE_34:
1468                         case SE_25_PISTON:
1469                             actor[spriteNum].t_data[4] = !actor[spriteNum].t_data[4];
1470                             P_DoQuote(actor[spriteNum].t_data[4] ? QUOTE_DEACTIVATED : QUOTE_ACTIVATED, g_player[playerNum].ps);
1471                             break;
1472 
1473                         case SE_21_DROP_FLOOR:
1474                             P_DoQuote(QUOTE_ACTIVATED, g_player[screenpeek].ps);
1475                             break;
1476                     }
1477                 }
1478             }
1479 
1480             G_OperateActivators(lotag, playerNum);
1481             G_OperateForceFields(g_player[playerNum].ps->i, lotag);
1482             G_OperateMasterSwitches(lotag);
1483 
1484             if (G_IsLikeDipswitch(nSwitchPicnum))
1485                 return 1;
1486 
1487             if (!hitag && CheckDoorTile(nSwitchPicnum) == 0)
1488                 S_PlaySound3D(SWITCH_ON, (switchType == SWITCH_SPRITE) ? wallOrSprite : g_player[playerNum].ps->i, &davector);
1489             else if (hitag)
1490             {
1491                 if (switchType == SWITCH_SPRITE && (g_sounds[hitag].m & SF_TALK) == 0)
1492                     S_PlaySound3D(hitag, wallOrSprite, &davector);
1493                 else
1494                     A_PlaySound(hitag, g_player[playerNum].ps->i);
1495             }
1496 
1497             return 1;
1498         }
1499     }
1500 
1501     return 0;
1502 }
1503 
G_ActivateBySector(int sectNum,int spriteNum)1504 void G_ActivateBySector(int sectNum, int spriteNum)
1505 {
1506     int activatedSectors = 0;
1507 
1508     for (bssize_t SPRITES_OF_SECT(sectNum, i))
1509         if (PN(i) == ACTIVATOR)
1510         {
1511             G_OperateActivators(SLT(i),-1);
1512             ++activatedSectors;
1513         }
1514 
1515     if (!activatedSectors)
1516         G_OperateSectors(sectNum, spriteNum);
1517 }
1518 
G_BreakWall(int tileNum,int spriteNum,int wallNum)1519 static void G_BreakWall(int tileNum, int spriteNum, int wallNum)
1520 {
1521     wall[wallNum].picnum = tileNum;
1522 #ifndef EDUKE32_STANDALONE
1523     A_PlaySound(VENT_BUST,spriteNum);
1524     A_PlaySound(GLASS_HEAVYBREAK,spriteNum);
1525     A_SpawnWallGlass(spriteNum,wallNum,10);
1526 #else
1527     UNREFERENCED_PARAMETER(spriteNum);
1528 #endif
1529 }
1530 
A_DamageWall_Internal(int spriteNum,int wallNum,const vec3_t & vPos,int weaponNum)1531 void A_DamageWall_Internal(int spriteNum, int wallNum, const vec3_t &vPos, int weaponNum)
1532 {
1533     int16_t sectNum = -1;
1534     walltype *pWall = &wall[wallNum];
1535 
1536     if ((g_tile[pWall->overpicnum].flags & SFLAG_DAMAGEEVENT) || (g_tile[pWall->picnum].flags & SFLAG_DAMAGEEVENT))
1537     {
1538         if (VM_OnEventWithReturn(EVENT_DAMAGEWALL, spriteNum, -1, wallNum) < 0)
1539             return;
1540     }
1541 
1542     if (pWall->overpicnum == MIRROR && pWall->pal != 4 &&
1543         A_CheckSpriteFlags(spriteNum, SFLAG_PROJECTILE) &&
1544         (SpriteProjectile[spriteNum].workslike & PROJECTILE_RPG))
1545     {
1546         if (pWall->nextwall == -1 || wall[pWall->nextwall].pal != 4)
1547         {
1548 #ifndef EDUKE32_STANDALONE
1549             A_SpawnWallGlass(spriteNum, wallNum, 70);
1550             A_PlaySound(GLASS_HEAVYBREAK, spriteNum);
1551 #endif
1552             pWall->cstat &= ~16;
1553             pWall->overpicnum = MIRRORBROKE;
1554             return;
1555         }
1556     }
1557 
1558     if (pWall->overpicnum == MIRROR && pWall->pal != 4)
1559     {
1560         switch (DYNAMICTILEMAP(weaponNum))
1561         {
1562             case RADIUSEXPLOSION__STATIC:
1563             case SEENINE__STATIC:
1564 #ifndef EDUKE32_STANDALONE
1565             case HEAVYHBOMB__STATIC:
1566             case RPG__STATIC:
1567             case HYDRENT__STATIC:
1568             case OOZFILTER__STATIC:
1569             case EXPLODINGBARREL__STATIC:
1570 #endif
1571                 if (pWall->nextwall == -1 || wall[pWall->nextwall].pal != 4)
1572                 {
1573 #ifndef EDUKE32_STANDALONE
1574                     A_SpawnWallGlass(spriteNum, wallNum, 70);
1575                     A_PlaySound(GLASS_HEAVYBREAK, spriteNum);
1576 #endif
1577                     pWall->cstat &= ~16;
1578                     pWall->overpicnum = MIRRORBROKE;
1579                     return;
1580                 }
1581         }
1582     }
1583 
1584     if ((((pWall->cstat & 16) || pWall->overpicnum == BIGFORCE) && pWall->nextsector >= 0) &&
1585         (sector[pWall->nextsector].floorz > vPos.z) &&
1586         (sector[pWall->nextsector].floorz != sector[pWall->nextsector].ceilingz))
1587     {
1588         int const switchPic = G_GetForcefieldPicnum(wallNum);
1589 
1590         switch (DYNAMICTILEMAP(switchPic))
1591         {
1592             case FANSPRITE__STATIC:
1593                 pWall->overpicnum = FANSPRITEBROKE;
1594                 pWall->cstat &= 65535 - 65;
1595                 if (pWall->nextwall >= 0)
1596                 {
1597                     wall[pWall->nextwall].overpicnum = FANSPRITEBROKE;
1598                     wall[pWall->nextwall].cstat &= 65535 - 65;
1599                 }
1600                 A_PlaySound(VENT_BUST, spriteNum);
1601                 A_PlaySound(GLASS_BREAKING, spriteNum);
1602                 return;
1603 
1604 #ifndef EDUKE32_STANDALONE
1605             case W_FORCEFIELD__STATIC:
1606                 pWall->extra = 1;  // tell the forces to animate
1607                 fallthrough__;
1608             case BIGFORCE__STATIC:
1609             {
1610                 updatesector(vPos.x, vPos.y, &sectNum);
1611                 if (sectNum < 0)
1612                     return;
1613 
1614                 int xRepeat = 32;
1615                 int yRepeat = 32;
1616 
1617                 if (weaponNum == -1)
1618                     xRepeat = yRepeat = 8;
1619                 else if (weaponNum == CHAINGUN)
1620                 {
1621                     xRepeat = 16 + sprite[spriteNum].xrepeat;
1622                     yRepeat = 16 + sprite[spriteNum].yrepeat;
1623                 }
1624 
1625                 int const i = A_InsertSprite(sectNum, vPos.x, vPos.y, vPos.z, FORCERIPPLE, -127, xRepeat, yRepeat, 0,
1626                                    0, 0, spriteNum, 5);
1627 
1628                 CS(i) |= 18 + 128;
1629                 SA(i) = getangle(pWall->x - wall[pWall->point2].x, pWall->y - wall[pWall->point2].y) - 512;
1630 
1631                 A_PlaySound(SOMETHINGHITFORCE, i);
1632             }
1633                 return;
1634 
1635             case GLASS__STATIC:
1636                 updatesector(vPos.x, vPos.y, &sectNum);
1637                 if (sectNum < 0)
1638                     return;
1639                 pWall->overpicnum = GLASS2;
1640                 A_SpawnWallGlass(spriteNum, wallNum, 10);
1641                 pWall->cstat = 0;
1642 
1643                 if (pWall->nextwall >= 0)
1644                     wall[pWall->nextwall].cstat = 0;
1645 
1646                 {
1647                     int const i = A_InsertSprite(sectNum, vPos.x, vPos.y, vPos.z, SECTOREFFECTOR, 0, 0, 0,
1648                         fix16_to_int(g_player[0].ps->q16ang), 0, 0, spriteNum, 3);
1649                     SLT(i) = 128;
1650                     T2(i)  = 5;
1651                     T3(i)  = wallNum;
1652                     A_PlaySound(GLASS_BREAKING, i);
1653                 }
1654                 return;
1655 
1656             case STAINGLASS1__STATIC:
1657                 updatesector(vPos.x, vPos.y, &sectNum);
1658                 if (sectNum < 0)
1659                     return;
1660                 A_SpawnRandomGlass(spriteNum, wallNum, 80);
1661                 pWall->cstat = 0;
1662                 if (pWall->nextwall >= 0)
1663                     wall[pWall->nextwall].cstat = 0;
1664                 A_PlaySound(VENT_BUST, spriteNum);
1665                 A_PlaySound(GLASS_BREAKING, spriteNum);
1666                 return;
1667 #endif
1668         }
1669     }
1670 
1671     switch (DYNAMICTILEMAP(pWall->picnum))
1672     {
1673         case COLAMACHINE__STATIC:
1674         case VENDMACHINE__STATIC:
1675             G_BreakWall(pWall->picnum + 2, spriteNum, wallNum);
1676             A_PlaySound(VENT_BUST, spriteNum);
1677             return;
1678 
1679         case OJ__STATIC:
1680         case FEMPIC2__STATIC:
1681         case FEMPIC3__STATIC:
1682 
1683         case SCREENBREAK6__STATIC:
1684         case SCREENBREAK7__STATIC:
1685         case SCREENBREAK8__STATIC:
1686 
1687         case SCREENBREAK1__STATIC:
1688         case SCREENBREAK2__STATIC:
1689         case SCREENBREAK3__STATIC:
1690         case SCREENBREAK4__STATIC:
1691         case SCREENBREAK5__STATIC:
1692 
1693         case SCREENBREAK9__STATIC:
1694         case SCREENBREAK10__STATIC:
1695         case SCREENBREAK11__STATIC:
1696         case SCREENBREAK12__STATIC:
1697         case SCREENBREAK13__STATIC:
1698         case SCREENBREAK14__STATIC:
1699         case SCREENBREAK15__STATIC:
1700         case SCREENBREAK16__STATIC:
1701         case SCREENBREAK17__STATIC:
1702         case SCREENBREAK18__STATIC:
1703         case SCREENBREAK19__STATIC:
1704         case BORNTOBEWILDSCREEN__STATIC:
1705 #ifndef EDUKE32_STANDALONE
1706             A_SpawnWallGlass(spriteNum, wallNum, 30);
1707             A_PlaySound(GLASS_HEAVYBREAK, spriteNum);
1708 #endif
1709             pWall->picnum = W_SCREENBREAK + (krand() % 3);
1710             return;
1711 
1712         case W_TECHWALL5__STATIC:
1713         case W_TECHWALL6__STATIC:
1714         case W_TECHWALL7__STATIC:
1715         case W_TECHWALL8__STATIC:
1716         case W_TECHWALL9__STATIC:
1717             G_BreakWall(pWall->picnum + 1, spriteNum, wallNum);
1718             return;
1719 
1720         case W_MILKSHELF__STATIC:
1721             G_BreakWall(W_MILKSHELFBROKE, spriteNum, wallNum);
1722             return;
1723 
1724         case W_TECHWALL10__STATIC:
1725             G_BreakWall(W_HITTECHWALL10, spriteNum, wallNum);
1726             return;
1727 
1728         case W_TECHWALL1__STATIC:
1729         case W_TECHWALL11__STATIC:
1730         case W_TECHWALL12__STATIC:
1731         case W_TECHWALL13__STATIC:
1732         case W_TECHWALL14__STATIC:
1733             G_BreakWall(W_HITTECHWALL1, spriteNum, wallNum);
1734             return;
1735 
1736         case W_TECHWALL15__STATIC:
1737             G_BreakWall(W_HITTECHWALL15, spriteNum, wallNum);
1738             return;
1739 
1740         case W_TECHWALL16__STATIC:
1741             G_BreakWall(W_HITTECHWALL16, spriteNum, wallNum);
1742             return;
1743 
1744         case W_TECHWALL2__STATIC:
1745             G_BreakWall(W_HITTECHWALL2, spriteNum, wallNum);
1746             return;
1747 
1748         case W_TECHWALL3__STATIC:
1749             G_BreakWall(W_HITTECHWALL3, spriteNum, wallNum);
1750             return;
1751 
1752         case W_TECHWALL4__STATIC:
1753             G_BreakWall(W_HITTECHWALL4, spriteNum, wallNum);
1754             return;
1755 
1756         case ATM__STATIC:
1757             pWall->picnum = ATMBROKE;
1758             A_SpawnMultiple(spriteNum, MONEY, 1 + (krand() & 7));
1759             A_PlaySound(GLASS_HEAVYBREAK, spriteNum);
1760             break;
1761 
1762         case WALLLIGHT1__STATIC:
1763         case WALLLIGHT2__STATIC:
1764         case WALLLIGHT3__STATIC:
1765         case WALLLIGHT4__STATIC:
1766         case TECHLIGHT2__STATIC:
1767         case TECHLIGHT4__STATIC:
1768         {
1769 #ifndef EDUKE32_STANDALONE
1770             A_PlaySound(rnd(128) ? GLASS_HEAVYBREAK : GLASS_BREAKING, spriteNum);
1771             A_SpawnWallGlass(spriteNum, wallNum, 30);
1772 #endif
1773 
1774             if (pWall->picnum == WALLLIGHT1)
1775                 pWall->picnum = WALLLIGHTBUST1;
1776 
1777             if (pWall->picnum == WALLLIGHT2)
1778                 pWall->picnum = WALLLIGHTBUST2;
1779 
1780             if (pWall->picnum == WALLLIGHT3)
1781                 pWall->picnum = WALLLIGHTBUST3;
1782 
1783             if (pWall->picnum == WALLLIGHT4)
1784                 pWall->picnum = WALLLIGHTBUST4;
1785 
1786             if (pWall->picnum == TECHLIGHT2)
1787                 pWall->picnum = TECHLIGHTBUST2;
1788 
1789             if (pWall->picnum == TECHLIGHT4)
1790                 pWall->picnum = TECHLIGHTBUST4;
1791 
1792             if (pWall->lotag == 0)
1793                 return;
1794 
1795             sectNum = pWall->nextsector;
1796 
1797             if (sectNum < 0)
1798                 return;
1799 
1800             int darkestWall = 0;
1801 
1802             pWall = &wall[sector[sectNum].wallptr];
1803 
1804             for (bssize_t i = sector[sectNum].wallnum; i > 0; i--, pWall++)
1805                 if (pWall->shade > darkestWall)
1806                     darkestWall = pWall->shade;
1807 
1808             int const random = krand() & 1;
1809 
1810             for (bssize_t SPRITES_OF(STAT_EFFECTOR, i))
1811                 if (SHT(i) == wall[wallNum].lotag && SLT(i) == SE_3_RANDOM_LIGHTS_AFTER_SHOT_OUT)
1812                 {
1813                     T3(i) = random;
1814                     T4(i) = darkestWall;
1815                     T5(i) = 1;
1816                 }
1817 
1818             break;
1819         }
1820     }
1821 }
1822 
A_DamageWall(int spriteNum,int wallNum,const vec3_t & vPos,int weaponNum)1823 void A_DamageWall(int spriteNum, int wallNum, const vec3_t &vPos, int weaponNum)
1824 {
1825     ud.returnvar[0] = -1;
1826     A_DamageWall_Internal(spriteNum, wallNum, vPos, weaponNum);
1827 }
1828 
Sect_DamageFloor_Internal(int const spriteNum,int const sectNum)1829 void Sect_DamageFloor_Internal(int const spriteNum, int const sectNum)
1830 {
1831     int16_t tileNum = sector[sectNum].floorpicnum;
1832     if (g_tile[tileNum].flags & SFLAG_DAMAGEEVENT)
1833     {
1834         if (VM_OnEventWithReturn(EVENT_DAMAGEFLOOR, spriteNum, -1, sectNum) < 0)
1835             return;
1836     }
1837 
1838     // NOTE: pass RETURN in the dist argument, too.
1839     int const     RETURN_in = 131072 + sectNum;
1840     /* int32_t const returnValue = */ VM_OnEvent(EVENT_DAMAGEHPLANE, -1, -1, RETURN_in, RETURN_in);
1841 
1842 #if 0
1843     // No hard-coded floor damage effects.
1844     if (returnValue < 0)
1845         return;
1846 #endif
1847 }
1848 
Sect_DamageFloor(int const spriteNum,int const sectNum)1849 void Sect_DamageFloor(int const spriteNum, int const sectNum)
1850 {
1851     ud.returnvar[0] = -1;
1852     Sect_DamageFloor_Internal(spriteNum, sectNum);
1853 }
1854 
Sect_DamageCeiling_Internal(int const spriteNum,int const sectNum)1855 void Sect_DamageCeiling_Internal(int const spriteNum, int const sectNum)
1856 {
1857     int16_t tileNum = sector[sectNum].ceilingpicnum;
1858     if (g_tile[tileNum].flags & SFLAG_DAMAGEEVENT)
1859     {
1860         if (VM_OnEventWithReturn(EVENT_DAMAGECEILING, spriteNum, -1, sectNum) < 0)
1861             return;
1862     }
1863 
1864     // NOTE: pass RETURN in the dist argument, too.
1865     int const     RETURN_in = 65536 + sectNum;
1866     int32_t const returnValue = VM_OnEvent(EVENT_DAMAGEHPLANE, -1, -1, RETURN_in, RETURN_in);
1867 
1868     if (returnValue < 0)
1869         return;
1870 
1871 #ifndef EDUKE32_STANDALONE
1872     int16_t * const pPicnum = &sector[sectNum].ceilingpicnum;
1873 #endif
1874 
1875     if (returnValue == (1<<20))
1876     {
1877         // Execute the hard-coded stuff without changing picnum (expected to
1878         // have been done by the event).
1879         goto GLASSBREAK_CODE;
1880     }
1881 
1882 #ifndef EDUKE32_STANDALONE
1883     switch (DYNAMICTILEMAP(*pPicnum))
1884     {
1885         case WALLLIGHT1__STATIC: *pPicnum = WALLLIGHTBUST1; goto GLASSBREAK_CODE;
1886         case WALLLIGHT2__STATIC: *pPicnum = WALLLIGHTBUST2; goto GLASSBREAK_CODE;
1887         case WALLLIGHT3__STATIC: *pPicnum = WALLLIGHTBUST3; goto GLASSBREAK_CODE;
1888         case WALLLIGHT4__STATIC: *pPicnum = WALLLIGHTBUST4; goto GLASSBREAK_CODE;
1889         case TECHLIGHT2__STATIC: *pPicnum = TECHLIGHTBUST2; goto GLASSBREAK_CODE;
1890         case TECHLIGHT4__STATIC: *pPicnum = TECHLIGHTBUST4;
1891 #else
1892     if (0)
1893     {
1894 #endif
1895     GLASSBREAK_CODE:
1896 #ifndef EDUKE32_STANDALONE
1897             A_SpawnCeilingGlass(g_player[myconnectindex].ps->i, sectNum, 10);
1898             A_PlaySound(GLASS_BREAKING, g_player[screenpeek].ps->i);
1899 #endif
1900             if (sector[sectNum].hitag == 0)
1901             {
1902                 for (bssize_t SPRITES_OF_SECT(sectNum, i))
1903                 {
1904                     if (PN(i) == SECTOREFFECTOR && SLT(i) == SE_12_LIGHT_SWITCH)
1905                     {
1906                         for (bssize_t SPRITES_OF(STAT_EFFECTOR, j))
1907                             if (sprite[j].hitag == SHT(i))
1908                                 actor[j].t_data[3] = 1;
1909                         break;
1910                     }
1911                 }
1912             }
1913 
1914             int j = krand() & 1;
1915 
1916             for (bssize_t SPRITES_OF(STAT_EFFECTOR, i))
1917             {
1918                 if (SHT(i) == sector[sectNum].hitag && SLT(i) == SE_3_RANDOM_LIGHTS_AFTER_SHOT_OUT)
1919                 {
1920                     T3(i) = j;
1921                     T5(i) = 1;
1922                 }
1923             }
1924     }
1925 }
1926 
1927 void Sect_DamageCeiling(int const spriteNum, int const sectNum)
1928 {
1929     ud.returnvar[0] = -1;
1930     Sect_DamageCeiling_Internal(spriteNum, sectNum);
1931 }
1932 
1933 // hard coded props... :(
1934 #ifndef EDUKE32_STANDALONE
1935 void A_DamageObject_Duke3D(int spriteNum, int const dmgSrc)
1936 {
1937     if (g_netClient)
1938         return;
1939 
1940     if (A_CheckSpriteFlags(spriteNum, SFLAG_DAMAGEEVENT))
1941     {
1942         if (VM_OnEventWithReturn(EVENT_DAMAGESPRITE, dmgSrc, -1, spriteNum) < 0)
1943             return;
1944     }
1945 
1946     spriteNum &= (MAXSPRITES-1);
1947 
1948     int radiusDamage = 0;
1949 
1950     if (A_CheckSpriteFlags(dmgSrc,SFLAG_PROJECTILE))
1951         if (SpriteProjectile[dmgSrc].workslike & PROJECTILE_RPG)
1952             radiusDamage = 1;
1953 
1954     switch (DYNAMICTILEMAP(PN(spriteNum)))
1955     {
1956     case GRATE1__STATIC:
1957         PN(spriteNum) = BGRATE1;
1958         CS(spriteNum) &= (65535-256-1);
1959         A_PlaySound(VENT_BUST, spriteNum);
1960         return;
1961 
1962     case FANSPRITE__STATIC:
1963         PN(spriteNum) = FANSPRITEBROKE;
1964         CS(spriteNum) &= (65535-257);
1965 
1966         if (sector[SECT(spriteNum)].floorpicnum == FANSHADOW)
1967             sector[SECT(spriteNum)].floorpicnum = FANSHADOWBROKE;
1968 
1969         A_PlaySound(GLASS_HEAVYBREAK, spriteNum);
1970 
1971         for (bssize_t j=16; j>0; j--)
1972         {
1973             auto const pSprite = &sprite[spriteNum];
1974             RANDOMSCRAP(pSprite, spriteNum);
1975         }
1976         return;
1977 
1978     case OCEANSPRITE1__STATIC:
1979     case OCEANSPRITE2__STATIC:
1980     case OCEANSPRITE3__STATIC:
1981     case OCEANSPRITE4__STATIC:
1982     case OCEANSPRITE5__STATIC:
1983         A_Spawn(spriteNum,SMALLSMOKE);
1984         A_DeleteSprite(spriteNum);
1985         return;
1986 
1987     case QUEBALL__STATIC:
1988     case STRIPEBALL__STATIC:
1989         if (sprite[dmgSrc].picnum == QUEBALL || sprite[dmgSrc].picnum == STRIPEBALL)
1990         {
1991             sprite[dmgSrc].xvel = (sprite[spriteNum].xvel>>1)+(sprite[spriteNum].xvel>>2);
1992             sprite[dmgSrc].ang -= (SA(spriteNum)<<1)+1024;
1993             SA(spriteNum) = getangle(SX(spriteNum)-sprite[dmgSrc].x,SY(spriteNum)-sprite[dmgSrc].y)-512;
1994             if (g_sounds[POOLBALLHIT].num < 2)
1995                 A_PlaySound(POOLBALLHIT, spriteNum);
1996         }
1997         else
1998         {
1999             if (krand()&3)
2000             {
2001                 sprite[spriteNum].xvel = 164;
2002                 sprite[spriteNum].ang = sprite[dmgSrc].ang;
2003             }
2004             else
2005             {
2006                 A_SpawnWallGlass(spriteNum,-1,3);
2007                 A_DeleteSprite(spriteNum);
2008             }
2009         }
2010         return;
2011 
2012     case TREE1__STATIC:
2013     case TREE2__STATIC:
2014     case TIRE__STATIC:
2015     case CONE__STATIC:
2016     case BOX__STATIC:
2017     {
2018         switch (DYNAMICTILEMAP(sprite[dmgSrc].picnum))
2019         {
2020         case FLAMETHROWERFLAME__STATIC:
2021         case FIREBALL__STATIC:
2022             if (!WORLDTOUR)
2023                 break;
2024             fallthrough__;
2025         case RADIUSEXPLOSION__STATIC:
2026         case RPG__STATIC:
2027         case FIRELASER__STATIC:
2028         case HYDRENT__STATIC:
2029         case HEAVYHBOMB__STATIC:
2030             radiusDamage = 1;
2031             break;
2032         }
2033 
2034         if (radiusDamage == 1)
2035             if (T1(spriteNum) == 0)
2036             {
2037                 CS(spriteNum) &= ~257;
2038                 T1(spriteNum) = 1;
2039                 A_Spawn(spriteNum,BURNING);
2040             }
2041         return;
2042     }
2043 
2044     case CACTUS__STATIC:
2045     {
2046         switch (DYNAMICTILEMAP(sprite[dmgSrc].picnum))
2047         {
2048         case RADIUSEXPLOSION__STATIC:
2049         case RPG__STATIC:
2050         case FIRELASER__STATIC:
2051         case HYDRENT__STATIC:
2052         case HEAVYHBOMB__STATIC:
2053             radiusDamage = 1;
2054             break;
2055         }
2056 
2057         if (radiusDamage == 1)
2058         {
2059             for (bssize_t k=64; k>0; k--)
2060             {
2061                 int newSprite =
2062                     A_InsertSprite(SECT(spriteNum), SX(spriteNum), SY(spriteNum), SZ(spriteNum) - (krand() % (48 << 8)), SCRAP3 + (krand() & 3), -8, 48, 48,
2063                         krand() & 2047, (krand() & 63) + 64, -(krand() & 4095) - (sprite[spriteNum].zvel >> 2), spriteNum, 5);
2064                 sprite[newSprite].pal = 8;
2065             }
2066             //        case CACTUSBROKE:
2067             if (PN(spriteNum) == CACTUS)
2068                 PN(spriteNum) = CACTUSBROKE;
2069             CS(spriteNum) &= ~257;
2070         }
2071         return;
2072     }
2073 
2074     case HANGLIGHT__STATIC:
2075     case GENERICPOLE2__STATIC:
2076         for (bssize_t k=6; k>0; k--)
2077             A_InsertSprite(SECT(spriteNum),SX(spriteNum),SY(spriteNum),SZ(spriteNum)-ZOFFSET3,SCRAP1+(krand()&15),-8,48,48,krand()&2047,(krand()&63)+64,-(krand()&4095)-(sprite[spriteNum].zvel>>2),spriteNum,5);
2078         A_PlaySound(GLASS_HEAVYBREAK,spriteNum);
2079         A_DeleteSprite(spriteNum);
2080         return;
2081 
2082     case WATERFOUNTAIN__STATIC:
2083         //    case WATERFOUNTAIN+1:
2084         //    case WATERFOUNTAIN+2:
2085         PN(spriteNum) = WATERFOUNTAINBROKE;
2086         A_Spawn(spriteNum,TOILETWATER);
2087         return;
2088 
2089     case SATELITE__STATIC:
2090     case FUELPOD__STATIC:
2091     case SOLARPANNEL__STATIC:
2092     case ANTENNA__STATIC:
2093         if (sprite[dmgSrc].extra != G_DefaultActorHealthForTile(SHOTSPARK1))
2094         {
2095             for (bssize_t j=15; j>0; j--)
2096                 A_InsertSprite(SECT(spriteNum),SX(spriteNum),SY(spriteNum),sector[SECT(spriteNum)].floorz-ZOFFSET4-(j<<9),SCRAP1+(krand()&15),-8,64,64,
2097                                krand()&2047,(krand()&127)+64,-(krand()&511)-256,spriteNum,5);
2098             A_Spawn(spriteNum,EXPLOSION2);
2099             A_DeleteSprite(spriteNum);
2100         }
2101         return;
2102 
2103     case BOTTLE1__STATIC:
2104     case BOTTLE2__STATIC:
2105     case BOTTLE3__STATIC:
2106     case BOTTLE4__STATIC:
2107     case BOTTLE5__STATIC:
2108     case BOTTLE6__STATIC:
2109     case BOTTLE8__STATIC:
2110     case BOTTLE10__STATIC:
2111     case BOTTLE11__STATIC:
2112     case BOTTLE12__STATIC:
2113     case BOTTLE13__STATIC:
2114     case BOTTLE14__STATIC:
2115     case BOTTLE15__STATIC:
2116     case BOTTLE16__STATIC:
2117     case BOTTLE17__STATIC:
2118     case BOTTLE18__STATIC:
2119     case BOTTLE19__STATIC:
2120     case WATERFOUNTAINBROKE__STATIC:
2121     case DOMELITE__STATIC:
2122     case SUSHIPLATE1__STATIC:
2123     case SUSHIPLATE2__STATIC:
2124     case SUSHIPLATE3__STATIC:
2125     case SUSHIPLATE4__STATIC:
2126     case SUSHIPLATE5__STATIC:
2127     case WAITTOBESEATED__STATIC:
2128     case VASE__STATIC:
2129     case STATUEFLASH__STATIC:
2130     case STATUE__STATIC:
2131         if (PN(spriteNum) == BOTTLE10)
2132             A_SpawnMultiple(spriteNum, MONEY, 4+(krand()&3));
2133         else if (PN(spriteNum) == STATUE || PN(spriteNum) == STATUEFLASH)
2134         {
2135             A_SpawnRandomGlass(spriteNum,-1,40);
2136             A_PlaySound(GLASS_HEAVYBREAK,spriteNum);
2137         }
2138         else if (PN(spriteNum) == VASE)
2139             A_SpawnWallGlass(spriteNum,-1,40);
2140 
2141         A_PlaySound(GLASS_BREAKING,spriteNum);
2142         SA(spriteNum) = krand()&2047;
2143         A_SpawnWallGlass(spriteNum,-1,8);
2144         A_DeleteSprite(spriteNum);
2145         return;
2146 
2147     case FETUS__STATIC:
2148         PN(spriteNum) = FETUSBROKE;
2149         A_PlaySound(GLASS_BREAKING,spriteNum);
2150         A_SpawnWallGlass(spriteNum,-1,10);
2151         return;
2152 
2153     case FETUSBROKE__STATIC:
2154         for (bssize_t j=48; j>0; j--)
2155         {
2156             A_Shoot(spriteNum,BLOODSPLAT1);
2157             SA(spriteNum) += 333;
2158         }
2159         A_PlaySound(GLASS_HEAVYBREAK,spriteNum);
2160         A_PlaySound(SQUISHED,spriteNum);
2161         fallthrough__;
2162     case BOTTLE7__STATIC:
2163         A_PlaySound(GLASS_BREAKING,spriteNum);
2164         A_SpawnWallGlass(spriteNum,-1,10);
2165         A_DeleteSprite(spriteNum);
2166         return;
2167 
2168     case E32_TILE5736__STATIC:
2169     case E32_TILE5737__STATIC:
2170         if (!WORLDTOUR)
2171             break;
2172         A_PlaySound(GLASS_BREAKING,spriteNum);
2173         A_SpawnWallGlass(spriteNum,-1,10);
2174         A_DeleteSprite(spriteNum);
2175         return;
2176 
2177     case HYDROPLANT__STATIC:
2178         PN(spriteNum) = BROKEHYDROPLANT;
2179         A_PlaySound(GLASS_BREAKING,spriteNum);
2180         A_SpawnWallGlass(spriteNum,-1,10);
2181         return;
2182 
2183     case FORCESPHERE__STATIC:
2184         sprite[spriteNum].xrepeat = 0;
2185         actor[OW(spriteNum)].t_data[0] = 32;
2186         actor[OW(spriteNum)].t_data[1] = !actor[OW(spriteNum)].t_data[1];
2187         actor[OW(spriteNum)].t_data[2] ++;
2188         A_Spawn(spriteNum,EXPLOSION2);
2189         return;
2190 
2191     case BROKEHYDROPLANT__STATIC:
2192         A_PlaySound(GLASS_BREAKING,spriteNum);
2193         A_SpawnWallGlass(spriteNum,-1,5);
2194         A_DeleteSprite(spriteNum);
2195         return;
2196 
2197     case TOILET__STATIC:
2198         PN(spriteNum) = TOILETBROKE;
2199         CS(spriteNum) |= (krand()&1)<<2;
2200         CS(spriteNum) &= ~257;
2201         A_Spawn(spriteNum,TOILETWATER);
2202         A_PlaySound(GLASS_BREAKING,spriteNum);
2203         return;
2204 
2205     case STALL__STATIC:
2206         PN(spriteNum) = STALLBROKE;
2207         CS(spriteNum) |= (krand()&1)<<2;
2208         CS(spriteNum) &= ~257;
2209         A_Spawn(spriteNum,TOILETWATER);
2210         A_PlaySound(GLASS_HEAVYBREAK,spriteNum);
2211         return;
2212 
2213     case HYDRENT__STATIC:
2214         PN(spriteNum) = BROKEFIREHYDRENT;
2215         A_Spawn(spriteNum,TOILETWATER);
2216 
2217         //            for(k=0;k<5;k++)
2218         //          {
2219         //            j = A_InsertSprite(SECT,SX,SY,SZ-(krand()%(48<<8)),SCRAP3+(krand()&3),-8,48,48,krand()&2047,(krand()&63)+64,-(krand()&4095)-(sprite[i].zvel>>2),i,5);
2220         //          sprite[j].pal = 2;
2221         //    }
2222         A_PlaySound(GLASS_HEAVYBREAK,spriteNum);
2223         return;
2224 
2225     case CIRCLEPANNEL__STATIC:
2226         PN(spriteNum) = CIRCLEPANNELBROKE;
2227         CS(spriteNum) &= (65535-256-1);
2228         A_PlaySound(VENT_BUST,spriteNum);
2229         return;
2230 
2231     case PANNEL1__STATIC:
2232     case PANNEL2__STATIC:
2233         PN(spriteNum) = BPANNEL1;
2234         CS(spriteNum) &= (65535-256-1);
2235         A_PlaySound(VENT_BUST,spriteNum);
2236         return;
2237 
2238     case PANNEL3__STATIC:
2239         PN(spriteNum) = BPANNEL3;
2240         CS(spriteNum) &= (65535-256-1);
2241         A_PlaySound(VENT_BUST,spriteNum);
2242         return;
2243 
2244     case PIPE1__STATIC:
2245     case PIPE2__STATIC:
2246     case PIPE3__STATIC:
2247     case PIPE4__STATIC:
2248     case PIPE5__STATIC:
2249     case PIPE6__STATIC:
2250     {
2251         switch (DYNAMICTILEMAP(PN(spriteNum)))
2252         {
2253         case PIPE1__STATIC:
2254             PN(spriteNum)=PIPE1B;
2255             break;
2256         case PIPE2__STATIC:
2257             PN(spriteNum)=PIPE2B;
2258             break;
2259         case PIPE3__STATIC:
2260             PN(spriteNum)=PIPE3B;
2261             break;
2262         case PIPE4__STATIC:
2263             PN(spriteNum)=PIPE4B;
2264             break;
2265         case PIPE5__STATIC:
2266             PN(spriteNum)=PIPE5B;
2267             break;
2268         case PIPE6__STATIC:
2269             PN(spriteNum)=PIPE6B;
2270             break;
2271         }
2272 
2273         int newSprite = A_Spawn(spriteNum, STEAM);
2274         sprite[newSprite].z = sector[SECT(spriteNum)].floorz-ZOFFSET5;
2275         return;
2276     }
2277 
2278     case MONK__STATIC:
2279     case LUKE__STATIC:
2280     case INDY__STATIC:
2281     case JURYGUY__STATIC:
2282         A_PlaySound(SLT(spriteNum),spriteNum);
2283         A_Spawn(spriteNum,SHT(spriteNum));
2284         fallthrough__;
2285     case E32_TILE5846__STATIC:
2286         if (!WORLDTOUR && PN(spriteNum) == E32_TILE5846)
2287             break;
2288         fallthrough__;
2289     case SPACEMARINE__STATIC:
2290         sprite[spriteNum].extra -= sprite[dmgSrc].extra;
2291         if (sprite[spriteNum].extra > 0)
2292             return;
2293         SA(spriteNum) = krand()&2047;
2294         A_Shoot(spriteNum,BLOODSPLAT1);
2295         SA(spriteNum) = krand()&2047;
2296         A_Shoot(spriteNum,BLOODSPLAT2);
2297         SA(spriteNum) = krand()&2047;
2298         A_Shoot(spriteNum,BLOODSPLAT3);
2299         SA(spriteNum) = krand()&2047;
2300         A_Shoot(spriteNum,BLOODSPLAT4);
2301         SA(spriteNum) = krand()&2047;
2302         A_Shoot(spriteNum,BLOODSPLAT1);
2303         SA(spriteNum) = krand()&2047;
2304         A_Shoot(spriteNum,BLOODSPLAT2);
2305         SA(spriteNum) = krand()&2047;
2306         A_Shoot(spriteNum,BLOODSPLAT3);
2307         SA(spriteNum) = krand()&2047;
2308         A_Shoot(spriteNum,BLOODSPLAT4);
2309         A_DoGuts(spriteNum,JIBS1,1);
2310         A_DoGuts(spriteNum,JIBS2,2);
2311         A_DoGuts(spriteNum,JIBS3,3);
2312         A_DoGuts(spriteNum,JIBS4,4);
2313         A_DoGuts(spriteNum,JIBS5,1);
2314         A_DoGuts(spriteNum,JIBS3,6);
2315         S_PlaySound(SQUISHED);
2316         A_DeleteSprite(spriteNum);
2317         return;
2318 
2319     case CHAIR1__STATIC:
2320     case CHAIR2__STATIC:
2321         PN(spriteNum) = BROKENCHAIR;
2322         CS(spriteNum) = 0;
2323         return;
2324 
2325     case CHAIR3__STATIC:
2326     case MOVIECAMERA__STATIC:
2327     case SCALE__STATIC:
2328     case VACUUM__STATIC:
2329     case CAMERALIGHT__STATIC:
2330     case IVUNIT__STATIC:
2331     case POT1__STATIC:
2332     case POT2__STATIC:
2333     case POT3__STATIC:
2334     case TRIPODCAMERA__STATIC:
2335         A_PlaySound(GLASS_HEAVYBREAK,spriteNum);
2336         for (bssize_t j=16; j>0; j--)
2337         {
2338             auto const pSprite = &sprite[spriteNum];
2339             RANDOMSCRAP(pSprite, spriteNum);
2340         }
2341         A_DeleteSprite(spriteNum);
2342         return;
2343 
2344     case PLAYERONWATER__STATIC:
2345         spriteNum = OW(spriteNum);
2346         fallthrough__;
2347     default:
2348         break; // NOT return
2349     }
2350 
2351     // implementation of the default case
2352 
2353     if ((sprite[spriteNum].cstat&16) && SHT(spriteNum) == 0 && SLT(spriteNum) == 0 && sprite[spriteNum].statnum == STAT_DEFAULT)
2354         return;
2355 
2356     if ((sprite[dmgSrc].picnum == FREEZEBLAST || sprite[dmgSrc].owner != spriteNum) && sprite[spriteNum].statnum != STAT_PROJECTILE)
2357     {
2358         if (A_CheckEnemySprite(&sprite[spriteNum]) == 1)
2359         {
2360             if (WORLDTOUR && sprite[spriteNum].picnum == FIREFLY && sprite[spriteNum].xrepeat < 48)
2361                 return;
2362 
2363             if (sprite[dmgSrc].picnum == RPG)
2364                 sprite[dmgSrc].extra <<= 1;
2365 
2366             if ((PN(spriteNum) != DRONE) && (PN(spriteNum) != ROTATEGUN) && (PN(spriteNum) != COMMANDER)
2367                 && (PN(spriteNum) < GREENSLIME || PN(spriteNum) > GREENSLIME + 7))
2368                 if (sprite[dmgSrc].picnum != FREEZEBLAST)
2369                     if (!A_CheckSpriteFlags(spriteNum, SFLAG_BADGUY) || A_CheckSpriteFlags(spriteNum, SFLAG_HURTSPAWNBLOOD))
2370                     {
2371                         int const newSprite = A_Spawn(dmgSrc, JIBS6);
2372                         sprite[newSprite].z += ZOFFSET6;
2373                         if (sprite[dmgSrc].pal == 6)
2374                             sprite[newSprite].pal = 6;
2375                         sprite[newSprite].xvel    = 16;
2376                         sprite[newSprite].xrepeat = sprite[newSprite].yrepeat = 24;
2377                         sprite[newSprite].ang += 32 - (krand() & 63);
2378                     }
2379 
2380             int const damageOwner = sprite[dmgSrc].owner;
2381 
2382             if (damageOwner >= 0 && sprite[damageOwner].picnum == APLAYER && PN(spriteNum) != ROTATEGUN && PN(spriteNum) != DRONE)
2383                 if (g_player[P_Get(damageOwner)].ps->curr_weapon == SHOTGUN_WEAPON)
2384                     if (!A_CheckSpriteFlags(spriteNum, SFLAG_BADGUY) || A_CheckSpriteFlags(spriteNum, SFLAG_HURTSPAWNBLOOD))
2385                     {
2386                         A_Shoot(spriteNum, BLOODSPLAT3);
2387                         A_Shoot(spriteNum, BLOODSPLAT1);
2388                         A_Shoot(spriteNum, BLOODSPLAT2);
2389                         A_Shoot(spriteNum, BLOODSPLAT4);
2390                     }
2391 
2392             if (!A_CheckSpriteFlags(spriteNum, SFLAG_NODAMAGEPUSH))
2393             {
2394                 if (sprite[spriteNum].extra > 0)
2395                 {
2396                     if ((sprite[spriteNum].cstat & 48) == 0)
2397                         SA(spriteNum)          = (sprite[dmgSrc].ang + 1024) & 2047;
2398                     sprite[spriteNum].xvel  = -(sprite[dmgSrc].extra << 2);
2399                     int16_t sectNum = SECT(spriteNum);
2400                     pushmove(&sprite[spriteNum].pos, &sectNum, 128L, (4L << 8), (4L << 8), CLIPMASK0);
2401                     if (sectNum != SECT(spriteNum) && (unsigned)sectNum < MAXSECTORS)
2402                         changespritesect(spriteNum, sectNum);
2403                 }
2404             }
2405 
2406             if (sprite[spriteNum].statnum == STAT_ZOMBIEACTOR)
2407             {
2408                 changespritestat(spriteNum, STAT_ACTOR);
2409                 actor[spriteNum].timetosleep = SLEEPTIME;
2410             }
2411             if ((sprite[spriteNum].xrepeat < 24 || PN(spriteNum) == SHARK) && sprite[dmgSrc].picnum == SHRINKSPARK)
2412                 return;
2413         }
2414 
2415         if (sprite[spriteNum].statnum != STAT_ZOMBIEACTOR)
2416         {
2417             if (sprite[dmgSrc].picnum == FREEZEBLAST && ((PN(spriteNum) == APLAYER && sprite[spriteNum].pal == 1) || (g_freezerSelfDamage == 0 && sprite[dmgSrc].owner == spriteNum)))
2418                 return;
2419 
2420             if (WORLDTOUR && sprite[dmgSrc].picnum == FIREBALL && sprite[sprite[spriteNum].owner].picnum != FIREBALL)
2421                 actor[spriteNum].picnum = FLAMETHROWERFLAME;
2422             else
2423                 actor[spriteNum].picnum = sprite[dmgSrc].picnum;
2424 
2425             actor[spriteNum].extra += sprite[dmgSrc].extra;
2426             actor[spriteNum].ang    = sprite[dmgSrc].ang;
2427             actor[spriteNum].owner  = sprite[dmgSrc].owner;
2428 
2429             if(A_CheckSpriteFlags(spriteNum, SFLAG_DAMAGEEVENT))
2430                 VM_OnEventWithReturn(EVENT_POSTDAMAGESPRITE, dmgSrc, -1, spriteNum);
2431         }
2432 
2433         if (sprite[spriteNum].statnum == STAT_PLAYER)
2434         {
2435             auto ps = g_player[P_Get(spriteNum)].ps;
2436 
2437             if (ps->newowner >= 0)
2438                 G_ClearCameraView(ps);
2439 
2440             if (sprite[spriteNum].xrepeat < 24 && sprite[dmgSrc].picnum == SHRINKSPARK)
2441                 return;
2442 
2443             if (sprite[actor[spriteNum].owner].picnum != APLAYER)
2444                 if (ud.player_skill >= 3)
2445                     sprite[dmgSrc].extra += (sprite[dmgSrc].extra>>1);
2446         }
2447     }
2448 }
2449 #endif
2450 
2451 void A_DamageObject_Generic(int spriteNum, int const dmgSrc)
2452 {
2453     if (g_netClient)
2454         return;
2455 
2456     if (A_CheckSpriteFlags(spriteNum, SFLAG_DAMAGEEVENT))
2457     {
2458         if (VM_OnEventWithReturn(EVENT_DAMAGESPRITE, dmgSrc, -1, spriteNum) < 0)
2459             return;
2460     }
2461 
2462     spriteNum &= (MAXSPRITES-1);
2463 
2464     switch (DYNAMICTILEMAP(PN(spriteNum)))
2465     {
2466     case GRATE1__STATIC:
2467         PN(spriteNum) = BGRATE1;
2468         CS(spriteNum) &= (65535-256-1);
2469         break;
2470 
2471     case FANSPRITE__STATIC:
2472         PN(spriteNum) = FANSPRITEBROKE;
2473         CS(spriteNum) &= (65535-257);
2474         break;
2475 
2476     case PLAYERONWATER__STATIC:
2477         spriteNum = OW(spriteNum);
2478         fallthrough__;
2479     default:
2480         if ((sprite[spriteNum].cstat&16) && SHT(spriteNum) == 0 && SLT(spriteNum) == 0 && sprite[spriteNum].statnum == STAT_DEFAULT)
2481             break;
2482 
2483         if (sprite[dmgSrc].owner != spriteNum && sprite[spriteNum].statnum != STAT_PROJECTILE)
2484         {
2485             if (A_CheckEnemySprite(&sprite[spriteNum]))
2486             {
2487                 if (!A_CheckSpriteFlags(spriteNum, SFLAG_NODAMAGEPUSH))
2488                 {
2489                     if (sprite[spriteNum].extra > 0)
2490                     {
2491                         if ((sprite[spriteNum].cstat & 48) == 0)
2492                             SA(spriteNum) = (sprite[dmgSrc].ang + 1024) & 2047;
2493                         sprite[spriteNum].xvel  = -(sprite[dmgSrc].extra << 2);
2494                         int16_t sectNum = SECT(spriteNum);
2495                         pushmove(&sprite[spriteNum].pos, &sectNum, 128L, (4L << 8), (4L << 8), CLIPMASK0);
2496                         if (sectNum != SECT(spriteNum) && (unsigned)sectNum < MAXSECTORS)
2497                             changespritesect(spriteNum, sectNum);
2498                     }
2499                 }
2500 
2501                 if (sprite[spriteNum].statnum == STAT_ZOMBIEACTOR)
2502                 {
2503                     changespritestat(spriteNum, STAT_ACTOR);
2504                     actor[spriteNum].timetosleep = SLEEPTIME;
2505                 }
2506             }
2507 
2508             if (sprite[spriteNum].statnum != STAT_ZOMBIEACTOR)
2509             {
2510                 actor[spriteNum].picnum = sprite[dmgSrc].picnum;
2511                 actor[spriteNum].extra += sprite[dmgSrc].extra;
2512                 actor[spriteNum].ang    = sprite[dmgSrc].ang;
2513                 actor[spriteNum].owner  = sprite[dmgSrc].owner;
2514 
2515                 if(A_CheckSpriteFlags(spriteNum, SFLAG_DAMAGEEVENT))
2516                     VM_OnEventWithReturn(EVENT_POSTDAMAGESPRITE, dmgSrc, -1, spriteNum);
2517             }
2518 
2519             if (sprite[spriteNum].statnum == STAT_PLAYER)
2520             {
2521                 auto ps = g_player[P_Get(spriteNum)].ps;
2522 
2523                 if (ps->newowner >= 0)
2524                     G_ClearCameraView(ps);
2525 
2526                 if (sprite[actor[spriteNum].owner].picnum != APLAYER)
2527                     if (ud.player_skill >= 3)
2528                         sprite[dmgSrc].extra += (sprite[dmgSrc].extra>>1);
2529             }
2530         }
2531 
2532         break;
2533     }
2534 }
2535 
2536 void A_DamageObject(int spriteNum, int const dmgSrc)
2537 {
2538     ud.returnvar[0] = -1;
2539 
2540 #ifndef EDUKE32_STANDALONE
2541     if (!FURY)
2542         A_DamageObject_Duke3D(spriteNum, dmgSrc);
2543     else
2544 #endif
2545         A_DamageObject_Generic(spriteNum, dmgSrc);
2546 }
2547 
2548 void G_AlignWarpElevators(void)
2549 {
2550     for (bssize_t SPRITES_OF(STAT_EFFECTOR, i))
2551     {
2552         if (SLT(i) == SE_17_WARP_ELEVATOR && SS(i) > 16)
2553         {
2554             for (bssize_t SPRITES_OF(STAT_EFFECTOR, j))
2555             {
2556                 if (i != j && sprite[j].lotag == SE_17_WARP_ELEVATOR && SHT(i) == sprite[j].hitag)
2557                 {
2558                     sector[sprite[j].sectnum].floorz   = sector[SECT(i)].floorz;
2559                     sector[sprite[j].sectnum].ceilingz = sector[SECT(i)].ceilingz;
2560                 }
2561             }
2562         }
2563     }
2564 }
2565 
2566 
2567 static int P_CheckDetonatorSpecialCase(DukePlayer_t *const pPlayer, int weaponNum)
2568 {
2569     if (weaponNum == HANDBOMB_WEAPON && pPlayer->ammo_amount[HANDBOMB_WEAPON] == 0)
2570     {
2571         int spriteNum = headspritestat[STAT_ACTOR];
2572 
2573         while (spriteNum >= 0)
2574         {
2575             if (sprite[spriteNum].picnum == HEAVYHBOMB && sprite[spriteNum].owner == pPlayer->i)
2576                 return 1;
2577 
2578             spriteNum = nextspritestat[spriteNum];
2579         }
2580     }
2581 
2582     return 0;
2583 }
2584 
2585 void P_HandleSharedKeys(int playerNum)
2586 {
2587     auto const pPlayer = g_player[playerNum].ps;
2588 
2589     if (pPlayer->cheat_phase == 1) return;
2590 
2591     uint32_t playerBits = g_player[playerNum].input.bits, weaponNum;
2592 
2593     // 1<<0  =  jump
2594     // 1<<1  =  crouch
2595     // 1<<2  =  fire
2596     // 1<<3  =  aim up
2597     // 1<<4  =  aim down
2598     // 1<<5  =  run
2599     // 1<<6  =  look left
2600     // 1<<7  =  look right
2601     // 15<<8 = !weapon selection (bits 8-11)
2602     // 1<<12 = !steroids
2603     // 1<<13 =  look up
2604     // 1<<14 =  look down
2605     // 1<<15 = !nightvis
2606     // 1<<16 = !medkit
2607     // 1<<17 =  (multiflag==1) ? changes meaning of bits 18 and 19
2608     // 1<<18 =  centre view
2609     // 1<<19 = !holster weapon
2610     // 1<<20 = !inventory left
2611     // 1<<21 = !pause
2612     // 1<<22 = !quick kick
2613     // 1<<23 =  aim mode
2614     // 1<<24 = !holoduke
2615     // 1<<25 = !jetpack
2616     // 1<<26 =  g_gameQuit
2617     // 1<<27 = !inventory right
2618     // 1<<28 = !turn around
2619     // 1<<29 = !open
2620     // 1<<30 = !inventory
2621     // 1<<31 = !escape
2622 
2623     int const aimMode = pPlayer->aim_mode;
2624 
2625     pPlayer->aim_mode = (playerBits>>SK_AIMMODE)&1;
2626     if (pPlayer->aim_mode < aimMode)
2627         pPlayer->return_to_center = 9;
2628 
2629     if (TEST_SYNC_KEY(playerBits, SK_QUICK_KICK) && pPlayer->quick_kick == 0)
2630         if (PWEAPON(playerNum, pPlayer->curr_weapon, WorksLike) != KNEE_WEAPON || pPlayer->kickback_pic == 0)
2631         {
2632             if (VM_OnEvent(EVENT_QUICKKICK,g_player[playerNum].ps->i,playerNum) == 0)
2633             {
2634                 pPlayer->quick_kick = 14;
2635                 if (pPlayer->fta == 0 || pPlayer->ftq == 80)
2636                     P_DoQuote(QUOTE_MIGHTY_FOOT,pPlayer);
2637             }
2638         }
2639 
2640     weaponNum = playerBits & ((15u<<SK_WEAPON_BITS)|BIT(SK_STEROIDS)|BIT(SK_NIGHTVISION)|BIT(SK_MEDKIT)|BIT(SK_QUICK_KICK)| \
2641                    BIT(SK_HOLSTER)|BIT(SK_INV_LEFT)|BIT(SK_PAUSE)|BIT(SK_HOLODUKE)|BIT(SK_JETPACK)|BIT(SK_INV_RIGHT)| \
2642                    BIT(SK_TURNAROUND)|BIT(SK_OPEN)|BIT(SK_INVENTORY)|BIT(SK_ESCAPE));
2643     playerBits = weaponNum & ~pPlayer->interface_toggle;
2644     pPlayer->interface_toggle |= playerBits | ((playerBits&0xf00)?0xf00:0);
2645     pPlayer->interface_toggle &= weaponNum | ((weaponNum&0xf00)?0xf00:0);
2646 
2647     if (playerBits && TEST_SYNC_KEY(playerBits, SK_MULTIFLAG) == 0)
2648     {
2649         if (TEST_SYNC_KEY(playerBits, SK_PAUSE))
2650         {
2651             KB_ClearKeyDown(sc_Pause);
2652             if (ud.pause_on)
2653                 ud.pause_on = 0;
2654             else ud.pause_on = 1+SHIFTS_IS_PRESSED;
2655             if (ud.pause_on)
2656             {
2657                 if (ud.recstat != 2) // edge case: pause during demo recording
2658                     S_PauseMusic(true);
2659                 S_PauseSounds(true);
2660             }
2661             else
2662             {
2663                 if (ud.config.MusicToggle) S_PauseMusic(false);
2664 
2665                 S_PauseSounds(false);
2666 
2667                 pub = NUMPAGES;
2668                 pus = NUMPAGES;
2669             }
2670         }
2671 
2672         if (ud.pause_on) return;
2673 
2674         if (sprite[pPlayer->i].extra <= 0) return;		// if dead...
2675 
2676         if (TEST_SYNC_KEY(playerBits, SK_INVENTORY) && pPlayer->newowner == -1)	// inventory button generates event for selected item
2677         {
2678             if (VM_OnEvent(EVENT_INVENTORY,g_player[playerNum].ps->i,playerNum) == 0)
2679             {
2680                 switch (pPlayer->inven_icon)
2681                 {
2682                     case ICON_JETPACK: playerBits |= BIT(SK_JETPACK); break;
2683                     case ICON_HOLODUKE: playerBits |= BIT(SK_HOLODUKE); break;
2684                     case ICON_HEATS: playerBits |= BIT(SK_NIGHTVISION); break;
2685                     case ICON_FIRSTAID: playerBits |= BIT(SK_MEDKIT); break;
2686                     case ICON_STEROIDS: playerBits |= BIT(SK_STEROIDS); break;
2687                 }
2688             }
2689         }
2690 
2691         if (TEST_SYNC_KEY(playerBits, SK_NIGHTVISION))
2692         {
2693             if (VM_OnEvent(EVENT_USENIGHTVISION,g_player[playerNum].ps->i,playerNum) == 0
2694                     &&  pPlayer->inv_amount[GET_HEATS] > 0)
2695             {
2696                 pPlayer->heat_on = !pPlayer->heat_on;
2697                 P_UpdateScreenPal(pPlayer);
2698                 pPlayer->inven_icon = ICON_HEATS;
2699 #ifndef EDUKE32_STANDALONE
2700                 A_PlaySound(NITEVISION_ONOFF,pPlayer->i);
2701 #endif
2702                 P_DoQuote(QUOTE_NVG_OFF-!!pPlayer->heat_on,pPlayer);
2703             }
2704         }
2705 
2706         if (TEST_SYNC_KEY(playerBits, SK_STEROIDS))
2707         {
2708             if (VM_OnEvent(EVENT_USESTEROIDS,g_player[playerNum].ps->i,playerNum) == 0)
2709             {
2710                 if (pPlayer->inv_amount[GET_STEROIDS] == 400)
2711                 {
2712                     pPlayer->inv_amount[GET_STEROIDS]--;
2713 #ifndef EDUKE32_STANDALONE
2714                     A_PlaySound(DUKE_TAKEPILLS,pPlayer->i);
2715 #endif
2716                     P_DoQuote(QUOTE_USED_STEROIDS,pPlayer);
2717                 }
2718                 if (pPlayer->inv_amount[GET_STEROIDS] > 0)
2719                     pPlayer->inven_icon = ICON_STEROIDS;
2720             }
2721             return;		// is there significance to returning?
2722         }
2723 
2724         if (pPlayer->newowner == -1 && (TEST_SYNC_KEY(playerBits, SK_INV_LEFT) || TEST_SYNC_KEY(playerBits, SK_INV_RIGHT)))
2725         {
2726             pPlayer->invdisptime = GAMETICSPERSEC*2;
2727 
2728             int const inventoryRight = !!(TEST_SYNC_KEY(playerBits, SK_INV_RIGHT));
2729 
2730             int32_t inventoryIcon = pPlayer->inven_icon;
2731 
2732             int i = 0;
2733 
2734 CHECKINV1:
2735             if (i < 9)
2736             {
2737                 i++;
2738 
2739                 switch (inventoryIcon)
2740                 {
2741                     case ICON_JETPACK:
2742                     case ICON_SCUBA:
2743                     case ICON_STEROIDS:
2744                     case ICON_HOLODUKE:
2745                     case ICON_HEATS:
2746                         if (pPlayer->inv_amount[icon_to_inv[inventoryIcon]] > 0 && i > 1)
2747                             break;
2748                         if (inventoryRight)
2749                             inventoryIcon++;
2750                         else
2751                             inventoryIcon--;
2752                         goto CHECKINV1;
2753                     case ICON_NONE:
2754                     case ICON_FIRSTAID:
2755                         if (pPlayer->inv_amount[GET_FIRSTAID] > 0 && i > 1)
2756                             break;
2757                         inventoryIcon = inventoryRight ? 2 : 7;
2758                         goto CHECKINV1;
2759                     case ICON_BOOTS:
2760                         if (pPlayer->inv_amount[GET_BOOTS] > 0 && i > 1)
2761                             break;
2762                         inventoryIcon = inventoryRight ? 1 : 6;
2763                         goto CHECKINV1;
2764                 }
2765             }
2766             else inventoryIcon = 0;
2767 
2768             if (TEST_SYNC_KEY(playerBits, SK_INV_LEFT))   // Inventory_Left
2769             {
2770                 /*Gv_SetVar(g_iReturnVarID,dainv,g_player[snum].ps->i,snum);*/
2771                 inventoryIcon = VM_OnEventWithReturn(EVENT_INVENTORYLEFT,g_player[playerNum].ps->i,playerNum, inventoryIcon);
2772             }
2773             else if (TEST_SYNC_KEY(playerBits, SK_INV_RIGHT))   // Inventory_Right
2774             {
2775                 /*Gv_SetVar(g_iReturnVarID,dainv,g_player[snum].ps->i,snum);*/
2776                 inventoryIcon = VM_OnEventWithReturn(EVENT_INVENTORYRIGHT,g_player[playerNum].ps->i,playerNum, inventoryIcon);
2777             }
2778 
2779             if (inventoryIcon >= 1)
2780             {
2781                 pPlayer->inven_icon = inventoryIcon;
2782 
2783                 static const int32_t invQuotes[8] = { QUOTE_MEDKIT, QUOTE_STEROIDS, QUOTE_HOLODUKE,
2784                     QUOTE_JETPACK, QUOTE_NVG, QUOTE_SCUBA, QUOTE_BOOTS, 0 };
2785 
2786                 if (inventoryIcon-1 < ARRAY_SSIZE(invQuotes))
2787                     P_DoQuote(invQuotes[inventoryIcon-1], pPlayer);
2788             }
2789         }
2790 
2791         weaponNum = ((playerBits&SK_WEAPON_MASK)>>SK_WEAPON_BITS) - 1;
2792 
2793         switch ((int32_t)weaponNum)
2794         {
2795         case -1:
2796             break;
2797         default:
2798             weaponNum = VM_OnEventWithReturn(EVENT_WEAPKEY1+weaponNum,pPlayer->i,playerNum, weaponNum);
2799             break;
2800         case 10:
2801             weaponNum = VM_OnEventWithReturn(EVENT_PREVIOUSWEAPON,pPlayer->i,playerNum, weaponNum);
2802             break;
2803         case 11:
2804             weaponNum = VM_OnEventWithReturn(EVENT_NEXTWEAPON,pPlayer->i,playerNum, weaponNum);
2805             break;
2806         case 12:
2807             weaponNum = VM_OnEventWithReturn(EVENT_ALTWEAPON,pPlayer->i,playerNum, weaponNum);
2808             break;
2809         case 13:
2810             weaponNum = VM_OnEventWithReturn(EVENT_LASTWEAPON,pPlayer->i,playerNum, weaponNum);
2811             break;
2812         }
2813 
2814         // NOTE: it is assumed that the above events return either -1 or a
2815         // valid weapon index. Presumably, neither other negative numbers nor
2816         // positive ones >= MAX_WEAPONS are allowed. However, the code below is
2817         // a bit inconsistent in checking "j".
2818 
2819         if (pPlayer->reloading == 1)
2820             weaponNum = -1;
2821         else if ((uint32_t)weaponNum < 12 && pPlayer->kickback_pic == 1 && pPlayer->weapon_pos == 1)
2822         {
2823             pPlayer->wantweaponfire = weaponNum;
2824             pPlayer->kickback_pic = 0;
2825         }
2826 
2827         if ((int32_t)weaponNum != -1 && pPlayer->last_pissed_time <= (GAMETICSPERSEC * 218) && pPlayer->show_empty_weapon == 0 &&
2828             pPlayer->kickback_pic == 0 && pPlayer->quick_kick == 0 && sprite[pPlayer->i].xrepeat > 32 && pPlayer->access_incs == 0 &&
2829             pPlayer->knee_incs == 0)
2830         {
2831             //            if(  ( p->weapon_pos == 0 || ( p->holster_weapon && p->weapon_pos == WEAPON_POS_LOWER ) ))
2832             {
2833                 if (weaponNum >= 12) // hack
2834                     weaponNum++;
2835                 if (weaponNum == 10 || weaponNum == 11)
2836                 {
2837                     int currentWeapon = pPlayer->curr_weapon;
2838 
2839                     weaponNum = (weaponNum == 10 ? -1 : 1);  // JBF: prev (-1) or next (1) weapon choice
2840                     int i = currentWeapon;
2841 
2842                     while ((currentWeapon >= 0 && currentWeapon < 11) || (PLUTOPAK && currentWeapon == GROW_WEAPON) || (WORLDTOUR && currentWeapon == FLAMETHROWER_WEAPON))
2843                     {
2844                         // this accounts for the expander when handling next/previous
2845 
2846                         switch (currentWeapon)
2847                         {
2848                             case DEVISTATOR_WEAPON:
2849                                 if ((int32_t) weaponNum == -1)
2850                                 {
2851                                     if (PLUTOPAK)
2852                                         currentWeapon = GROW_WEAPON;
2853                                     else
2854                                         currentWeapon--;
2855                                 }
2856                                 else
2857                                     currentWeapon++;
2858                                 break;
2859 
2860                             case GROW_WEAPON:
2861                                 currentWeapon = ((int32_t) weaponNum == -1) ? SHRINKER_WEAPON : DEVISTATOR_WEAPON;
2862                                 break;
2863 
2864                             case SHRINKER_WEAPON:
2865                                 if ((int32_t)weaponNum == 1)
2866                                 {
2867                                     if (PLUTOPAK)
2868                                         currentWeapon = GROW_WEAPON;
2869                                     else
2870                                         currentWeapon++;
2871                                 }
2872                                 else
2873                                     currentWeapon--;
2874                                 break;
2875 
2876                             case KNEE_WEAPON:
2877                                 if ((int32_t) weaponNum == -1)
2878                                 {
2879                                     if (WORLDTOUR)
2880                                         currentWeapon = FLAMETHROWER_WEAPON;
2881                                     else
2882                                         currentWeapon = FREEZE_WEAPON;
2883                                 }
2884                                 else
2885                                     currentWeapon++;
2886                                 break;
2887 
2888                             case FLAMETHROWER_WEAPON:
2889                                 currentWeapon = ((int32_t) weaponNum == -1) ? FREEZE_WEAPON : KNEE_WEAPON;
2890                                 break;
2891 
2892                             case FREEZE_WEAPON:
2893                                 if ((int32_t)weaponNum == 1)
2894                                 {
2895                                     if (WORLDTOUR)
2896                                         currentWeapon = FLAMETHROWER_WEAPON;
2897                                     else
2898                                         currentWeapon = KNEE_WEAPON;
2899                                 }
2900                                 else
2901                                     currentWeapon--;
2902                                 break;
2903 
2904                             case HANDREMOTE_WEAPON:
2905                                 i = currentWeapon = HANDBOMB_WEAPON;
2906                                 fallthrough__;
2907                             default:
2908                                 currentWeapon += weaponNum;
2909                                 break;
2910                         }
2911 
2912                         if (((pPlayer->gotweapon & (1<<currentWeapon)) && pPlayer->ammo_amount[currentWeapon] > 0) || P_CheckDetonatorSpecialCase(pPlayer, currentWeapon))
2913                         {
2914                             weaponNum = currentWeapon;
2915                             break;
2916                         }
2917 
2918                         if (i == currentWeapon) // absolutely no weapons, so use foot
2919                         {
2920                             weaponNum = KNEE_WEAPON;
2921                             break;
2922                         }
2923                     }
2924 
2925                     if (weaponNum == SHRINKER_WEAPON)
2926                         pPlayer->subweapon &= ~(1 << GROW_WEAPON);
2927                     else if (weaponNum == GROW_WEAPON)
2928                         pPlayer->subweapon |= (1<<GROW_WEAPON);
2929                     else if (weaponNum == FREEZE_WEAPON)
2930                         pPlayer->subweapon &= ~(1 << FLAMETHROWER_WEAPON);
2931                     else if (weaponNum == FLAMETHROWER_WEAPON)
2932                         pPlayer->subweapon |= (1<<FLAMETHROWER_WEAPON);
2933                 }
2934 
2935                 // last used weapon will depend on subweapon
2936                 if (weaponNum >= 13) // alt weapon, last used weapon
2937                 {
2938                     uint32_t const weaponNumSwitch = weaponNum == 14 ? pPlayer->last_used_weapon : pPlayer->curr_weapon;
2939                     switch (weaponNumSwitch)
2940                     {
2941                         case HANDREMOTE_WEAPON:
2942                             weaponNum = HANDBOMB_WEAPON;
2943                             break;
2944                         case GROW_WEAPON:
2945                             weaponNum = SHRINKER_WEAPON;
2946                             break;
2947                         case FLAMETHROWER_WEAPON:
2948                             weaponNum = FREEZE_WEAPON;
2949                             break;
2950                         default:
2951                             weaponNum = weaponNumSwitch;
2952                             break;
2953                     }
2954                 }
2955 
2956                 P_SetWeaponGamevars(playerNum, pPlayer);
2957 
2958                 weaponNum = VM_OnEventWithReturn(EVENT_SELECTWEAPON,pPlayer->i,playerNum, weaponNum);
2959 
2960                 if ((int32_t)weaponNum != -1 && weaponNum < MAX_WEAPONS)
2961                 {
2962                     if (P_CheckDetonatorSpecialCase(pPlayer, weaponNum))
2963                     {
2964                         pPlayer->gotweapon |= (1<<HANDBOMB_WEAPON);
2965                         weaponNum = HANDREMOTE_WEAPON;
2966                     }
2967 
2968                     if (weaponNum == SHRINKER_WEAPON && PLUTOPAK)   // JBF 20040116: so we don't select the grower with v1.3d
2969                     {
2970                         if (screenpeek == playerNum) pus = NUMPAGES;
2971 
2972                         if (pPlayer->curr_weapon != GROW_WEAPON && pPlayer->curr_weapon != SHRINKER_WEAPON)
2973                         {
2974                             if (pPlayer->ammo_amount[GROW_WEAPON] > 0)
2975                             {
2976                                 if ((pPlayer->subweapon&(1<<GROW_WEAPON)) == (1<<GROW_WEAPON))
2977                                     weaponNum = GROW_WEAPON;
2978                                 else if (pPlayer->ammo_amount[SHRINKER_WEAPON] == 0)
2979                                 {
2980                                     weaponNum = GROW_WEAPON;
2981                                     pPlayer->subweapon |= (1<<GROW_WEAPON);
2982                                 }
2983                             }
2984                             else if (pPlayer->ammo_amount[SHRINKER_WEAPON] > 0)
2985                                 pPlayer->subweapon &= ~(1<<GROW_WEAPON);
2986                         }
2987                         else if (pPlayer->curr_weapon == SHRINKER_WEAPON)
2988                         {
2989                             pPlayer->subweapon |= (1<<GROW_WEAPON);
2990                             weaponNum = GROW_WEAPON;
2991                         }
2992                         else
2993                             pPlayer->subweapon &= ~(1<<GROW_WEAPON);
2994                     }
2995 
2996                     if (weaponNum == FREEZE_WEAPON && WORLDTOUR)
2997                     {
2998                         if (screenpeek == playerNum) pus = NUMPAGES;
2999 
3000                         if (pPlayer->curr_weapon != FLAMETHROWER_WEAPON && pPlayer->curr_weapon != FREEZE_WEAPON)
3001                         {
3002                             if (pPlayer->ammo_amount[FLAMETHROWER_WEAPON] > 0)
3003                             {
3004                                 if ((pPlayer->subweapon&(1<<FLAMETHROWER_WEAPON)) == (1<<FLAMETHROWER_WEAPON))
3005                                     weaponNum = FLAMETHROWER_WEAPON;
3006                                 else if (pPlayer->ammo_amount[FREEZE_WEAPON] == 0)
3007                                 {
3008                                     weaponNum = FLAMETHROWER_WEAPON;
3009                                     pPlayer->subweapon |= (1<<FLAMETHROWER_WEAPON);
3010                                 }
3011                             }
3012                             else if (pPlayer->ammo_amount[FREEZE_WEAPON] > 0)
3013                                 pPlayer->subweapon &= ~(1<<FLAMETHROWER_WEAPON);
3014                         }
3015                         else if (pPlayer->curr_weapon == FREEZE_WEAPON)
3016                         {
3017                             pPlayer->subweapon |= (1<<FLAMETHROWER_WEAPON);
3018                             weaponNum = FLAMETHROWER_WEAPON;
3019                         }
3020                         else
3021                             pPlayer->subweapon &= ~(1<<FLAMETHROWER_WEAPON);
3022                     }
3023 
3024                     if (pPlayer->holster_weapon)
3025                     {
3026                         playerBits |= BIT(SK_HOLSTER);
3027                         pPlayer->weapon_pos = WEAPON_POS_LOWER;
3028                     }
3029                     else if ((uint32_t)weaponNum < MAX_WEAPONS && (pPlayer->gotweapon & (1<<weaponNum)) && (uint32_t)pPlayer->curr_weapon != weaponNum)
3030                         switch (weaponNum)
3031                         {
3032                         case PISTOL_WEAPON:
3033                         case SHOTGUN_WEAPON:
3034                         case CHAINGUN_WEAPON:
3035                         case RPG_WEAPON:
3036                         case DEVISTATOR_WEAPON:
3037                         case FREEZE_WEAPON:
3038                         case GROW_WEAPON:
3039                         case SHRINKER_WEAPON:
3040                         case FLAMETHROWER_WEAPON:
3041                             if (pPlayer->ammo_amount[weaponNum] == 0 && pPlayer->show_empty_weapon == 0)
3042                             {
3043                                 pPlayer->last_full_weapon = pPlayer->curr_weapon;
3044                                 pPlayer->show_empty_weapon = 32;
3045                             }
3046                             fallthrough__;
3047                         case KNEE_WEAPON:
3048                         case HANDREMOTE_WEAPON:
3049                             P_AddWeapon(pPlayer, weaponNum, 1);
3050                             break;
3051                         case HANDBOMB_WEAPON:
3052                         case TRIPBOMB_WEAPON:
3053                             if (pPlayer->ammo_amount[weaponNum] > 0 && (pPlayer->gotweapon & (1<<weaponNum)))
3054                                 P_AddWeapon(pPlayer, weaponNum, 1);
3055                             break;
3056                         }
3057                 }
3058             }
3059         }
3060 
3061         if (TEST_SYNC_KEY(playerBits, SK_HOLODUKE) && (pPlayer->newowner == -1 || pPlayer->holoduke_on != -1))
3062         {
3063             if (pPlayer->holoduke_on == -1)
3064             {
3065                 if (VM_OnEvent(EVENT_HOLODUKEON,g_player[playerNum].ps->i,playerNum) == 0)
3066                 {
3067                     if (pPlayer->inv_amount[GET_HOLODUKE] > 0)
3068                     {
3069                         pPlayer->inven_icon = ICON_HOLODUKE;
3070 
3071                         if (pPlayer->cursectnum > -1)
3072                         {
3073                             int const i = A_InsertSprite(pPlayer->cursectnum, pPlayer->pos.x, pPlayer->pos.y,
3074                                 pPlayer->pos.z+(30<<8), APLAYER, -64, 0, 0, fix16_to_int(pPlayer->q16ang), 0, 0, -1, 10);
3075                             pPlayer->holoduke_on = i;
3076                             T4(i) = T5(i) = 0;
3077                             sprite[i].yvel = playerNum;
3078                             sprite[i].extra = 0;
3079                             P_DoQuote(QUOTE_HOLODUKE_ON,pPlayer);
3080 #ifndef EDUKE32_STANDALONE
3081                             if (!FURY)
3082                                 A_PlaySound(TELEPORTER,pPlayer->holoduke_on);
3083 #endif
3084                         }
3085                     }
3086                     else P_DoQuote(QUOTE_HOLODUKE_NOT_FOUND,pPlayer);
3087                 }
3088             }
3089             else
3090             {
3091                 if (VM_OnEvent(EVENT_HOLODUKEOFF,g_player[playerNum].ps->i,playerNum) == 0)
3092                 {
3093 #ifndef EDUKE32_STANDALONE
3094                     if (!FURY)
3095                         A_PlaySound(TELEPORTER,pPlayer->holoduke_on);
3096 #endif
3097                     pPlayer->holoduke_on = -1;
3098                     P_DoQuote(QUOTE_HOLODUKE_OFF,pPlayer);
3099                 }
3100             }
3101         }
3102 
3103         if (TEST_SYNC_KEY(playerBits, SK_MEDKIT))
3104         {
3105             if (VM_OnEvent(EVENT_USEMEDKIT,g_player[playerNum].ps->i,playerNum) == 0)
3106             {
3107                 if (pPlayer->inv_amount[GET_FIRSTAID] > 0 && sprite[pPlayer->i].extra < pPlayer->max_player_health)
3108                 {
3109                     int healthDiff = pPlayer->max_player_health-sprite[pPlayer->i].extra;
3110 
3111                     if (pPlayer->inv_amount[GET_FIRSTAID] > healthDiff)
3112                     {
3113                         pPlayer->inv_amount[GET_FIRSTAID] -= healthDiff;
3114                         sprite[pPlayer->i].extra = pPlayer->max_player_health;
3115                         pPlayer->inven_icon = ICON_FIRSTAID;
3116                     }
3117                     else
3118                     {
3119                         sprite[pPlayer->i].extra += pPlayer->inv_amount[GET_FIRSTAID];
3120                         pPlayer->inv_amount[GET_FIRSTAID] = 0;
3121                         P_SelectNextInvItem(pPlayer);
3122                     }
3123 #ifndef EDUKE32_STANDALONE
3124                     if (!FURY)
3125                         A_PlaySound(DUKE_USEMEDKIT,pPlayer->i);
3126 #endif
3127                 }
3128             }
3129         }
3130 
3131         if ((pPlayer->newowner == -1 || pPlayer->jetpack_on) && TEST_SYNC_KEY(playerBits, SK_JETPACK))
3132         {
3133             if (VM_OnEvent(EVENT_USEJETPACK,g_player[playerNum].ps->i,playerNum) == 0)
3134             {
3135                 if (pPlayer->inv_amount[GET_JETPACK] > 0)
3136                 {
3137                     pPlayer->jetpack_on = !pPlayer->jetpack_on;
3138                     if (pPlayer->jetpack_on)
3139                     {
3140                         pPlayer->inven_icon = ICON_JETPACK;
3141                         if (pPlayer->scream_voice > FX_Ok)
3142                         {
3143                             FX_StopSound(pPlayer->scream_voice);
3144                             pPlayer->scream_voice = -1;
3145                         }
3146 #ifndef EDUKE32_STANDALONE
3147                         if (!FURY)
3148                             A_PlaySound(DUKE_JETPACK_ON,pPlayer->i);
3149 #endif
3150                         P_DoQuote(QUOTE_JETPACK_ON,pPlayer);
3151                     }
3152                     else
3153                     {
3154                         pPlayer->hard_landing = 0;
3155                         pPlayer->vel.z = 0;
3156 #ifndef EDUKE32_STANDALONE
3157                         if (!FURY)
3158                         {
3159                             A_PlaySound(DUKE_JETPACK_OFF, pPlayer->i);
3160                             S_StopEnvSound(DUKE_JETPACK_IDLE, pPlayer->i);
3161                             S_StopEnvSound(DUKE_JETPACK_ON, pPlayer->i);
3162                         }
3163 #endif
3164                         P_DoQuote(QUOTE_JETPACK_OFF,pPlayer);
3165                     }
3166                 }
3167                 else P_DoQuote(QUOTE_JETPACK_NOT_FOUND,pPlayer);
3168             }
3169         }
3170 
3171         if (TEST_SYNC_KEY(playerBits, SK_TURNAROUND) && pPlayer->one_eighty_count == 0)
3172             if (VM_OnEvent(EVENT_TURNAROUND,pPlayer->i,playerNum) == 0)
3173                 pPlayer->one_eighty_count = -1024;
3174     }
3175 }
3176 
3177 int32_t A_CheckHitSprite(int spriteNum, int16_t *hitSprite)
3178 {
3179     hitdata_t hitData;
3180     int32_t   zOffset = 0;
3181 
3182     if (A_CheckEnemySprite(&sprite[spriteNum]))
3183         zOffset = (42 << 8);
3184     else if (PN(spriteNum) == APLAYER)
3185         zOffset = (39 << 8);
3186 
3187     SZ(spriteNum) -= zOffset;
3188     hitscan((const vec3_t *)&sprite[spriteNum], SECT(spriteNum), sintable[(SA(spriteNum) + 512) & 2047],
3189             sintable[SA(spriteNum) & 2047], 0, &hitData, CLIPMASK1);
3190     SZ(spriteNum) += zOffset;
3191 
3192     if (hitSprite)
3193         *hitSprite = hitData.sprite;
3194 
3195     if (hitData.wall >= 0 && (wall[hitData.wall].cstat&16) && A_CheckEnemySprite( &sprite[spriteNum]))
3196         return 1<<30;
3197 
3198     return FindDistance2D(hitData.pos.x-SX(spriteNum),hitData.pos.y-SY(spriteNum));
3199 }
3200 
3201 static int P_FindWall(DukePlayer_t *pPlayer, int *hitWall)
3202 {
3203     hitdata_t hitData;
3204 
3205     hitscan((const vec3_t *)pPlayer, pPlayer->cursectnum, sintable[(fix16_to_int(pPlayer->q16ang) + 512) & 2047],
3206             sintable[fix16_to_int(pPlayer->q16ang) & 2047], 0, &hitData, CLIPMASK0);
3207 
3208     *hitWall = hitData.wall;
3209 
3210     if (hitData.wall < 0)
3211         return INT32_MAX;
3212 
3213     return FindDistance2D(hitData.pos.x - pPlayer->pos.x, hitData.pos.y - pPlayer->pos.y);
3214 }
3215 
3216 // returns 1 if sprite i should not be considered by neartag
3217 static int32_t our_neartag_blacklist(int32_t spriteNum)
3218 {
3219     return sprite[spriteNum].picnum >= SECTOREFFECTOR__STATIC && sprite[spriteNum].picnum <= GPSPEED__STATIC;
3220 }
3221 
3222 static void G_ClearCameras(DukePlayer_t *p)
3223 {
3224     G_ClearCameraView(p);
3225 
3226     if (I_EscapeTrigger())
3227         I_EscapeTriggerClear();
3228 }
3229 
3230 void P_CheckSectors(int playerNum)
3231 {
3232     auto const pPlayer = g_player[playerNum].ps;
3233 
3234     if (pPlayer->cursectnum > -1)
3235     {
3236         sectortype *const pSector = &sector[pPlayer->cursectnum];
3237         switch ((uint16_t)pSector->lotag)
3238         {
3239             case 32767:
3240                 pSector->lotag = 0;
3241                 P_DoQuote(QUOTE_FOUND_SECRET, pPlayer);
3242                 pPlayer->secret_rooms++;
3243                 return;
3244 
3245             case UINT16_MAX:
3246                 pSector->lotag = 0;
3247                 P_EndLevel();
3248                 return;
3249 
3250             case UINT16_MAX-1:
3251                 pSector->lotag           = 0;
3252                 pPlayer->timebeforeexit  = GAMETICSPERSEC * 8;
3253                 pPlayer->customexitsound = pSector->hitag;
3254                 return;
3255 
3256             default:
3257                 if (pSector->lotag >= 10000 && pSector->lotag < 16383)
3258                 {
3259                     if (playerNum == screenpeek || (g_gametypeFlags[ud.coop] & GAMETYPE_COOPSOUND))
3260                         A_PlaySound(pSector->lotag - 10000, pPlayer->i);
3261                     pSector->lotag = 0;
3262                 }
3263                 break;
3264         }
3265     }
3266 
3267     //After this point the the player effects the map with space
3268 
3269     if (pPlayer->gm &MODE_TYPE || sprite[pPlayer->i].extra <= 0)
3270         return;
3271 
3272     if (TEST_SYNC_KEY(g_player[playerNum].input.bits, SK_OPEN))
3273     {
3274         if (VM_OnEvent(EVENT_USE, pPlayer->i, playerNum) != 0)
3275             g_player[playerNum].input.bits &= ~BIT(SK_OPEN);
3276     }
3277 
3278 #ifndef EDUKE32_STANDALONE
3279     if (!FURY && ud.cashman && TEST_SYNC_KEY(g_player[playerNum].input.bits, SK_OPEN))
3280         A_SpawnMultiple(pPlayer->i, MONEY, 2);
3281 #endif
3282 
3283     if (pPlayer->newowner >= 0)
3284     {
3285         if (klabs(g_player[playerNum].input.svel) > 768 || klabs(g_player[playerNum].input.fvel) > 768)
3286         {
3287             G_ClearCameras(pPlayer);
3288             return;
3289         }
3290     }
3291 
3292     if (!TEST_SYNC_KEY(g_player[playerNum].input.bits, SK_OPEN) && !TEST_SYNC_KEY(g_player[playerNum].input.bits, SK_ESCAPE))
3293         pPlayer->toggle_key_flag = 0;
3294     else if (!pPlayer->toggle_key_flag)
3295     {
3296         int foundWall;
3297 
3298         int16_t nearSector, nearWall, nearSprite;
3299         int32_t nearDist;
3300 
3301         if (TEST_SYNC_KEY(g_player[playerNum].input.bits, SK_ESCAPE))
3302         {
3303             if (pPlayer->newowner >= 0)
3304                 G_ClearCameras(pPlayer);
3305             return;
3306         }
3307 
3308         nearSprite = -1;
3309         pPlayer->toggle_key_flag = 1;
3310         foundWall = -1;
3311 
3312         int wallDist = P_FindWall(pPlayer, &foundWall);
3313 
3314         if (foundWall >= 0 && wallDist < 1280 && wall[foundWall].overpicnum == MIRROR)
3315             if (wall[foundWall].lotag > 0 && !A_CheckSoundPlaying(pPlayer->i,wall[foundWall].lotag) && playerNum == screenpeek)
3316             {
3317                 A_PlaySound(wall[foundWall].lotag,pPlayer->i);
3318                 return;
3319             }
3320 
3321         if (foundWall >= 0 && (wall[foundWall].cstat&16))
3322             if (wall[foundWall].lotag)
3323                 return;
3324 
3325         int const intang = fix16_to_int(pPlayer->oq16ang);
3326 
3327         if (pPlayer->newowner >= 0)
3328             neartag(pPlayer->opos.x, pPlayer->opos.y, pPlayer->opos.z, sprite[pPlayer->i].sectnum, intang, &nearSector,
3329                 &nearWall, &nearSprite, &nearDist, 1280, 1, our_neartag_blacklist);
3330         else
3331         {
3332             neartag(pPlayer->pos.x, pPlayer->pos.y, pPlayer->pos.z, sprite[pPlayer->i].sectnum, intang, &nearSector,
3333                 &nearWall, &nearSprite, &nearDist, 1280, 1, our_neartag_blacklist);
3334             if (nearSprite == -1 && nearWall == -1 && nearSector == -1)
3335                 neartag(pPlayer->pos.x, pPlayer->pos.y, pPlayer->pos.z+ZOFFSET3, sprite[pPlayer->i].sectnum, intang, &nearSector,
3336                     &nearWall, &nearSprite, &nearDist, 1280, 1, our_neartag_blacklist);
3337             if (nearSprite == -1 && nearWall == -1 && nearSector == -1)
3338                 neartag(pPlayer->pos.x, pPlayer->pos.y, pPlayer->pos.z+ZOFFSET2, sprite[pPlayer->i].sectnum, intang, &nearSector,
3339                     &nearWall, &nearSprite, &nearDist, 1280, 1, our_neartag_blacklist);
3340             if (nearSprite == -1 && nearWall == -1 && nearSector == -1)
3341             {
3342                 neartag(pPlayer->pos.x, pPlayer->pos.y, pPlayer->pos.z+ZOFFSET2, sprite[pPlayer->i].sectnum, intang, &nearSector,
3343                     &nearWall, &nearSprite, &nearDist, 1280, 3, our_neartag_blacklist);
3344                 if (nearSprite >= 0)
3345                 {
3346                     switch (DYNAMICTILEMAP(sprite[nearSprite].picnum))
3347                     {
3348                         case FEM1__STATIC:
3349                         case FEM2__STATIC:
3350                         case FEM3__STATIC:
3351                         case FEM4__STATIC:
3352                         case FEM5__STATIC:
3353                         case FEM6__STATIC:
3354                         case FEM7__STATIC:
3355                         case FEM8__STATIC:
3356                         case FEM9__STATIC:
3357                         case FEM10__STATIC:
3358                         case PODFEM1__STATIC:
3359                         case NAKED1__STATIC:
3360                         case STATUE__STATIC:
3361                         case TOUGHGAL__STATIC: return;
3362                     }
3363                 }
3364 
3365                 nearSprite = -1;
3366                 nearWall   = -1;
3367                 nearSector = -1;
3368             }
3369         }
3370 
3371         if (pPlayer->newowner == -1 && nearSprite == -1 && nearSector == -1 && nearWall == -1)
3372         {
3373             if (isanunderoperator(sector[sprite[pPlayer->i].sectnum].lotag))
3374                 nearSector = sprite[pPlayer->i].sectnum;
3375         }
3376 
3377         if (nearSector >= 0 && (sector[nearSector].lotag&16384))
3378             return;
3379 
3380         if (nearSprite == -1 && nearWall == -1)
3381         {
3382             if (pPlayer->cursectnum >= 0 && sector[pPlayer->cursectnum].lotag == 2)
3383             {
3384                 if (A_CheckHitSprite(pPlayer->i, &nearSprite) > 1280)
3385                     nearSprite = -1;
3386             }
3387         }
3388 
3389         if (nearSprite >= 0)
3390         {
3391             if (P_ActivateSwitch(playerNum, nearSprite, 1))
3392                 return;
3393 
3394             switch (DYNAMICTILEMAP(sprite[nearSprite].picnum))
3395             {
3396 #ifndef EDUKE32_STANDALONE
3397             case TOILET__STATIC:
3398             case STALL__STATIC:
3399                 if (pPlayer->last_pissed_time == 0)
3400                 {
3401                     if (ud.lockout == 0)
3402                         A_PlaySound(DUKE_URINATE, pPlayer->i);
3403 
3404                     pPlayer->last_pissed_time = GAMETICSPERSEC * 220;
3405                     pPlayer->transporter_hold = 29 * 2;
3406 
3407                     if (pPlayer->holster_weapon == 0)
3408                     {
3409                         pPlayer->holster_weapon = 1;
3410                         pPlayer->weapon_pos     = -1;
3411                     }
3412 
3413                     if (sprite[pPlayer->i].extra <= (pPlayer->max_player_health - (pPlayer->max_player_health / 10)))
3414                     {
3415                         sprite[pPlayer->i].extra += pPlayer->max_player_health / 10;
3416                         pPlayer->last_extra = sprite[pPlayer->i].extra;
3417                     }
3418                     else if (sprite[pPlayer->i].extra < pPlayer->max_player_health)
3419                         sprite[pPlayer->i].extra = pPlayer->max_player_health;
3420                 }
3421                 else if (!A_CheckSoundPlaying(nearSprite,FLUSH_TOILET))
3422                     A_PlaySound(FLUSH_TOILET,nearSprite);
3423                 return;
3424 
3425             case NUKEBUTTON__STATIC:
3426             {
3427                 int wallNum;
3428 
3429                 P_FindWall(pPlayer, &wallNum);
3430 
3431                 if (wallNum >= 0 && wall[wallNum].overpicnum == 0)
3432                 {
3433                     if (actor[nearSprite].t_data[0] == 0)
3434                     {
3435                         if (ud.noexits && (g_netServer || ud.multimode > 1))
3436                         {
3437                             // NUKEBUTTON frags the player
3438                             actor[pPlayer->i].picnum = NUKEBUTTON;
3439                             actor[pPlayer->i].extra  = 250;
3440                         }
3441                         else
3442                         {
3443                             actor[nearSprite].t_data[0] = 1;
3444                             sprite[nearSprite].owner    = pPlayer->i;
3445                             // assignment of buttonpalette here is not a bug
3446                             ud.secretlevel =
3447                             (pPlayer->buttonpalette = sprite[nearSprite].pal) ? sprite[nearSprite].lotag : 0;
3448                         }
3449                     }
3450                 }
3451                 return;
3452             }
3453 
3454             case WATERFOUNTAIN__STATIC:
3455                 if (actor[nearSprite].t_data[0] != 1)
3456                 {
3457                     actor[nearSprite].t_data[0] = 1;
3458                     sprite[nearSprite].owner    = pPlayer->i;
3459 
3460                     if (sprite[pPlayer->i].extra < pPlayer->max_player_health)
3461                     {
3462                         sprite[pPlayer->i].extra++;
3463                         A_PlaySound(DUKE_DRINKING,pPlayer->i);
3464                     }
3465                 }
3466                 return;
3467 
3468             case PLUG__STATIC:
3469                 A_PlaySound(SHORT_CIRCUIT, pPlayer->i);
3470                 sprite[pPlayer->i].extra -= 2+(krand()&3);
3471 
3472                 P_PalFrom(pPlayer, 32, 48,48,64);
3473                 break;
3474 #endif
3475 
3476             case VIEWSCREEN__STATIC:
3477             case VIEWSCREEN2__STATIC:
3478                 // Try to find a camera sprite for the viewscreen.
3479                 for (bssize_t SPRITES_OF(STAT_ACTOR, spriteNum))
3480                 {
3481                     if (PN(spriteNum) == CAMERA1 && SP(spriteNum) == 0 && sprite[nearSprite].hitag == SLT(spriteNum))
3482                     {
3483                         sprite[spriteNum].yvel   = 1;  // Using this camera
3484                         A_PlaySound(MONITOR_ACTIVE, pPlayer->i);
3485                         sprite[nearSprite].owner = spriteNum;
3486                         sprite[nearSprite].yvel  = 1;  // VIEWSCREEN_YVEL
3487                         g_curViewscreen          = nearSprite;
3488 
3489                         int const playerSectnum = pPlayer->cursectnum;
3490                         pPlayer->cursectnum     = SECT(spriteNum);
3491                         P_UpdateScreenPal(pPlayer);
3492                         pPlayer->cursectnum     = playerSectnum;
3493                         pPlayer->newowner       = spriteNum;
3494 
3495                         P_UpdatePosWhenViewingCam(pPlayer);
3496 
3497                         return;
3498                     }
3499                 }
3500 
3501                 G_ClearCameras(pPlayer);
3502                 return;
3503             }  // switch
3504         }
3505 
3506         if (TEST_SYNC_KEY(g_player[playerNum].input.bits, SK_OPEN) == 0)
3507             return;
3508 
3509         if (pPlayer->newowner >= 0)
3510         {
3511             G_ClearCameras(pPlayer);
3512             return;
3513         }
3514 
3515         if (nearWall == -1 && nearSector == -1 && nearSprite == -1)
3516         {
3517             if (klabs(A_GetHitscanRange(pPlayer->i)) < 512)
3518             {
3519                 A_PlaySound(((krand()&255) < 16) ? DUKE_SEARCH2 : DUKE_SEARCH, pPlayer->i);
3520                 return;
3521             }
3522         }
3523 
3524         if (nearWall >= 0)
3525         {
3526             if (wall[nearWall].lotag > 0 && CheckDoorTile(wall[nearWall].picnum))
3527             {
3528                 if (foundWall == nearWall || foundWall == -1)
3529                     P_ActivateSwitch(playerNum,nearWall,0);
3530                 return;
3531             }
3532         }
3533 
3534         if (nearSector >= 0 && (sector[nearSector].lotag&16384) == 0 &&
3535                 isanearoperator(sector[nearSector].lotag))
3536         {
3537             for (bssize_t SPRITES_OF_SECT(nearSector, spriteNum))
3538             {
3539                 if (PN(spriteNum) == ACTIVATOR || PN(spriteNum) == MASTERSWITCH)
3540                     return;
3541             }
3542 
3543             G_OperateSectors(nearSector,pPlayer->i);
3544         }
3545         else if ((sector[sprite[pPlayer->i].sectnum].lotag&16384) == 0)
3546         {
3547             if (isanunderoperator(sector[sprite[pPlayer->i].sectnum].lotag))
3548             {
3549                 for (bssize_t SPRITES_OF_SECT(sprite[pPlayer->i].sectnum, spriteNum))
3550                 {
3551                     if (PN(spriteNum) == ACTIVATOR || PN(spriteNum) == MASTERSWITCH)
3552                         return;
3553                 }
3554 
3555                 G_OperateSectors(sprite[pPlayer->i].sectnum,pPlayer->i);
3556             }
3557             else P_ActivateSwitch(playerNum,nearWall,0);
3558         }
3559     }
3560 }
3561 
3562