player.cpp 214 KB
Newer Older
1
2
//-------------------------------------------------------------------------
/*
3
Copyright (C) 2010 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
*/
//-------------------------------------------------------------------------

#include "duke3d.h"
Richard Gobeille's avatar
Damnit    
Richard Gobeille committed
24
#include "demo.h"
25
#include "enet/enet.h"
26

27
28
29
30
#ifdef __ANDROID__
#include "android.h"
#endif

Richard Gobeille's avatar
Richard Gobeille committed
31
int32_t lastvisinc;
32
33
hudweapon_t hudweap;

34
#ifdef SPLITSCREEN_MOD_HACKS
35
static int32_t g_snum;
36
#endif
37

38
extern int32_t g_levelTextTime, ticrandomseed;
39

40
41
int32_t g_numObituaries = 0;
int32_t g_numSelfObituaries = 0;
Richard Gobeille's avatar
Richard Gobeille committed
42

43
44
45
46
47
48
49

int const icon_to_inv[ICON_MAX] = { GET_FIRSTAID, GET_FIRSTAID, GET_STEROIDS, GET_HOLODUKE,
                                    GET_JETPACK,  GET_HEATS,    GET_SCUBA,    GET_BOOTS };

int const inv_to_icon[GET_MAX] = { ICON_STEROIDS, ICON_NONE,  ICON_SCUBA, ICON_HOLODUKE, ICON_JETPACK, ICON_NONE,
                                   ICON_NONE,     ICON_HEATS, ICON_NONE,  ICON_FIRSTAID, ICON_BOOTS };

50
51
52
53
54
void P_AddKills(DukePlayer_t * const pPlayer, uint16_t kills)
{
    pPlayer->actors_killed += kills;
}

55
void P_UpdateScreenPal(DukePlayer_t * const pPlayer)
56
{
57
58
    int       inWater       = 0;
    int const playerSectnum = pPlayer->cursectnum;
59

60
61
62
63
64
    if (pPlayer->heat_on)
        pPlayer->palette = SLIMEPAL;
    else if (playerSectnum < 0)
        pPlayer->palette = BASEPAL;
    else if (sector[playerSectnum].ceilingpicnum >= FLOORSLIME && sector[playerSectnum].ceilingpicnum <= FLOORSLIME + 2)
65
    {
66
67
        pPlayer->palette = SLIMEPAL;
        inWater          = 1;
68
69
70
    }
    else
    {
71
72
        pPlayer->palette     = (sector[pPlayer->cursectnum].lotag == ST_2_UNDERWATER) ? WATERPAL : BASEPAL;
        inWater              = 1;
73
    }
74

75
    g_restorePalette = 1+inWater;
76
77
}

78
static void P_IncurDamage(DukePlayer_t * const pPlayer)
79
{
80
    if (VM_OnEvent(EVENT_INCURDAMAGE, pPlayer->i, P_Get(pPlayer->i)) != 0)
81
        return;
82

83
    sprite[pPlayer->i].extra -= pPlayer->extra_extra8>>8;
84

85
    int playerDamage = sprite[pPlayer->i].extra - pPlayer->last_extra;
86

87
    if (playerDamage >= 0)
88
        return;
89

90
    pPlayer->extra_extra8 = 0;
91

92
    if (pPlayer->inv_amount[GET_SHIELD] > 0)
93
    {
94
        int const shieldDamage = playerDamage * (20 + (krand()%30)) / 100;
95

96
97
        playerDamage                     -= shieldDamage;
        pPlayer->inv_amount[GET_SHIELD] += shieldDamage;
98

99
        if (pPlayer->inv_amount[GET_SHIELD] < 0)
100
        {
101
102
            playerDamage += pPlayer->inv_amount[GET_SHIELD];
            pPlayer->inv_amount[GET_SHIELD] = 0;
103
        }
104
105
    }

106
    sprite[pPlayer->i].extra = pPlayer->last_extra + playerDamage;
107
108
}

109
void P_QuickKill(DukePlayer_t * const pPlayer)
110
{
111
    P_PalFrom(pPlayer, 48, 48,48,48);
112

113
114
    sprite[pPlayer->i].extra = 0;
    sprite[pPlayer->i].cstat |= 32768;
115

116
#ifndef EDUKE32_STANDALONE
117
    if (!IONMAIDEN && ud.god == 0)
118
        A_DoGuts(pPlayer->i,JIBS6,8);
119
#endif
120
121
}

