actors.cpp 328 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
    // rockets from the Devastator skip propagating damage to other sectors
272
    if (!FURY && (pSprite->picnum == RPG && pSprite->xrepeat < 11))
273
        goto wallsfinished;
Richard Gobeille's avatar
Richard Gobeille committed
274
275
#endif

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

289
290
        vec2_t  closest  = {};
        int32_t distance = INT32_MAX;
291

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

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

302
303
            if (bitmap_test(wallTouched, w) == 0)
                walldist = getwalldist(p, w, &p);
304

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

313
314
315
                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 };
316

317
                updatesector(vect.x, vect.y, &aSector);
318

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

325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
                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);

                    A_DamageWall_Internal(spriteNum, w, { p.x, p.y, pSprite->z }, pSprite->picnum);
                }

                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;
                }
350
            }
Richard Gobeille's avatar
Richard Gobeille committed
351
        }
352
353
354
355
356
357
358
359
360
361
362
363

        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
364
365
    }

366
wallsfinished:
Richard Gobeille's avatar
Richard Gobeille committed
367
368
369
370
371
372
373
374
375
    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
376
            auto      pDamage    = &sprite[damageSprite];
Richard Gobeille's avatar
Richard Gobeille committed
377

378
            if (bitmap_test(g_radiusDmgStatnums, pDamage->statnum))
Richard Gobeille's avatar
Richard Gobeille committed
379
            {
380
381
382
383
                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
384
385
386
387
388
389

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

            damageSprite = nextSprite;
390
391
392
393
        }
    }
}

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

403
404
405
    auto const pSprite = &sprite[spriteNum];
    auto const otherse = (uspriteptr_t)&sprite[pSEffector->owner];

406
    actor[spriteNum].lasttransport = ((int32_t) totalclock & UINT8_MAX);
407

408
409
    pSprite->x += (otherse->x - pSEffector->x);
    pSprite->y += (otherse->y - pSEffector->y);
410

411
412
413
414
    // 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
415

416
    actor[spriteNum].bpos = sprite[spriteNum].pos;
417
    changespritesect(spriteNum, otherse->sectnum);
418

Richard Gobeille's avatar
Richard Gobeille committed
419
    return 1;
420
421
422
423
}

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

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

459
460
    auto const pSprite = (uspriteptr_t)&sprite[spriteNum];
    int const  newZ    = pSprite->z + (zChange >> 1);
461

462
    *pZcoord = newZ;
463

464
465
    int const clipDist = A_GetClipdist(spriteNum, -1);

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

468
    if (newZ > actor[spriteNum].ceilingz && newZ <= actor[spriteNum].floorz)
469
470
471
        return 1;

#ifdef YAX_ENABLE
472
473
474
    int const sectNum   = pSprite->sectnum;
    int const sectLotag = sector[sectNum].lotag;
    int32_t   otherSect;
475

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

487
488
            actor[spriteNum].flags |= SFLAG_DIDNOSE7WATER;
            return -otherSect-1;
489
        }
490
    }
491
492
#endif

493
    return 2;
494
495
}

496
int A_GetClipdist(int spriteNum, int clipDist)
497
{
498
    if (clipDist < 0)
499
    {
500
        auto const pSprite = &sprite[spriteNum];
Richard Gobeille's avatar
Richard Gobeille committed
501
        int const  isEnemy = A_CheckEnemySprite(pSprite);
502

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

529
530
531
532
533
    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
534
535
536
    auto const   pSprite = &sprite[spriteNum];
    int const    isEnemy = A_CheckEnemySprite(pSprite);
    vec2_t const oldPos  = pSprite->pos.vec2;
537
538
539
540
541

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

#ifndef EDUKE32_STANDALONE
542
    if (!FURY && (pSprite->statnum == STAT_MISC || (isEnemy && pSprite->xrepeat < 4)))
543
544
545
546
547
548
    {
        pSprite->x += change->x;
        pSprite->y += change->y;
        pSprite->z += change->z;

        if (isEnemy)
549
            setsprite(spriteNum, &pSprite->pos);
550
551
552
553
554

        return 0;
    }
#endif

555
    setsprite(spriteNum, &pSprite->pos);
556

557
558
559
    if (!(change->x|change->y|change->z))
        return 0;

560
561
    clipDist = A_GetClipdist(spriteNum, clipDist);

562
    int16_t   newSectnum = pSprite->sectnum;
563
#ifndef EDUKE32_STANDALONE
Richard Gobeille's avatar
Richard Gobeille committed
564
    int const oldSectnum = newSectnum;
565
#endif
566

567
    // Handle horizontal movement first.
568
569

    int returnValue;
Richard Gobeille's avatar
Richard Gobeille committed
570
    int32_t diffZ;
571
    spriteheightofs(spriteNum, &diffZ, 1);
572

573
574
575
    if (pSprite->statnum == STAT_PROJECTILE)
        returnValue = clipmovex(&pSprite->pos, &newSectnum, change->x << 13, change->y << 13, clipDist, diffZ >> 3, diffZ >> 3, clipType, 1);
    else
576
    {
577
578
579
        pSprite->z -= diffZ >> 1;
        returnValue = clipmove(&pSprite->pos, &newSectnum, change->x << 13, change->y << 13, clipDist, ZOFFSET6, ZOFFSET6, clipType);
        pSprite->z += diffZ >> 1;
580
    }
581

582
583
584
    // Testing: For some reason the assert below this was tripping for clients
    EDUKE32_UNUSED int16_t   dbg_ClipMoveSectnum = newSectnum;

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

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

602
            setsprite(spriteNum, &pSprite->pos);
603

604
605
            if (newSectnum < 0)
                newSectnum = 0;
606

607
            return 16384+newSectnum;
608
        }
Richard Gobeille's avatar
   
Richard Gobeille committed
609

610
611
        if ((returnValue&49152) >= 32768 && actor[spriteNum].cgg==0)
            pSprite->ang += 768;
612
    }
