Subversion Repositories eduke32

Rev

Rev 8789 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
5 Plagman 1
//-------------------------------------------------------------------------
2
/*
1652 terminx 3
Copyright (C) 2010 EDuke32 developers and contributors
5 Plagman 4
 
1652 terminx 5
This file is part of EDuke32.
5 Plagman 6
 
7
EDuke32 is free software; you can redistribute it and/or
8
modify it under the terms of the GNU General Public License version 2
9
as published by the Free Software Foundation.
10
 
11
This program is distributed in the hope that it will be useful,
12
but WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
 
15
See the GNU General Public License for more details.
16
 
17
You should have received a copy of the GNU General Public License
18
along with this program; if not, write to the Free Software
4541 hendricks2 19
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
5 Plagman 20
*/
21
//-------------------------------------------------------------------------
22
 
23
#include "duke3d.h"
1678 terminx 24
#include "demo.h"
8175 terminx 25
#include "enet.h"
5 Plagman 26
 
4440 terminx 27
#ifdef __ANDROID__
28
#include "android.h"
29
#endif
30
 
2652 terminx 31
int32_t lastvisinc;
3408 helixhorne 32
hudweapon_t hudweap;
33
 
5116 hendricks2 34
#ifdef SPLITSCREEN_MOD_HACKS
2905 helixhorne 35
static int32_t g_snum;
5116 hendricks2 36
#endif
5 Plagman 37
 
1567 terminx 38
extern int32_t g_levelTextTime, ticrandomseed;
1068 terminx 39
 
5979 hendricks2 40
int32_t g_numObituaries = 0;
41
int32_t g_numSelfObituaries = 0;
1220 terminx 42
 
6320 hendricks2 43
 
44
int const icon_to_inv[ICON_MAX] = { GET_FIRSTAID, GET_FIRSTAID, GET_STEROIDS, GET_HOLODUKE,
45
                                    GET_JETPACK,  GET_HEATS,    GET_SCUBA,    GET_BOOTS };
46
 
47
int const inv_to_icon[GET_MAX] = { ICON_STEROIDS, ICON_NONE,  ICON_SCUBA, ICON_HOLODUKE, ICON_JETPACK, ICON_NONE,
48
                                   ICON_NONE,     ICON_HEATS, ICON_NONE,  ICON_FIRSTAID, ICON_BOOTS };
49
 
6319 hendricks2 50
void P_AddKills(DukePlayer_t * const pPlayer, uint16_t kills)
51
{
52
    pPlayer->actors_killed += kills;
53
}
54
 
5826 terminx 55
void P_UpdateScreenPal(DukePlayer_t * const pPlayer)
5 Plagman 56
{
5825 terminx 57
    int       inWater       = 0;
58
    int const playerSectnum = pPlayer->cursectnum;
1899 helixhorne 59
 
5825 terminx 60
    if (pPlayer->heat_on)
61
        pPlayer->palette = SLIMEPAL;
62
    else if (playerSectnum < 0)
63
        pPlayer->palette = BASEPAL;
64
    else if (sector[playerSectnum].ceilingpicnum >= FLOORSLIME && sector[playerSectnum].ceilingpicnum <= FLOORSLIME + 2)
335 terminx 65
    {
5825 terminx 66
        pPlayer->palette = SLIMEPAL;
67
        inWater          = 1;
335 terminx 68
    }
69
    else
70
    {
5825 terminx 71
        pPlayer->palette     = (sector[pPlayer->cursectnum].lotag == ST_2_UNDERWATER) ? WATERPAL : BASEPAL;
72
        inWater              = 1;
5 Plagman 73
    }
2955 helixhorne 74
 
5825 terminx 75
    g_restorePalette = 1+inWater;
5 Plagman 76
}
77
 
5826 terminx 78
static void P_IncurDamage(DukePlayer_t * const pPlayer)
5 Plagman 79
{
5825 terminx 80
    if (VM_OnEvent(EVENT_INCURDAMAGE, pPlayer->i, P_Get(pPlayer->i)) != 0)
3081 terminx 81
        return;
5 Plagman 82
 
5825 terminx 83
    sprite[pPlayer->i].extra -= pPlayer->extra_extra8>>8;
5 Plagman 84
 
5825 terminx 85
    int playerDamage = sprite[pPlayer->i].extra - pPlayer->last_extra;
5 Plagman 86
 
5825 terminx 87
    if (playerDamage >= 0)
3081 terminx 88
        return;
5 Plagman 89
 
5825 terminx 90
    pPlayer->extra_extra8 = 0;
5 Plagman 91
 
5825 terminx 92
    if (pPlayer->inv_amount[GET_SHIELD] > 0)
3081 terminx 93
    {
5825 terminx 94
        int const shieldDamage = playerDamage * (20 + (krand()%30)) / 100;
5 Plagman 95
 
5825 terminx 96
        playerDamage                     -= shieldDamage;
97
        pPlayer->inv_amount[GET_SHIELD] += shieldDamage;
3081 terminx 98
 
5825 terminx 99
        if (pPlayer->inv_amount[GET_SHIELD] < 0)
3081 terminx 100
        {
5825 terminx 101
            playerDamage += pPlayer->inv_amount[GET_SHIELD];
102
            pPlayer->inv_amount[GET_SHIELD] = 0;
5 Plagman 103
        }
104
    }
105
 
5825 terminx 106
    sprite[pPlayer->i].extra = pPlayer->last_extra + playerDamage;
5 Plagman 107
}
108
 
5826 terminx 109
void P_QuickKill(DukePlayer_t * const pPlayer)
5 Plagman 110
{
5825 terminx 111
    P_PalFrom(pPlayer, 48, 48,48,48);
5 Plagman 112
 
5825 terminx 113
    sprite[pPlayer->i].extra = 0;
114
    sprite[pPlayer->i].cstat |= 32768;
3081 terminx 115
 
6911 terminx 116
#ifndef EDUKE32_STANDALONE
7808 terminx 117
    if (!FURY && ud.god == 0)
5825 terminx 118
        A_DoGuts(pPlayer->i,JIBS6,8);
6911 terminx 119
#endif
5 Plagman 120
}
121
 
6912 terminx 122
static void Proj_DoWaterTracers(vec3_t startPos, vec3_t const *endPos, int n, int16_t sectNum)
5 Plagman 123
{
6168 terminx 124
    if ((klabs(startPos.x - endPos->x) + klabs(startPos.y - endPos->y)) < 3084)
5 Plagman 125
        return;
126
 
6168 terminx 127
    vec3_t const v_inc = { tabledivide32_noinline(endPos->x - startPos.x, n + 1), tabledivide32_noinline(endPos->y - startPos.y, n + 1),
128
                           tabledivide32_noinline(endPos->z - startPos.z, n + 1) };
5825 terminx 129
 
5829 terminx 130
    for (bssize_t i=n; i>0; i--)
5 Plagman 131
    {
6168 terminx 132
        startPos.x += v_inc.x;
133
        startPos.y += v_inc.y;
134
        startPos.z += v_inc.z;
5825 terminx 135
 
6168 terminx 136
        updatesector(startPos.x, startPos.y, &sectNum);
5825 terminx 137
 
138
        if (sectNum < 0)
1490 terminx 139
            break;
140
 
6912 terminx 141
        A_InsertSprite(sectNum, startPos.x, startPos.y, startPos.z, WATERBUBBLE, -32, 4 + (krand() & 3), 4 + (krand() & 3), krand() & 2047, 0, 0,
142
                       g_player[0].ps->i, 5);
5 Plagman 143
    }
144
}
145
 
5825 terminx 146
static inline projectile_t *Proj_GetProjectile(int tile)
5080 terminx 147
{
5159 helixhorne 148
    return ((unsigned)tile < MAXTILES && g_tile[tile].proj) ? g_tile[tile].proj : &DefaultProjectile;
5080 terminx 149
}
150
 
5828 terminx 151
static void A_HitscanProjTrail(const vec3_t *startPos, const vec3_t *endPos, int projAng, int tileNum, int16_t sectNum)
5 Plagman 152
{
5825 terminx 153
    const projectile_t *const pProj = Proj_GetProjectile(tileNum);
5 Plagman 154
 
5828 terminx 155
    vec3_t        spawnPos = { startPos->x + tabledivide32_noinline(sintable[(348 + projAng + 512) & 2047], pProj->offset),
156
                               startPos->y + tabledivide32_noinline(sintable[(projAng + 348) & 2047], pProj->offset),
5825 terminx 157
                               startPos->z + 1024 + (pProj->toffset << 8) };
3361 helixhorne 158
 
5825 terminx 159
    int32_t      n         = ((FindDistance2D(spawnPos.x - endPos->x, spawnPos.y - endPos->y)) >> 8) + 1;
5 Plagman 160
 
5825 terminx 161
    vec3_t const increment = { tabledivide32_noinline((endPos->x - spawnPos.x), n),
162
                               tabledivide32_noinline((endPos->y - spawnPos.y), n),
163
                               tabledivide32_noinline((endPos->z - spawnPos.z), n) };
5 Plagman 164
 
5825 terminx 165
    spawnPos.x += increment.x >> 2;
166
    spawnPos.y += increment.y >> 2;
167
    spawnPos.z += increment.z >> 2;
5 Plagman 168
 
5825 terminx 169
    int32_t j;
252 terminx 170
 
5829 terminx 171
    for (bssize_t i = pProj->tnum; i > 0; --i)
5825 terminx 172
    {
173
        spawnPos.x += increment.x;
174
        spawnPos.y += increment.y;
175
        spawnPos.z += increment.z;
1207 terminx 176
 
5828 terminx 177
        updatesectorz(spawnPos.x, spawnPos.y, spawnPos.z, &sectNum);
5825 terminx 178
 
179
        if (sectNum < 0)
1488 terminx 180
            break;
5825 terminx 181
 
5828 terminx 182
        getzsofslope(sectNum, spawnPos.x, spawnPos.y, &n, &j);
5825 terminx 183
 
184
        if (spawnPos.z > j || spawnPos.z < n)
861 terminx 185
            break;
5825 terminx 186
 
187
        j = A_InsertSprite(sectNum, spawnPos.x, spawnPos.y, spawnPos.z, pProj->trail, -32,
5828 terminx 188
                           pProj->txrepeat, pProj->tyrepeat, projAng, 0, 0, g_player[0].ps->i, 0);
2642 helixhorne 189
        changespritestat(j, STAT_ACTOR);
5 Plagman 190
    }
191
}
192
 
5828 terminx 193
int32_t A_GetHitscanRange(int spriteNum)
5 Plagman 194
{
5828 terminx 195
    int const zOffset = (PN(spriteNum) == APLAYER) ? PHEIGHT : 0;
5825 terminx 196
    hitdata_t hitData;
5 Plagman 197
 
5825 terminx 198
    SZ(spriteNum) -= zOffset;
199
    hitscan((const vec3_t *)&sprite[spriteNum], SECT(spriteNum), sintable[(SA(spriteNum) + 512) & 2047],
200
            sintable[SA(spriteNum) & 2047], 0, &hitData, CLIPMASK1);
201
    SZ(spriteNum) += zOffset;
5 Plagman 202
 
5825 terminx 203
    return (FindDistance2D(hitData.pos.x - SX(spriteNum), hitData.pos.y - SY(spriteNum)));
5 Plagman 204
}
205
 
5828 terminx 206
static int A_FindTargetSprite(const spritetype *pSprite, int projAng, int projecTile)
5 Plagman 207
{
5828 terminx 208
    static int const aimstats[] = {
3955 helixhorne 209
        STAT_PLAYER, STAT_DUMMYPLAYER, STAT_ACTOR, STAT_ZOMBIEACTOR
210
    };
5 Plagman 211
 
5825 terminx 212
    int const playerNum = pSprite->picnum == APLAYER ? P_GetP(pSprite) : -1;
4226 helixhorne 213
 
6942 terminx 214
    if (playerNum != -1)
335 terminx 215
    {
5825 terminx 216
        if (!g_player[playerNum].ps->auto_aim)
19 terminx 217
            return -1;
3081 terminx 218
 
5825 terminx 219
        if (g_player[playerNum].ps->auto_aim == 2)
19 terminx 220
        {
5825 terminx 221
            if (A_CheckSpriteTileFlags(projecTile,SFLAG_PROJECTILE) && (Proj_GetProjectile(projecTile)->workslike & PROJECTILE_RPG))
19 terminx 222
                return -1;
3955 helixhorne 223
 
6942 terminx 224
#ifndef EDUKE32_STANDALONE
7808 terminx 225
            if (!FURY)
3955 helixhorne 226
            {
7455 terminx 227
                switch (DYNAMICTILEMAP(projecTile))
228
                {
229
                    case TONGUE__STATIC:
230
                    case FREEZEBLAST__STATIC:
231
                    case SHRINKSPARK__STATIC:
232
                    case SHRINKER__STATIC:
233
                    case RPG__STATIC:
234
                    case FIRELASER__STATIC:
235
                    case SPIT__STATIC:
236
                    case COOLEXPLOSION1__STATIC:
237
                        return -1;
238
                    default:
239
                        break;
240
                }
3955 helixhorne 241
            }
6942 terminx 242
#endif
19 terminx 243
        }
244
    }
120 terminx 245
 
5825 terminx 246
    int const spriteAng = pSprite->ang;
120 terminx 247
 
6942 terminx 248
#ifndef EDUKE32_STANDALONE
249
    int const isShrinker = (pSprite->picnum == APLAYER && PWEAPON(playerNum, g_player[playerNum].ps->curr_weapon, WorksLike) == SHRINKER_WEAPON);
250
    int const isFreezer  = (pSprite->picnum == APLAYER && PWEAPON(playerNum, g_player[playerNum].ps->curr_weapon, WorksLike) == FREEZE_WEAPON);
251
#endif
120 terminx 252
 
5828 terminx 253
    vec2_t const d1 = { sintable[(spriteAng + 512 - projAng) & 2047], sintable[(spriteAng - projAng) & 2047] };
254
    vec2_t const d2 = { sintable[(spriteAng + 512 + projAng) & 2047], sintable[(spriteAng + projAng) & 2047] };
5825 terminx 255
    vec2_t const d3 = { sintable[(spriteAng + 512) & 2047], sintable[spriteAng & 2047] };
5 Plagman 256
 
6942 terminx 257
    int lastDist   = INT32_MAX;
258
    int bestSprite = -1;
5 Plagman 259
 
5829 terminx 260
    for (bssize_t k=0; k<4; k++)
5 Plagman 261
    {
6942 terminx 262
        if (bestSprite >= 0)
5 Plagman 263
            break;
5825 terminx 264
 
5829 terminx 265
        for (bssize_t spriteNum=headspritestat[aimstats[k]]; spriteNum >= 0; spriteNum=nextspritestat[spriteNum])
5825 terminx 266
        {
267
            if ((sprite[spriteNum].xrepeat > 0 && sprite[spriteNum].extra >= 0 &&
5854 terminx 268
                 (sprite[spriteNum].cstat & (257 + 32768)) == 257) &&
5825 terminx 269
                (A_CheckEnemySprite(&sprite[spriteNum]) || k < 2))
270
            {
6942 terminx 271
                if (A_CheckEnemySprite(&sprite[spriteNum]) || PN(spriteNum) == APLAYER)
5 Plagman 272
                {
5825 terminx 273
                    if (PN(spriteNum) == APLAYER && pSprite->picnum == APLAYER && pSprite != &sprite[spriteNum] &&
274
                        (GTFLAGS(GAMETYPE_PLAYERSFRIENDLY) ||
275
                         (GTFLAGS(GAMETYPE_TDM) && g_player[P_Get(spriteNum)].ps->team == g_player[playerNum].ps->team)))
276
                        continue;
5 Plagman 277
 
6942 terminx 278
#ifndef EDUKE32_STANDALONE
7808 terminx 279
                    if (!FURY && ((isShrinker && sprite[spriteNum].xrepeat < 30
5863 hendricks2 280
                        && (PN(spriteNum) == SHARK || !(PN(spriteNum) >= GREENSLIME && PN(spriteNum) <= GREENSLIME + 7)))
7566 hendricks2 281
                        || (isFreezer && sprite[spriteNum].pal == 1)))
5825 terminx 282
                        continue;
6942 terminx 283
#endif
5825 terminx 284
                }
5 Plagman 285
 
6942 terminx 286
                vec2_t const vd = { (SX(spriteNum) - pSprite->x), (SY(spriteNum) - pSprite->y) };
5 Plagman 287
 
5825 terminx 288
                if ((d1.y * vd.x <= d1.x * vd.y) && (d2.y * vd.x >= d2.x * vd.y))
289
                {
6254 hendricks2 290
                    int const spriteDist = mulscale14(d3.x, vd.x) + mulscale14(d3.y, vd.y);
5825 terminx 291
 
292
                    if (spriteDist > 512 && spriteDist < lastDist)
3081 terminx 293
                    {
5825 terminx 294
                        int onScreen = 1;
3081 terminx 295
 
5825 terminx 296
                        if (pSprite->picnum == APLAYER)
5 Plagman 297
                        {
7776 terminx 298
                            auto const ps = g_player[P_GetP(pSprite)].ps;
6725 terminx 299
                            onScreen = (klabs(scale(SZ(spriteNum)-pSprite->z,10,spriteDist)-fix16_to_int(ps->q16horiz+ps->q16horizoff-F16(100))) < 100);
5825 terminx 300
                        }
5 Plagman 301
 
6942 terminx 302
#ifndef EDUKE32_STANDALONE
7808 terminx 303
                        int const zOffset = (!FURY && (PN(spriteNum) == ORGANTIC || PN(spriteNum) == ROTATEGUN)) ? 0 : ZOFFSET5;
6942 terminx 304
#else
7702 terminx 305
                        int const zOffset = ZOFFSET5;
6942 terminx 306
#endif
307
                        int const canSee = cansee(SX(spriteNum), SY(spriteNum), SZ(spriteNum) - zOffset, SECT(spriteNum),
308
                                                  pSprite->x, pSprite->y, pSprite->z - ZOFFSET5, pSprite->sectnum);
5 Plagman 309
 
5825 terminx 310
                        if (onScreen && canSee)
311
                        {
6942 terminx 312
                            lastDist   = spriteDist;
313
                            bestSprite = spriteNum;
5 Plagman 314
                        }
3081 terminx 315
                    }
5 Plagman 316
                }
5825 terminx 317
            }
318
        }
5 Plagman 319
    }
320
 
6942 terminx 321
    return bestSprite;
5 Plagman 322
}
323
 