122
static void Proj_DoWaterTracers(vec3_t startPos, vec3_t const *endPos, int n, int16_t sectNum)
123
{
Richard Gobeille's avatar
Richard Gobeille committed
124
    if ((klabs(startPos.x - endPos->x) + klabs(startPos.y - endPos->y)) < 3084)
125
126
        return;

Richard Gobeille's avatar
Richard Gobeille committed
127
128
    vec3_t const v_inc = { tabledivide32_noinline(endPos->x - startPos.x, n + 1), tabledivide32_noinline(endPos->y - startPos.y, n + 1),
                           tabledivide32_noinline(endPos->z - startPos.z, n + 1) };
129

130
    for (bssize_t i=n; i>0; i--)
131
    {
Richard Gobeille's avatar
Richard Gobeille committed
132
133
134
        startPos.x += v_inc.x;
        startPos.y += v_inc.y;
        startPos.z += v_inc.z;
135

Richard Gobeille's avatar
Richard Gobeille committed
136
        updatesector(startPos.x, startPos.y, &sectNum);
137
138

        if (sectNum < 0)
139
140
            break;

141
142
        A_InsertSprite(sectNum, startPos.x, startPos.y, startPos.z, WATERBUBBLE, -32, 4 + (krand() & 3), 4 + (krand() & 3), krand() & 2047, 0, 0,
                       g_player[0].ps->i, 5);
143
144
145
    }
}

146
static inline projectile_t *Proj_GetProjectile(int tile)
147
{
148
    return ((unsigned)tile < MAXTILES && g_tile[tile].proj) ? g_tile[tile].proj : &DefaultProjectile;
149
150
}

151
static void A_HitscanProjTrail(const vec3_t *startPos, const vec3_t *endPos, int projAng, int tileNum, int16_t sectNum)
152
{
153
    const projectile_t *const pProj = Proj_GetProjectile(tileNum);
154

155
156
    vec3_t        spawnPos = { startPos->x + tabledivide32_noinline(sintable[(348 + projAng + 512) & 2047], pProj->offset),
                               startPos->y + tabledivide32_noinline(sintable[(projAng + 348) & 2047], pProj->offset),
157
                               startPos->z + 1024 + (pProj->toffset << 8) };
158

159
    int32_t      n         = ((FindDistance2D(spawnPos.x - endPos->x, spawnPos.y - endPos->y)) >> 8) + 1;
160

161
162
163
    vec3_t const increment = { tabledivide32_noinline((endPos->x - spawnPos.x), n),
                               tabledivide32_noinline((endPos->y - spawnPos.y), n),
                               tabledivide32_noinline((endPos->z - spawnPos.z), n) };
164

165
166
167
    spawnPos.x += increment.x >> 2;
    spawnPos.y += increment.y >> 2;
    spawnPos.z += increment.z >> 2;
Richard Gobeille's avatar
   
Richard Gobeille committed
168

169
    int32_t j;
170

171
    for (bssize_t i = pProj->tnum; i > 0; --i)
172
    {
173
174
175
176
        spawnPos.x += increment.x;
        spawnPos.y += increment.y;
        spawnPos.z += increment.z;

177
        updatesectorz(spawnPos.x, spawnPos.y, spawnPos.z, &sectNum);
178
179

        if (sectNum < 0)
180
            break;
181

182
        getzsofslope(sectNum, spawnPos.x, spawnPos.y, &n, &j);
183
184

        if (spawnPos.z > j || spawnPos.z < n)
Richard Gobeille's avatar
   
Richard Gobeille committed
185
            break;
186
187

        j = A_InsertSprite(sectNum, spawnPos.x, spawnPos.y, spawnPos.z, pProj->trail, -32,
188
                           pProj->txrepeat, pProj->tyrepeat, projAng, 0, 0, g_player[0].ps->i, 0);
189
        changespritestat(j, STAT_ACTOR);
190
191
192
    }
}

193
int32_t A_GetHitscanRange(int spriteNum)
194
{
195
    int const zOffset = (PN(spriteNum) == APLAYER) ? PHEIGHT : 0;
196
    hitdata_t hitData;
197

198
199
200
201
    SZ(spriteNum) -= zOffset;
    hitscan((const vec3_t *)&sprite[spriteNum], SECT(spriteNum), sintable[(SA(spriteNum) + 512) & 2047],
            sintable[SA(spriteNum) & 2047], 0, &hitData, CLIPMASK1);
    SZ(spriteNum) += zOffset;
202

203
    return (FindDistance2D(hitData.pos.x - SX(spriteNum), hitData.pos.y - SY(spriteNum)));
204
205
}

