actors.cpp 329 KB
Newer Older
1
2
//-------------------------------------------------------------------------
/*
3
Copyright (C) 2016 EDuke32 developers and contributors
4

5
This file is part of EDuke32.
6
7
8
9
10
11
12
13
14
15
16
17
18

EDuke32 is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
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
24
#define actors_c_

Evan Ramos's avatar
Evan Ramos committed
25
26
27
28
#ifndef EDUKE32_STANDALONE
#include <map>
#endif

29
30
#include "duke3d.h"

31
32
33
34
35
36
#if KRANDDEBUG
# define ACTOR_STATIC
#else
# define ACTOR_STATIC static
#endif

37
38
uint8_t g_radiusDmgStatnums[(MAXSTATUS+7)>>3];

Richard Gobeille's avatar
Richard Gobeille committed
39
#define DELETE_SPRITE_AND_CONTINUE(KX) do { A_DeleteSprite(KX); goto next_sprite; } while (0)
40

41
42
int32_t otherp;

43
int G_SetInterpolation(int32_t *const posptr)
44
{
45
    if (g_interpolationCnt >= MAXINTERPOLATIONS)
46
47
        return 1;

48
    for (bssize_t i = 0; i < g_interpolationCnt; ++i)
49
50
51
        if (curipos[i] == posptr)
            return 0;

52
53
54
    curipos[g_interpolationCnt] = posptr;
    oldipos[g_interpolationCnt] = *posptr;
    g_interpolationCnt++;
55
    return 0;
56
57
}

58
void G_StopInterpolation(const int32_t * const posptr)
59
{
60
    for (bssize_t i = 0; i < g_interpolationCnt; ++i)
61
62
        if (curipos[i] == posptr)
        {
63
64
65
66
            g_interpolationCnt--;
            oldipos[i] = oldipos[g_interpolationCnt];
            bakipos[i] = bakipos[g_interpolationCnt];
            curipos[i] = curipos[g_interpolationCnt];
67
68
69
        }
}

70
void G_DoInterpolations(int smoothRatio)
71
{
72
73
74
    if (g_interpolationLock++)
        return;

75
    int32_t ndelta = 0;
Richard Gobeille's avatar
Richard Gobeille committed
76

77
    for (bssize_t i = 0, j = 0; i < g_interpolationCnt; ++i)
78
    {
79
        int32_t const odelta = ndelta;
Richard Gobeille's avatar
Richard Gobeille committed
80
81
82
        bakipos[i] = *curipos[i];
        ndelta = (*curipos[i]) - oldipos[i];
        if (odelta != ndelta)
83
            j = mulscale16(ndelta, smoothRatio);
Richard Gobeille's avatar
Richard Gobeille committed
84
        *curipos[i] = oldipos[i] + j;
85
86
87
    }
}

88
89
90
void G_ClearCameraView(DukePlayer_t *ps)
{
    ps->newowner = -1;
Richard Gobeille's avatar
Richard Gobeille committed
91
    ps->pos = ps->opos;
92
    ps->q16ang = ps->oq16ang;
93
94
95
96

    updatesector(ps->pos.x, ps->pos.y, &ps->cursectnum);
    P_UpdateScreenPal(ps);

97
    for (bssize_t SPRITES_OF(STAT_ACTOR, k))
98
99
100
101
        if (sprite[k].picnum==CAMERA1)
            sprite[k].yvel = 0;
}

Richard Gobeille's avatar
Richard Gobeille committed
102
103
void A_RadiusDamageObject_Internal(int const spriteNum, int const otherSprite, int const blastRadius, int spriteDist,
                                   int const zOffset, int const dmg1, int dmg2, int dmg3, int dmg4)
104
{
105
    auto const pSprite = (uspriteptr_t)&sprite[spriteNum];
Richard Gobeille's avatar
Richard Gobeille committed
106
    auto const pOther  = &sprite[otherSprite];
Richard Gobeille's avatar
Richard Gobeille committed
107

Evan Ramos's avatar
Evan Ramos committed
108
109
110
#ifndef EDUKE32_STANDALONE
    if (WORLDTOUR && pSprite->picnum == FLAMETHROWERFLAME)
    {
111
112
        // enemies in WT don't damage other enemies of the same type with FLAMETHROWERFLAME
        if (sprite[pSprite->owner].picnum == pOther->picnum && pOther->picnum != APLAYER)
Evan Ramos's avatar
Evan Ramos committed
113
114
115
            return;
    }
#endif
116

Richard Gobeille's avatar
Richard Gobeille committed
117
    // DEFAULT, ZOMBIEACTOR, MISC
118
119
120
121
122
    if (pOther->statnum == STAT_DEFAULT || pOther->statnum == STAT_ZOMBIEACTOR || pOther->statnum == STAT_MISC
#ifndef EDUKE32_STANDALONE
        || (!FURY && AFLAMABLE(pOther->picnum))
#endif
        )
Richard Gobeille's avatar
Richard Gobeille committed
123
    {
Richard Gobeille's avatar
Richard Gobeille committed
124
#ifndef EDUKE32_STANDALONE
Richard Gobeille's avatar
Richard Gobeille committed
125
        if (pSprite->picnum != SHRINKSPARK || (pOther->cstat&257))
Richard Gobeille's avatar
Richard Gobeille committed
126
#endif
Richard Gobeille's avatar
Richard Gobeille committed
127
128
129
        {
            if (A_CheckEnemySprite(pOther) && !cansee(pOther->x, pOther->y, pOther->z+zOffset, pOther->sectnum, pSprite->x, pSprite->y, pSprite->z+zOffset, pSprite->sectnum))
                return;
130

Richard Gobeille's avatar
Richard Gobeille committed
131
132
133
134
135
136
#ifndef EDUKE32_STANDALONE
            if (!FURY)
                A_DamageObject_Duke3D(otherSprite, spriteNum);
            else
#endif
                A_DamageObject_Generic(otherSprite, spriteNum);
Richard Gobeille's avatar
Richard Gobeille committed
137
138
139
140
141
142
143
        }
    }
    else if (pOther->extra >= 0 && (uspriteptr_t)pOther != pSprite && ((pOther->cstat & 257) ||
#ifndef EDUKE32_STANDALONE
        pOther->picnum == TRIPBOMB || pOther->picnum == QUEBALL || pOther->picnum == STRIPEBALL || pOther->picnum == DUKELYINGDEAD ||
#endif
        A_CheckEnemySprite(pOther)))
144
    {
Richard Gobeille's avatar
Richard Gobeille committed
145
146
147
148
149
150
151
152
#ifndef EDUKE32_STANDALONE
        if ((pSprite->picnum == SHRINKSPARK && pOther->picnum != SHARK && (otherSprite == pSprite->owner || pOther->xrepeat < 24))
            || (pSprite->picnum == MORTER && otherSprite == pSprite->owner))
            return;
#endif
        if (spriteDist >= blastRadius || !cansee(pOther->x, pOther->y, pOther->z - ZOFFSET3, pOther->sectnum,
                                                 pSprite->x, pSprite->y, pSprite->z - ZOFFSET4, pSprite->sectnum))
            return;
153

Richard Gobeille's avatar
Richard Gobeille committed
154
155
156
        if (A_CheckSpriteFlags(otherSprite, SFLAG_DAMAGEEVENT))
            if (VM_OnEventWithReturn(EVENT_DAMAGESPRITE, spriteNum, -1, otherSprite) < 0)
                return;
157

Richard Gobeille's avatar
Richard Gobeille committed
158
159
160
161
162
163
164
165
166
167
168
169
170
171
        auto &dmgActor = actor[otherSprite];

        dmgActor.ang = getangle(pOther->x - pSprite->x, pOther->y - pSprite->y);

        if ((pOther->extra > 0 && ((A_CheckSpriteFlags(spriteNum, SFLAG_PROJECTILE) && SpriteProjectile[spriteNum].workslike & PROJECTILE_RADIUS_PICNUM)
#ifndef EDUKE32_STANDALONE
            || pSprite->picnum == RPG
#endif
            ))
#ifndef EDUKE32_STANDALONE
            || (pSprite->picnum == SHRINKSPARK)
#endif
            )
            dmgActor.picnum = pSprite->picnum;
Evan Ramos's avatar
Evan Ramos committed
172
173
174
175
176
#ifndef EDUKE32_STANDALONE
        else if (WORLDTOUR && (pSprite->picnum == FLAMETHROWERFLAME || pSprite->picnum == LAVAPOOL
                 || (pSprite->picnum == FIREBALL && sprite[pSprite->owner].picnum == APLAYER)))
            dmgActor.picnum = FLAMETHROWERFLAME;
#endif
177
178
        else
            dmgActor.picnum = RADIUSEXPLOSION;
Richard Gobeille's avatar
Richard Gobeille committed
179
180

#ifndef EDUKE32_STANDALONE
Evan Ramos's avatar
Evan Ramos committed
181
        if (pSprite->picnum != SHRINKSPARK && (!WORLDTOUR || pSprite->picnum != LAVAPOOL))
Richard Gobeille's avatar
Richard Gobeille committed
182
#endif
183
        {
Richard Gobeille's avatar
Richard Gobeille committed
184
185
            // this is really weird
            int const k = blastRadius/3;
186
            int dmgBase = 0, dmgFuzz = 1;
187

Richard Gobeille's avatar
Richard Gobeille committed
188
            if (spriteDist < k)
Richard Gobeille's avatar
Richard Gobeille committed
189
                dmgBase = dmg3, dmgFuzz = dmg4;
Richard Gobeille's avatar
Richard Gobeille committed
190
            else if (spriteDist < k*2)
Richard Gobeille's avatar
Richard Gobeille committed
191
                dmgBase = dmg2, dmgFuzz = dmg3;
Richard Gobeille's avatar
Richard Gobeille committed
192
            else if (spriteDist < blastRadius)
Richard Gobeille's avatar
Richard Gobeille committed
193
194
195
196
197
198
                dmgBase = dmg1, dmgFuzz = dmg2;

            if (dmgBase == dmgFuzz)
                ++dmgFuzz;

            dmgActor.extra = dmgBase + (krand()%(dmgFuzz-dmgBase));
199

Richard Gobeille's avatar
Richard Gobeille committed
200
            if (!A_CheckSpriteFlags(otherSprite, SFLAG_NODAMAGEPUSH))
Richard Gobeille's avatar
Richard Gobeille committed
201
            {
Richard Gobeille's avatar
Richard Gobeille committed
202
203
204
                if (pOther->xvel < 0) pOther->xvel = 0;
                pOther->xvel += (pSprite->extra<<2);
            }
205

Richard Gobeille's avatar
Richard Gobeille committed
206
207
            if (A_CheckSpriteFlags(otherSprite, SFLAG_DAMAGEEVENT))
                VM_OnEventWithReturn(EVENT_POSTDAMAGESPRITE, spriteNum, -1, otherSprite);
208

Richard Gobeille's avatar
Richard Gobeille committed
209
210
211
212
#ifndef EDUKE32_STANDALONE
            if (!FURY)
            {
                switch (DYNAMICTILEMAP(pOther->picnum))
Richard Gobeille's avatar
Richard Gobeille committed
213
                {
Richard Gobeille's avatar
Richard Gobeille committed
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
                    case PODFEM1__STATIC:
                    case FEM1__STATIC:
                    case FEM2__STATIC:
                    case FEM3__STATIC:
                    case FEM4__STATIC:
                    case FEM5__STATIC:
                    case FEM6__STATIC:
                    case FEM7__STATIC:
                    case FEM8__STATIC:
                    case FEM9__STATIC:
                    case FEM10__STATIC:
                    case STATUE__STATIC:
                    case STATUEFLASH__STATIC:
                    case SPACEMARINE__STATIC:
                    case QUEBALL__STATIC:
                    case STRIPEBALL__STATIC:
Richard Gobeille's avatar
Richard Gobeille committed
230
                        A_DamageObject_Duke3D(otherSprite, spriteNum);
Richard Gobeille's avatar
Richard Gobeille committed
231
                        break;
232
                }
233
            }
Richard Gobeille's avatar
Richard Gobeille committed
234
235
236
237
238
#endif
        }
#ifndef EDUKE32_STANDALONE
        else if (!FURY && pSprite->extra == 0) dmgActor.extra = 0;
#endif
Richard Gobeille's avatar
Richard Gobeille committed
239

Richard Gobeille's avatar
Richard Gobeille committed
240
241
242
243
244
245
        if (pOther->picnum != RADIUSEXPLOSION &&
            pSprite->owner >= 0 && sprite[pSprite->owner].statnum < MAXSTATUS)
        {
            if (pOther->picnum == APLAYER)
            {
                auto pPlayer = g_player[P_GetP(pOther)].ps;
Richard Gobeille's avatar
Richard Gobeille committed
246

Richard Gobeille's avatar
Richard Gobeille committed
247
248
249
                if (pPlayer->newowner >= 0)
                    G_ClearCameraView(pPlayer);
            }
Richard Gobeille's avatar
Richard Gobeille committed
250

Richard Gobeille's avatar
Richard Gobeille committed
251
            dmgActor.owner = pSprite->owner;
252
253
        }
    }
Richard Gobeille's avatar
Richard Gobeille committed
254
}
255

256
#define MAXDAMAGESECTORS 128
257

Richard Gobeille's avatar
Richard Gobeille committed
258
259
260
261
262
void A_RadiusDamage(int const spriteNum, int const blastRadius, int const dmg1, int const dmg2, int const dmg3, int const dmg4)
{
    // Allow checking for radius damage in EVENT_DAMAGE(SPRITE/WALL/FLOOR/CEILING) events.
    decltype(ud.returnvar) const parms = { blastRadius, dmg1, dmg2, dmg3, dmg4 };
    Bmemcpy(ud.returnvar, parms, sizeof(parms));
263

Richard Gobeille's avatar
Richard Gobeille committed
264
    auto const pSprite = (uspriteptr_t)&sprite[spriteNum];
Richard Gobeille's avatar
Richard Gobeille committed
265

266
267
268
    int16_t numSectors, sectorList[MAXDAMAGESECTORS];
    uint8_t * const sectorMap = (uint8_t *)Balloca((numsectors+7)>>3);
    bfirst_search_init(sectorList, sectorMap, &numSectors, numsectors, pSprite->sectnum);
269

Richard Gobeille's avatar
Richard Gobeille committed
270
#ifndef EDUKE32_STANDALONE
271
272
    int wallDamage = true;

273
    // rockets from the Devastator skip propagating damage to other sectors
274
    if (!FURY && (pSprite->picnum == RPG && pSprite->xrepeat < 11))
275
        wallDamage = false;
Richard Gobeille's avatar
Richard Gobeille committed
276
277
#endif

278
279
280
281
282
283
284
285
    uint8_t *wallTouched;
    wallTouched = (uint8_t *)Balloca((numwalls+7)>>3);
    Bmemset(wallTouched, 0, (numwalls+7)>>3);

    uint8_t *wallCanSee;
    wallCanSee = (uint8_t *)Balloca((numwalls+7)>>3);
    Bmemset(wallCanSee, 0, (numwalls+7)>>3);

Richard Gobeille's avatar
Richard Gobeille committed
286
287
    for (int sectorCount=0; sectorCount < numSectors; ++sectorCount)
    {
288
        int const   sectorNum  = sectorList[sectorCount];
Richard Gobeille's avatar
Richard Gobeille committed
289
        auto const &listSector = sector[sectorNum];
Richard Gobeille's avatar
Richard Gobeille committed
290

291
292
        vec2_t  closest  = {};
        int32_t distance = INT32_MAX;
293

Richard Gobeille's avatar
Richard Gobeille committed
294
295
        int const startWall = listSector.wallptr;
        int const endWall   = listSector.wallnum + startWall;
296

Richard Gobeille's avatar
Richard Gobeille committed
297
298
        int w = startWall;
        
Richard Gobeille's avatar
Richard Gobeille committed
299
        for (auto pWall = (uwallptr_t)&wall[startWall]; w < endWall; ++w, ++pWall)
Richard Gobeille's avatar
Richard Gobeille committed
300
        {
301
302
            vec2_t  p        = pSprite->pos.vec2;
            int32_t walldist = blastRadius - 1;
Richard Gobeille's avatar
Richard Gobeille committed
303

304
305
            if (bitmap_test(wallTouched, w) == 0)
                walldist = getwalldist(p, w, &p);
306

307
            if (walldist < blastRadius)
Richard Gobeille's avatar
Richard Gobeille committed
308
            {
309
310
311
312
313
                if (walldist < distance)
                {
                    distance = walldist;
                    closest  = p;
                }
Richard Gobeille's avatar
Richard Gobeille committed
314

315
316
317
                int16_t aSector = sectorNum;
                vec3_t  vect    = { (((pWall->x + wall[pWall->point2].x) >> 1) + pSprite->x) >> 1,
                                    (((pWall->y + wall[pWall->point2].y) >> 1) + pSprite->y) >> 1, pSprite->z };
318

319
                updatesector(vect.x, vect.y, &aSector);
320

321
322
323
324
325
                if (aSector == -1)
                {
                    vect.vec2 = p;
                    aSector   = sectorNum;
                }
326

327
328
329
330
331
332
333
334
335
336
337
338
                bitmap_set(wallTouched, w);

                if (pWall->nextwall != -1)
                    bitmap_set(wallTouched, pWall->nextwall);

                if (bitmap_test(wallCanSee, w) == 1 || cansee(vect.x, vect.y, vect.z, aSector, pSprite->x, pSprite->y, pSprite->z, pSprite->sectnum))
                {
                    bitmap_set(wallCanSee, w);

                    if (pWall->nextwall != -1)
                        bitmap_set(wallCanSee, pWall->nextwall);

339
340
341
342
#ifndef EDUKE32_STANDALONE
                    if (wallDamage)
#endif
                        A_DamageWall_Internal(spriteNum, w, { p.x, p.y, pSprite->z }, pSprite->picnum);
343
344
345
346
347
348
349
350
351
352
353
354
                }

                int const nextSector = pWall->nextsector;

                if (nextSector >= 0)
                    bfirst_search_try(sectorList, sectorMap, &numSectors, nextSector);

                if (numSectors == MAXDAMAGESECTORS)
                {
                    OSD_Printf("Sprite %d tried to damage more than %d sectors!\n", spriteNum, MAXDAMAGESECTORS);
                    goto wallsfinished;
                }
355
            }
Richard Gobeille's avatar
Richard Gobeille committed
356
        }
357
358
359
360
361
362
363
364
365
366
367
368

        if (distance >= blastRadius)
            continue;

        int32_t floorZ, ceilZ;
        getzsofslope(sectorNum, closest.x, closest.y, &ceilZ, &floorZ);

        if (((ceilZ - pSprite->z) >> 8) < blastRadius)
            Sect_DamageCeiling_Internal(spriteNum, sectorNum);

        if (((pSprite->z - floorZ) >> 8) < blastRadius)
            Sect_DamageFloor_Internal(spriteNum, sectorNum);
Richard Gobeille's avatar
Richard Gobeille committed
369
370
    }

371
wallsfinished:
Richard Gobeille's avatar
Richard Gobeille committed
372
373
374
375
376
377
378
379
380
    int const randomZOffset = -ZOFFSET2 + (krand()&(ZOFFSET5-1));

    for (int sectorCount=0; sectorCount < numSectors; ++sectorCount)
    {
        int damageSprite = headspritesect[sectorList[sectorCount]];

        while (damageSprite >= 0)
        {
            int const nextSprite = nextspritesect[damageSprite];
Richard Gobeille's avatar
Richard Gobeille committed
381
            auto      pDamage    = &sprite[damageSprite];
Richard Gobeille's avatar
Richard Gobeille committed
382

383
            if (bitmap_test(g_radiusDmgStatnums, pDamage->statnum))
Richard Gobeille's avatar
Richard Gobeille committed
384
            {
385
386
387
388
                int spriteDist = dist(pSprite, pDamage);
                
                if (pDamage->picnum == APLAYER)
                    spriteDist = FindDistance3D(pSprite->x - pDamage->x, pSprite->y - pDamage->y, pSprite->z - (pDamage->z - PHEIGHT));
Richard Gobeille's avatar
Richard Gobeille committed
389
390
391
392
393
394

                if (spriteDist < blastRadius)
                    A_RadiusDamageObject_Internal(spriteNum, damageSprite, blastRadius, spriteDist, randomZOffset, dmg1, dmg2, dmg3, dmg4);
            }

            damageSprite = nextSprite;
395
396
397
398
        }
    }
}

399
400
401
402
// Maybe do a projectile transport via an SE7.
// <spritenum>: the projectile
// <i>: the SE7
// <fromunderp>: below->above change?
403
static int32_t Proj_MaybeDoTransport(int32_t spriteNum, uspriteptr_t const pSEffector, int32_t fromunderp, int32_t daz)
404
{
405
    if (((int32_t) totalclock & UINT8_MAX) == actor[spriteNum].lasttransport)
Richard Gobeille's avatar
Richard Gobeille committed
406
407
        return 0;

408
409
410
    auto const pSprite = &sprite[spriteNum];
    auto const otherse = (uspriteptr_t)&sprite[pSEffector->owner];

411
    actor[spriteNum].lasttransport = ((int32_t) totalclock & UINT8_MAX);
412

413
414
    pSprite->x += (otherse->x - pSEffector->x);
    pSprite->y += (otherse->y - pSEffector->y);
415

416
417
418
419
    // above->below
    pSprite->z = (!fromunderp) ? sector[otherse->sectnum].ceilingz - daz + sector[pSEffector->sectnum].floorz
                               : sector[otherse->sectnum].floorz - daz + sector[pSEffector->sectnum].ceilingz;
    // below->above
420

421
    actor[spriteNum].bpos = sprite[spriteNum].pos;
422
    changespritesect(spriteNum, otherse->sectnum);
423

Richard Gobeille's avatar
Richard Gobeille committed
424
    return 1;
425
426
427
428
}

// Check whether sprite <s> is on/in a non-SE7 water sector.
// <othersectptr>: if not NULL, the sector on the other side.
429
int A_CheckNoSE7Water(uspriteptr_t const pSprite, int sectNum, int sectLotag, int32_t *pOther)
430
{
431
    if (sectLotag == ST_1_ABOVE_WATER || sectLotag == ST_2_UNDERWATER)
432
    {
433
434
435
        int const otherSect =
        yax_getneighborsect(pSprite->x, pSprite->y, sectNum, sectLotag == ST_1_ABOVE_WATER ? YAX_FLOOR : YAX_CEILING);
        int const otherLotag = (sectLotag == ST_1_ABOVE_WATER) ? ST_2_UNDERWATER : ST_1_ABOVE_WATER;
436
437
438
439
440

        // If submerging, the lower sector MUST have lotag 2.
        // If emerging, the upper sector MUST have lotag 1.
        // This way, the x/y coordinates where above/below water
        // changes can happen are the same.
441
        if (otherSect >= 0 && sector[otherSect].lotag == otherLotag)
442
        {
443
444
            if (pOther)
                *pOther = otherSect;
445
446
447
448
449
450
451
452
453
454
455
            return 1;
        }
    }

    return 0;
}

// Check whether to do a z position update of sprite <spritenum>.
// Returns:
//  0 if no.
//  1 if yes, but stayed inside [actor[].ceilingz+1, actor[].floorz].
456
457
// <0 if yes, but passed a TROR no-SE7 water boundary. -returnvalue-1 is the
//       other-side sector number.
458
459
static int32_t A_CheckNeedZUpdate(int32_t spriteNum, int32_t zChange, int32_t *pZcoord,
    int32_t *ceilhit, int32_t *florhit)
460
{
461
462
    if (zChange == 0)
        return 0;
463

464
465
    auto const pSprite = (uspriteptr_t)&sprite[spriteNum];
    int const  newZ    = pSprite->z + (zChange >> 1);
466

467
    *pZcoord = newZ;
468

469
470
    int const clipDist = A_GetClipdist(spriteNum, -1);

471
    VM_GetZRange(spriteNum, ceilhit, florhit, pSprite->statnum == STAT_PROJECTILE ? clipDist << 3 : clipDist);
472

473
    if (newZ > actor[spriteNum].ceilingz && newZ <= actor[spriteNum].floorz)
474
475
476
        return 1;

#ifdef YAX_ENABLE
477
478
479
    int const sectNum   = pSprite->sectnum;
    int const sectLotag = sector[sectNum].lotag;
    int32_t   otherSect;
480

481
482
    // Non-SE7 water.
    // PROJECTILE_CHSECT
483
484
485
    if ((zChange < 0 && sectLotag == ST_2_UNDERWATER) || (zChange > 0 && sectLotag == ST_1_ABOVE_WATER))
    {
        if (A_CheckNoSE7Water(pSprite, sprite[spriteNum].sectnum, sectLotag, &otherSect))
486
        {
487
            A_Spawn(spriteNum, WATERSPLASH2);
488
489
490
            // NOTE: Don't tweak its z position afterwards like with
            // SE7-induced projectile teleportation. It doesn't look good
            // with TROR water.
491

492
493
            actor[spriteNum].flags |= SFLAG_DIDNOSE7WATER;
            return -otherSect-1;
494
        }
495
    }
496
497
#endif

498
    return 2;
499
500
}

501
int A_GetClipdist(int spriteNum, int clipDist)
502
{
503
    if (clipDist < 0)
504
    {
505
        auto const pSprite = &sprite[spriteNum];
Richard Gobeille's avatar
Richard Gobeille committed
506
        int const  isEnemy = A_CheckEnemySprite(pSprite);
507

508
509
        if (A_CheckSpriteFlags(spriteNum, SFLAG_REALCLIPDIST))
            clipDist = pSprite->clipdist << 2;
510
511
        else if ((pSprite->cstat & 48) == 16)
            clipDist = 0;
512
513
514
515
        else if (isEnemy)
        {
            if (pSprite->xrepeat > 60)
                clipDist = 1024;
516
#ifndef EDUKE32_STANDALONE
517
            else if (!FURY && pSprite->picnum == LIZMAN)
518
                clipDist = 292;
519
#endif
520
521
522
523
524
            else if (A_CheckSpriteFlags(spriteNum, SFLAG_BADGUY))
                clipDist = pSprite->clipdist << 2;
            else
                clipDist = 192;
        }
525
        else
526
527
        {
            if (pSprite->statnum == STAT_PROJECTILE && (SpriteProjectile[spriteNum].workslike & PROJECTILE_REALCLIPDIST) == 0)
528
                clipDist = 16;
529
530
531
            else
                clipDist = pSprite->clipdist << 2;
        }
532
533
    }

534
535
536
537
538
    return clipDist;
}

int32_t A_MoveSpriteClipdist(int32_t spriteNum, vec3_t const * const change, uint32_t clipType, int32_t clipDist)
{
Richard Gobeille's avatar
Richard Gobeille committed
539
540
541
    auto const   pSprite = &sprite[spriteNum];
    int const    isEnemy = A_CheckEnemySprite(pSprite);
    vec2_t const oldPos  = pSprite->pos.vec2;
542
543
544
545
546

    // check to make sure the netcode didn't leave a deleted sprite in the sprite lists.
    Bassert(pSprite->sectnum < MAXSECTORS);

#ifndef EDUKE32_STANDALONE
547
    if (!FURY && (pSprite->statnum == STAT_MISC || (isEnemy && pSprite->xrepeat < 4)))
548
549
550
551
552
553
    {
        pSprite->x += change->x;
        pSprite->y += change->y;
        pSprite->z += change->z;

        if (isEnemy)
554
            setsprite(spriteNum, &pSprite->pos);
555
556
557
558
559

        return 0;
    }
#endif

560
    setsprite(spriteNum, &pSprite->pos);
561

562
563
564
    if (!(change->x|change->y|change->z))
        return 0;

565
566
    clipDist = A_GetClipdist(spriteNum, clipDist);

567
    int16_t   newSectnum = pSprite->sectnum;
568
#ifndef EDUKE32_STANDALONE
Richard Gobeille's avatar
Richard Gobeille committed
569
    int const oldSectnum = newSectnum;
570
#endif
571

572
    // Handle horizontal movement first.
573
574

    int returnValue;
Richard Gobeille's avatar
Richard Gobeille committed
575
    int32_t diffZ;
576
    spriteheightofs(spriteNum, &diffZ, 1);
577

578
579
580
    if (pSprite->statnum == STAT_PROJECTILE)
        returnValue = clipmovex(&pSprite->pos, &newSectnum, change->x << 13, change->y << 13, clipDist, diffZ >> 3, diffZ >> 3, clipType, 1);
    else
581
    {
582
583
584
        pSprite->z -= diffZ >> 1;
        returnValue = clipmove(&pSprite->pos, &newSectnum, change->x << 13, change->y << 13, clipDist, ZOFFSET6, ZOFFSET6, clipType);
        pSprite->z += diffZ >> 1;
585
    }
586

587
588
589
    // Testing: For some reason the assert below this was tripping for clients
    EDUKE32_UNUSED int16_t   dbg_ClipMoveSectnum = newSectnum;

590
    if (isEnemy)
591
    {
592
        // Handle potential stayput condition (map-provided or hard-coded).
593
        if (newSectnum < 0 || ((actor[spriteNum].stayput >= 0 && actor[spriteNum].stayput != newSectnum)
594
                || ((g_tile[pSprite->picnum].flags & SFLAG_NOWATERSECTOR) && sector[newSectnum].lotag == ST_1_ABOVE_WATER)
595
#ifndef EDUKE32_STANDALONE
596
597
598
                || (!FURY && pSprite->picnum == BOSS2 && pSprite->pal == 0 && sector[newSectnum].lotag != ST_3)
                || (!FURY && (pSprite->picnum == BOSS1 || pSprite->picnum == BOSS2) && sector[newSectnum].lotag == ST_1_ABOVE_WATER)
                || (!FURY && sector[oldSectnum].lotag != ST_1_ABOVE_WATER && sector[newSectnum].lotag == ST_1_ABOVE_WATER
599
600
601
                    && (pSprite->picnum == LIZMAN || (pSprite->picnum == LIZTROOP && pSprite->zvel == 0)))
#endif
                ))
602
        {
603
            pSprite->pos.vec2 = oldPos;
604
605
606

            // NOTE: in Duke3D, LIZMAN on water takes on random angle here.

607
            setsprite(spriteNum, &pSprite->pos);
608

609
610
            if (newSectnum < 0)
                newSectnum = 0;
611

612
            return 16384+newSectnum;
613
        }
Richard Gobeille's avatar
   
Richard Gobeille committed
614

615
616
        if ((returnValue&49152) >= 32768 && actor[spriteNum].cgg==0)
            pSprite->ang += 768;
617
    }
618

619
620
    EDUKE32_UNUSED int16_t   dbg_newSectnum2 = newSectnum;

621
    if (newSectnum == -1)
622
    {
623
        newSectnum = pSprite->sectnum;
624
//        OSD_Printf("%s:%d wtf\n",__FILE__,__LINE__);
625
    }
626
    else if (newSectnum != pSprite->sectnum)
627
    {
628
        changespritesect(spriteNum, newSectnum);
629
        // A_GetZLimits(spritenum);
Richard Gobeille's avatar
   
Richard Gobeille committed
630
    }
631

632
    Bassert(newSectnum == pSprite->sectnum);
633

634
    int newZ = pSprite->z;
635
636
    int32_t ceilhit, florhit;
    int const doZUpdate = change->z ? A_CheckNeedZUpdate(spriteNum, change->z, &newZ, &ceilhit, &florhit) : 0;
637

638
    // Update sprite's z positions and (for TROR) maybe the sector number.
639
    if (doZUpdate == 2)
640
641
642
643
    {
        if (returnValue == 0)
            returnValue = change->z < 0 ? ceilhit : florhit;
    }
644
    else if (doZUpdate)
Philipp Kutin's avatar
Philipp Kutin committed
645
    {
646
        pSprite->z = newZ;
Philipp Kutin's avatar
Philipp Kutin committed
647
#ifdef YAX_ENABLE
648
        if (doZUpdate < 0)
649
650
651
652
653
654
        {
            // If we passed a TROR no-SE7 water boundary, signal to the outside
            // that the ceiling/floor was not hit. However, this is not enough:
            // later, code checks for (retval&49152)!=49152
            // [i.e. not "was ceiling or floor hit", but "was no sprite hit"]
            // and calls G_WeaponHitCeilingOrFloor() then, so we need to set
655
            // actor[].flags |= SFLAG_DIDNOSE7WATER in A_CheckNeedZUpdate()
656
657
            // previously.
            // XXX: Why is this contrived data flow necessary? (If at all.)
658
            changespritesect(spriteNum, -doZUpdate-1);
659
660
661
            return 0;
        }

662
663
        if (yax_getbunch(newSectnum, (change->z>0))>=0
                && (SECTORFLD(newSectnum,stat, (change->z>0))&yax_waltosecmask(clipType))==0)
664
        {
665
            setspritez(spriteNum, &pSprite->pos);
666
        }
Philipp Kutin's avatar
Philipp Kutin committed
667
668
#endif
    }
669
670
    else if (change->z != 0 && returnValue == 0)
        returnValue = 16384+newSectnum;
671

672
673
674
    if (returnValue == 16384 + newSectnum)
    {
        if (pSprite->statnum == STAT_PROJECTILE)
675
        {
676
677
            // Projectile sector changes due to transport SEs (SE7_PROJECTILE).
            // PROJECTILE_CHSECT
678
            for (bssize_t SPRITES_OF(STAT_TRANSPORT, otherSpriteNum))
679
680
            {
                if (sprite[otherSpriteNum].sectnum == newSectnum)
681
                {
682
                    int const sectLotag = sector[newSectnum].lotag;
683

684
                    if (sectLotag == ST_1_ABOVE_WATER && newZ >= actor[spriteNum].floorz)
685
                        if (Proj_MaybeDoTransport(spriteNum, (uspriteptr_t)&sprite[otherSpriteNum], 0, newZ))
686
                            return 0;
687

688
                    if (sectLotag == ST_2_UNDERWATER && newZ <= actor[spriteNum].ceilingz)
689
                        if (Proj_MaybeDoTransport(spriteNum, (uspriteptr_t)&sprite[otherSpriteNum], 1, newZ))
690
                            return 0;
691
                }
692
            }
693
        }
694
    }
Richard Gobeille's avatar
   
Richard Gobeille committed
695

696
    return returnValue;
697
698
}

Richard Gobeille's avatar
Richard Gobeille committed
699
int32_t block_deletesprite = 0;
Richard Gobeille's avatar
   
Richard Gobeille committed
700

701
#ifdef POLYMER
702
static void A_DeleteLight(int32_t s)
703
{
704
705
    if (actor[s].lightId >= 0)
        polymer_deletelight(actor[s].lightId);
706
707
708
709
710
711
712
713
714
    actor[s].lightId = -1;
    actor[s].lightptr = NULL;
}

void G_Polymer_UnInit(void)
{
    int32_t i;

    for (i=0; i<MAXSPRITES; i++)
715
        A_DeleteLight(i);
716
717
718
}
#endif

719
// deletesprite() game wrapper
720
void A_DeleteSprite(int spriteNum)
Richard Gobeille's avatar
Richard Gobeille committed
721
{
722
    if (EDUKE32_PREDICT_FALSE(block_deletesprite))
Richard Gobeille's avatar
   
Richard Gobeille committed
723
    {
724
        OSD_Printf(OSD_ERROR "A_DeleteSprite(): tried to remove sprite %d in EVENT_EGS\n", spriteNum);
Richard Gobeille's avatar
   
Richard Gobeille committed
725
726
        return;
    }
Richard Gobeille's avatar
Richard Gobeille committed
727

728
    if (VM_HaveEvent(EVENT_KILLIT))
Richard Gobeille's avatar
   
Richard Gobeille committed
729
    {
730
731
        int32_t playerDist;
        int playerNum = A_FindPlayer(&sprite[spriteNum], &playerDist);
Richard Gobeille's avatar
   
Richard Gobeille committed
732

733
        if (VM_ExecuteEvent(EVENT_KILLIT, spriteNum, playerNum, playerDist))
Richard Gobeille's avatar
   
Richard Gobeille committed
734
735
            return;
    }
Richard Gobeille's avatar
Richard Gobeille committed
736
737

#ifdef POLYMER
738
    if (actor[spriteNum].lightptr != NULL && videoGetRenderMode() == REND_POLYMER)
739
        A_DeleteLight(spriteNum);
Richard Gobeille's avatar
Richard Gobeille committed
740
741
#endif

742
    // AMBIENT_SFX_PLAYING
743
744
    if (sprite[spriteNum].picnum == MUSICANDSFX && actor[spriteNum].t_data[0] == 1)
        S_StopEnvSound(sprite[spriteNum].lotag, spriteNum);
745

746
#ifdef NETCODE_DISABLE
747
    deletesprite(spriteNum);
748
749
750
#else
    Net_DeleteSprite(spriteNum);
#endif
751
}
Richard Gobeille's avatar
Richard Gobeille committed
752

753
void A_AddToDeleteQueue(int spriteNum)
754
{
755
    if (g_netClient || (g_deleteQueueSize == 0)) // [75] Clients should not use SpriteDeletionQueue[] and just set the sprites invisible immediately in A_DeleteSprite
756
    {
757
        A_DeleteSprite(spriteNum);
Richard Gobeille's avatar
   
Richard Gobeille committed
758
        return;
759
    }
Richard Gobeille's avatar
Richard Gobeille committed
760

761
    auto &deleteSpriteNum = SpriteDeletionQueue[g_spriteDeleteQueuePos];
762

763
764
765
766
767
    if (deleteSpriteNum >= 0 && actor[deleteSpriteNum].flags & SFLAG_QUEUEDFORDELETE)
        A_DeleteSprite(deleteSpriteNum);

    deleteSpriteNum = spriteNum;
    actor[spriteNum].flags |= SFLAG_QUEUEDFORDELETE;
768
    g_spriteDeleteQueuePos = (g_spriteDeleteQueuePos+1)%g_deleteQueueSize;
769
770
}

771
void A_SpawnMultiple(int spriteNum, int tileNum, int spawnCnt)
772
{
773
    auto const pSprite = &sprite[spriteNum];
Richard Gobeille's avatar
Richard Gobeille committed
774

775
    for (; spawnCnt>0; spawnCnt--)
776
    {
777
778
        int const j = A_InsertSprite(pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z - (krand() % (47 << 8)), tileNum, -32, 8,
                               8, krand() & 2047, 0, 0, spriteNum, 5);
Richard Gobeille's avatar
Richard Gobeille committed
779
780
        A_Spawn(-1, j);
        sprite[j].cstat = krand()&12;
781
782
783
    }
}

784
#ifndef EDUKE32_STANDALONE
785
void A_DoGuts(int spriteNum, int tileNum, int spawnCnt)
786
{
787
788
    auto const pSprite = (uspriteptr_t)&sprite[spriteNum];
    vec2_t     repeat  = { 32, 32 };
789

790
791
    if (A_CheckEnemySprite(pSprite) && pSprite->xrepeat < 16)
        repeat.x = repeat.y = 8;
792

793
794
    int gutZ   = pSprite->z - ZOFFSET3;
    int floorz = getflorzofslope(pSprite->sectnum, pSprite->x, pSprite->y);
795

796
797
    if (gutZ > (floorz-ZOFFSET3))
        gutZ = floorz-ZOFFSET3;
798

799
800
    if (pSprite->picnum == COMMANDER)
        gutZ -= (24<<8);
801

802
    for (bssize_t j=spawnCnt; j>0; j--)
803
    {
804
805
806
807
808
        int const i = A_InsertSprite(pSprite->sectnum, pSprite->x + (krand() & 255) - 128,
                                     pSprite->y + (krand() & 255) - 128, gutZ - (krand() & 8191), tileNum, -32, repeat.x,
                                     repeat.y, krand() & 2047, 48 + (krand() & 31), -512 - (krand() & 2047), spriteNum, 5);

        if (PN(i) == JIBS2)
809
810
811
812
        {
            sprite[i].xrepeat >>= 2;
            sprite[i].yrepeat >>= 2;
        }
813

814
        sprite[i].pal = pSprite->pal;
815
816
817
    }
}

818
void A_DoGutsDir(int spriteNum, int tileNum, int spawnCnt)
819
{
820
821
    auto const s      = (uspriteptr_t)&sprite[spriteNum];
    vec2_t     repeat = { 32, 32 };
Richard Gobeille's avatar
Richard Gobeille committed
822

Richard Gobeille's avatar
Richard Gobeille committed
823
    if (A_CheckEnemySprite(s) && s->xrepeat < 16)
824
        repeat.x = repeat.y = 8;
825

826
827
    int gutZ = s->z-ZOFFSET3;
    int floorZ = getflorzofslope(s->sectnum,s->x,s->y);
828

829
830
    if (gutZ > (floorZ-ZOFFSET3))
        gutZ = floorZ-ZOFFSET3;
831

Richard Gobeille's avatar
Richard Gobeille committed
832
    if (s->picnum == COMMANDER)
833
        gutZ -= (24<<8);
834

835
    for (bssize_t j=spawnCnt; j>0; j--)
836
    {
837
838
        int const i = A_InsertSprite(s->sectnum, s->x, s->y, gutZ, tileNum, -32, repeat.x, repeat.y, krand() & 2047,
                                     256 + (krand() & 127), -512 - (krand() & 2047), spriteNum, 5);
Richard Gobeille's avatar
   
Richard Gobeille committed
839
        sprite[i].pal = s->pal;
840
841
    }
}
842
#endif
843

NY00123's avatar
NY00123 committed
844
static int32_t G_ToggleWallInterpolation(int32_t wallNum, int32_t setInterpolation)
845
{
846
    if (setInterpolation)
847
    {
848
        return G_SetInterpolation(&wall[wallNum].x) || G_SetInterpolation(&wall[wallNum].y);
849
850
851
    }
    else
    {
852
853
        G_StopInterpolation(&wall[wallNum].x);
        G_StopInterpolation(&wall[wallNum].y);
854
        return 0;
855
856
857
    }
}

858
void Sect_ToggleInterpolation(int sectNum, int setInterpolation)
859
{
860
    for (bssize_t j = sector[sectNum].wallptr, endwall = sector[sectNum].wallptr + sector[sectNum].wallnum; j < endwall; j++)
861
    {
862
863
864
        G_ToggleWallInterpolation(j, setInterpolation);

        int const nextWall = wall[j].nextwall;
865

866
        if (nextWall >= 0)