actors.cpp 317 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_

25
26
#include "duke3d.h"

27
28
29
30
31
32
#if KRANDDEBUG
# define ACTOR_STATIC
#else
# define ACTOR_STATIC static
#endif

33
34
uint8_t g_radiusDmgStatnums[(MAXSTATUS+7)>>3];

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

37
38
int32_t otherp;

39
int G_SetInterpolation(int32_t *const posptr)
40
{
41
    if (g_interpolationCnt >= MAXINTERPOLATIONS)
42
43
        return 1;

44
    for (bssize_t i = 0; i < g_interpolationCnt; ++i)
45
46
47
        if (curipos[i] == posptr)
            return 0;

48
49
50
    curipos[g_interpolationCnt] = posptr;
    oldipos[g_interpolationCnt] = *posptr;
    g_interpolationCnt++;
51
    return 0;
52
53
}

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

66
void G_DoInterpolations(int smoothRatio)
67
{
68
69
70
    if (g_interpolationLock++)
        return;

71
    int32_t ndelta = 0;
Richard Gobeille's avatar
Richard Gobeille committed
72

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

84
85
86
void G_ClearCameraView(DukePlayer_t *ps)
{
    ps->newowner = -1;
Richard Gobeille's avatar
Richard Gobeille committed
87
    ps->pos = ps->opos;
88
    ps->q16ang = ps->oq16ang;
89
90
91
92

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

93
    for (bssize_t SPRITES_OF(STAT_ACTOR, k))
94
95
96
97
        if (sprite[k].picnum==CAMERA1)
            sprite[k].yvel = 0;
}

Richard Gobeille's avatar
Richard Gobeille committed
98
99
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)
100
{
101
    auto const pSprite = (uspriteptr_t)&sprite[spriteNum];
Richard Gobeille's avatar
Richard Gobeille committed
102
    auto const pOther  = &sprite[otherSprite];
Richard Gobeille's avatar
Richard Gobeille committed
103

Richard Gobeille's avatar
Richard Gobeille committed
104
105
106
    // DEFAULT, ZOMBIEACTOR, MISC
    if (pOther->statnum == STAT_DEFAULT || pOther->statnum == STAT_ZOMBIEACTOR || pOther->statnum == STAT_MISC || AFLAMABLE(pOther->picnum))
    {
Richard Gobeille's avatar
Richard Gobeille committed
107
#ifndef EDUKE32_STANDALONE
Richard Gobeille's avatar
Richard Gobeille committed
108
        if (pSprite->picnum != SHRINKSPARK || (pOther->cstat&257))
Richard Gobeille's avatar
Richard Gobeille committed
109
#endif
Richard Gobeille's avatar
Richard Gobeille committed
110
111
112
        {
            if (A_CheckEnemySprite(pOther) && !cansee(pOther->x, pOther->y, pOther->z+zOffset, pOther->sectnum, pSprite->x, pSprite->y, pSprite->z+zOffset, pSprite->sectnum))
                return;
113

Richard Gobeille's avatar
Richard Gobeille committed
114
115
116
117
118
119
#ifndef EDUKE32_STANDALONE
            if (!FURY)
                A_DamageObject_Duke3D(otherSprite, spriteNum);
            else
#endif
                A_DamageObject_Generic(otherSprite, spriteNum);
Richard Gobeille's avatar
Richard Gobeille committed
120
121
122
123
124
125
126
        }
    }
    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)))