206
static int A_FindTargetSprite(const spritetype *pSprite, int projAng, int projecTile)
207
{
208
    static int const aimstats[] = {
Philipp Kutin's avatar
Philipp Kutin committed
209
210
        STAT_PLAYER, STAT_DUMMYPLAYER, STAT_ACTOR, STAT_ZOMBIEACTOR
    };
211

212
    int const playerNum = pSprite->picnum == APLAYER ? P_GetP(pSprite) : -1;
213

Richard Gobeille's avatar
Richard Gobeille committed
214
    if (playerNum != -1)
215
    {
216
        if (!g_player[playerNum].ps->auto_aim)
Richard Gobeille's avatar
Richard Gobeille committed
217
            return -1;
218

219
        if (g_player[playerNum].ps->auto_aim == 2)
Richard Gobeille's avatar
Richard Gobeille committed
220
        {
221
            if (A_CheckSpriteTileFlags(projecTile,SFLAG_PROJECTILE) && (Proj_GetProjectile(projecTile)->workslike & PROJECTILE_RPG))
Richard Gobeille's avatar
Richard Gobeille committed
222
                return -1;
Philipp Kutin's avatar
Philipp Kutin committed
223

Richard Gobeille's avatar
Richard Gobeille committed
224
#ifndef EDUKE32_STANDALONE
225
            if (!IONMAIDEN)
Philipp Kutin's avatar
Philipp Kutin committed
226
            {
227
228
229
230
231
232
233
234
235
236
237
238
239
240
                switch (DYNAMICTILEMAP(projecTile))
                {
                    case TONGUE__STATIC:
                    case FREEZEBLAST__STATIC:
                    case SHRINKSPARK__STATIC:
                    case SHRINKER__STATIC:
                    case RPG__STATIC:
                    case FIRELASER__STATIC:
                    case SPIT__STATIC:
                    case COOLEXPLOSION1__STATIC:
                        return -1;
                    default:
                        break;
                }
Philipp Kutin's avatar
Philipp Kutin committed
241
            }
Richard Gobeille's avatar
Richard Gobeille committed
242
#endif
Richard Gobeille's avatar
Richard Gobeille committed
243
244
        }
    }
245

246
    int const spriteAng = pSprite->ang;
247

Richard Gobeille's avatar
Richard Gobeille committed
248
249
250
251
#ifndef EDUKE32_STANDALONE
    int const isShrinker = (pSprite->picnum == APLAYER && PWEAPON(playerNum, g_player[playerNum].ps->curr_weapon, WorksLike) == SHRINKER_WEAPON);
    int const isFreezer  = (pSprite->picnum == APLAYER && PWEAPON(playerNum, g_player[playerNum].ps->curr_weapon, WorksLike) == FREEZE_WEAPON);
#endif
252

253
254
    vec2_t const d1 = { sintable[(spriteAng + 512 - projAng) & 2047], sintable[(spriteAng - projAng) & 2047] };
    vec2_t const d2 = { sintable[(spriteAng + 512 + projAng) & 2047], sintable[(spriteAng + projAng) & 2047] };
255
    vec2_t const d3 = { sintable[(spriteAng + 512) & 2047], sintable[spriteAng & 2047] };
256

Richard Gobeille's avatar
Richard Gobeille committed
257
258
    int lastDist   = INT32_MAX;
    int bestSprite = -1;
259

260
    for (bssize_t k=0; k<4; k++)
261
    {
Richard Gobeille's avatar
Richard Gobeille committed
262
        if (bestSprite >= 0)
263
            break;
264

265
        for (bssize_t spriteNum=headspritestat[aimstats[k]]; spriteNum >= 0; spriteNum=nextspritestat[spriteNum])
266
267
        {
            if ((sprite[spriteNum].xrepeat > 0 && sprite[spriteNum].extra >= 0 &&
Richard Gobeille's avatar
Richard Gobeille committed
268
                 (sprite[spriteNum].cstat & (257 + 32768)) == 257) &&
269
270
                (A_CheckEnemySprite(&sprite[spriteNum]) || k < 2))
            {
Richard Gobeille's avatar
Richard Gobeille committed
271
                if (A_CheckEnemySprite(&sprite[spriteNum]) || PN(spriteNum) == APLAYER)
272
                {
273
274
275
276
277
                    if (PN(spriteNum) == APLAYER && pSprite->picnum == APLAYER && pSprite != &sprite[spriteNum] &&
                        (GTFLAGS(GAMETYPE_PLAYERSFRIENDLY) ||
                         (GTFLAGS(GAMETYPE_TDM) && g_player[P_Get(spriteNum)].ps->team == g_player[playerNum].ps->team)))
                        continue;

Richard Gobeille's avatar
Richard Gobeille committed
278
#ifndef EDUKE32_STANDALONE
279
                    if (!IONMAIDEN && (isShrinker && sprite[spriteNum].xrepeat < 30
280
                        && (PN(spriteNum) == SHARK || !(PN(spriteNum) >= GREENSLIME && PN(spriteNum) <= GREENSLIME + 7)))
Richard Gobeille's avatar
Richard Gobeille committed
281
                        || (isFreezer && sprite[spriteNum].pal == 1))
282
                        continue;
Richard Gobeille's avatar
Richard Gobeille committed
283
#endif
284
                }
285

Richard Gobeille's avatar
Richard Gobeille committed
286
                vec2_t const vd = { (SX(spriteNum) - pSprite->x), (SY(spriteNum) - pSprite->y) };
287

288
289
                if ((d1.y * vd.x <= d1.x * vd.y) && (d2.y * vd.x >= d2.x * vd.y))
                {
290
                    int const spriteDist = mulscale14(d3.x, vd.x) + mulscale14(d3.y, vd.y);
291

292
                    if (spriteDist > 512 && spriteDist < lastDist)
293
                    {
294
                        int onScreen = 1;
295

296
                        if (pSprite->picnum == APLAYER)
297
                        {
298
                            const DukePlayer_t *const ps = g_player[P_GetP(pSprite)].ps;
299
                            onScreen = (klabs(scale(SZ(spriteNum)-pSprite->z,10,spriteDist)-fix16_to_int(ps->q16horiz+ps->q16horizoff-F16(100))) < 100);
300
                        }
301

Richard Gobeille's avatar
Richard Gobeille committed
302
#ifndef EDUKE32_STANDALONE
303
                        int const zOffset = IONMAIDEN ? 0 : (PN(spriteNum) == ORGANTIC || PN(spriteNum) == ROTATEGUN) ? 0 : ZOFFSET5;
Richard Gobeille's avatar
Richard Gobeille committed
304
305
306
307
308
#else
                        int const zOffset = 0;
#endif
                        int const canSee = cansee(SX(spriteNum), SY(spriteNum), SZ(spriteNum) - zOffset, SECT(spriteNum),
                                                  pSprite->x, pSprite->y, pSprite->z - ZOFFSET5, pSprite->sectnum);
309

310
311
                        if (onScreen && canSee)
                        {
Richard Gobeille's avatar
Richard Gobeille committed
312
313
                            lastDist   = spriteDist;
                            bestSprite = spriteNum;
314
                        }
315
                    }
316
                }
317
318
            }
        }
319
320
    }

Richard Gobeille's avatar
Richard Gobeille committed
321
    return bestSprite;
322
323
}