5828 terminx 324
static void A_SetHitData(int spriteNum, const hitdata_t *hitData)
2979 helixhorne 325
{
5825 terminx 326
    actor[spriteNum].t_data[6] = hitData->wall;
327
    actor[spriteNum].t_data[7] = hitData->sect;
328
    actor[spriteNum].t_data[8] = hitData->sprite;
2979 helixhorne 329
}
330
 
6942 terminx 331
#ifndef EDUKE32_STANDALONE
5828 terminx 332
static int CheckShootSwitchTile(int tileNum)
2980 helixhorne 333
{
7808 terminx 334
    if (FURY)
7455 terminx 335
        return 0;
336
 
5828 terminx 337
    return tileNum == DIPSWITCH || tileNum == DIPSWITCH + 1 || tileNum == DIPSWITCH2 || tileNum == DIPSWITCH2 + 1 ||
338
           tileNum == DIPSWITCH3 || tileNum == DIPSWITCH3 + 1 || tileNum == HANDSWITCH || tileNum == HANDSWITCH + 1;
2980 helixhorne 339
}
6942 terminx 340
#endif
2980 helixhorne 341
 
5825 terminx 342
static int32_t safeldist(int32_t spriteNum, const void *pSprite)
3462 helixhorne 343
{
5825 terminx 344
    int32_t distance = ldist(&sprite[spriteNum], pSprite);
345
    return distance ? distance : 1;
3462 helixhorne 346
}
347
 
2983 helixhorne 348
// flags:
349
//  1: do sprite center adjustment (cen-=(8<<8)) for GREENSLIME or ROTATEGUN
350
//  2: do auto getangle only if not RECON (if clear, do unconditionally)
5828 terminx 351
static int GetAutoAimAng(int spriteNum, int playerNum, int projecTile, int zAdjust, int aimFlags,
352
                               const vec3_t *startPos, int projVel, int32_t *pZvel, int *pAng)
5 Plagman 353
{
5828 terminx 354
    int returnSprite = -1;
2983 helixhorne 355
 
5825 terminx 356
    Bassert((unsigned)playerNum < MAXPLAYERS);
2983 helixhorne 357
 
3366 helixhorne 358
#ifdef LUNATIC
5825 terminx 359
    g_player[playerNum].ps->autoaimang = g_player[playerNum].ps->auto_aim == 3 ? AUTO_AIM_ANGLE<<1 : AUTO_AIM_ANGLE;
3366 helixhorne 360
#else
5825 terminx 361
    Gv_SetVar(g_aimAngleVarID, g_player[playerNum].ps->auto_aim == 3 ? AUTO_AIM_ANGLE<<1 : AUTO_AIM_ANGLE, spriteNum, playerNum);
3366 helixhorne 362
#endif
3081 terminx 363
 
5825 terminx 364
    VM_OnEvent(EVENT_GETAUTOAIMANGLE, spriteNum, playerNum);
2983 helixhorne 365
 
3366 helixhorne 366
#ifdef LUNATIC
5828 terminx 367
    int aimang = g_player[playerNum].ps->autoaimang;
3366 helixhorne 368
#else
5828 terminx 369
    int aimang = Gv_GetVar(g_aimAngleVarID, spriteNum, playerNum);
3366 helixhorne 370
#endif
5825 terminx 371
    if (aimang > 0)
372
        returnSprite = A_FindTargetSprite(&sprite[spriteNum], aimang, projecTile);
2983 helixhorne 373
 
5825 terminx 374
    if (returnSprite >= 0)
2983 helixhorne 375
    {
7603 terminx 376
        auto const pSprite = (uspriteptr_t)&sprite[returnSprite];
377
        int        zCenter = 2 * (pSprite->yrepeat * tilesiz[pSprite->picnum].y) + zAdjust;
2983 helixhorne 378
 
6942 terminx 379
#ifndef EDUKE32_STANDALONE
7808 terminx 380
        if (!FURY && aimFlags &&
6971 terminx 381
            ((pSprite->picnum >= GREENSLIME && pSprite->picnum <= GREENSLIME + 7) || pSprite->picnum == ROTATEGUN || pSprite->cstat & CSTAT_SPRITE_YCENTER))
382
#else
383
        if (aimFlags && pSprite->cstat & CSTAT_SPRITE_YCENTER)
384
#endif
5825 terminx 385
            zCenter -= ZOFFSET3;
2983 helixhorne 386
 
5825 terminx 387
        int spriteDist = safeldist(g_player[playerNum].ps->i, &sprite[returnSprite]);
388
        *pZvel         = tabledivide32_noinline((pSprite->z - startPos->z - zCenter) * projVel, spriteDist);
2983 helixhorne 389
 
5825 terminx 390
        if (!(aimFlags&2) || sprite[returnSprite].picnum != RECON)
5828 terminx 391
            *pAng = getangle(pSprite->x-startPos->x, pSprite->y-startPos->y);
2983 helixhorne 392
    }
393
 
5825 terminx 394
    return returnSprite;
2983 helixhorne 395
}
396
 
5828 terminx 397
static void Proj_MaybeSpawn(int spriteNum, int projecTile, const hitdata_t *hitData)
3353 helixhorne 398
{
3360 helixhorne 399
    // atwith < 0 is for hard-coded projectiles
5825 terminx 400
    projectile_t *const pProj      = Proj_GetProjectile(projecTile);
5828 terminx 401
    int                 spawnTile  = projecTile < 0 ? -projecTile : pProj->spawns;
3360 helixhorne 402
 
5828 terminx 403
    if (spawnTile >= 0)
3353 helixhorne 404
    {
5828 terminx 405
        int spawned = A_Spawn(spriteNum, spawnTile);
3359 helixhorne 406
 
5825 terminx 407
        if (projecTile >= 0)
3360 helixhorne 408
        {
5828 terminx 409
            if (pProj->sxrepeat > 4)
410
                sprite[spawned].xrepeat = pProj->sxrepeat;
411
 
412
            if (pProj->syrepeat > 4)
413
                sprite[spawned].yrepeat = pProj->syrepeat;
3360 helixhorne 414
        }
3359 helixhorne 415
 
5825 terminx 416
        A_SetHitData(spawned, hitData);
3353 helixhorne 417
    }
418
}
419
 
3360 helixhorne 420
// <extra>: damage that this shotspark does
5828 terminx 421
static int Proj_InsertShotspark(const hitdata_t *hitData, int spriteNum, int projecTile, int sparkSize, int sparkAng, int damage)
3353 helixhorne 422
{
5828 terminx 423
    int returnSprite = A_InsertSprite(hitData->sect, hitData->pos.x, hitData->pos.y, hitData->pos.z, SHOTSPARK1, -15,
424
                                     sparkSize, sparkSize, sparkAng, 0, 0, spriteNum, 4);
3353 helixhorne 425
 
5828 terminx 426
    sprite[returnSprite].extra = damage;
427
    sprite[returnSprite].yvel  = projecTile;  // This is a hack to allow you to detect which weapon spawned a SHOTSPARK1
5825 terminx 428
 
5828 terminx 429
    A_SetHitData(returnSprite, hitData);
5825 terminx 430
 
5828 terminx 431
    return returnSprite;
3353 helixhorne 432
}
433
 
5828 terminx 434
int Proj_GetDamage(projectile_t const *pProj)
3359 helixhorne 435
{
5825 terminx 436
    Bassert(pProj);
437
 
5828 terminx 438
    int damage = pProj->extra;
5825 terminx 439
 
440
    if (pProj->extra_rand > 0)
441
        damage += (krand() % pProj->extra_rand);
442
 
443
    return damage;
3359 helixhorne 444
}
445
 
5828 terminx 446
static void Proj_MaybeAddSpread(int doSpread, int32_t *zvel, int *shootAng, int zRange, int angRange)
3358 helixhorne 447
{
5828 terminx 448
    if (doSpread)
3358 helixhorne 449
    {
3715 helixhorne 450
        // Ranges <= 1 mean no spread at all. A range of 1 calls krand() though.
451
        if (zRange > 0)
5828 terminx 452
            *zvel += (zRange >> 1) - krand() % zRange;
453
 
3715 helixhorne 454
        if (angRange > 0)
5828 terminx 455
            *shootAng += (angRange >> 1) - krand() % angRange;
3358 helixhorne 456
    }
457
}
458
 
5828 terminx 459
static int g_overrideShootZvel = 0;  // a boolean
460
static int g_shootZvel;  // the actual zvel if the above is !=0
3465 helixhorne 461
 
5828 terminx 462
static int A_GetShootZvel(int defaultZvel)
3465 helixhorne 463
{
5828 terminx 464
    return g_overrideShootZvel ? g_shootZvel : defaultZvel;
3465 helixhorne 465
}
466
 
3358 helixhorne 467
// Prepare hitscan weapon fired from player p.
5828 terminx 468
static void P_PreFireHitscan(int spriteNum, int playerNum, int projecTile, vec3_t *srcVect, int32_t *zvel, int *shootAng,
469
                             int accurateAim, int doSpread)
3358 helixhorne 470
{
5828 terminx 471
    int angRange  = 32;
472
    int zRange    = 256;
473
    int aimSprite = GetAutoAimAng(spriteNum, playerNum, projecTile, 5 << 8, 0 + 1, srcVect, 256, zvel, shootAng);
3358 helixhorne 474
 
7776 terminx 475
    auto const pPlayer = g_player[playerNum].ps;
3358 helixhorne 476
 
3366 helixhorne 477
#ifdef LUNATIC
5828 terminx 478
    pPlayer->angrange = angRange;
479
    pPlayer->zrange = zRange;
3366 helixhorne 480
#else
6942 terminx 481
    Gv_SetVar(g_angRangeVarID, angRange, spriteNum, playerNum);
482
    Gv_SetVar(g_zRangeVarID, zRange, spriteNum, playerNum);
3366 helixhorne 483
#endif
3358 helixhorne 484
 
5828 terminx 485
    VM_OnEvent(EVENT_GETSHOTRANGE, spriteNum, playerNum);
3366 helixhorne 486
 
3405 helixhorne 487
#ifdef LUNATIC
5828 terminx 488
    angRange = pPlayer->angrange;
489
    zRange   = pPlayer->zrange;
3366 helixhorne 490
#else
5828 terminx 491
    angRange = Gv_GetVar(g_angRangeVarID, spriteNum, playerNum);
492
    zRange   = Gv_GetVar(g_zRangeVarID, spriteNum, playerNum);
3358 helixhorne 493
#endif
3366 helixhorne 494
 
5828 terminx 495
    if (accurateAim)
3358 helixhorne 496
    {
5828 terminx 497
        if (!pPlayer->auto_aim)
3358 helixhorne 498
        {
5828 terminx 499
            hitdata_t hitData;
3358 helixhorne 500
 
6725 terminx 501
            *zvel = A_GetShootZvel(fix16_to_int(F16(100)-pPlayer->q16horiz-pPlayer->q16horizoff)<<5);
3358 helixhorne 502
 
5828 terminx 503
            hitscan(srcVect, sprite[spriteNum].sectnum, sintable[(*shootAng + 512) & 2047],
504
                    sintable[*shootAng & 2047], *zvel << 6, &hitData, CLIPMASK1);
3358 helixhorne 505
 
5828 terminx 506
            if (hitData.sprite != -1)
3358 helixhorne 507
            {
5828 terminx 508
                int const statNumMap = ((1 << STAT_ACTOR) | (1 << STAT_ZOMBIEACTOR) | (1 << STAT_PLAYER) | (1 << STAT_DUMMYPLAYER));
509
                int const statNum    = sprite[hitData.sprite].statnum;
3358 helixhorne 510
 
6235 terminx 511
                if ((unsigned)statNum <= 30 && (statNumMap & (1 << statNum)))
5828 terminx 512
                    aimSprite = hitData.sprite;
3358 helixhorne 513
            }
514
        }
515
 
5828 terminx 516
        if (aimSprite == -1)
6235 terminx 517
            goto notarget;
3358 helixhorne 518
    }
519
    else
520
    {
5828 terminx 521
        if (aimSprite == -1)  // no target
6241 terminx 522
        {
6235 terminx 523
notarget:
6725 terminx 524
            *zvel = fix16_to_int(F16(100)-pPlayer->q16horiz-pPlayer->q16horizoff)<<5;
6241 terminx 525
        }
526
 
5828 terminx 527
        Proj_MaybeAddSpread(doSpread, zvel, shootAng, zRange, angRange);
3358 helixhorne 528
    }
529
 
7614 terminx 530
    // ZOFFSET6 is added to this position at the same time as the player's pyoff in A_ShootWithZvel()
531
    srcVect->z -= ZOFFSET6;
3358 helixhorne 532
}
533
 