127
    {
Richard Gobeille's avatar
Richard Gobeille committed
128
129
130
131
132
133
134
#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 (pOther->picnum == APLAYER)
            spriteDist = FindDistance3D(pSprite->x - pOther->x, pSprite->y - pOther->y, pSprite->z - (pOther->z - PHEIGHT));
Richard Gobeille's avatar
Richard Gobeille committed
135

Richard Gobeille's avatar
Richard Gobeille committed
136
137
138
        if (spriteDist >= blastRadius || !cansee(pOther->x, pOther->y, pOther->z - ZOFFSET3, pOther->sectnum,
                                                 pSprite->x, pSprite->y, pSprite->z - ZOFFSET4, pSprite->sectnum))
            return;
139

Richard Gobeille's avatar
Richard Gobeille committed
140
141
142
        if (A_CheckSpriteFlags(otherSprite, SFLAG_DAMAGEEVENT))
            if (VM_OnEventWithReturn(EVENT_DAMAGESPRITE, spriteNum, -1, otherSprite) < 0)
                return;
143

Richard Gobeille's avatar
Richard Gobeille committed
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
        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;
        else dmgActor.picnum = RADIUSEXPLOSION;

#ifndef EDUKE32_STANDALONE
        if (pSprite->picnum != SHRINKSPARK)
#endif
163
        {
Richard Gobeille's avatar
Richard Gobeille committed
164
165
            // this is really weird
            int const k = blastRadius/3;
166
            int dmgBase = 0, dmgFuzz = 1;
167

Richard Gobeille's avatar
Richard Gobeille committed
168
            if (spriteDist < k)
Richard Gobeille's avatar
Richard Gobeille committed
169
                dmgBase = dmg3, dmgFuzz = dmg4;
Richard Gobeille's avatar
Richard Gobeille committed
170
            else if (spriteDist < k*2)
Richard Gobeille's avatar
Richard Gobeille committed
171
                dmgBase = dmg2, dmgFuzz = dmg3;
Richard Gobeille's avatar
Richard Gobeille committed
172
            else if (spriteDist < blastRadius)
Richard Gobeille's avatar
Richard Gobeille committed
173
174
175
176
177
178
                dmgBase = dmg1, dmgFuzz = dmg2;

            if (dmgBase == dmgFuzz)
                ++dmgFuzz;

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

Richard Gobeille's avatar
Richard Gobeille committed
180
            if (!A_CheckSpriteFlags(otherSprite, SFLAG_NODAMAGEPUSH))
Richard Gobeille's avatar
Richard Gobeille committed
181
            {
Richard Gobeille's avatar
Richard Gobeille committed
182
183
184
                if (pOther->xvel < 0) pOther->xvel = 0;
                pOther->xvel += (pSprite->extra<<2);
            }
185

Richard Gobeille's avatar
Richard Gobeille committed
186
187
            if (A_CheckSpriteFlags(otherSprite, SFLAG_DAMAGEEVENT))
                VM_OnEventWithReturn(EVENT_POSTDAMAGESPRITE, spriteNum, -1, otherSprite);
188

Richard Gobeille's avatar
Richard Gobeille committed
189
190
191
192
#ifndef EDUKE32_STANDALONE
            if (!FURY)
            {
                switch (DYNAMICTILEMAP(pOther->picnum))
Richard Gobeille's avatar
Richard Gobeille committed
193
                {
Richard Gobeille's avatar
Richard Gobeille committed
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
                    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
210
                        A_DamageObject_Duke3D(otherSprite, spriteNum);
Richard Gobeille's avatar
Richard Gobeille committed
211
                        break;
212
                }
213
            }
Richard Gobeille's avatar
Richard Gobeille committed
214
215
216
217
218
#endif
        }
#ifndef EDUKE32_STANDALONE
        else if (!FURY && pSprite->extra == 0) dmgActor.extra = 0;
#endif
Richard Gobeille's avatar
Richard Gobeille committed
219

Richard Gobeille's avatar
Richard Gobeille committed
220
221
222
223
224
225
        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
226

Richard Gobeille's avatar
Richard Gobeille committed
227
228
229
                if (pPlayer->newowner >= 0)
                    G_ClearCameraView(pPlayer);
            }
Richard Gobeille's avatar
Richard Gobeille committed
230

Richard Gobeille's avatar
Richard Gobeille committed
231
            dmgActor.owner = pSprite->owner;
232
233
        }
    }
Richard Gobeille's avatar
Richard Gobeille committed
234
}
235

236
#define MAXDAMAGESECTORS 128
237

Richard Gobeille's avatar
Richard Gobeille committed
238
239
240
241
242
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));
243

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

Richard Gobeille's avatar
Richard Gobeille committed
246
247
248
    int16_t sectorList[MAXDAMAGESECTORS];
    uint8_t sectorMap[(MAXSECTORS+7)>>3];
    int16_t numSectors;
249

Richard Gobeille's avatar
Richard Gobeille committed
250
    bfirst_search_init(sectorList, sectorMap, &numSectors, MAXSECTORS, pSprite->sectnum);