324
static void A_SetHitData(int spriteNum, const hitdata_t *hitData)
325
{
326
327
328
    actor[spriteNum].t_data[6] = hitData->wall;
    actor[spriteNum].t_data[7] = hitData->sect;
    actor[spriteNum].t_data[8] = hitData->sprite;
329
330
}

Richard Gobeille's avatar
Richard Gobeille committed
331
#ifndef EDUKE32_STANDALONE
332
static int CheckShootSwitchTile(int tileNum)
333
{
334
335
336
    if (IONMAIDEN)
        return 0;

337
338
    return tileNum == DIPSWITCH || tileNum == DIPSWITCH + 1 || tileNum == DIPSWITCH2 || tileNum == DIPSWITCH2 + 1 ||
           tileNum == DIPSWITCH3 || tileNum == DIPSWITCH3 + 1 || tileNum == HANDSWITCH || tileNum == HANDSWITCH + 1;
339
}
Richard Gobeille's avatar
Richard Gobeille committed
340
#endif
341

342
static int32_t safeldist(int32_t spriteNum, const void *pSprite)
343
{
344
345
    int32_t distance = ldist(&sprite[spriteNum], pSprite);
    return distance ? distance : 1;
346
347
}

348
349
350
// flags:
//  1: do sprite center adjustment (cen-=(8<<8)) for GREENSLIME or ROTATEGUN
//  2: do auto getangle only if not RECON (if clear, do unconditionally)
351
352
static int GetAutoAimAng(int spriteNum, int playerNum, int projecTile, int zAdjust, int aimFlags,
                               const vec3_t *startPos, int projVel, int32_t *pZvel, int *pAng)