613

614
615
    EDUKE32_UNUSED int16_t   dbg_newSectnum2 = newSectnum;

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

627
    Bassert(newSectnum == pSprite->sectnum);
628

629
    int newZ = pSprite->z;
630
631
    int32_t ceilhit, florhit;
    int const doZUpdate = change->z ? A_CheckNeedZUpdate(spriteNum, change->z, &newZ, &ceilhit, &florhit) : 0;
632

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

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

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

679
                    if (sectLotag == ST_1_ABOVE_WATER && newZ >= actor[spriteNum].floorz)
680
                        if (Proj_MaybeDoTransport(spriteNum, (uspriteptr_t)&sprite[otherSpriteNum], 0, newZ))
681
                            return 0;
682

683
                    if (sectLotag == ST_2_UNDERWATER && newZ <= actor[spriteNum].ceilingz)
684
                        if (Proj_MaybeDoTransport(spriteNum, (uspriteptr_t)&sprite[otherSpriteNum], 1, newZ))
685
                            return 0;
686
                }
687
            }
688
        }
689
    }
Richard Gobeille's avatar
   
Richard Gobeille committed
690

691
    return returnValue;
692
693
}

Richard Gobeille's avatar
Richard Gobeille committed
694
int32_t block_deletesprite = 0;
Richard Gobeille's avatar
   
Richard Gobeille committed
695

696
#ifdef POLYMER
697
static void A_DeleteLight(int32_t s)
698
{
699
700
    if (actor[s].lightId >= 0)
        polymer_deletelight(actor[s].lightId);
701
702
703
704
705
706
707
708
709
    actor[s].lightId = -1;
    actor[s].lightptr = NULL;
}

void G_Polymer_UnInit(void)
{
    int32_t i;

    for (i=0; i<MAXSPRITES; i++)
710
        A_DeleteLight(i);
711
712
713
}
#endif

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

723
    if (VM_HaveEvent(EVENT_KILLIT))
Richard Gobeille's avatar
   
Richard Gobeille committed
724
    {
725
726
        int32_t playerDist;
        int playerNum = A_FindPlayer(&sprite[spriteNum], &playerDist);
Richard Gobeille's avatar
   
Richard Gobeille committed
727

728
        if (VM_ExecuteEvent(EVENT_KILLIT, spriteNum, playerNum, playerDist))
Richard Gobeille's avatar
   
Richard Gobeille committed
729
730
            return;
    }
Richard Gobeille's avatar
Richard Gobeille committed
731
732

#ifdef POLYMER
733
    if (actor[spriteNum].lightptr != NULL && videoGetRenderMode() == REND_POLYMER)
734
        A_DeleteLight(spriteNum);
Richard Gobeille's avatar
Richard Gobeille committed
735
736
#endif

737
    // AMBIENT_SFX_PLAYING
738
739
    if (sprite[spriteNum].picnum == MUSICANDSFX && actor[spriteNum].t_data[0] == 1)
        S_StopEnvSound(sprite[spriteNum].lotag, spriteNum);
740

741
#ifdef NETCODE_DISABLE
742
    deletesprite(spriteNum);
743
744
745
#else
    Net_DeleteSprite(spriteNum);
#endif
746
}
Richard Gobeille's avatar
Richard Gobeille committed
747

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

756
    auto &deleteSpriteNum = SpriteDeletionQueue[g_spriteDeleteQueuePos];
757

758
759
760
761
762
    if (deleteSpriteNum >= 0 && actor[deleteSpriteNum].flags & SFLAG_QUEUEDFORDELETE)
        A_DeleteSprite(deleteSpriteNum);

    deleteSpriteNum = spriteNum;
    actor[spriteNum].flags |= SFLAG_QUEUEDFORDELETE;