251

Richard Gobeille's avatar
Richard Gobeille committed
252
#ifndef EDUKE32_STANDALONE
253
    if (!FURY && (pSprite->picnum == RPG && pSprite->xrepeat < 11))
Richard Gobeille's avatar
Richard Gobeille committed
254
        goto SKIPWALLCHECK;
Richard Gobeille's avatar
Richard Gobeille committed
255
256
#endif

Richard Gobeille's avatar
Richard Gobeille committed
257
258
    for (int sectorCount=0; sectorCount < numSectors; ++sectorCount)
    {
259
        int const   sectorNum  = sectorList[sectorCount];
Richard Gobeille's avatar
Richard Gobeille committed
260
        auto const &listSector = sector[sectorNum];
261
        vec2_t      closest;
Richard Gobeille's avatar
Richard Gobeille committed
262

263
        if (getsectordist(pSprite->pos.vec2, sectorNum, &closest) >= blastRadius)
Richard Gobeille's avatar
Richard Gobeille committed
264
            continue;
265

Richard Gobeille's avatar
Richard Gobeille committed
266
267
        int const startWall = listSector.wallptr;
        int const endWall   = listSector.wallnum + startWall;
268

269
270
271
272
        int32_t floorZ, ceilZ;
        getzsofslope(sectorNum, closest.x, closest.y, &ceilZ, &floorZ);

        if (((ceilZ - pSprite->z) >> 8) < blastRadius)
Richard Gobeille's avatar
Richard Gobeille committed
273
            Sect_DamageCeiling_Internal(spriteNum, sectorNum);
274

275
        if (((pSprite->z - floorZ) >> 8) < blastRadius)
Richard Gobeille's avatar
Richard Gobeille committed
276
            Sect_DamageFloor_Internal(spriteNum, sectorNum);
277

Richard Gobeille's avatar
Richard Gobeille committed
278
279
        int w = startWall;
        
Richard Gobeille's avatar
Richard Gobeille committed
280
        for (auto pWall = (uwallptr_t)&wall[startWall]; w < endWall; ++w, ++pWall)
Richard Gobeille's avatar
Richard Gobeille committed
281
282
283
        {
            if (getwalldist(pSprite->pos.vec2, w, &closest) >= blastRadius)
                continue;
284

285
286
287
            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 };
Richard Gobeille's avatar
Richard Gobeille committed
288

289
290
291
            updatesector(vect.x, vect.y, &aSector);

            if (aSector == -1)
Richard Gobeille's avatar
Richard Gobeille committed
292
            {
293
294
295
                vect.vec2 = closest;
                aSector   = sectorNum;
            }
Richard Gobeille's avatar
Richard Gobeille committed
296

297
298
            if (cansee(vect.x, vect.y, vect.z, aSector, pSprite->x, pSprite->y, pSprite->z, pSprite->sectnum))
                A_DamageWall_Internal(spriteNum, w, { closest.x, closest.y, pSprite->z }, pSprite->picnum);
299

300
            int const nextSector = pWall->nextsector;
301

302
303
304
305
306
307
308
            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 SKIPWALLCHECK;
309
            }
Richard Gobeille's avatar
Richard Gobeille committed
310
311
312
313
314
315
316
317
318
319
320
321
322
        }
    }

SKIPWALLCHECK:
    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
323
            auto      pDamage    = &sprite[damageSprite];
Richard Gobeille's avatar
Richard Gobeille committed
324

325
            if (bitmap_test(g_radiusDmgStatnums, pDamage->statnum))
Richard Gobeille's avatar
Richard Gobeille committed
326
327
328
329
330
331
332
333
            {
                int const spriteDist = dist(pSprite, pDamage);

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

            damageSprite = nextSprite;
334
335
336
337
        }
    }
}

