actors.cpp 330 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
#include "duke3d.h"
30
#include "microprofile.h"
31

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

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

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

42
43
int32_t otherp;

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

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

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

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

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

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

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

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

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

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

Richard Gobeille's avatar
Richard Gobeille committed
103
104
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)
105
{
106
    auto const pSprite = (uspriteptr_t)&sprite[spriteNum];
Richard Gobeille's avatar
Richard Gobeille committed
107
    auto const pOther  = &sprite[otherSprite];
Richard Gobeille's avatar
Richard Gobeille committed
108

Evan Ramos's avatar
Evan Ramos committed
109
110
111
#ifndef EDUKE32_STANDALONE
    if (WORLDTOUR && pSprite->picnum == FLAMETHROWERFLAME)
    {
112
113
        // 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
114
115
116
            return;
    }
#endif
117

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

Richard Gobeille's avatar
Richard Gobeille committed
132
133
134
135
136
137
#ifndef EDUKE32_STANDALONE
            if (!FURY)
                A_DamageObject_Duke3D(otherSprite, spriteNum);
            else
#endif
                A_DamageObject_Generic(otherSprite, spriteNum);
Richard Gobeille's avatar
Richard Gobeille committed
138
139
140
141
142
143
144
        }
    }
    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)))
145
    {
Richard Gobeille's avatar
Richard Gobeille committed
146
147
148
149
150
151
152
153
#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;
154

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

Richard Gobeille's avatar
Richard Gobeille committed
159
160
161
162
163
164
165
166
167
168
169
170
171
172
        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
173
174
175
176
177
#ifndef EDUKE32_STANDALONE
        else if (WORLDTOUR && (pSprite->picnum == FLAMETHROWERFLAME || pSprite->picnum == LAVAPOOL
                 || (pSprite->picnum == FIREBALL && sprite[pSprite->owner].picnum == APLAYER)))
            dmgActor.picnum = FLAMETHROWERFLAME;
#endif
178
179
        else
            dmgActor.picnum = RADIUSEXPLOSION;
Richard Gobeille's avatar
Richard Gobeille committed
180
181

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

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

            if (dmgBase == dmgFuzz)
                ++dmgFuzz;

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

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

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

Richard Gobeille's avatar
Richard Gobeille committed
210
211
212
213
#ifndef EDUKE32_STANDALONE
            if (!FURY)
            {
                switch (DYNAMICTILEMAP(pOther->picnum))
Richard Gobeille's avatar
Richard Gobeille committed
214
                {
Richard Gobeille's avatar
Richard Gobeille committed
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
                    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
231
                        A_DamageObject_Duke3D(otherSprite, spriteNum);
Richard Gobeille's avatar
Richard Gobeille committed
232
                        break;
233
                }
234
            }
Richard Gobeille's avatar
Richard Gobeille committed
235
236
237
238
239
#endif
        }
#ifndef EDUKE32_STANDALONE
        else if (!FURY && pSprite->extra == 0) dmgActor.extra = 0;
#endif
Richard Gobeille's avatar
Richard Gobeille committed
240

Richard Gobeille's avatar
Richard Gobeille committed
241
242
243
244
245
246
        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
247

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

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

257
#define MAXDAMAGESECTORS 128
258

Richard Gobeille's avatar
Richard Gobeille committed
259
260
261
262
263
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));
264

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

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

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

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

279
280
281
282
283
284
285
286
    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
287
288
    for (int sectorCount=0; sectorCount < numSectors; ++sectorCount)
    {
289
        int const   sectorNum  = sectorList[sectorCount];
Richard Gobeille's avatar
Richard Gobeille committed
290
        auto const &listSector = sector[sectorNum];
Richard Gobeille's avatar
Richard Gobeille committed
291

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

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

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

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

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

316
317
318
                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 };
319

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

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

328
329
330
331
332
333
334
335
336
337
338
339
                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);

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

                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;
                }
356
            }
Richard Gobeille's avatar
Richard Gobeille committed
357
        }
358
359
360
361
362
363
364
365
366
367
368
369

        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
370
371
    }

372
wallsfinished:
Richard Gobeille's avatar
Richard Gobeille committed
373
374
375
376
377
378
379
380
381
    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
382
            auto      pDamage    = &sprite[damageSprite];
Richard Gobeille's avatar
Richard Gobeille committed
383

384
            if (bitmap_test(g_radiusDmgStatnums, pDamage->statnum))