353
{
354
    int returnSprite = -1;
355

356
    Bassert((unsigned)playerNum < MAXPLAYERS);
357

358
#ifdef LUNATIC
359
    g_player[playerNum].ps->autoaimang = g_player[playerNum].ps->auto_aim == 3 ? AUTO_AIM_ANGLE<<1 : AUTO_AIM_ANGLE;
360
#else
361
    Gv_SetVar(g_aimAngleVarID, g_player[playerNum].ps->auto_aim == 3 ? AUTO_AIM_ANGLE<<1 : AUTO_AIM_ANGLE, spriteNum, playerNum);
362
#endif
363

364
    VM_OnEvent(EVENT_GETAUTOAIMANGLE, spriteNum, playerNum);
365

366
#ifdef LUNATIC
367
    int aimang = g_player[playerNum].ps->autoaimang;
368
#else
369
    int aimang = Gv_GetVar(g_aimAngleVarID, spriteNum, playerNum);
370
#endif
371
372
    if (aimang > 0)
        returnSprite = A_FindTargetSprite(&sprite[spriteNum], aimang, projecTile);
373

374
    if (returnSprite >= 0)
375
    {
376
        const uspritetype *const pSprite = (uspritetype *)&sprite[returnSprite];
377
        int                      zCenter = 2 * (pSprite->yrepeat * tilesiz[pSprite->picnum].y) + zAdjust;
378

Richard Gobeille's avatar
Richard Gobeille committed
379
#ifndef EDUKE32_STANDALONE
380
        if (!IONMAIDEN && aimFlags &&
381
382
383
            ((pSprite->picnum >= GREENSLIME && pSprite->picnum <= GREENSLIME + 7) || pSprite->picnum == ROTATEGUN || pSprite->cstat & CSTAT_SPRITE_YCENTER))
#else
        if (aimFlags && pSprite->cstat & CSTAT_SPRITE_YCENTER)
Richard Gobeille's avatar
Richard Gobeille committed
384
#endif
385
            zCenter -= ZOFFSET3;
386

387
388
        int spriteDist = safeldist(g_player[playerNum].ps->i, &sprite[returnSprite]);
        *pZvel         = tabledivide32_noinline((pSprite->z - startPos->z - zCenter) * projVel, spriteDist);
389

390
        if (!(aimFlags&2) || sprite[returnSprite].picnum != RECON)
391
            *pAng = getangle(pSprite->x-startPos->x, pSprite->y-startPos->y);
392
393
    }

394
    return returnSprite;
395
396
}

397
static void Proj_MaybeSpawn(int spriteNum, int projecTile, const hitdata_t *hitData)
398
{
399
    // atwith < 0 is for hard-coded projectiles
400
    projectile_t *const pProj      = Proj_GetProjectile(projecTile);
401
    int                 spawnTile  = projecTile < 0 ? -projecTile : pProj->spawns;
402

403
    if (spawnTile >= 0)
404
    {
405
        int spawned = A_Spawn(spriteNum, spawnTile);
406

407
        if (projecTile >= 0)
408
        {
409
410
411
412
413
            if (pProj->sxrepeat > 4)
                sprite[spawned].xrepeat = pProj->sxrepeat;

            if (pProj->syrepeat > 4)
                sprite[spawned].yrepeat = pProj->syrepeat;
414
        }
415

416
        A_SetHitData(spawned, hitData);
417
418
419
    }
}

420
// <extra>: damage that this shotspark does
421
static int Proj_InsertShotspark(const hitdata_t *hitData, int spriteNum, int projecTile, int sparkSize, int sparkAng, int damage)
422
{
423
424
    int returnSprite = A_InsertSprite(hitData->sect, hitData->pos.x, hitData->pos.y, hitData->pos.z, SHOTSPARK1, -15,
                                     sparkSize, sparkSize, sparkAng, 0, 0, spriteNum, 4);
425

426
427
    sprite[returnSprite].extra = damage;
    sprite[returnSprite].yvel  = projecTile;  // This is a hack to allow you to detect which weapon spawned a SHOTSPARK1
428

429
    A_SetHitData(returnSprite, hitData);
430

431
    return returnSprite;
432
433
}

434
int Proj_GetDamage(projectile_t const *pProj)
435
{
436
437
    Bassert(pProj);

438
    int damage = pProj->extra;
439
440
441
442
443

    if (pProj->extra_rand > 0)
        damage += (krand() % pProj->extra_rand);

    return damage;
444
445
}