534
// Hitscan weapon fired from actor (sprite s);
6244 terminx 535
static void A_PreFireHitscan(const spritetype *pSprite, vec3_t * const srcVect, int32_t * const zvel, int * const shootAng, int const doSpread)
3358 helixhorne 536
{
7776 terminx 537
    int const  playerNum  = A_FindPlayer(pSprite, NULL);
538
    auto const pPlayer    = g_player[playerNum].ps;
539
    int const  playerDist = safeldist(pPlayer->i, pSprite);
3358 helixhorne 540
 
5828 terminx 541
    *zvel = tabledivide32_noinline((pPlayer->pos.z - srcVect->z) << 8, playerDist);
3358 helixhorne 542
 
5828 terminx 543
    srcVect->z -= ZOFFSET6;
3358 helixhorne 544
 
6235 terminx 545
    if (pSprite->picnum == BOSS1)
546
        *shootAng = getangle(pPlayer->pos.x - srcVect->x, pPlayer->pos.y - srcVect->y);
3358 helixhorne 547
 
6244 terminx 548
    Proj_MaybeAddSpread(doSpread, zvel, shootAng, 256, 128 >> (uint8_t)(pSprite->picnum != BOSS1));
3358 helixhorne 549
}
550
 
6244 terminx 551
static int Proj_DoHitscan(int spriteNum, int32_t const cstatmask, const vec3_t * const srcVect, int zvel, int const shootAng, hitdata_t * const hitData)
3359 helixhorne 552
{
7776 terminx 553
    auto const pSprite = &sprite[spriteNum];
3359 helixhorne 554
 
5828 terminx 555
    pSprite->cstat &= ~cstatmask;
3921 helixhorne 556
    zvel = A_GetShootZvel(zvel);
8107 terminx 557
    int16_t sectnum = pSprite->sectnum;
558
    updatesector(srcVect->x, srcVect->y, &sectnum);
559
    hitscan(srcVect, sectnum, sintable[(shootAng + 512) & 2047], sintable[shootAng & 2047], zvel << 6, hitData, CLIPMASK1);
5828 terminx 560
    pSprite->cstat |= cstatmask;
3359 helixhorne 561
 
5828 terminx 562
    return (hitData->sect < 0);
3359 helixhorne 563
}
564
 
6235 terminx 565
static void Proj_DoRandDecalSize(int const spriteNum, int const projecTile)
3362 helixhorne 566
{
5828 terminx 567
    const projectile_t *const proj    = Proj_GetProjectile(projecTile);
7776 terminx 568
    auto const         pSprite = &sprite[spriteNum];
3362 helixhorne 569
 
570
    if (proj->workslike & PROJECTILE_RANDDECALSIZE)
6235 terminx 571
        pSprite->xrepeat = pSprite->yrepeat = clamp((krand() & proj->xrepeat), pSprite->yrepeat, pSprite->xrepeat);
3362 helixhorne 572
    else
573
    {
5828 terminx 574
        pSprite->xrepeat = proj->xrepeat;
575
        pSprite->yrepeat = proj->yrepeat;
3362 helixhorne 576
    }
577
}
578
 
6244 terminx 579
static int SectorContainsSE13(int const sectNum)
3362 helixhorne 580
{
5828 terminx 581
    if (sectNum >= 0)
582
    {
5829 terminx 583
        for (bssize_t SPRITES_OF_SECT(sectNum, i))
5828 terminx 584
        {
3362 helixhorne 585
            if (sprite[i].statnum == STAT_EFFECTOR && sprite[i].lotag == SE_13_EXPLOSIVE)
586
                return 1;
5828 terminx 587
        }
588
    }
3362 helixhorne 589
    return 0;
590
}
591
 
592
// Maybe handle bit 2 (swap wall bottoms).
593
// (in that case walltype *hitwal may be stale)
5828 terminx 594
static inline void HandleHitWall(hitdata_t *hitData)
3362 helixhorne 595
{
7603 terminx 596
    auto const hitWall = (uwallptr_t)&wall[hitData->wall];
3362 helixhorne 597
 
5828 terminx 598
    if ((hitWall->cstat & 2) && redwallp(hitWall) && (hitData->pos.z >= sector[hitWall->nextsector].floorz))
599
        hitData->wall = hitWall->nextwall;
3362 helixhorne 600
}
601
 
4204 helixhorne 602
// Maybe damage a ceiling or floor as the consequence of projectile impact.
603
// Returns 1 if projectile hit a parallaxed ceiling.
604
// NOTE: Compare with Proj_MaybeDamageCF() in actors.c
6771 hendricks2 605
static int Proj_MaybeDamageCF2(int const spriteNum, int const zvel, int const hitSect)
4204 helixhorne 606
{
5828 terminx 607
    Bassert(hitSect >= 0);
608
 
4204 helixhorne 609
    if (zvel < 0)
610
    {
5828 terminx 611
        if (sector[hitSect].ceilingstat&1)
4204 helixhorne 612
            return 1;
613
 
6771 hendricks2 614
        Sect_DamageCeiling(spriteNum, hitSect);
4204 helixhorne 615
    }
4205 helixhorne 616
    else if (zvel > 0)
617
    {
5828 terminx 618
        if (sector[hitSect].floorstat&1)
4205 helixhorne 619
        {
620
            // Keep original Duke3D behavior: pass projectiles through
621
            // parallaxed ceilings, but NOT through such floors.
622
            return 0;
623
        }
624
 
6771 hendricks2 625
        Sect_DamageFloor(spriteNum, hitSect);
4205 helixhorne 626
    }
627
 
4204 helixhorne 628
    return 0;
629
}
630
 
3360 helixhorne 631
// Finish shooting hitscan weapon from player <p>. <k> is the inserted SHOTSPARK1.
5828 terminx 632
// * <spawnObject> is passed to Proj_MaybeSpawn()
633
// * <decalTile> and <wallDamage> are for wall impact
634
// * <wallDamage> is passed to A_DamageWall()
635
// * <decalFlags> is for decals upon wall impact:
3360 helixhorne 636
//    1: handle random decal size (tile <atwith>)
637
//    2: set cstat to wall-aligned + random x/y flip
638
//
639
// TODO: maybe split into 3 cases (hit neither wall nor sprite, hit sprite, hit wall)?
6942 terminx 640
static int P_PostFireHitscan(int playerNum, int const spriteNum, hitdata_t *const hitData, int const spriteOwner,
6244 terminx 641
                             int const projecTile, int const zvel, int const spawnTile, int const decalTile, int const wallDamage,
642
                             int const decalFlags)
3360 helixhorne 643
{
6942 terminx 644
#ifdef EDUKE32_STANDALONE
645
    UNREFERENCED_PARAMETER(playerNum);
646
#endif
5828 terminx 647
    if (hitData->wall == -1 && hitData->sprite == -1)
3360 helixhorne 648
    {
6771 hendricks2 649
        if (Proj_MaybeDamageCF2(spriteNum, zvel, hitData->sect))
3360 helixhorne 650
        {
5828 terminx 651
            sprite[spriteNum].xrepeat = 0;
652
            sprite[spriteNum].yrepeat = 0;
4204 helixhorne 653
            return -1;
3360 helixhorne 654
        }
655
 
5828 terminx 656
        Proj_MaybeSpawn(spriteNum, spawnTile, hitData);
3360 helixhorne 657
    }
5828 terminx 658
    else if (hitData->sprite >= 0)
3360 helixhorne 659
    {
5828 terminx 660
        A_DamageObject(hitData->sprite, spriteNum);
3360 helixhorne 661
 
7808 terminx 662
        if (!FURY && sprite[hitData->sprite].picnum == APLAYER &&
3360 helixhorne 663
            (ud.ffire == 1 || (!GTFLAGS(GAMETYPE_PLAYERSFRIENDLY) && GTFLAGS(GAMETYPE_TDM) &&
5828 terminx 664
                               g_player[P_Get(hitData->sprite)].ps->team != g_player[P_Get(spriteOwner)].ps->team)))
3360 helixhorne 665
        {
6942 terminx 666
#ifndef EDUKE32_STANDALONE
5828 terminx 667
            int jibSprite = A_Spawn(spriteNum, JIBS6);
668
 
669
            sprite[spriteNum].xrepeat = sprite[spriteNum].yrepeat = 0;
670
            sprite[jibSprite].z += ZOFFSET6;
671
            sprite[jibSprite].xvel    = 16;
672
            sprite[jibSprite].xrepeat = sprite[jibSprite].yrepeat = 24;
673
            sprite[jibSprite].ang += 64 - (krand() & 127);
6942 terminx 674
#endif
3360 helixhorne 675
        }
676
        else
677
        {
5828 terminx 678
            Proj_MaybeSpawn(spriteNum, spawnTile, hitData);
3360 helixhorne 679
        }
6942 terminx 680
#ifndef EDUKE32_STANDALONE
7808 terminx 681
        if (!FURY && playerNum >= 0 && CheckShootSwitchTile(sprite[hitData->sprite].picnum))
3360 helixhorne 682
        {
5828 terminx 683
            P_ActivateSwitch(playerNum, hitData->sprite, 1);
3360 helixhorne 684
            return -1;
685
        }
6942 terminx 686
#endif
3360 helixhorne 687
    }
5828 terminx 688
    else if (hitData->wall >= 0)
3360 helixhorne 689
    {
7603 terminx 690
        auto const hitWall = (uwallptr_t)&wall[hitData->wall];
3360 helixhorne 691
 
5828 terminx 692
        Proj_MaybeSpawn(spriteNum, spawnTile, hitData);
3360 helixhorne 693
 
5828 terminx 694
        if (CheckDoorTile(hitWall->picnum) == 1)
3360 helixhorne 695
            goto SKIPBULLETHOLE;
696
 
6942 terminx 697
#ifndef EDUKE32_STANDALONE
7808 terminx 698
        if (!FURY && playerNum >= 0 && CheckShootSwitchTile(hitWall->picnum))
3360 helixhorne 699
        {
5828 terminx 700
            P_ActivateSwitch(playerNum, hitData->wall, 0);
3360 helixhorne 701
            return -1;
702
        }
6942 terminx 703
#endif
3360 helixhorne 704
 
5828 terminx 705
        if (hitWall->hitag != 0 || (hitWall->nextwall >= 0 && wall[hitWall->nextwall].hitag != 0))
3360 helixhorne 706
            goto SKIPBULLETHOLE;
707
 
5828 terminx 708
        if ((hitData->sect >= 0 && sector[hitData->sect].lotag == 0) &&
709
            (hitWall->overpicnum != BIGFORCE && (hitWall->cstat & 16) == 0) &&
710
            ((hitWall->nextsector >= 0 && sector[hitWall->nextsector].lotag == 0) || (hitWall->nextsector == -1 && sector[hitData->sect].lotag == 0)))
711
        {
712
            int decalSprite;
3360 helixhorne 713
 
5828 terminx 714
            if (SectorContainsSE13(hitWall->nextsector))
715
                goto SKIPBULLETHOLE;
3360 helixhorne 716
 
5828 terminx 717
            for (SPRITES_OF(STAT_MISC, decalSprite))
718
                if (sprite[decalSprite].picnum == decalTile && dist(&sprite[decalSprite], &sprite[spriteNum]) < (12 + (krand() & 7)))
719
                    goto SKIPBULLETHOLE;
3360 helixhorne 720
 
5828 terminx 721
            if (decalTile >= 0)
722
            {
723
                decalSprite = A_Spawn(spriteNum, decalTile);
3360 helixhorne 724
 
7614 terminx 725
                auto const decal = &sprite[decalSprite];
726
 
5828 terminx 727
                A_SetHitData(decalSprite, hitData);
5388 terminx 728
 
5828 terminx 729
                if (!A_CheckSpriteFlags(decalSprite, SFLAG_DECAL))
730
                    actor[decalSprite].flags |= SFLAG_DECAL;
3360 helixhorne 731
 
7614 terminx 732
                int32_t diffZ;
733
                spriteheightofs(decalSprite, &diffZ, 0);
5392 terminx 734
 
7614 terminx 735
                decal->z += diffZ >> 1;
736
                decal->ang = (getangle(hitWall->x - wall[hitWall->point2].x, hitWall->y - wall[hitWall->point2].y) + 1536) & 2047;
737
 
5828 terminx 738
                if (decalFlags & 1)
739
                    Proj_DoRandDecalSize(decalSprite, projecTile);
3360 helixhorne 740
 
5828 terminx 741
                if (decalFlags & 2)
7614 terminx 742
                    decal->cstat = 16 + (krand() & (8 + 4));
3360 helixhorne 743
 
5828 terminx 744
                A_SetSprite(decalSprite, CLIPMASK0);
3360 helixhorne 745
 
5828 terminx 746
                // BULLETHOLE already adds itself to the deletion queue in
747
                // A_Spawn(). However, some other tiles do as well.
748
                if (decalTile != BULLETHOLE)
749
                    A_AddToDeleteQueue(decalSprite);
750
            }
751
        }
3360 helixhorne 752
 
753
SKIPBULLETHOLE:
5828 terminx 754
        HandleHitWall(hitData);
8080 terminx 755
        A_DamageWall(spriteNum, hitData->wall, hitData->pos, wallDamage);
3360 helixhorne 756
    }
757
 
758
    return 0;
759
}
760
 
761
// Finish shooting hitscan weapon from actor (sprite <i>).
6235 terminx 762
static int A_PostFireHitscan(const hitdata_t *hitData, int const spriteNum, int const projecTile, int const zvel, int const shootAng,
763
                             int const extra, int const spawnTile, int const wallDamage)