Richard Gobeille's avatar
Richard Gobeille committed
385
            {
386
387
388
389
                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
390
391
392
393
394
395

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

            damageSprite = nextSprite;
396
397
398
399
        }
    }
}

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

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

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

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

417
418
419
420
    // 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
421

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

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

// Check whether sprite <s> is on/in a non-SE7 water sector.
// <othersectptr>: if not NULL, the sector on the other side.
430
int A_CheckNoSE7Water(uspriteptr_t const pSprite, int sectNum, int sectLotag, int32_t *pOther)
431
{
432
    if (sectLotag == ST_1_ABOVE_WATER || sectLotag == ST_2_UNDERWATER)
433
    {
434
435
436
        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;
437
438
439
440
441

        // 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.
442
        if (otherSect >= 0 && sector[otherSect].lotag == otherLotag)
443
        {
444
445
            if (pOther)
                *pOther = otherSect;
446
447
448
449
450
451
452
453
454
455
456
            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].
457
458
// <0 if yes, but passed a TROR no-SE7 water boundary. -returnvalue-1 is the
//       other-side sector number.
459
460
static int32_t A_CheckNeedZUpdate(int32_t spriteNum, int32_t zChange, int32_t *pZcoord,
    int32_t *ceilhit, int32_t *florhit)
461
{
462
463
    if (zChange == 0)
        return 0;
464

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

468
    *pZcoord = newZ;
469

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

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

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

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

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

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

499
    return 2;
500
501
}

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

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

535
536
537
538
539
    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
540
541
542
    auto const   pSprite = &sprite[spriteNum];
    int const    isEnemy = A_CheckEnemySprite(pSprite);
    vec2_t const oldPos  = pSprite->pos.vec2;
543
544
545
546
547

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

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

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

        return 0;
    }
#endif

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

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

566
567
    clipDist = A_GetClipdist(spriteNum, clipDist);

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

573
    // Handle horizontal movement first.
574
575

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

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

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

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

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

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

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

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

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

620
621
    EDUKE32_UNUSED int16_t   dbg_newSectnum2 = newSectnum;

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

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

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

639
    // Update sprite's z positions and (for TROR) maybe the sector number.
640
    if (doZUpdate == 2)
641
642
643
644
    {
        if (returnValue == 0)
            returnValue = change->z < 0 ? ceilhit : florhit;
    }
645
    else if (doZUpdate)
Philipp Kutin's avatar
Philipp Kutin committed
646
    {
647
        pSprite->z = newZ;
Philipp Kutin's avatar
Philipp Kutin committed
648
#ifdef YAX_ENABLE
649
        if (doZUpdate < 0)
650
651
652
653
654
655
        {
            // 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
656
            // actor[].flags |= SFLAG_DIDNOSE7WATER in A_CheckNeedZUpdate()
657
658
            // previously.
            // XXX: Why is this contrived data flow necessary? (If at all.)
659
            changespritesect(spriteNum, -doZUpdate-1);
660
661
662
            return 0;
        }

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

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

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

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

697
    return returnValue;
698
699
}

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

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

void G_Polymer_UnInit(void)
{
    int32_t i;

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

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

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

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

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

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

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

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

762
    auto &deleteSpriteNum = SpriteDeletionQueue[g_spriteDeleteQueuePos];
763

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

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

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

776
    for (; spawnCnt>0; spawnCnt--)
777
    {
778
779
        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
780
781
        A_Spawn(-1, j);
        sprite[j].cstat = krand()&12;
782
783
784
    }
}

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

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

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

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

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

803
    for (bssize_t j=spawnCnt; j>0; j--)
804
    {
805
806
807
808
809
        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)
810
811
812
813
        {
            sprite[i].xrepeat >>= 2;
            sprite[i].yrepeat >>= 2;
        }
814

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

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

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

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

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

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

836
    for (bssize_t j=spawnCnt; j>0; j--)
837
    {
838
839
        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
840
        sprite[i].pal = s->pal;
841
842
    }
}
843
#endif
844

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

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

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

867
        if (nextWall >= 0)
868
        {
869
870
            G_ToggleWallInterpolation(nextWall, setInterpolation);
            G_ToggleWallInterpolation(wall[nextWall].point2, setInterpolation);
871
872
873
874
        }
    }
}

875
static int32_t move_rotfixed_sprite(int32_t spriteNum, int32_t pivotSpriteNum, int32_t pivotAngle)
876
{