446
static void Proj_MaybeAddSpread(int doSpread, int32_t *zvel, int *shootAng, int zRange, int angRange)
447
{
448
    if (doSpread)
449
    {
450
451
        // Ranges <= 1 mean no spread at all. A range of 1 calls krand() though.
        if (zRange > 0)
452
453
            *zvel += (zRange >> 1) - krand() % zRange;

454
        if (angRange > 0)
455
            *shootAng += (angRange >> 1) - krand() % angRange;
456
457
458
    }
}

459
460
static int g_overrideShootZvel = 0;  // a boolean
static int g_shootZvel;  // the actual zvel if the above is !=0
461

462
static int A_GetShootZvel(int defaultZvel)
463
{
464
    return g_overrideShootZvel ? g_shootZvel : defaultZvel;
465
466
}

467
// Prepare hitscan weapon fired from player p.
468
469
static void P_PreFireHitscan(int spriteNum, int playerNum, int projecTile, vec3_t *srcVect, int32_t *zvel, int *shootAng,
                             int accurateAim, int doSpread)
470
{
471
472
473
    int angRange  = 32;
    int zRange    = 256;
    int aimSprite = GetAutoAimAng(spriteNum, playerNum, projecTile, 5 << 8, 0 + 1, srcVect, 256, zvel, shootAng);
474

475
    DukePlayer_t *const pPlayer = g_player[playerNum].ps;
476

477
#ifdef LUNATIC
478
479
    pPlayer->angrange = angRange;
    pPlayer->zrange = zRange;
480
#else
Richard Gobeille's avatar
Richard Gobeille committed
481
482
    Gv_SetVar(g_angRangeVarID, angRange, spriteNum, playerNum);
    Gv_SetVar(g_zRangeVarID, zRange, spriteNum, playerNum);
483
#endif
484

485
    VM_OnEvent(EVENT_GETSHOTRANGE, spriteNum, playerNum);
486

487
#ifdef LUNATIC
488
489
    angRange = pPlayer->angrange;
    zRange   = pPlayer->zrange;
490
#else
491
492
    angRange = Gv_GetVar(g_angRangeVarID, spriteNum, playerNum);
    zRange   = Gv_GetVar(g_zRangeVarID, spriteNum, playerNum);
493
#endif
494

495
    if (accurateAim)
496
    {
497
        if (!pPlayer->auto_aim)
498
        {
499
            hitdata_t hitData;
500

501
            *zvel = A_GetShootZvel(fix16_to_int(F16(100)-pPlayer->q16horiz-pPlayer->q16horizoff)<<5);
502

503
504
            hitscan(srcVect, sprite[spriteNum].sectnum, sintable[(*shootAng + 512) & 2047],
                    sintable[*shootAng & 2047], *zvel << 6, &hitData, CLIPMASK1);
505

506
            if (hitData.sprite != -1)
507
            {
508
509
                int const statNumMap = ((1 << STAT_ACTOR) | (1 << STAT_ZOMBIEACTOR) | (1 << STAT_PLAYER) | (1 << STAT_DUMMYPLAYER));
                int const statNum    = sprite[hitData.sprite].statnum;
510

Richard Gobeille's avatar
Richard Gobeille committed
511
                if ((unsigned)statNum <= 30 && (statNumMap & (1 << statNum)))
512
                    aimSprite = hitData.sprite;
513
514
515
            }
        }

516
        if (aimSprite == -1)
Richard Gobeille's avatar
Richard Gobeille committed
517
            goto notarget;
518
519
520
    }
    else
    {
521
        if (aimSprite == -1)  // no target
Richard Gobeille's avatar
Richard Gobeille committed
522
        {
Richard Gobeille's avatar
Richard Gobeille committed
523
notarget:
524
            *zvel = fix16_to_int(F16(100)-pPlayer->q16horiz-pPlayer->q16horizoff)<<5;
Richard Gobeille's avatar
Richard Gobeille committed
525
526
        }

527
        Proj_MaybeAddSpread(doSpread, zvel, shootAng, zRange, angRange);
528
529
    }

530
    srcVect->z -= (2<<8);
531
532
533
}