338
339
340
341
// Maybe do a projectile transport via an SE7.
// <spritenum>: the projectile
// <i>: the SE7
// <fromunderp>: below->above change?
342
static int32_t Proj_MaybeDoTransport(int32_t spriteNum, uspriteptr_t const pSEffector, int32_t fromunderp, int32_t daz)
343
{
344
    if (((int32_t) totalclock & UINT8_MAX) == actor[spriteNum].lasttransport)
Richard Gobeille's avatar
Richard Gobeille committed
345
346
        return 0;

347
348
349
    auto const pSprite = &sprite[spriteNum];
    auto const otherse = (uspriteptr_t)&sprite[pSEffector->owner];

350
    actor[spriteNum].lasttransport = ((int32_t) totalclock & UINT8_MAX);
351

352
353
    pSprite->x += (otherse->x - pSEffector->x);
    pSprite->y += (otherse->y - pSEffector->y);
354

355
356
357
358
    // 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
359

360
    actor[spriteNum].bpos = sprite[spriteNum].pos;
361
    changespritesect(spriteNum, otherse->sectnum);
362

Richard Gobeille's avatar
Richard Gobeille committed
363
    return 1;
364
365
366
367
}

// Check whether sprite <s> is on/in a non-SE7 water sector.
// <othersectptr>: if not NULL, the sector on the other side.
368
int A_CheckNoSE7Water(uspriteptr_t const pSprite, int sectNum, int sectLotag, int32_t *pOther)
369
{
370
    if (sectLotag == ST_1_ABOVE_WATER || sectLotag == ST_2_UNDERWATER)
371
    {
372
373
374
        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;
375
376
377
378
379

        // 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.
380
        if (otherSect >= 0 && sector[otherSect].lotag == otherLotag)
381
        {
382
383
            if (pOther)
                *pOther = otherSect;
384
385
386
387
388
389
390
391
392
393
394
            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].
395
396
// <0 if yes, but passed a TROR no-SE7 water boundary. -returnvalue-1 is the
//       other-side sector number.
397
398
static int32_t A_CheckNeedZUpdate(int32_t spriteNum, int32_t zChange, int32_t *pZcoord,
    int32_t *ceilhit, int32_t *florhit)
399
{
400
401
    if (zChange == 0)
        return 0;
402

403
404
    auto const pSprite = (uspriteptr_t)&sprite[spriteNum];
    int const  newZ    = pSprite->z + (zChange >> 1);
405

406
    *pZcoord = newZ;
407

408
409
    int const clipDist = A_GetClipdist(spriteNum, -1);

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

412
    if (newZ > actor[spriteNum].ceilingz && newZ <= actor[spriteNum].floorz)
413
414
415
        return 1;

#ifdef YAX_ENABLE
416
417
418
    int const sectNum   = pSprite->sectnum;
    int const sectLotag = sector[sectNum].lotag;
    int32_t   otherSect;
419

420
421
    // Non-SE7 water.
    // PROJECTILE_CHSECT
422
423
424
    if ((zChange < 0 && sectLotag == ST_2_UNDERWATER) || (zChange > 0 && sectLotag == ST_1_ABOVE_WATER))
    {
        if (A_CheckNoSE7Water(pSprite, sprite[spriteNum].sectnum, sectLotag, &otherSect))
425
        {
426
            A_Spawn(spriteNum, WATERSPLASH2);
427
428
429
            // NOTE: Don't tweak its z position afterwards like with
            // SE7-induced projectile teleportation. It doesn't look good
            // with TROR water.
430

431
432
            actor[spriteNum].flags |= SFLAG_DIDNOSE7WATER;
            return -otherSect-1;
433
        }
434
    }
435
436
#endif

437
    return 2;
438
439
}

440
int A_GetClipdist(int spriteNum, int clipDist)
441
{
442
    if (clipDist < 0)
443
    {
444
        auto const pSprite = &sprite[spriteNum];
Richard Gobeille's avatar
Richard Gobeille committed
445
        int const  isEnemy = A_CheckEnemySprite(pSprite);
446

447
448
        if (A_CheckSpriteFlags(spriteNum, SFLAG_REALCLIPDIST))
            clipDist = pSprite->clipdist << 2;
449
450
        else if ((pSprite->cstat & 48) == 16)
            clipDist = 0;
451
452
453
454
        else if (isEnemy)
        {
            if (pSprite->xrepeat > 60)
                clipDist = 1024;
455
#ifndef EDUKE32_STANDALONE
456
            else if (!FURY && pSprite->picnum == LIZMAN)
457
                clipDist = 292;
458
#endif
459
460
461
462
463
            else if (A_CheckSpriteFlags(spriteNum, SFLAG_BADGUY))
                clipDist = pSprite->clipdist << 2;
            else
                clipDist = 192;
        }
464
        else
465
466
        {
            if (pSprite->statnum == STAT_PROJECTILE && (SpriteProjectile[spriteNum].workslike & PROJECTILE_REALCLIPDIST) == 0)
467
                clipDist = 16;
468
469
470
            else
                clipDist = pSprite->clipdist << 2;
        }
471
472
    }

473
474
475
476
477
    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
478
479
480
    auto const   pSprite = &sprite[spriteNum];
    int const    isEnemy = A_CheckEnemySprite(pSprite);
    vec2_t const oldPos  = pSprite->pos.vec2;
481
482
483
484
485

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

#ifndef EDUKE32_STANDALONE
486
    if (!FURY && (pSprite->statnum == STAT_MISC || (isEnemy && pSprite->xrepeat < 4)))
487
488
489
490
491
492
    {
        pSprite->x += change->x;
        pSprite->y += change->y;
        pSprite->z += change->z;

        if (isEnemy)
493
            setsprite(spriteNum, &pSprite->pos);
494
495
496
497
498

        return 0;
    }
#endif

499
    setsprite(spriteNum, &pSprite->pos);
500

501
502
503
    if (!(change->x|change->y|change->z))
        return 0;

504
505
    clipDist = A_GetClipdist(spriteNum, clipDist);

506
    int16_t   newSectnum = pSprite->sectnum;
507
#ifndef EDUKE32_STANDALONE
Richard Gobeille's avatar
Richard Gobeille committed
508
    int const oldSectnum = newSectnum;
509
#endif
510

511
    // Handle horizontal movement first.
512
513

    int returnValue;
Richard Gobeille's avatar
Richard Gobeille committed
514
    int32_t diffZ;
515
    spriteheightofs(spriteNum, &diffZ, 1);
516

517
518
519
    if (pSprite->statnum == STAT_PROJECTILE)
        returnValue = clipmovex(&pSprite->pos, &newSectnum, change->x << 13, change->y << 13, clipDist, diffZ >> 3, diffZ >> 3, clipType, 1);
    else
520
    {
521
522
523
        pSprite->z -= diffZ >> 1;
        returnValue = clipmove(&pSprite->pos, &newSectnum, change->x << 13, change->y << 13, clipDist, ZOFFSET6, ZOFFSET6, clipType);
        pSprite->z += diffZ >> 1;
524
    }
525

526
527
528
    // Testing: For some reason the assert below this was tripping for clients
    EDUKE32_UNUSED int16_t   dbg_ClipMoveSectnum = newSectnum;

529
    if (isEnemy)
530
    {
531
        // Handle potential stayput condition (map-provided or hard-coded).
532
        if (newSectnum < 0 || ((actor[spriteNum].stayput >= 0 && actor[spriteNum].stayput != newSectnum)
533
                || ((g_tile[pSprite->picnum].flags & SFLAG_NOWATERSECTOR) && sector[newSectnum].lotag == ST_1_ABOVE_WATER)
534
#ifndef EDUKE32_STANDALONE
535
536
537
                || (!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
538
539
540
                    && (pSprite->picnum == LIZMAN || (pSprite->picnum == LIZTROOP && pSprite->zvel == 0)))
#endif
                ))
541
        {
542
            pSprite->pos.vec2 = oldPos;
543
544
545

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

546
            setsprite(spriteNum, &pSprite->pos);
547

548
549
            if (newSectnum < 0)
                newSectnum = 0;
550

551
            return 16384+newSectnum;
552
        }
Richard Gobeille's avatar
   
Richard Gobeille committed
553

554
555
        if ((returnValue&49152) >= 32768 && actor[spriteNum].cgg==0)
            pSprite->ang += 768;
556
    }
557

558
559
    EDUKE32_UNUSED int16_t   dbg_newSectnum2 = newSectnum;

560
    if (newSectnum == -1)
561
    {
562
        newSectnum = pSprite->sectnum;
563
//        OSD_Printf("%s:%d wtf\n",__FILE__,__LINE__);
564
    }
565
    else if (newSectnum != pSprite->sectnum)
566
    {
567
        changespritesect(spriteNum, newSectnum);
568
        // A_GetZLimits(spritenum);
Richard Gobeille's avatar
   
Richard Gobeille committed
569
    }
570

571
    Bassert(newSectnum == pSprite->sectnum);
572

573
    int newZ = pSprite->z;
574
575
    int32_t ceilhit, florhit;
    int const doZUpdate = change->z ? A_CheckNeedZUpdate(spriteNum, change->z, &newZ, &ceilhit, &florhit) : 0;
576

577
    // Update sprite's z positions and (for TROR) maybe the sector number.
578
    if (doZUpdate == 2)
579
580
581
582
    {
        if (returnValue == 0)
            returnValue = change->z < 0 ? ceilhit : florhit;
    }
583
    else if (doZUpdate)
Philipp Kutin's avatar
Philipp Kutin committed
584
    {
585
        pSprite->z = newZ;
Philipp Kutin's avatar
Philipp Kutin committed
586
#ifdef YAX_ENABLE
587
        if (doZUpdate < 0)
588
589
590
591
592
593
        {
            // 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
594
            // actor[].flags |= SFLAG_DIDNOSE7WATER in A_CheckNeedZUpdate()
595
596
            // previously.
            // XXX: Why is this contrived data flow necessary? (If at all.)
597
            changespritesect(spriteNum, -doZUpdate-1);
598
599
600
            return 0;
        }

601
602
        if (yax_getbunch(newSectnum, (change->z>0))>=0
                && (SECTORFLD(newSectnum,stat, (change->z>0))&yax_waltosecmask(clipType))==0)
603
        {
604
            setspritez(spriteNum, &pSprite->pos);
605
        }
Philipp Kutin's avatar
Philipp Kutin committed
606
607
#endif
    }
608
609
    else if (change->z != 0 && returnValue == 0)
        returnValue = 16384+newSectnum;
610

611
612
613
    if (returnValue == 16384 + newSectnum)
    {
        if (pSprite->statnum == STAT_PROJECTILE)
614
        {
615
616
            // Projectile sector changes due to transport SEs (SE7_PROJECTILE).
            // PROJECTILE_CHSECT
617
            for (bssize_t SPRITES_OF(STAT_TRANSPORT, otherSpriteNum))
618
619
            {
                if (sprite[otherSpriteNum].sectnum == newSectnum)
620
                {
621
                    int const sectLotag = sector[newSectnum].lotag;
622

623
                    if (sectLotag == ST_1_ABOVE_WATER && newZ >= actor[spriteNum].floorz)
624
                        if (Proj_MaybeDoTransport(spriteNum, (uspriteptr_t)&sprite[otherSpriteNum], 0, newZ))
625
                            return 0;
626

627
                    if (sectLotag == ST_2_UNDERWATER && newZ <= actor[spriteNum].ceilingz)
628
                        if (Proj_MaybeDoTransport(spriteNum, (uspriteptr_t)&sprite[otherSpriteNum], 1, newZ))
629
                            return 0;
630
                }
631
            }
632
        }
633
    }
Richard Gobeille's avatar
   
Richard Gobeille committed
634

635
    return returnValue;
636
637
}

Richard Gobeille's avatar
Richard Gobeille committed
638
int32_t block_deletesprite = 0;
Richard Gobeille's avatar
   
Richard Gobeille committed
639

640
#ifdef POLYMER
641
static void A_DeleteLight(int32_t s)
642
{
643
644
    if (actor[s].lightId >= 0)
        polymer_deletelight(actor[s].lightId);
645
646
647
648
649
650
651
652
653
    actor[s].lightId = -1;
    actor[s].lightptr = NULL;
}

void G_Polymer_UnInit(void)
{
    int32_t i;

    for (i=0; i<MAXSPRITES; i++)
654
        A_DeleteLight(i);
655
656
657
}
#endif

658
// deletesprite() game wrapper
659
void A_DeleteSprite(int spriteNum)
Richard Gobeille's avatar
Richard Gobeille committed
660
{
661
    if (EDUKE32_PREDICT_FALSE(block_deletesprite))
Richard Gobeille's avatar
   
Richard Gobeille committed
662
    {
663
        OSD_Printf(OSD_ERROR "A_DeleteSprite(): tried to remove sprite %d in EVENT_EGS\n", spriteNum);
Richard Gobeille's avatar
   
Richard Gobeille committed
664
665
        return;
    }
Richard Gobeille's avatar
Richard Gobeille committed
666

667
    if (VM_HaveEvent(EVENT_KILLIT))
Richard Gobeille's avatar
   
Richard Gobeille committed
668
    {
669
670
        int32_t playerDist;
        int playerNum = A_FindPlayer(&sprite[spriteNum], &playerDist);
Richard Gobeille's avatar
   
Richard Gobeille committed
671

672
        if (VM_ExecuteEvent(EVENT_KILLIT, spriteNum, playerNum, playerDist))
Richard Gobeille's avatar
   
Richard Gobeille committed
673
674
            return;
    }
Richard Gobeille's avatar
Richard Gobeille committed
675
676

#ifdef POLYMER
677
    if (actor[spriteNum].lightptr != NULL && videoGetRenderMode() == REND_POLYMER)
678
        A_DeleteLight(spriteNum);
Richard Gobeille's avatar
Richard Gobeille committed
679
680
#endif

681
    // AMBIENT_SFX_PLAYING
682
683
    if (sprite[spriteNum].picnum == MUSICANDSFX && actor[spriteNum].t_data[0] == 1)
        S_StopEnvSound(sprite[spriteNum].lotag, spriteNum);
684

685
#ifdef NETCODE_DISABLE
686
    deletesprite(spriteNum);
687
688
689
#else
    Net_DeleteSprite(spriteNum);
#endif
690
}
Richard Gobeille's avatar
Richard Gobeille committed
691

692
void A_AddToDeleteQueue(int spriteNum)
693
{
694
    if (g_netClient || (g_deleteQueueSize == 0)) // [75] Clients should not use SpriteDeletionQueue[] and just set the sprites invisible immediately in A_DeleteSprite
695
    {
696
        A_DeleteSprite(spriteNum);
Richard Gobeille's avatar
   
Richard Gobeille committed
697
        return;
698
    }
Richard Gobeille's avatar
Richard Gobeille committed
699

700
    auto &deleteSpriteNum = SpriteDeletionQueue[g_spriteDeleteQueuePos];
701

702
703
704
705
706
    if (deleteSpriteNum >= 0 && actor[deleteSpriteNum].flags & SFLAG_QUEUEDFORDELETE)
        A_DeleteSprite(deleteSpriteNum);

    deleteSpriteNum = spriteNum;
    actor[spriteNum].flags |= SFLAG_QUEUEDFORDELETE;
707
    g_spriteDeleteQueuePos = (g_spriteDeleteQueuePos+1)%g_deleteQueueSize;
708
709
}

710
void A_SpawnMultiple(int spriteNum, int tileNum, int spawnCnt)
711
{
712
    auto const pSprite = &sprite[spriteNum];
Richard Gobeille's avatar
Richard Gobeille committed
713

714
    for (; spawnCnt>0; spawnCnt--)
715
    {
716
717
        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
718
719
        A_Spawn(-1, j);
        sprite[j].cstat = krand()&12;
720
721
722
    }
}

723
#ifndef EDUKE32_STANDALONE
724
void A_DoGuts(int spriteNum, int tileNum, int spawnCnt)
725
{
726
727
    auto const pSprite = (uspriteptr_t)&sprite[spriteNum];
    vec2_t     repeat  = { 32, 32 };
728

729
730
    if (A_CheckEnemySprite(pSprite) && pSprite->xrepeat < 16)
        repeat.x = repeat.y = 8;
731

732
733
    int gutZ   = pSprite->z - ZOFFSET3;
    int floorz = getflorzofslope(pSprite->sectnum, pSprite->x, pSprite->y);
734

735
736
    if (gutZ > (floorz-ZOFFSET3))
        gutZ = floorz-ZOFFSET3;
737

738
739
    if (pSprite->picnum == COMMANDER)
        gutZ -= (24<<8);
740

741
    for (bssize_t j=spawnCnt; j>0; j--)
742
    {
743
744
745
746
747
        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)
748
749
750
751
        {
            sprite[i].xrepeat >>= 2;
            sprite[i].yrepeat >>= 2;
        }
752

753
        sprite[i].pal = pSprite->pal;
754
755
756
    }
}

757
void A_DoGutsDir(int spriteNum, int tileNum, int spawnCnt)
758
{
759
760
    auto const s      = (uspriteptr_t)&sprite[spriteNum];
    vec2_t     repeat = { 32, 32 };
Richard Gobeille's avatar
Richard Gobeille committed
761

Richard Gobeille's avatar
Richard Gobeille committed
762
    if (A_CheckEnemySprite(s) && s->xrepeat < 16)
763
        repeat.x = repeat.y = 8;
764

765
766
    int gutZ = s->z-ZOFFSET3;
    int floorZ = getflorzofslope(s->sectnum,s->x,s->y);
767

768
769
    if (gutZ > (floorZ-ZOFFSET3))
        gutZ = floorZ-ZOFFSET3;
770

Richard Gobeille's avatar
Richard Gobeille committed
771
    if (s->picnum == COMMANDER)
772
        gutZ -= (24<<8);
773

774
    for (bssize_t j=spawnCnt; j>0; j--)
775
    {
776
777
        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
778
        sprite[i].pal = s->pal;
779
780
    }
}
781
#endif
782

783
LUNATIC_EXTERN int32_t G_ToggleWallInterpolation(int32_t wallNum, int32_t setInterpolation)
784
{
785
    if (setInterpolation)
786
    {
787
        return G_SetInterpolation(&wall[wallNum].x) || G_SetInterpolation(&wall[wallNum].y);
788
789
790
    }
    else
    {
791
792
        G_StopInterpolation(&wall[wallNum].x);
        G_StopInterpolation(&wall[wallNum].y);
793
        return 0;
794
795
796
    }
}

797
void Sect_ToggleInterpolation(int sectNum, int setInterpolation)
798
{
799
    for (bssize_t j = sector[sectNum].wallptr, endwall = sector[sectNum].wallptr + sector[sectNum].wallnum; j < endwall; j++)
800
    {
801
802
803
        G_ToggleWallInterpolation(j, setInterpolation);

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

805
        if (nextWall >= 0)
806
        {
807
808
            G_ToggleWallInterpolation(nextWall, setInterpolation);
            G_ToggleWallInterpolation(wall[nextWall].point2, setInterpolation);
809
810
811
812
        }
    }
}

813
static int32_t move_rotfixed_sprite(int32_t spriteNum, int32_t pivotSpriteNum, int32_t pivotAngle)
814
{
815
816
817
818
    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))