3360 helixhorne 764
{
6235 terminx 765
    int const returnSprite = Proj_InsertShotspark(hitData, spriteNum, projecTile, 24, shootAng, extra);
3360 helixhorne 766
 
5828 terminx 767
    if (hitData->sprite >= 0)
3360 helixhorne 768
    {
5828 terminx 769
        A_DamageObject(hitData->sprite, returnSprite);
3360 helixhorne 770
 
5828 terminx 771
        if (sprite[hitData->sprite].picnum != APLAYER)
772
            Proj_MaybeSpawn(returnSprite, spawnTile, hitData);
3360 helixhorne 773
        else
5828 terminx 774
            sprite[returnSprite].xrepeat = sprite[returnSprite].yrepeat = 0;
3360 helixhorne 775
    }
5828 terminx 776
    else if (hitData->wall >= 0)
5783 terminx 777
    {
8080 terminx 778
        A_DamageWall(returnSprite, hitData->wall, hitData->pos, wallDamage);
5828 terminx 779
        Proj_MaybeSpawn(returnSprite, spawnTile, hitData);
5783 terminx 780
    }
781
    else
782
    {
6771 hendricks2 783
        if (Proj_MaybeDamageCF2(returnSprite, zvel, hitData->sect))
5783 terminx 784
        {
5828 terminx 785
            sprite[returnSprite].xrepeat = 0;
786
            sprite[returnSprite].yrepeat = 0;
5783 terminx 787
        }
5828 terminx 788
        else Proj_MaybeSpawn(returnSprite, spawnTile, hitData);
5783 terminx 789
    }
3360 helixhorne 790
 
5828 terminx 791
    return returnSprite;
3360 helixhorne 792
}
793
 
3362 helixhorne 794
// Common "spawn blood?" predicate.
795
// minzdiff: minimal "step" height for blood to be spawned
6235 terminx 796
static int Proj_CheckBlood(vec3_t const *const srcVect, hitdata_t const *const hitData, int const bloodRange, int const minZdiff)
3362 helixhorne 797
{
5828 terminx 798
    if (hitData->wall < 0 || hitData->sect < 0)
4680 terminx 799
        return 0;
800
 
7603 terminx 801
    auto const hitWall = (uwallptr_t)&wall[hitData->wall];
4680 terminx 802
 
5828 terminx 803
    if ((FindDistance2D(srcVect->x - hitData->pos.x, srcVect->y - hitData->pos.y) < bloodRange)
804
        && (hitWall->overpicnum != BIGFORCE && (hitWall->cstat & 16) == 0)
805
        && (sector[hitData->sect].lotag == 0)
806
        && (hitWall->nextsector < 0 || (sector[hitWall->nextsector].lotag == 0 && sector[hitData->sect].lotag == 0
807
                                        && sector[hitData->sect].floorz - sector[hitWall->nextsector].floorz > minZdiff)))
808
        return 1;
3362 helixhorne 809
 
810
    return 0;
811
}
812
 
6235 terminx 813
static void Proj_HandleKnee(hitdata_t *const hitData, int const spriteNum, int const playerNum, int const projecTile, int const shootAng,
814
                            const projectile_t *const proj, int const inserttile, int const randomDamage, int const spawnTile,
815
                            int const soundNum)
3362 helixhorne 816
{
7776 terminx 817
    auto const pPlayer = playerNum >= 0 ? g_player[playerNum].ps : NULL;
3362 helixhorne 818
 
5828 terminx 819
    int kneeSprite = A_InsertSprite(hitData->sect,hitData->pos.x,hitData->pos.y,hitData->pos.z,
820
                                    inserttile,-15,0,0,shootAng,32,0,spriteNum,4);
3362 helixhorne 821
 
822
    if (proj != NULL)
823
    {
824
        // Custom projectiles.
5828 terminx 825
        SpriteProjectile[kneeSprite].workslike = Proj_GetProjectile(sprite[kneeSprite].picnum)->workslike;
826
        sprite[kneeSprite].extra = proj->extra;
3362 helixhorne 827
    }
828
 
5828 terminx 829
    if (randomDamage > 0)
830
        sprite[kneeSprite].extra += (krand()&randomDamage);
3362 helixhorne 831
 
5828 terminx 832
    if (playerNum >= 0)
3362 helixhorne 833
    {
5828 terminx 834
        if (spawnTile >= 0)
3362 helixhorne 835
        {
5828 terminx 836
            int k = A_Spawn(kneeSprite, spawnTile);
5825 terminx 837
            sprite[k].z -= ZOFFSET3;
5828 terminx 838
            A_SetHitData(k, hitData);
3362 helixhorne 839
        }
840
 
5828 terminx 841
        if (soundNum >= 0)
842
            A_PlaySound(soundNum, kneeSprite);
3362 helixhorne 843
    }
844
 
7092 terminx 845
    if (pPlayer != NULL && pPlayer->inv_amount[GET_STEROIDS] > 0 && pPlayer->inv_amount[GET_STEROIDS] < 400)
5828 terminx 846
        sprite[kneeSprite].extra += (pPlayer->max_player_health>>2);
3362 helixhorne 847
 
5828 terminx 848
    if (hitData->sprite >= 0 && sprite[hitData->sprite].picnum != ACCESSSWITCH && sprite[hitData->sprite].picnum != ACCESSSWITCH2)
3362 helixhorne 849
    {
5828 terminx 850
        A_DamageObject(hitData->sprite, kneeSprite);
851
        if (playerNum >= 0)
852
            P_ActivateSwitch(playerNum, hitData->sprite,1);
3362 helixhorne 853
    }
5828 terminx 854
    else if (hitData->wall >= 0)
3362 helixhorne 855
    {
5828 terminx 856
        HandleHitWall(hitData);
3362 helixhorne 857
 
5828 terminx 858
        if (wall[hitData->wall].picnum != ACCESSSWITCH && wall[hitData->wall].picnum != ACCESSSWITCH2)
3362 helixhorne 859
        {
8080 terminx 860
            A_DamageWall(kneeSprite, hitData->wall, hitData->pos, projecTile);
5828 terminx 861
            if (playerNum >= 0)
862
                P_ActivateSwitch(playerNum, hitData->wall,0);
3362 helixhorne 863
        }
864
    }
865
}
866
 
5825 terminx 867
#define MinibossScale(i, s) (((s)*sprite[i].yrepeat)/80)
3992 terminx 868
 
5828 terminx 869
static int A_ShootCustom(int const spriteNum, int const projecTile, int shootAng, vec3_t * const startPos)
2983 helixhorne 870
{
3992 terminx 871
    /* Custom projectiles */
5825 terminx 872
    hitdata_t           hitData;
873
    projectile_t *const pProj     = Proj_GetProjectile(projecTile);
7776 terminx 874
    auto const   pSprite   = &sprite[spriteNum];
5825 terminx 875
    int const           playerNum = (pSprite->picnum == APLAYER) ? P_GetP(pSprite) : -1;
7776 terminx 876
    auto const pPlayer   = playerNum >= 0 ? g_player[playerNum].ps : NULL;
2876 helixhorne 877
 
5700 terminx 878
#ifdef POLYMER
6829 terminx 879
    if (videoGetRenderMode() == REND_POLYMER && pProj->flashcolor)
5700 terminx 880
    {
5825 terminx 881
        int32_t x = ((sintable[(pSprite->ang + 512) & 2047]) >> 7), y = ((sintable[(pSprite->ang) & 2047]) >> 7);
5700 terminx 882
 
5825 terminx 883
        pSprite->x += x;
884
        pSprite->y += y;
885
        G_AddGameLight(0, spriteNum, PHEIGHT, 8192, pProj->flashcolor, PR_LIGHT_PRIO_MAX_GAME);
886
        actor[spriteNum].lightcount = 2;
887
        pSprite->x -= x;
888
        pSprite->y -= y;
5700 terminx 889
    }
890
#endif // POLYMER
891
 
5825 terminx 892
    if (pProj->offset == 0)
893
        pProj->offset = 1;
5 Plagman 894
 
6632 hendricks2 895
    int     otherSprite = -1;
5825 terminx 896
    int32_t zvel = 0;
897
 
898
    switch (pProj->workslike & PROJECTILE_TYPE_MASK)
5 Plagman 899
    {
3992 terminx 900
    case PROJECTILE_HITSCAN:
5825 terminx 901
        if (!(pProj->workslike & PROJECTILE_NOSETOWNERSHADE) && pSprite->extra >= 0)
902
            pSprite->shade = pProj->shade;
1207 terminx 903
 
5825 terminx 904
        if (playerNum >= 0)
5828 terminx 905
            P_PreFireHitscan(spriteNum, playerNum, projecTile, startPos, &zvel, &shootAng,
5825 terminx 906
                             pProj->workslike & PROJECTILE_ACCURATE_AUTOAIM, !(pProj->workslike & PROJECTILE_ACCURATE));
3992 terminx 907
        else
5828 terminx 908
            A_PreFireHitscan(pSprite, startPos, &zvel, &shootAng, !(pProj->workslike & PROJECTILE_ACCURATE));
3081 terminx 909
 
5828 terminx 910
        if (Proj_DoHitscan(spriteNum, (pProj->cstat >= 0) ? pProj->cstat : 256 + 1, startPos, zvel, shootAng, &hitData))
3992 terminx 911
            return -1;
1315 terminx 912
 
5825 terminx 913
        if (pProj->range > 0 && klabs(startPos->x - hitData.pos.x) + klabs(startPos->y - hitData.pos.y) > pProj->range)
3992 terminx 914
            return -1;
1315 terminx 915
 
5825 terminx 916
        if (pProj->trail >= 0)
5828 terminx 917
            A_HitscanProjTrail(startPos, &hitData.pos, shootAng, projecTile, pSprite->sectnum);
5 Plagman 918
 
5825 terminx 919
        if (pProj->workslike & PROJECTILE_WATERBUBBLES)
3992 terminx 920
        {
5825 terminx 921
            if ((krand() & 15) == 0 && sector[hitData.sect].lotag == ST_2_UNDERWATER)
6912 terminx 922
                Proj_DoWaterTracers(hitData.pos, startPos, 8 - (ud.multimode >> 1), pSprite->sectnum);
3992 terminx 923
        }
5 Plagman 924
 
5825 terminx 925
        if (playerNum >= 0)
1319 terminx 926
        {
5828 terminx 927
            otherSprite = Proj_InsertShotspark(&hitData, spriteNum, projecTile, 10, shootAng, Proj_GetDamage(pProj));
1402 terminx 928
 
5825 terminx 929
            if (P_PostFireHitscan(playerNum, otherSprite, &hitData, spriteNum, projecTile, zvel, projecTile, pProj->decal,
930
                                  projecTile, 1 + 2) < 0)
3992 terminx 931
                return -1;
1319 terminx 932
        }
3992 terminx 933
        else
934
        {
5825 terminx 935
            otherSprite =
5828 terminx 936
            A_PostFireHitscan(&hitData, spriteNum, projecTile, zvel, shootAng, Proj_GetDamage(pProj), projecTile, projecTile);
3992 terminx 937
        }
1319 terminx 938
 
5825 terminx 939
        if ((krand() & 255) < 4 && pProj->isound >= 0)
940
            S_PlaySound3D(pProj->isound, otherSprite, &hitData.pos);
5 Plagman 941
 
3992 terminx 942
        return -1;
943
 
944
    case PROJECTILE_RPG:
5825 terminx 945
        if (!(pProj->workslike & PROJECTILE_NOSETOWNERSHADE) && pSprite->extra >= 0)
946
            pSprite->shade = pProj->shade;
3992 terminx 947
 
7092 terminx 948
        if (pPlayer != NULL)
5 Plagman 949
        {
4229 helixhorne 950
            // NOTE: j is a SPRITE_INDEX
5828 terminx 951
            otherSprite = GetAutoAimAng(spriteNum, playerNum, projecTile, 8<<8, 0+2, startPos, pProj->vel, &zvel, &shootAng);
5 Plagman 952
 
5825 terminx 953
            if (otherSprite < 0)
6725 terminx 954
                zvel = fix16_to_int(F16(100)-pPlayer->q16horiz-pPlayer->q16horizoff)*(pProj->vel/8);
5 Plagman 955
 
5825 terminx 956
            if (pProj->sound >= 0)
957
                A_PlaySound(pProj->sound, spriteNum);
3992 terminx 958
        }
959
        else
960
        {
5825 terminx 961
            if (!(pProj->workslike & PROJECTILE_NOAIM))
5 Plagman 962
            {
5826 terminx 963
                int const otherPlayer     = A_FindPlayer(pSprite, NULL);
964
                int const otherPlayerDist = safeldist(g_player[otherPlayer].ps->i, pSprite);
3361 helixhorne 965
 
5828 terminx 966
                shootAng = getangle(g_player[otherPlayer].ps->opos.x - startPos->x,
5826 terminx 967
                                      g_player[otherPlayer].ps->opos.y - startPos->y);
5 Plagman 968
 
5826 terminx 969
                zvel = tabledivide32_noinline((g_player[otherPlayer].ps->opos.z - startPos->z) * pProj->vel, otherPlayerDist);
5825 terminx 970
 
971
                if (A_CheckEnemySprite(pSprite) && (AC_MOVFLAGS(pSprite, &actor[spriteNum]) & face_player_smart))
5828 terminx 972
                    shootAng = pSprite->ang + (krand() & 31) - 16;
3992 terminx 973
            }
974
        }
5 Plagman 975
 
3992 terminx 976
        if (numplayers > 1 && g_netClient) return -1;
5825 terminx 977
        else
978
        {
979
            // l may be a SPRITE_INDEX, see above
980
            int const l = (playerNum >= 0 && otherSprite >= 0) ? otherSprite : -1;
1658 terminx 981
 
5825 terminx 982
            zvel = A_GetShootZvel(zvel);
983
            otherSprite = A_InsertSprite(pSprite->sectnum,
5828 terminx 984
                startPos->x + tabledivide32_noinline(sintable[(348 + shootAng + 512) & 2047], pProj->offset),
985
                startPos->y + tabledivide32_noinline(sintable[(shootAng + 348) & 2047], pProj->offset),
986
                startPos->z - (1 << 8), projecTile, 0, 14, 14, shootAng, pProj->vel, zvel, spriteNum, 4);
4229 helixhorne 987
 
6978 terminx 988
            sprite[otherSprite].extra = Proj_GetDamage(pProj);
5 Plagman 989
 
5825 terminx 990
            if (!(pProj->workslike & PROJECTILE_BOUNCESOFFWALLS))
991
                sprite[otherSprite].yvel = l;  // NOT_BOUNCESOFFWALLS_YVEL
992
            else
993
            {
994
                sprite[otherSprite].yvel = (pProj->bounces >= 1) ? pProj->bounces : g_numFreezeBounces;
995
                sprite[otherSprite].zvel -= (2 << 4);
996
            }
5498 terminx 997
 
5825 terminx 998
            sprite[otherSprite].pal       = (pProj->pal >= 0) ? pProj->pal : 0;
999
            sprite[otherSprite].xrepeat   = pProj->xrepeat;
1000
            sprite[otherSprite].yrepeat   = pProj->yrepeat;
1001
            sprite[otherSprite].cstat     = (pProj->cstat >= 0) ? pProj->cstat : 128;
1002
            sprite[otherSprite].clipdist  = (pProj->clipdist != 255) ? pProj->clipdist : 40;
1003
            SpriteProjectile[otherSprite] = *Proj_GetProjectile(sprite[otherSprite].picnum);
5 Plagman 1004
 
5825 terminx 1005
            return otherSprite;
3992 terminx 1006
        }
5 Plagman 1007
 
3992 terminx 1008
    case PROJECTILE_KNEE:
5825 terminx 1009
        if (playerNum >= 0)
3992 terminx 1010
        {
6725 terminx 1011
            zvel = fix16_to_int(F16(100) - pPlayer->q16horiz - pPlayer->q16horizoff) << 5;
5825 terminx 1012
            startPos->z += (6 << 8);
5828 terminx 1013
            shootAng += 15;
3992 terminx 1014
        }
5825 terminx 1015
        else if (!(pProj->workslike & PROJECTILE_NOAIM))
3992 terminx 1016
        {
5825 terminx 1017
            int32_t playerDist;
1018
            otherSprite = g_player[A_FindPlayer(pSprite, &playerDist)].ps->i;
1019
            zvel = tabledivide32_noinline((sprite[otherSprite].z - startPos->z) << 8, playerDist + 1);
5828 terminx 1020
            shootAng = getangle(sprite[otherSprite].x - startPos->x, sprite[otherSprite].y - startPos->y);
3992 terminx 1021
        }
5 Plagman 1022
 
5828 terminx 1023
        Proj_DoHitscan(spriteNum, 0, startPos, zvel, shootAng, &hitData);
5 Plagman 1024
 
5825 terminx 1025
        if (hitData.sect < 0) return -1;
5 Plagman 1026
 
5825 terminx 1027
        if (pProj->range == 0)
1028
            pProj->range = 1024;
3152 helixhorne 1029
 
5825 terminx 1030
        if (pProj->range > 0 && klabs(startPos->x - hitData.pos.x) + klabs(startPos->y - hitData.pos.y) > pProj->range)
168 terminx 1031
            return -1;
5 Plagman 1032
 
5828 terminx 1033
        Proj_HandleKnee(&hitData, spriteNum, playerNum, projecTile, shootAng,
1034
                        pProj, projecTile, pProj->extra_rand, pProj->spawns, pProj->sound);
5 Plagman 1035
 
3992 terminx 1036
        return -1;
1490 terminx 1037
 
3992 terminx 1038
    case PROJECTILE_BLOOD:
5828 terminx 1039
        shootAng += 64 - (krand() & 127);
1040
 
1041
        if (playerNum < 0)
1042
            shootAng += 1024;
1043
 
3992 terminx 1044
        zvel = 1024 - (krand() & 2047);
1209 terminx 1045
 
5828 terminx 1046
        Proj_DoHitscan(spriteNum, 0, startPos, zvel, shootAng, &hitData);
1209 terminx 1047
 
5825 terminx 1048
        if (pProj->range == 0)
1049
            pProj->range = 1024;
1209 terminx 1050
 
5825 terminx 1051
        if (Proj_CheckBlood(startPos, &hitData, pProj->range, mulscale3(pProj->yrepeat, tilesiz[pProj->decal].y) << 8))
3992 terminx 1052
        {
7603 terminx 1053
            uwallptr_t const hitWall = (uwallptr_t)&wall[hitData.wall];
1209 terminx 1054
 
5825 terminx 1055
            if (FindDistance2D(hitWall->x - wall[hitWall->point2].x, hitWall->y - wall[hitWall->point2].y) >
1056
                (mulscale3(pProj->xrepeat + 8, tilesiz[pProj->decal].x)))
1209 terminx 1057
            {
5825 terminx 1058
                if (SectorContainsSE13(hitWall->nextsector))
3992 terminx 1059
                    return -1;
1209 terminx 1060
 
5825 terminx 1061
                if (hitWall->nextwall >= 0 && wall[hitWall->nextwall].hitag != 0)
3360 helixhorne 1062
                    return -1;
1209 terminx 1063
 
5825 terminx 1064
                if (hitWall->hitag == 0 && pProj->decal >= 0)
3992 terminx 1065
                {
5825 terminx 1066
                    otherSprite = A_Spawn(spriteNum, pProj->decal);
1209 terminx 1067
 
5825 terminx 1068
                    A_SetHitData(otherSprite, &hitData);
5388 terminx 1069
 
5825 terminx 1070
                    if (!A_CheckSpriteFlags(otherSprite, SFLAG_DECAL))
1071
                        actor[otherSprite].flags |= SFLAG_DECAL;
1209 terminx 1072
 
5825 terminx 1073
                    sprite[otherSprite].ang = getangle(hitWall->x - wall[hitWall->point2].x,
1074
                        hitWall->y - wall[hitWall->point2].y) + 512;
1075
                    Bmemcpy(&sprite[otherSprite], &hitData.pos, sizeof(vec3_t));
1209 terminx 1076
 
5825 terminx 1077
                    Proj_DoRandDecalSize(otherSprite, projecTile);
1209 terminx 1078
 
5825 terminx 1079
                    sprite[otherSprite].z += sprite[otherSprite].yrepeat << 8;
1209 terminx 1080
 
5825 terminx 1081
                    //                                sprite[spawned].cstat = 16+(krand()&12);
1082
                    sprite[otherSprite].cstat = 16;
1625 terminx 1083
 
3992 terminx 1084
                    if (krand() & 1)
5825 terminx 1085
                        sprite[otherSprite].cstat |= 4;
1625 terminx 1086
 
3992 terminx 1087
                    if (krand() & 1)
5825 terminx 1088
                        sprite[otherSprite].cstat |= 8;
1209 terminx 1089
 
5825 terminx 1090
                    sprite[otherSprite].shade = sector[sprite[otherSprite].sectnum].floorshade;
1209 terminx 1091
 
5825 terminx 1092
                    A_SetSprite(otherSprite, CLIPMASK0);
1093
                    A_AddToDeleteQueue(otherSprite);
1094
                    changespritestat(otherSprite, 5);
1209 terminx 1095
                }
1096
            }
3992 terminx 1097
        }
1209 terminx 1098
 
3992 terminx 1099
        return -1;
1209 terminx 1100
 
3992 terminx 1101
    default:
1102
        return -1;
1103
    }
1104
}
1567 terminx 1105
 