// Hitscan weapon fired from actor (sprite s);
534
static void A_PreFireHitscan(const spritetype *pSprite, vec3_t * const srcVect, int32_t * const zvel, int * const shootAng, int const doSpread)
535
{
536
537
538
    int const           playerNum  = A_FindPlayer(pSprite, NULL);
    const DukePlayer_t *pPlayer    = g_player[playerNum].ps;
    int const           playerDist = safeldist(pPlayer->i, pSprite);
539

540
    *zvel = tabledivide32_noinline((pPlayer->pos.z - srcVect->z) << 8, playerDist);
541

542
    srcVect->z -= ZOFFSET6;
543

Richard Gobeille's avatar
Richard Gobeille committed
544
545
    if (pSprite->picnum == BOSS1)
        *shootAng = getangle(pPlayer->pos.x - srcVect->x, pPlayer->pos.y - srcVect->y);
546

547
    Proj_MaybeAddSpread(doSpread, zvel, shootAng, 256, 128 >> (uint8_t)(pSprite->picnum != BOSS1));
548
549
}

550
static int Proj_DoHitscan(int spriteNum, int32_t const cstatmask, const vec3_t * const srcVect, int zvel, int const shootAng, hitdata_t * const hitData)
551
{
552
    spritetype *const pSprite = &sprite[spriteNum];
553

554
    pSprite->cstat &= ~cstatmask;
555
    zvel = A_GetShootZvel(zvel);
556
557
    hitscan(srcVect, pSprite->sectnum, sintable[(shootAng + 512) & 2047], sintable[shootAng & 2047], zvel << 6, hitData, CLIPMASK1);
    pSprite->cstat |= cstatmask;
558

559
    return (hitData->sect < 0);
560
561
}

Richard Gobeille's avatar
Richard Gobeille committed
562
static void Proj_DoRandDecalSize(int const spriteNum, int const projecTile)
Philipp Kutin's avatar
Philipp Kutin committed
563
{
564
565
    const projectile_t *const proj    = Proj_GetProjectile(projecTile);
    spritetype *const         pSprite = &sprite[spriteNum];
Philipp Kutin's avatar
Philipp Kutin committed
566
567

    if (proj->workslike & PROJECTILE_RANDDECALSIZE)
Richard Gobeille's avatar
Richard Gobeille committed
568
        pSprite->xrepeat = pSprite->yrepeat = clamp((krand() & proj->xrepeat), pSprite->yrepeat, pSprite->xrepeat);
Philipp Kutin's avatar
Philipp Kutin committed
569
570
    else
    {
571
572
        pSprite->xrepeat = proj->xrepeat;
        pSprite->yrepeat = proj->yrepeat;
Philipp Kutin's avatar
Philipp Kutin committed
573
574
575
    }
}

576
static int SectorContainsSE13(int const sectNum)
Philipp Kutin's avatar
Philipp Kutin committed
577
{
578
579
    if (sectNum >= 0)
    {
580
        for (bssize_t SPRITES_OF_SECT(sectNum, i))
581
        {
Philipp Kutin's avatar
Philipp Kutin committed
582
583
            if (sprite[i].statnum == STAT_EFFECTOR && sprite[i].lotag == SE_13_EXPLOSIVE)
                return 1;
584
585
        }
    }
Philipp Kutin's avatar
Philipp Kutin committed
586
587
588
589
590
    return 0;
}

// Maybe handle bit 2 (swap wall bottoms).
// (in that case walltype *hitwal may be stale)
591
static inline void HandleHitWall(hitdata_t *hitData)
Philipp Kutin's avatar
Philipp Kutin committed
592
{
593
    uwalltype const * const hitWall = (uwalltype *)&wall[hitData->wall];
Philipp Kutin's avatar
Philipp Kutin committed
594

595
596
    if ((hitWall->cstat & 2) && redwallp(hitWall) && (hitData->pos.z >= sector[hitWall->nextsector].floorz))
        hitData->wall = hitWall->nextwall;
Philipp Kutin's avatar
Philipp Kutin committed
597
598
}

599
600
601
// Maybe damage a ceiling or floor as the consequence of projectile impact.
// Returns 1 if projectile hit a parallaxed ceiling.
// NOTE: Compare with Proj_MaybeDamageCF() in actors.c
602
static int Proj_MaybeDamageCF2(int const spriteNum, int const zvel, int const hitSect)
603
{
604
605
    Bassert(hitSect >= 0);

606
607
    if (zvel < 0)
    {
608
        if (sector[hitSect].ceilingstat&1)
609
610
            return 1;

611
        Sect_DamageCeiling(spriteNum, hitSect);
612
613
614
    }
    else if (zvel > 0)
    {
615
        if (sector[hitSect].floorstat&1)
616
617
618
619
620
621
        {
            // Keep original Duke3D behavior: pass projectiles through
            // parallaxed ceilings, but NOT through such floors.
            return 0;
        }

622
        Sect_DamageFloor(spriteNum, hitSect);
623
624
625
626
627
    }

    return 0;
}