763
    g_spriteDeleteQueuePos = (g_spriteDeleteQueuePos+1)%g_deleteQueueSize;
764
765
}

766
void A_SpawnMultiple(int spriteNum, int tileNum, int spawnCnt)
767
{
768
    auto const pSprite = &sprite[spriteNum];
Richard Gobeille's avatar
Richard Gobeille committed
769

770
    for (; spawnCnt>0; spawnCnt--)
771
    {
772
773
        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
774
775
        A_Spawn(-1, j);
        sprite[j].cstat = krand()&12;
776
777
778
    }
}

779
#ifndef EDUKE32_STANDALONE
780
void A_DoGuts(int spriteNum, int tileNum, int spawnCnt)
781
{
782
783
    auto const pSprite = (uspriteptr_t)&sprite[spriteNum];
    vec2_t     repeat  = { 32, 32 };
784

785
786
    if (A_CheckEnemySprite(pSprite) && pSprite->xrepeat < 16)
        repeat.x = repeat.y = 8;
787

788
789
    int gutZ   = pSprite->z - ZOFFSET3;
    int floorz = getflorzofslope(pSprite->sectnum, pSprite->x, pSprite->y);
790

791
792
    if (gutZ > (floorz-ZOFFSET3))
        gutZ = floorz-ZOFFSET3;
793

794
795
    if (pSprite->picnum == COMMANDER)
        gutZ -= (24<<8);
796

797
    for (bssize_t j=spawnCnt; j>0; j--)
798
    {
799
800
801
802
803
        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)
804
805
806
807
        {
            sprite[i].xrepeat >>= 2;
            sprite[i].yrepeat >>= 2;
        }
808

809
        sprite[i].pal = pSprite->pal;
810
811
812
    }
}

813
void A_DoGutsDir(int spriteNum, int tileNum, int spawnCnt)
814
{
815
816
    auto const s      = (uspriteptr_t)&sprite[spriteNum];
    vec2_t     repeat = { 32, 32 };
Richard Gobeille's avatar
Richard Gobeille committed
817

Richard Gobeille's avatar
Richard Gobeille committed
818
    if (A_CheckEnemySprite(s) && s->xrepeat < 16)
819
        repeat.x = repeat.y = 8;
820

821
822
    int gutZ = s->z-ZOFFSET3;
    int floorZ = getflorzofslope(s->sectnum,s->x,s->y);
823

824
825
    if (gutZ > (floorZ-ZOFFSET3))
        gutZ = floorZ-ZOFFSET3;
826

Richard Gobeille's avatar
Richard Gobeille committed
827
    if (s->picnum == COMMANDER)
828
        gutZ -= (24<<8);
829

830
    for (bssize_t j=spawnCnt; j>0; j--)
831
    {
832
833
        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
834
        sprite[i].pal = s->pal;
835
836
    }
}
837
#endif
838

NY00123's avatar
NY00123 committed
839
static int32_t G_ToggleWallInterpolation(int32_t wallNum, int32_t setInterpolation)
840
{
841
    if (setInterpolation)
842
    {
843
        return G_SetInterpolation(&wall[wallNum].x) || G_SetInterpolation(&wall[wallNum].y);
844
845
846
    }
    else
    {
847
848
        G_StopInterpolation(&wall[wallNum].x);
        G_StopInterpolation(&wall[wallNum].y);
849
        return 0;
850
851
852
    }
}

853
void Sect_ToggleInterpolation(int sectNum, int setInterpolation)
854
{
855
    for (bssize_t j = sector[sectNum].wallptr, endwall = sector[sectNum].wallptr + sector[sectNum].wallnum; j < endwall; j++)
856
    {
857
858
859
        G_ToggleWallInterpolation(j, setInterpolation);

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

861
        if (nextWall >= 0)
862
        {
863
864
            G_ToggleWallInterpolation(nextWall, setInterpolation);
            G_ToggleWallInterpolation(wall[nextWall].point2, setInterpolation);
865
866
867
868
        }
    }
}

869
static int32_t move_rotfixed_sprite(int32_t spriteNum, int32_t pivotSpriteNum, int32_t pivotAngle)
870
{
871
872
873
874
    if ((ROTFIXSPR_STATNUMP(sprite[spriteNum].statnum) ||
         ((sprite[spriteNum].statnum == STAT_ACTOR || sprite[spriteNum].statnum == STAT_ZOMBIEACTOR) &&
          A_CheckSpriteFlags(spriteNum, SFLAG_ROTFIXED))) &&
        actor[spriteNum].t_data[7] == (ROTFIXSPR_MAGIC | pivotSpriteNum))
875
    {
876
        rotatepoint(zerovec, *(vec2_t *)&actor[spriteNum].t_data[8], pivotAngle & 2047, &sprite[spriteNum].pos.vec2);