6804 terminx 1106
#ifndef EDUKE32_STANDALONE
5828 terminx 1107
static int32_t A_ShootHardcoded(int spriteNum, int projecTile, int shootAng, vec3_t startPos,
5826 terminx 1108
                                spritetype *pSprite, int const playerNum, DukePlayer_t * const pPlayer)
3992 terminx 1109
{
5825 terminx 1110
    hitdata_t hitData;
5826 terminx 1111
    int const spriteSectnum = pSprite->sectnum;
1112
    int32_t Zvel;
1113
    int vel;
1209 terminx 1114
 
5825 terminx 1115
    switch (DYNAMICTILEMAP(projecTile))
3992 terminx 1116
    {
5826 terminx 1117
        case BLOODSPLAT1__STATIC:
1118
        case BLOODSPLAT2__STATIC:
1119
        case BLOODSPLAT3__STATIC:
1120
        case BLOODSPLAT4__STATIC:
5828 terminx 1121
            shootAng += 64 - (krand() & 127);
5826 terminx 1122
            if (playerNum < 0)
5828 terminx 1123
                shootAng += 1024;
5826 terminx 1124
            Zvel = 1024 - (krand() & 2047);
6976 terminx 1125
            fallthrough__;
5826 terminx 1126
        case KNEE__STATIC:
1127
            if (projecTile == KNEE)
1209 terminx 1128
            {
5826 terminx 1129
                if (playerNum >= 0)
1130
                {
6725 terminx 1131
                    Zvel = fix16_to_int(F16(100) - pPlayer->q16horiz - pPlayer->q16horizoff) << 5;
5826 terminx 1132
                    startPos.z += (6 << 8);
5828 terminx 1133
                    shootAng += 15;
5826 terminx 1134
                }
1135
                else
1136
                {
1137
                    int32_t   playerDist;
1138
                    int const playerSprite = g_player[A_FindPlayer(pSprite, &playerDist)].ps->i;
1139
                    Zvel                   = tabledivide32_noinline((sprite[playerSprite].z - startPos.z) << 8, playerDist + 1);
5828 terminx 1140
                    shootAng             = getangle(sprite[playerSprite].x - startPos.x, sprite[playerSprite].y - startPos.y);
5826 terminx 1141
                }
1209 terminx 1142
            }
1143
 
5828 terminx 1144
            Proj_DoHitscan(spriteNum, 0, &startPos, Zvel, shootAng, &hitData);
3992 terminx 1145
 
5826 terminx 1146
            if (projecTile >= BLOODSPLAT1 && projecTile <= BLOODSPLAT4)
1209 terminx 1147
            {
5826 terminx 1148
                if (Proj_CheckBlood(&startPos, &hitData, 1024, 16 << 8))
1149
                {
7603 terminx 1150
                    uwallptr_t const hitwal = (uwallptr_t)&wall[hitData.wall];
1209 terminx 1151
 
5826 terminx 1152
                    if (SectorContainsSE13(hitwal->nextsector))
1153
                        return -1;
1209 terminx 1154
 
5826 terminx 1155
                    if (hitwal->nextwall >= 0 && wall[hitwal->nextwall].hitag != 0)
1156
                        return -1;
5435 helixhorne 1157
 
5826 terminx 1158
                    if (hitwal->hitag == 0)
1159
                    {
1160
                        int const spawnedSprite = A_Spawn(spriteNum, projecTile);
1161
                        sprite[spawnedSprite].ang
1162
                        = (getangle(hitwal->x - wall[hitwal->point2].x, hitwal->y - wall[hitwal->point2].y) + 1536) & 2047;
7973 terminx 1163
                        sprite[spawnedSprite].pos = hitData.pos;
5826 terminx 1164
                        sprite[spawnedSprite].cstat |= (krand() & 4);
1165
                        A_SetSprite(spawnedSprite, CLIPMASK0);
7973 terminx 1166
                        setsprite(spawnedSprite, &sprite[spawnedSprite].pos);
5826 terminx 1167
                        if (PN(spriteNum) == OOZFILTER || PN(spriteNum) == NEWBEAST)
1168
                            sprite[spawnedSprite].pal = 6;
1169
                    }
1170
                }
1209 terminx 1171
 
5826 terminx 1172
                return -1;
5435 helixhorne 1173
            }
1209 terminx 1174
 
5826 terminx 1175
            if (hitData.sect < 0)
1176
                break;
1209 terminx 1177
 
5826 terminx 1178
            if (klabs(startPos.x - hitData.pos.x) + klabs(startPos.y - hitData.pos.y) < 1024)
5828 terminx 1179
                Proj_HandleKnee(&hitData, spriteNum, playerNum, projecTile, shootAng, NULL, KNEE, 7, SMALLSMOKE, KICK_HIT);
5826 terminx 1180
            break;
1209 terminx 1181
 
5826 terminx 1182
        case SHOTSPARK1__STATIC:
1183
        case SHOTGUN__STATIC:
1184
        case CHAINGUN__STATIC:
1185
        {
1186
            if (pSprite->extra >= 0)
1187
                pSprite->shade = -96;
1209 terminx 1188
 
5826 terminx 1189
            if (playerNum >= 0)
5828 terminx 1190
                P_PreFireHitscan(spriteNum, playerNum, projecTile, &startPos, &Zvel, &shootAng,
6866 hendricks2 1191
                    projecTile == SHOTSPARK1__STATIC && !WW2GI, 1);
5826 terminx 1192
            else
5828 terminx 1193
                A_PreFireHitscan(pSprite, &startPos, &Zvel, &shootAng, 1);
3362 helixhorne 1194
 
5828 terminx 1195
            if (Proj_DoHitscan(spriteNum, 256 + 1, &startPos, Zvel, shootAng, &hitData))
5826 terminx 1196
                return -1;
3362 helixhorne 1197
 
5826 terminx 1198
            if ((krand() & 15) == 0 && sector[hitData.sect].lotag == ST_2_UNDERWATER)
6912 terminx 1199
                Proj_DoWaterTracers(hitData.pos, &startPos, 8 - (ud.multimode >> 1), pSprite->sectnum);
1209 terminx 1200
 
5826 terminx 1201
            int spawnedSprite;
1209 terminx 1202
 
5826 terminx 1203
            if (playerNum >= 0)
1204
            {
5828 terminx 1205
                spawnedSprite = Proj_InsertShotspark(&hitData, spriteNum, projecTile, 10, shootAng, G_DefaultActorHealth(projecTile) + (krand() % 6));
1209 terminx 1206
 
5826 terminx 1207
                if (P_PostFireHitscan(playerNum, spawnedSprite, &hitData, spriteNum, projecTile, Zvel, -SMALLSMOKE, BULLETHOLE, SHOTSPARK1, 0) < 0)
1208
                    return -1;
1209
            }
1210
            else
1211
            {
5828 terminx 1212
                spawnedSprite = A_PostFireHitscan(&hitData, spriteNum, projecTile, Zvel, shootAng, G_DefaultActorHealth(projecTile), -SMALLSMOKE,
5826 terminx 1213
                    SHOTSPARK1);
1214
            }
1209 terminx 1215
 
5826 terminx 1216
            if ((krand() & 255) < 4)
1217
                S_PlaySound3D(PISTOL_RICOCHET, spawnedSprite, &hitData.pos);
1209 terminx 1218
 
5826 terminx 1219
            return -1;
1220
        }
1209 terminx 1221
 
5826 terminx 1222
        case GROWSPARK__STATIC:
1223
        {
1224
            if (playerNum >= 0)
5828 terminx 1225
                P_PreFireHitscan(spriteNum, playerNum, projecTile, &startPos, &Zvel, &shootAng, 1, 1);
5826 terminx 1226
            else
5828 terminx 1227
                A_PreFireHitscan(pSprite, &startPos, &Zvel, &shootAng, 1);
1209 terminx 1228
 
5828 terminx 1229
            if (Proj_DoHitscan(spriteNum, 256 + 1, &startPos, Zvel, shootAng, &hitData))
5826 terminx 1230
                return -1;
1209 terminx 1231
 
5826 terminx 1232
            int const otherSprite = A_InsertSprite(hitData.sect, hitData.pos.x, hitData.pos.y, hitData.pos.z, GROWSPARK, -16, 28, 28,
5828 terminx 1233
                                                   shootAng, 0, 0, spriteNum, 1);
3989 terminx 1234
 
5826 terminx 1235
            sprite[otherSprite].pal = 2;
1236
            sprite[otherSprite].cstat |= 130;
1237
            sprite[otherSprite].xrepeat = sprite[otherSprite].yrepeat = 1;
1238
            A_SetHitData(otherSprite, &hitData);
3989 terminx 1239
 
5826 terminx 1240
            if (hitData.wall == -1 && hitData.sprite == -1 && hitData.sect >= 0)
1241
            {
6771 hendricks2 1242
                Proj_MaybeDamageCF2(otherSprite, Zvel, hitData.sect);
5826 terminx 1243
            }
1244
            else if (hitData.sprite >= 0)
1245
                A_DamageObject(hitData.sprite, otherSprite);
1246
            else if (hitData.wall >= 0 && wall[hitData.wall].picnum != ACCESSSWITCH && wall[hitData.wall].picnum != ACCESSSWITCH2)
8080 terminx 1247
                A_DamageWall(otherSprite, hitData.wall, hitData.pos, projecTile);
5435 helixhorne 1248
        }
1249
        break;
3989 terminx 1250
 
8788 hendricks2 1251
        case FIREBALL__STATIC:
1252
            if (!WORLDTOUR)
1253
                break;
1254
            fallthrough__;
5826 terminx 1255
        case FIRELASER__STATIC:
1209 terminx 1256
        case SPIT__STATIC:
1257
        case COOLEXPLOSION1__STATIC:
3464 helixhorne 1258
        {
5826 terminx 1259
            if (pSprite->extra >= 0)
1260
                pSprite->shade = -96;
1209 terminx 1261
 
5826 terminx 1262
            switch (projecTile)
1263
            {
1264
                case SPIT__STATIC: vel = 292; break;
1265
                case COOLEXPLOSION1__STATIC:
1266
                    vel = (pSprite->picnum == BOSS2) ? 644 : 348;
1267
                    startPos.z -= (4 << 7);
1268
                    break;
8788 hendricks2 1269
                case FIREBALL__STATIC:
1270
                    if (pSprite->picnum == BOSS5 || pSprite->picnum == BOSS5STAYPUT)
1271
                    {
1272
                        vel = 968;
1273
                        startPos.z += 0x1800;
1274
                        break;
1275
                    }
1276
                    fallthrough__;
5826 terminx 1277
                case FIRELASER__STATIC:
1278
                default:
1279
                    vel = 840;
1280
                    startPos.z -= (4 << 7);
1281
                    break;
1282
            }
1209 terminx 1283
 
5826 terminx 1284
            if (playerNum >= 0)
1209 terminx 1285
            {
8788 hendricks2 1286
                if (projecTile == FIREBALL)
1287
                {
6725 terminx 1288
                    Zvel = fix16_to_int(F16(100) - pPlayer->q16horiz - pPlayer->q16horizoff) * 98;
8788 hendricks2 1289
                    startPos.x += sintable[(348+shootAng+512)&2047]/448;
1290
                    startPos.y += sintable[(348+shootAng)&2047]/448;
1291
                    startPos.z += 0x300;
1292
                }
1293
                else if (GetAutoAimAng(spriteNum, playerNum, projecTile, -ZOFFSET4, 0, &startPos, vel, &Zvel, &shootAng) < 0)
1294
                    Zvel = fix16_to_int(F16(100) - pPlayer->q16horiz - pPlayer->q16horizoff) * 98;
1209 terminx 1295
            }
1296
            else
5826 terminx 1297
            {
1298
                int const otherPlayer = A_FindPlayer(pSprite, NULL);
5828 terminx 1299
                shootAng           += 16 - (krand() & 31);
5826 terminx 1300
                hitData.pos.x         = safeldist(g_player[otherPlayer].ps->i, pSprite);
1301
                Zvel                  = tabledivide32_noinline((g_player[otherPlayer].ps->opos.z - startPos.z + (3 << 8)) * vel, hitData.pos.x);
1302
            }
1209 terminx 1303
 
5826 terminx 1304
            Zvel = A_GetShootZvel(Zvel);
3465 helixhorne 1305
 
5828 terminx 1306
            int spriteSize = (playerNum >= 0) ? 7 : 18;
5826 terminx 1307
 
1308
            if (projecTile == SPIT)
1209 terminx 1309
            {
5828 terminx 1310
                spriteSize = 18;
5826 terminx 1311
                startPos.z -= (10 << 8);
1209 terminx 1312
            }
1313
 
5828 terminx 1314
            int const returnSprite = A_InsertSprite(spriteSectnum, startPos.x, startPos.y, startPos.z, projecTile, -127, spriteSize, spriteSize,
1315
                                                    shootAng, vel, Zvel, spriteNum, 4);
1209 terminx 1316
 
5826 terminx 1317
            sprite[returnSprite].extra += (krand() & 7);
1209 terminx 1318
 
5826 terminx 1319
            if (projecTile == COOLEXPLOSION1)
1320
            {
1321
                sprite[returnSprite].shade = 0;
1209 terminx 1322
 
5826 terminx 1323
                if (PN(spriteNum) == BOSS2)
1324
                {
1325
                    int const saveXvel        = sprite[returnSprite].xvel;
1326
                    sprite[returnSprite].xvel = MinibossScale(spriteNum, 1024);
1327
                    A_SetSprite(returnSprite, CLIPMASK0);
1328
                    sprite[returnSprite].xvel = saveXvel;
1329
                    sprite[returnSprite].ang += 128 - (krand() & 255);
1330
                }
1331
            }
8788 hendricks2 1332
            else if (projecTile == FIREBALL)
1333
            {
1334
                if (PN(spriteNum) == BOSS5 || PN(spriteNum) == BOSS5STAYPUT || playerNum >= 0)
1335
                {
1336
                    sprite[returnSprite].xrepeat = 40;
1337
                    sprite[returnSprite].yrepeat = 40;
1338
                }
1339
                sprite[returnSprite].yvel = playerNum;
1340
                //sprite[returnSprite].cstat |= 0x4000;
1341
            }
1209 terminx 1342
 
5826 terminx 1343
            sprite[returnSprite].cstat    = 128;
1344
            sprite[returnSprite].clipdist = 4;
1209 terminx 1345
 
5826 terminx 1346
            return returnSprite;
1347
        }
1348
 
6370 terminx 1349
        case FREEZEBLAST__STATIC:
1350
            startPos.z += (3 << 8);
1351
            fallthrough__;
5826 terminx 1352
        case RPG__STATIC:
5435 helixhorne 1353
        {
5826 terminx 1354
            // XXX: "CODEDUP"
1355
            if (pSprite->extra >= 0)
1356
                pSprite->shade = -96;
1209 terminx 1357
 
5826 terminx 1358
            vel = 644;
1209 terminx 1359
 
5826 terminx 1360
            int j = -1;
1361
 
1362
            if (playerNum >= 0)
1209 terminx 1363
            {
5826 terminx 1364
                // NOTE: j is a SPRITE_INDEX
5828 terminx 1365
                j = GetAutoAimAng(spriteNum, playerNum, projecTile, 8 << 8, 0 + 2, &startPos, vel, &Zvel, &shootAng);
1209 terminx 1366
 
5826 terminx 1367
                if (j < 0)
6725 terminx 1368
                    Zvel = fix16_to_int(F16(100) - pPlayer->q16horiz - pPlayer->q16horizoff) * 81;
1209 terminx 1369
 
5826 terminx 1370
                if (projecTile == RPG)
1371
                    A_PlaySound(RPG_SHOOT, spriteNum);
1372
            }
1373
            else
1374
            {
1375
                // NOTE: j is a player index
1376
                j          = A_FindPlayer(pSprite, NULL);
5828 terminx 1377
                shootAng = getangle(g_player[j].ps->opos.x - startPos.x, g_player[j].ps->opos.y - startPos.y);
5826 terminx 1378
                if (PN(spriteNum) == BOSS3)
5827 terminx 1379
                    startPos.z -= MinibossScale(spriteNum, ZOFFSET5);
5826 terminx 1380
                else if (PN(spriteNum) == BOSS2)
1381
                {
1382
                    vel += 128;
1383
                    startPos.z += MinibossScale(spriteNum, 24 << 8);
1384
                }
1209 terminx 1385
 
5826 terminx 1386
                Zvel = tabledivide32_noinline((g_player[j].ps->opos.z - startPos.z) * vel, safeldist(g_player[j].ps->i, pSprite));
1567 terminx 1387
 
5826 terminx 1388
                if (A_CheckEnemySprite(pSprite) && (AC_MOVFLAGS(pSprite, &actor[spriteNum]) & face_player_smart))
5828 terminx 1389
                    shootAng = pSprite->ang + (krand() & 31) - 16;
5826 terminx 1390
            }
1567 terminx 1391
 
5826 terminx 1392
            if (numplayers > 1 && g_netClient)
1393
                return -1;
1209 terminx 1394
 
5826 terminx 1395
            Zvel                   = A_GetShootZvel(Zvel);
5828 terminx 1396
            int const returnSprite = A_InsertSprite(spriteSectnum, startPos.x + (sintable[(348 + shootAng + 512) & 2047] / 448),
1397
                                                    startPos.y + (sintable[(shootAng + 348) & 2047] / 448), startPos.z - (1 << 8),
1398
                                                    projecTile, 0, 14, 14, shootAng, vel, Zvel, spriteNum, 4);
7776 terminx 1399
            auto const pReturn = &sprite[returnSprite];
1209 terminx 1400
 
5826 terminx 1401
            pReturn->extra += (krand() & 7);
1402
            if (projecTile != FREEZEBLAST)
1403
                pReturn->yvel = (playerNum >= 0 && j >= 0) ? j : -1;  // RPG_YVEL
1404
            else
1209 terminx 1405
            {
5826 terminx 1406
                pReturn->yvel = g_numFreezeBounces;
1407
                pReturn->xrepeat >>= 1;
1408
                pReturn->yrepeat >>= 1;
1409
                pReturn->zvel -= (2 << 4);
1410
            }
1411
 
1412
            if (playerNum == -1)
1413
            {
1414
                if (PN(spriteNum) == BOSS3)
1209 terminx 1415
                {
5826 terminx 1416
                    if (krand() & 1)
1417
                    {
5828 terminx 1418
                        pReturn->x -= MinibossScale(spriteNum, sintable[shootAng & 2047] >> 6);
1419
                        pReturn->y -= MinibossScale(spriteNum, sintable[(shootAng + 1024 + 512) & 2047] >> 6);
5826 terminx 1420
                        pReturn->ang -= MinibossScale(spriteNum, 8);
1421
                    }
1422
                    else
1423
                    {
5828 terminx 1424
                        pReturn->x += MinibossScale(spriteNum, sintable[shootAng & 2047] >> 6);
1425
                        pReturn->y += MinibossScale(spriteNum, sintable[(shootAng + 1024 + 512) & 2047] >> 6);
5826 terminx 1426
                        pReturn->ang += MinibossScale(spriteNum, 4);
1427
                    }
1428
                    pReturn->xrepeat = MinibossScale(spriteNum, 42);
1429
                    pReturn->yrepeat = MinibossScale(spriteNum, 42);
1209 terminx 1430
                }
5826 terminx 1431
                else if (PN(spriteNum) == BOSS2)
1209 terminx 1432
                {
5828 terminx 1433
                    pReturn->x -= MinibossScale(spriteNum, sintable[shootAng & 2047] / 56);
1434
                    pReturn->y -= MinibossScale(spriteNum, sintable[(shootAng + 1024 + 512) & 2047] / 56);
5826 terminx 1435
                    pReturn->ang -= MinibossScale(spriteNum, 8) + (krand() & 255) - 128;
1436
                    pReturn->xrepeat = 24;
1437
                    pReturn->yrepeat = 24;
1209 terminx 1438
                }
5826 terminx 1439
                else if (projecTile != FREEZEBLAST)
1440
                {
1441
                    pReturn->xrepeat = 30;
1442
                    pReturn->yrepeat = 30;
1443
                    pReturn->extra >>= 2;
1444
                }
1209 terminx 1445
            }
5826 terminx 1446
            else if (PWEAPON(playerNum, g_player[playerNum].ps->curr_weapon, WorksLike) == DEVISTATOR_WEAPON)
1209 terminx 1447
            {
5826 terminx 1448
                pReturn->extra >>= 2;
1449
                pReturn->ang += 16 - (krand() & 31);
1450
                pReturn->zvel += 256 - (krand() & 511);
1209 terminx 1451
 
5826 terminx 1452
                if (g_player[playerNum].ps->hbomb_hold_delay)
1453
                {
5828 terminx 1454
                    pReturn->x -= sintable[shootAng & 2047] / 644;
1455
                    pReturn->y -= sintable[(shootAng + 1024 + 512) & 2047] / 644;
5826 terminx 1456
                }
1457
                else
1458
                {
5828 terminx 1459
                    pReturn->x += sintable[shootAng & 2047] >> 8;
1460
                    pReturn->y += sintable[(shootAng + 1024 + 512) & 2047] >> 8;
5826 terminx 1461
                }
1462
                pReturn->xrepeat >>= 1;
1463
                pReturn->yrepeat >>= 1;
1209 terminx 1464
            }
1465
 
5826 terminx 1466
            pReturn->cstat    = 128;
1467
            pReturn->clipdist = (projecTile == RPG) ? 4 : 40;
1209 terminx 1468
 
5826 terminx 1469
            return returnSprite;
1470
        }
1209 terminx 1471
 
5826 terminx 1472
        case HANDHOLDINGLASER__STATIC:
1473
        {
1474
            int const zOffset     = (playerNum >= 0) ? g_player[playerNum].ps->pyoff : 0;
6725 terminx 1475
            Zvel                  = (playerNum >= 0) ? fix16_to_int(F16(100) - pPlayer->q16horiz - pPlayer->q16horizoff) * 32 : 0;
1209 terminx 1476
 
5826 terminx 1477
            startPos.z -= zOffset;
5828 terminx 1478
            Proj_DoHitscan(spriteNum, 0, &startPos, Zvel, shootAng, &hitData);
5826 terminx 1479
            startPos.z += zOffset;
1209 terminx 1480
 
5826 terminx 1481
            int placeMine = 0;
1482
            if (hitData.sprite >= 0)
1483
                break;
5435 helixhorne 1484
 
5826 terminx 1485
            if (hitData.wall >= 0 && hitData.sect >= 0)
1486
                if (((hitData.pos.x - startPos.x) * (hitData.pos.x - startPos.x)
1487
                     + (hitData.pos.y - startPos.y) * (hitData.pos.y - startPos.y))
1488
                    < (290 * 290))
1209 terminx 1489
                {
5826 terminx 1490
                    // ST_2_UNDERWATER
1491
                    if (wall[hitData.wall].nextsector >= 0)
1492
                    {
1493
                        if (sector[wall[hitData.wall].nextsector].lotag <= 2 && sector[hitData.sect].lotag <= 2)
1494
                            placeMine = 1;
1495
                    }
1496
                    else if (sector[hitData.sect].lotag <= 2)
1497
                        placeMine = 1;
1209 terminx 1498
                }
1499
 
5826 terminx 1500
            if (placeMine == 1)
1501
            {
6244 terminx 1502
                int const tripBombMode = (playerNum < 0) ? 0 :
3414 helixhorne 1503
#ifdef LUNATIC
5826 terminx 1504
                                                           g_player[playerNum].ps->tripbombControl;
3414 helixhorne 1505
#else
5826 terminx 1506
                                                           Gv_GetVarByLabel("TRIPBOMB_CONTROL", TRIPBOMB_TRIPWIRE,
1507
                                                                            g_player[playerNum].ps->i, playerNum);
3414 helixhorne 1508
#endif
5826 terminx 1509
                int const spawnedSprite = A_InsertSprite(hitData.sect, hitData.pos.x, hitData.pos.y, hitData.pos.z, TRIPBOMB, -16, 4, 5,
5828 terminx 1510
                                                         shootAng, 0, 0, spriteNum, 6);
6244 terminx 1511
                if (tripBombMode & TRIPBOMB_TIMER)
5826 terminx 1512
                {
3414 helixhorne 1513
#ifdef LUNATIC
5826 terminx 1514
                    int32_t lLifetime    = g_player[playerNum].ps->tripbombLifetime;
1515
                    int32_t lLifetimeVar = g_player[playerNum].ps->tripbombLifetimeVar;
3414 helixhorne 1516
#else
5826 terminx 1517
                    int32_t lLifetime = Gv_GetVarByLabel("STICKYBOMB_LIFETIME", NAM_GRENADE_LIFETIME, g_player[playerNum].ps->i, playerNum);
1518
                    int32_t lLifetimeVar
1519
                    = Gv_GetVarByLabel("STICKYBOMB_LIFETIME_VAR", NAM_GRENADE_LIFETIME_VAR, g_player[playerNum].ps->i, playerNum);
3414 helixhorne 1520
#endif
5826 terminx 1521
                    // set timer.  blows up when at zero....
6254 hendricks2 1522
                    actor[spawnedSprite].t_data[7] = lLifetime + mulscale14(krand(), lLifetimeVar) - lLifetimeVar;
5826 terminx 1523
                    // TIMER_CONTROL
1524
                    actor[spawnedSprite].t_data[6] = 1;
1525
                }
1526
                else
1527
                    sprite[spawnedSprite].hitag = spawnedSprite;
1209 terminx 1528
 
5826 terminx 1529
                A_PlaySound(LASERTRIP_ONWALL, spawnedSprite);
1530
                sprite[spawnedSprite].xvel = -20;
1531
                A_SetSprite(spawnedSprite, CLIPMASK0);
1532
                sprite[spawnedSprite].cstat = 16;
1209 terminx 1533
 
6244 terminx 1534
                int const p2      = wall[hitData.wall].point2;
1535
                int const wallAng = getangle(wall[hitData.wall].x - wall[p2].x, wall[hitData.wall].y - wall[p2].y) - 512;
5826 terminx 1536
 
6244 terminx 1537
                actor[spawnedSprite].t_data[5] = sprite[spawnedSprite].ang = wallAng;
1538
 
5826 terminx 1539
                return spawnedSprite;
1209 terminx 1540
            }
5826 terminx 1541
            return -1;
2977 helixhorne 1542
        }
3680 helixhorne 1543
 
5826 terminx 1544
        case BOUNCEMINE__STATIC:
1545
        case MORTER__STATIC:
1546
        {
1547
            if (pSprite->extra >= 0)
1548
                pSprite->shade = -96;
1209 terminx 1549
 
5826 terminx 1550
            int const playerSprite = g_player[A_FindPlayer(pSprite, NULL)].ps->i;
1551
            int const playerDist   = ldist(&sprite[playerSprite], pSprite);
1209 terminx 1552
 
5826 terminx 1553
            Zvel = -playerDist >> 1;
1209 terminx 1554
 
5826 terminx 1555
            if (Zvel < -4096)
1556
                Zvel = -2048;
1209 terminx 1557
 
5826 terminx 1558
            vel  = playerDist >> 4;
1559
            Zvel = A_GetShootZvel(Zvel);
3465 helixhorne 1560
 
5828 terminx 1561
            A_InsertSprite(spriteSectnum, startPos.x + (sintable[(512 + shootAng + 512) & 2047] >> 8),
1562
                           startPos.y + (sintable[(shootAng + 512) & 2047] >> 8), startPos.z + (6 << 8), projecTile, -64, 32, 32,
1563
                           shootAng, vel, Zvel, spriteNum, 1);
5826 terminx 1564
            break;
1565
        }
5435 helixhorne 1566
 
5826 terminx 1567
        case SHRINKER__STATIC:
5435 helixhorne 1568
        {
5826 terminx 1569
            if (pSprite->extra >= 0)
1570
                pSprite->shade = -96;
5435 helixhorne 1571
 
5826 terminx 1572
            if (playerNum >= 0)
1573
            {
8726 terminx 1574
                if (NAM_WW2GI || GetAutoAimAng(spriteNum, playerNum, projecTile, ZOFFSET6, 0, &startPos, 768, &Zvel, &shootAng) < 0)
6725 terminx 1575
                    Zvel = fix16_to_int(F16(100) - pPlayer->q16horiz - pPlayer->q16horizoff) * 98;
5826 terminx 1576
            }
1577
            else if (pSprite->statnum != STAT_EFFECTOR)
1578
            {
1579
                int const otherPlayer = A_FindPlayer(pSprite, NULL);
1580
                Zvel                  = tabledivide32_noinline((g_player[otherPlayer].ps->opos.z - startPos.z) * 512,
1581
                                              safeldist(g_player[otherPlayer].ps->i, pSprite));
1582
            }
1583
            else
1584
                Zvel = 0;
1209 terminx 1585
 
5826 terminx 1586
            Zvel                   = A_GetShootZvel(Zvel);
5828 terminx 1587
            int const returnSprite = A_InsertSprite(spriteSectnum, startPos.x + (sintable[(512 + shootAng + 512) & 2047] >> 12),
1588
                                                    startPos.y + (sintable[(shootAng + 512) & 2047] >> 12), startPos.z + (2 << 8),
1589
                                                    SHRINKSPARK, -16, 28, 28, shootAng, 768, Zvel, spriteNum, 4);
5826 terminx 1590
            sprite[returnSprite].cstat    = 128;
1591
            sprite[returnSprite].clipdist = 32;
5435 helixhorne 1592
 
5826 terminx 1593
            return returnSprite;
1594
        }
8788 hendricks2 1595
        case FLAMETHROWERFLAME__STATIC:
1596
        {
1597
            if (!WORLDTOUR)
1598
                break;
1599
 
1600
            if (pSprite->extra >= 0) pSprite->shade = -96;
8791 hendricks2 1601
            vel = 400;
8788 hendricks2 1602
            int j, underwater;
1603
            if (playerNum >= 0)
1604
            {
1605
                Zvel = fix16_to_int(F16(100) - pPlayer->q16horiz - pPlayer->q16horizoff) * 81;
1606
                int xv = sprite[pPlayer->i].xvel;
1607
                if (xv)
1608
                {
1609
                    int ang = getangle(startPos.x-pPlayer->opos.x,startPos.y-pPlayer->opos.y);
1610
                    ang = 512-(1024-klabs(klabs(ang-shootAng)-1024));
8791 hendricks2 1611
                    vel = 400+int(float(ang)*(1.f/512.f)*float(xv));
8788 hendricks2 1612
                }
1613
                underwater = sector[pPlayer->cursectnum].lotag == ST_2_UNDERWATER;
1614
            }
1615
            else
1616
            {
1617
                // NOTE: j is a player index
1618
                j          = A_FindPlayer(pSprite, NULL);
1619
                shootAng = getangle(g_player[j].ps->opos.x - startPos.x, g_player[j].ps->opos.y - startPos.y);
1620
                if (PN(spriteNum) == BOSS3 || PN(spriteNum) == BOSS3STAYPUT)
1621
                    startPos.z -= MinibossScale(spriteNum, ZOFFSET5);
1622
                else if (PN(spriteNum) == BOSS5 || PN(spriteNum) == BOSS5STAYPUT)
1623
                {
1624
                    vel += 128;
1625
                    startPos.z += MinibossScale(spriteNum, 24 << 8);
1626
                }
1627
 
1628
                Zvel = tabledivide32_noinline((g_player[j].ps->opos.z - startPos.z) * vel, safeldist(g_player[j].ps->i, pSprite));
1629
 
1630
                if (A_CheckEnemySprite(pSprite) && (AC_MOVFLAGS(pSprite, &actor[spriteNum]) & face_player_smart))
1631
                    shootAng = pSprite->ang + (krand() & 31) - 16;
1632
                underwater = sector[pSprite->sectnum].lotag == 2;
1633
            }
1634
            if (underwater)
1635
            {
1636
                if ((krand() % 5) != 0)
1637
                    return -1;
1638
                j = A_Spawn(spriteNum, WATERBUBBLE);
1639
            }
1640
            else
1641
            {
1642
                j = A_Spawn(spriteNum, projecTile);
1643
                sprite[j].zvel = Zvel;
1644
                sprite[j].xvel = vel;
1645
            }
1646
            sprite[j].x = startPos.x+sintable[(shootAng+630)&2047]/448;
1647
            sprite[j].y = startPos.y+sintable[(shootAng+112)&2047]/448;
1648
            sprite[j].z = startPos.z-0x100;
1649
            sprite[j].cstat = 128;
1650
            sprite[j].ang = shootAng;
1651
            sprite[j].xrepeat = sprite[j].xrepeat = 2;
1652
            sprite[j].clipdist = 40;
1653
            sprite[j].owner = spriteNum;
1654
            sprite[j].yvel = playerNum;
1655
            if (playerNum == -1 && (sprite[spriteNum].picnum == BOSS5 || sprite[spriteNum].picnum == BOSS5))
1656
            {
1657
                sprite[j].xrepeat = sprite[j].yrepeat = 10;
1658
                sprite[j].x -= sintable[shootAng&2047]/56;
1659
                sprite[j].y -= sintable[(shootAng-512)&2047]/56;
1660
            }
1661
            return j;
1662
        }
1663
        case FIREFLY__STATIC:
1664
        {
1665
            if (!WORLDTOUR)
1666
                break;
1667
 
1668
            int j = A_Spawn(spriteNum, projecTile);
1669
            sprite[j].pos = startPos;
1670
            sprite[j].ang = shootAng;
1671
            sprite[j].xvel = 500;
1672
            sprite[j].zvel = 0;
1673
            return j;
1674
        }
5435 helixhorne 1675
    }
1676
 
1677
    return -1;
1678
}
6804 terminx 1679
#endif
5435 helixhorne 1680
 