628
// Finish shooting hitscan weapon from player <p>. <k> is the inserted SHOTSPARK1.
629
630
631
632
// * <spawnObject> is passed to Proj_MaybeSpawn()
// * <decalTile> and <wallDamage> are for wall impact
// * <wallDamage> is passed to A_DamageWall()
// * <decalFlags> is for decals upon wall impact:
633
634
635
636
//    1: handle random decal size (tile <atwith>)
//    2: set cstat to wall-aligned + random x/y flip
//
// TODO: maybe split into 3 cases (hit neither wall nor sprite, hit sprite, hit wall)?
Richard Gobeille's avatar
Richard Gobeille committed
637
static int P_PostFireHitscan(int playerNum, int const spriteNum, hitdata_t *const hitData, int const spriteOwner,
638
639
                             int const projecTile, int const zvel, int const spawnTile, int const decalTile, int const wallDamage,
                             int const decalFlags)
640
{
Richard Gobeille's avatar
Richard Gobeille committed
641
642
643
#ifdef EDUKE32_STANDALONE
    UNREFERENCED_PARAMETER(playerNum);
#endif
644
    if (hitData->wall == -1 && hitData->sprite == -1)
645
    {
646
        if (Proj_MaybeDamageCF2(spriteNum, zvel, hitData->sect))
647
        {
648
649
            sprite[spriteNum].xrepeat = 0;
            sprite[spriteNum].yrepeat = 0;
650
            return -1;
651
652
        }

653
        Proj_MaybeSpawn(spriteNum, spawnTile, hitData);
654
    }
655
    else if (hitData->sprite >= 0)
656
    {
657
        A_DamageObject(hitData->sprite, spriteNum);
658

659
        if (!IONMAIDEN && sprite[hitData->sprite].picnum == APLAYER &&
660
            (ud.ffire == 1 || (!GTFLAGS(GAMETYPE_PLAYERSFRIENDLY) && GTFLAGS(GAMETYPE_TDM) &&
661
                               g_player[P_Get(hitData->sprite)].ps->team != g_player[P_Get(spriteOwner)].ps->team)))
662
        {
Richard Gobeille's avatar
Richard Gobeille committed
663
#ifndef EDUKE32_STANDALONE
664
665
666
667
668
669
670
            int jibSprite = A_Spawn(spriteNum, JIBS6);

            sprite[spriteNum].xrepeat = sprite[spriteNum].yrepeat = 0;
            sprite[jibSprite].z += ZOFFSET6;
            sprite[jibSprite].xvel    = 16;
            sprite[jibSprite].xrepeat = sprite[jibSprite].yrepeat = 24;
            sprite[jibSprite].ang += 64 - (krand() & 127);
Richard Gobeille's avatar
Richard Gobeille committed
671
#endif
672
673
674
        }
        else
        {
675
            Proj_MaybeSpawn(spriteNum, spawnTile, hitData);
676
        }
Richard Gobeille's avatar
Richard Gobeille committed
677
#ifndef EDUKE32_STANDALONE
678
        if (!IONMAIDEN && playerNum >= 0 && CheckShootSwitchTile(sprite[hitData->sprite].picnum))
679
        {
680
            P_ActivateSwitch(playerNum, hitData->sprite, 1);
681
682
            return -1;
        }
Richard Gobeille's avatar
Richard Gobeille committed
683
#endif
684
    }
685
    else if (hitData->wall >= 0)
686
    {
687
        uwalltype const * const hitWall = (uwalltype *)&wall[hitData->wall];
688

689
        Proj_MaybeSpawn(spriteNum, spawnTile, hitData);
690

691
        if (CheckDoorTile(hitWall->picnum) == 1)
692
693
            goto SKIPBULLETHOLE;

Richard Gobeille's avatar
Richard Gobeille committed
694
#ifndef EDUKE32_STANDALONE
695
        if (!IONMAIDEN && playerNum >= 0 && CheckShootSwitchTile(hitWall->picnum))
696
        {
697
            P_ActivateSwitch(playerNum, hitData->wall, 0);
698
699
            return -1;
        }
Richard Gobeille's avatar
Richard Gobeille committed
700
#endif
701

702
        if (hitWall->hitag != 0 || (hitWall->nextwall >= 0 && wall[hitWall->nextwall].hitag != 0))
703
704
            goto SKIPBULLETHOLE;

705
706