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] == §or[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] == §or[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] == §or[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 = §or[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, §Num);
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, §Num);
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, §Num);
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 = §or[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, §Num, 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, §Num, 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 = §or[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