6244 terminx 1681
int A_ShootWithZvel(int const spriteNum, int const projecTile, int const forceZvel)
5435 helixhorne 1682
{
5825 terminx 1683
    Bassert(projecTile >= 0);
5435 helixhorne 1684
 
8104 terminx 1685
    auto const pSprite   = &sprite[spriteNum];
1686
    int const  playerNum = (pSprite->picnum == APLAYER) ? P_GetP(pSprite) : -1;
7776 terminx 1687
    auto const pPlayer   = playerNum >= 0 ? g_player[playerNum].ps : NULL;
5435 helixhorne 1688
 
5825 terminx 1689
    if (forceZvel != SHOOT_HARDCODED_ZVEL)
5435 helixhorne 1690
    {
1691
        g_overrideShootZvel = 1;
5825 terminx 1692
        g_shootZvel = forceZvel;
5435 helixhorne 1693
    }
1694
    else
1695
        g_overrideShootZvel = 0;
1696
 
5828 terminx 1697
    int    shootAng;
5825 terminx 1698
    vec3_t startPos;
1699
 
7006 terminx 1700
    if (pPlayer != NULL)
5435 helixhorne 1701
    {
7697 terminx 1702
        startPos = pPlayer->pos;
1703
        startPos.z += pPlayer->pyoff + ZOFFSET6;
1704
        shootAng = fix16_to_int(pPlayer->q16ang);
1705
 
7430 terminx 1706
        pPlayer->crack_time = PCRACKTIME;
5435 helixhorne 1707
    }
1708
    else
1709
    {
5828 terminx 1710
        shootAng = pSprite->ang;
7697 terminx 1711
        startPos = pSprite->pos;
5828 terminx 1712
        startPos.z -= (((pSprite->yrepeat * tilesiz[pSprite->picnum].y)<<1) - ZOFFSET6);
5435 helixhorne 1713
 
5825 terminx 1714
        if (pSprite->picnum != ROTATEGUN)
5435 helixhorne 1715
        {
5825 terminx 1716
            startPos.z -= (7<<8);
5435 helixhorne 1717
 
5825 terminx 1718
            if (A_CheckEnemySprite(pSprite) && PN(spriteNum) != COMMANDER)
1209 terminx 1719
            {
5828 terminx 1720
                startPos.x += (sintable[(shootAng+1024+96)&2047]>>7);
1721
                startPos.y += (sintable[(shootAng+512+96)&2047]>>7);
5435 helixhorne 1722
            }
1723
        }
5700 terminx 1724
 
7410 terminx 1725
#ifndef EDUKE32_STANDALONE
5700 terminx 1726
#ifdef POLYMER
5825 terminx 1727
        switch (DYNAMICTILEMAP(projecTile))
5700 terminx 1728
        {
5825 terminx 1729
            case FIRELASER__STATIC:
1730
            case SHOTGUN__STATIC:
1731
            case SHOTSPARK1__STATIC:
1732
            case CHAINGUN__STATIC:
1733
            case RPG__STATIC:
1734
            case MORTER__STATIC:
1735
                {
6303 hendricks2 1736
                    vec2_t const v = { ((sintable[(pSprite->ang + 512) & 2047]) >> 7),
5825 terminx 1737
                                       ((sintable[(pSprite->ang) & 2047]) >> 7) };
1738
 
1739
                    pSprite->x += v.x;
1740
                    pSprite->y += v.y;
1741
                    G_AddGameLight(0, spriteNum, PHEIGHT, 8192, 255 + (95 << 8), PR_LIGHT_PRIO_MAX_GAME);
1742
                    actor[spriteNum].lightcount = 2;
1743
                    pSprite->x -= v.x;
1744
                    pSprite->y -= v.y;
1745
                }
1746
 
1747
                break;
5700 terminx 1748
            }
1749
#endif // POLYMER
7410 terminx 1750
#endif // !EDUKE32_STANDALONE
1209 terminx 1751
    }
3465 helixhorne 1752
 
6804 terminx 1753
#ifdef EDUKE32_STANDALONE
6821 terminx 1754
    return A_CheckSpriteTileFlags(projecTile, SFLAG_PROJECTILE) ? A_ShootCustom(spriteNum, projecTile, shootAng, &startPos) : -1;
6804 terminx 1755
#else
5825 terminx 1756
    return A_CheckSpriteTileFlags(projecTile, SFLAG_PROJECTILE)
5828 terminx 1757
           ? A_ShootCustom(spriteNum, projecTile, shootAng, &startPos)
7808 terminx 1758
           : !FURY ? A_ShootHardcoded(spriteNum, projecTile, shootAng, startPos, pSprite, playerNum, pPlayer) : -1;
6804 terminx 1759
#endif
1209 terminx 1760
}
1761
 