819
    {
820
        rotatepoint(zerovec, *(vec2_t *)&actor[spriteNum].t_data[8], pivotAngle & 2047, &sprite[spriteNum].pos.vec2);
821
822
        sprite[spriteNum].x += sprite[pivotSpriteNum].x;
        sprite[spriteNum].y += sprite[pivotSpriteNum].y;
823
824
825
826
827
828
        return 0;
    }

    return 1;
}

829
void A_MoveSector(int spriteNum)
830
{
831
    // T1,T2 and T3 are used for all the sector moving stuff!!!
832

833
834
835
836
837
    int32_t    playerDist;
    auto const pSprite     = &sprite[spriteNum];
    int const  playerNum   = A_FindPlayer(pSprite, &playerDist);
    int const  rotateAngle = VM_OnEvent(EVENT_MOVESECTOR, spriteNum, playerNum, playerDist, T3(spriteNum));
    int        originIdx   = T2(spriteNum);
838

839
840
    pSprite->x += (pSprite->xvel * (sintable[(pSprite->ang + 512) & 2047])) >> 14;
    pSprite->y += (pSprite->xvel * (sintable[pSprite->ang & 2047])) >> 14;
841

842
    int const endWall = sector[pSprite->sectnum].wallptr + sector[pSprite->sectnum].wallnum;
843

844
    for (bssize_t wallNum = sector[pSprite->sectnum].wallptr; wallNum < endWall; wallNum++)
845
    {
846
847
848
849
        vec2_t const origin = g_origins[originIdx];
        vec2_t result;
        rotatepoint(zerovec, origin, rotateAngle & 2047, &result);
        dragpoint(wallNum, pSprite->x + result.x, pSprite->y + result.y, 0);
Richard Gobeille's avatar