2917 helixhorne 1762
 
1763
//////////////////// HUD WEAPON / MISC. DISPLAY CODE ////////////////////
1764
 
5116 hendricks2 1765
static void P_DisplaySpit(void)
1209 terminx 1766
{
7776 terminx 1767
    auto const pPlayer     = g_player[screenpeek].ps;
8104 terminx 1768
    int const  loogCounter = pPlayer->loogcnt;
1209 terminx 1769
 
5825 terminx 1770
    if (loogCounter == 0)
2639 helixhorne 1771
        return;
1209 terminx 1772
 
5825 terminx 1773
    if (VM_OnEvent(EVENT_DISPLAYSPIT, pPlayer->i, screenpeek) != 0)
5118 hendricks2 1774
        return;
1775
 
5825 terminx 1776
    int const rotY = loogCounter<<2;
5118 hendricks2 1777
 
5829 terminx 1778
    for (bssize_t i=0; i < pPlayer->numloogs; i++)
1209 terminx 1779
    {
5828 terminx 1780
        int const rotAng = klabs(sintable[((loogCounter + i) << 5) & 2047]) >> 5;
5825 terminx 1781
        int const rotZoom  = 4096 + ((loogCounter + i) << 9);
7775 terminx 1782
        int const rotX     = (-fix16_to_int(g_player[screenpeek].input->q16avel) >> 1) + (sintable[((loogCounter + i) << 6) & 2047] >> 10);
1209 terminx 1783
 
5825 terminx 1784
        rotatesprite_fs((pPlayer->loogiex[i] + rotX) << 16, (200 + pPlayer->loogiey[i] - rotY) << 16, rotZoom - (i << 8),
5828 terminx 1785
                        256 - rotAng, LOOGIE, 0, 0, 2);
1209 terminx 1786
    }
1787
}
1788
 
5825 terminx 1789
int P_GetHudPal(const DukePlayer_t *p)
2518 helixhorne 1790
{
1791
    if (sprite[p->i].pal == 1)
1792
        return 1;
1793
 
1794
    if (p->cursectnum >= 0)
1795
    {
5825 terminx 1796
        int const hudPal = sector[p->cursectnum].floorpal;
1797
        if (!g_noFloorPal[hudPal])
1798
            return hudPal;
2518 helixhorne 1799
    }
1800
 
1801
    return 0;
1802
}
1803
 
6954 hendricks2 1804
int P_GetKneePal(DukePlayer_t const * pPlayer)
1805
{
1806
    return P_GetKneePal(pPlayer, P_GetHudPal(pPlayer));
1807
}
1808
 
1809
int P_GetKneePal(DukePlayer_t const * pPlayer, int const hudPal)
1810
{
1811
    return hudPal == 0 ? pPlayer->palookup : hudPal;
1812
}
1813
 
6955 hendricks2 1814
int P_GetOverheadPal(DukePlayer_t const * pPlayer)
1815
{
1816
    return sprite[pPlayer->i].pal;
1817
}
1818
 
5825 terminx 1819
static int P_DisplayFist(int const fistShade)
1209 terminx 1820
{
5825 terminx 1821
    DukePlayer_t const *const pPlayer = g_player[screenpeek].ps;
1822
    int fistInc = pPlayer->fist_incs;
1209 terminx 1823
 
5825 terminx 1824
    if (fistInc > 32)
1825
        fistInc = 32;
2932 helixhorne 1826
 
5825 terminx 1827
    if (fistInc <= 0)
1828
        return 0;
2917 helixhorne 1829
 
5825 terminx 1830
    switch (VM_OnEvent(EVENT_DISPLAYFIST, pPlayer->i, screenpeek))
5118 hendricks2 1831
    {
5825 terminx 1832
        case 1: return 1;
1833
        case -1: return 0;
5118 hendricks2 1834
    }
1835
 
5825 terminx 1836
    int const fistY       = klabs(pPlayer->look_ang) / 9;
1837
    int const fistZoom    = clamp(65536 - (sintable[(512 + (fistInc << 6)) & 2047] << 2), 40920, 90612);
1838
    int const fistYOffset = 194 + (sintable[((6 + fistInc) << 7) & 2047] >> 9);
1839
    int const fistPal     = P_GetHudPal(pPlayer);
1840
    int       wx[2]       = { windowxy1.x, windowxy2.x };
1209 terminx 1841
 
4249 hendricks2 1842
#ifdef SPLITSCREEN_MOD_HACKS
2945 helixhorne 1843
    // XXX: this is outdated, doesn't handle above/below split.
2957 helixhorne 1844
    if (g_fakeMultiMode==2)
2932 helixhorne 1845
        wx[(g_snum==0)] = (wx[0]+wx[1])/2+1;
4249 hendricks2 1846
#endif
2932 helixhorne 1847
 
7775 terminx 1848
    rotatesprite((-fistInc + 222 + (fix16_to_int(g_player[screenpeek].input->q16avel) >> 5)) << 16, (fistY + fistYOffset) << 16,
5825 terminx 1849
                 fistZoom, 0, FIST, fistShade, fistPal, 2, wx[0], windowxy1.y, wx[1], windowxy2.y);
1209 terminx 1850
 
1851
    return 1;
1852
}
1853
 
2917 helixhorne 1854
#define DRAWEAP_CENTER 262144
3992 terminx 1855
#define weapsc(sc) scale(sc, ud.weaponscale, 100)
2917 helixhorne 1856
 
3695 helixhorne 1857
static int32_t g_dts_yadd;
1858
 
5825 terminx 1859
static void G_DrawTileScaled(int drawX, int drawY, int tileNum, int drawShade, int drawBits, int drawPal)
1209 terminx 1860
{
5800 terminx 1861
    int32_t wx[2] = { windowxy1.x, windowxy2.x };
1862
    int32_t wy[2] = { windowxy1.y, windowxy2.y };
2905 helixhorne 1863
 
5825 terminx 1864
    int drawYOffset = 0;
6028 hendricks2 1865
    int drawXOffset = 192<<16;
5825 terminx 1866
 
3408 helixhorne 1867
    switch (hudweap.cur)
1209 terminx 1868
    {
5825 terminx 1869
        case DEVISTATOR_WEAPON:
1870
        case TRIPBOMB_WEAPON:
6028 hendricks2 1871
            drawXOffset = 160<<16;
5825 terminx 1872
            break;
1873
        default:
1874
            if (drawBits & DRAWEAP_CENTER)
1875
            {
6028 hendricks2 1876
                drawXOffset = 160<<16;
5825 terminx 1877
                drawBits &= ~DRAWEAP_CENTER;
1878
            }
1879
            break;
1209 terminx 1880
    }
1881
 
3353 helixhorne 1882
    // bit 4 means "flip x" for G_DrawTileScaled
5825 terminx 1883
    int const drawAng = (drawBits & 4) ? 1024 : 0;
1209 terminx 1884
 
4249 hendricks2 1885
#ifdef SPLITSCREEN_MOD_HACKS
2957 helixhorne 1886
    if (g_fakeMultiMode==2)
2905 helixhorne 1887
    {
5825 terminx 1888
        int const sideBySide = (ud.screen_size!=0);
2944 helixhorne 1889
 
2905 helixhorne 1890
        // splitscreen HACK
5825 terminx 1891
        drawBits &= ~(1024|512|256);
1892
        if (sideBySide)
2944 helixhorne 1893
        {
5825 terminx 1894
            drawBits &= ~8;
2944 helixhorne 1895
            wx[(g_snum==0)] = (wx[0]+wx[1])/2 + 2;
1896
        }
1897
        else
1898
        {
5825 terminx 1899
            drawBits |= 8;
2944 helixhorne 1900
            if (g_snum==0)
5825 terminx 1901
                drawYOffset = -(100<<16);
2944 helixhorne 1902
            wy[(g_snum==0)] = (wy[0]+wy[1])/2 + 2;
1903
        }
2905 helixhorne 1904
    }
4249 hendricks2 1905
#endif
2905 helixhorne 1906
 
1820 terminx 1907
#ifdef USE_OPENGL
6829 terminx 1908
    if (videoGetRenderMode() >= REND_POLYMOST && usemodels && md_tilehasmodel(tileNum,drawPal) >= 0)
6028 hendricks2 1909
        drawYOffset += (224<<16)-weapsc(224<<16);
1209 terminx 1910
#endif
6028 hendricks2 1911
    rotatesprite(weapsc(drawX<<16) + (drawXOffset-weapsc(drawXOffset)),
1912
                 weapsc((drawY<<16) + g_dts_yadd) + ((200<<16)-weapsc(200<<16)) + drawYOffset,
5825 terminx 1913
                 weapsc(65536L),drawAng,tileNum,drawShade,drawPal,(2|drawBits),
2944 helixhorne 1914
                 wx[0],wy[0], wx[1],wy[1]);
1209 terminx 1915
}
1916
 
6779 hendricks2 1917
static void G_DrawWeaponTile(int weaponX, int weaponY, int weaponTile, int weaponShade, int weaponBits, int weaponPal)
1209 terminx 1918
{
6779 hendricks2 1919
    static int shadef = 0;
1920
    static int palf = 0;
1657 terminx 1921
 
1922
    // basic fading between player weapon shades
6779 hendricks2 1923
    if (shadef != weaponShade && (!weaponPal || palf == weaponPal))
1657 terminx 1924
    {
6779 hendricks2 1925
        shadef += (weaponShade - shadef) >> 2;
1657 terminx 1926
 
6779 hendricks2 1927
        if (!((weaponShade - shadef) >> 2))
1928
            shadef = logapproach(shadef, weaponShade);
1657 terminx 1929
    }
1930
    else
6779 hendricks2 1931
        shadef = weaponShade;
1657 terminx 1932
 
6779 hendricks2 1933
    palf = weaponPal;
1657 terminx 1934
 
3695 helixhorne 1935
#ifdef USE_OPENGL
6829 terminx 1936
    if (videoGetRenderMode() >= REND_POLYMOST)
5826 terminx 1937
    {
1938
        if (weaponTile >= CHAINGUN + 1 && weaponTile <= CHAINGUN + 4)
1939
        {
1940
            if (!usemodels || md_tilehasmodel(weaponTile, weaponPal) < 0)
5117 hendricks2 1941
            {
1942
                // HACK: Draw the upper part of the chaingun two screen
1943
                // pixels (not texels; multiplied by weapon scale) lower
1944
                // first, preventing ugly horizontal seam.
1945
                g_dts_yadd = tabledivide32_noinline(65536 * 2 * 200, ydim);
6779 hendricks2 1946
                G_DrawTileScaled(weaponX, weaponY, weaponTile, shadef, weaponBits, weaponPal);
5117 hendricks2 1947
                g_dts_yadd = 0;
1948
            }
5826 terminx 1949
        }
1950
    }
3695 helixhorne 1951
#endif
5826 terminx 1952
 
6779 hendricks2 1953
    G_DrawTileScaled(weaponX, weaponY, weaponTile, shadef, weaponBits, weaponPal);
1209 terminx 1954
}
1955
 
5826 terminx 1956
static inline void G_DrawWeaponTileWithID(int uniqueID, int weaponX, int weaponY, int weaponTile, int weaponShade,
6779 hendricks2 1957
                                          int weaponBits, int p)
4703 terminx 1958
{
5826 terminx 1959
    int lastUniqueID = guniqhudid;
1960
    guniqhudid       = uniqueID;
4703 terminx 1961
 
6779 hendricks2 1962
    G_DrawWeaponTile(weaponX, weaponY, weaponTile, weaponShade, weaponBits, p);
5826 terminx 1963
 
1964
    guniqhudid       = lastUniqueID;
4703 terminx 1965
}
1966
 
6779 hendricks2 1967
static inline void G_DrawWeaponTileUnfadedWithID(int uniqueID, int weaponX, int weaponY, int weaponTile, int weaponShade,
1968
                                          int weaponBits, int p)
1969
{
1970
    int lastUniqueID = guniqhudid;
1971
    guniqhudid       = uniqueID;
1972
 
1973
    G_DrawTileScaled(weaponX, weaponY, weaponTile, weaponShade, weaponBits, p); // skip G_DrawWeaponTile
1974
 
1975
    guniqhudid       = lastUniqueID;
1976
}
1977
 
5826 terminx 1978
static int P_DisplayKnee(int kneeShade)
1209 terminx 1979
{
5828 terminx 1980
    static int8_t const       knee_y[] = { 0, -8, -16, -32, -64, -84, -108, -108, -108, -72, -32, -8 };
7776 terminx 1981
    auto const ps = g_player[screenpeek].ps;
1209 terminx 1982
 
5118 hendricks2 1983
    if (ps->knee_incs == 0)
3436 helixhorne 1984
        return 0;
1209 terminx 1985
 
5118 hendricks2 1986
    switch (VM_OnEvent(EVENT_DISPLAYKNEE, ps->i, screenpeek))
1987
    {
5826 terminx 1988
        case 1: return 1;
1989
        case -1: return 0;
5118 hendricks2 1990
    }
1991
 
1992
    if (ps->knee_incs >= ARRAY_SIZE(knee_y) || sprite[ps->i].extra <= 0)
1993
        return 0;
1994
 
5826 terminx 1995
    int const kneeY   = knee_y[ps->knee_incs] + (klabs(ps->look_ang) / 9) - (ps->hard_landing << 3);
6954 hendricks2 1996
    int const kneePal = P_GetKneePal(ps);
5118 hendricks2 1997
 
7775 terminx 1998
    G_DrawTileScaled(105+(fix16_to_int(g_player[screenpeek].input->q16avel)>>5)-(ps->look_ang>>1)+(knee_y[ps->knee_incs]>>2),
6725 terminx 1999
                     kneeY+280-(fix16_to_int(ps->q16horiz-ps->q16horizoff)>>4),KNEE,kneeShade,4+DRAWEAP_CENTER,kneePal);
2917 helixhorne 2000
 
1209 terminx 2001
    return 1;
2002
}
2003
 
5826 terminx 2004
static int P_DisplayKnuckles(int knuckleShade)
1209 terminx 2005
{
5452 hendricks2 2006
    if (WW2GI)
2007
        return 0;
2008
 
7776 terminx 2009
    auto const pPlayer = g_player[screenpeek].ps;
5826 terminx 2010
 
2011
    if (pPlayer->knuckle_incs == 0)
3436 helixhorne 2012
        return 0;
1209 terminx 2013
 
5828 terminx 2014
    static int8_t const knuckleFrames[] = { 0, 1, 2, 2, 3, 3, 3, 2, 2, 1, 0 };
5826 terminx 2015
 
2016
    switch (VM_OnEvent(EVENT_DISPLAYKNUCKLES, pPlayer->i, screenpeek))
5118 hendricks2 2017
    {
5826 terminx 2018
        case 1: return 1;
2019
        case -1: return 0;
5118 hendricks2 2020
    }
2021
 
5826 terminx 2022
    if ((unsigned) (pPlayer->knuckle_incs>>1) >= ARRAY_SIZE(knuckleFrames) || sprite[pPlayer->i].extra <= 0)
5118 hendricks2 2023
        return 0;
2024
 
5826 terminx 2025
    int const knuckleY   = (klabs(pPlayer->look_ang) / 9) - (pPlayer->hard_landing << 3);
2026
    int const knucklePal = P_GetHudPal(pPlayer);
5118 hendricks2 2027
 
7775 terminx 2028
    G_DrawTileScaled(160 + (fix16_to_int(g_player[screenpeek].input->q16avel) >> 5) - (pPlayer->look_ang >> 1),
6725 terminx 2029
                     knuckleY + 180 - (fix16_to_int(pPlayer->q16horiz - pPlayer->q16horizoff) >> 4),
5826 terminx 2030
                     CRACKKNUCKLES + knuckleFrames[pPlayer->knuckle_incs >> 1], knuckleShade, 4 + DRAWEAP_CENTER,
2031
                     knucklePal);
1209 terminx 2032
 
2033
    return 1;
2034
}
2035
 
3955 helixhorne 2036
#if !defined LUNATIC
2037
// Set C-CON's WEAPON and WORKSLIKE gamevars.
5826 terminx 2038
void P_SetWeaponGamevars(int playerNum, const DukePlayer_t * const pPlayer)
3413 helixhorne 2039
{