Subversion Repositories eduke32

Rev

Rev 8789 | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 8789 Rev 8791
1
//-------------------------------------------------------------------------
1
//-------------------------------------------------------------------------
2
/*
2
/*
3
Copyright (C) 2010 EDuke32 developers and contributors
3
Copyright (C) 2010 EDuke32 developers and contributors
4

4

5
This file is part of EDuke32.
5
This file is part of EDuke32.
6

6

7
EDuke32 is free software; you can redistribute it and/or
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
8
modify it under the terms of the GNU General Public License version 2
9
as published by the Free Software Foundation.
9
as published by the Free Software Foundation.
10

10

11
This program is distributed in the hope that it will be useful,
11
This program is distributed in the hope that it will be useful,
12
but WITHOUT ANY WARRANTY; without even the implied warranty of
12
but WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14

14

15
See the GNU General Public License for more details.
15
See the GNU General Public License for more details.
16

16

17
You should have received a copy of the GNU General Public License
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
18
along with this program; if not, write to the Free Software
19
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20
*/
20
*/
21
//-------------------------------------------------------------------------
21
//-------------------------------------------------------------------------
22
22
23
#include "duke3d.h"
23
#include "duke3d.h"
24
#include "demo.h"
24
#include "demo.h"
25
#include "enet.h"
25
#include "enet.h"
26
26
27
#ifdef __ANDROID__
27
#ifdef __ANDROID__
28
#include "android.h"
28
#include "android.h"
29
#endif
29
#endif
30
30
31
int32_t lastvisinc;
31
int32_t lastvisinc;
32
hudweapon_t hudweap;
32
hudweapon_t hudweap;
33
33
34
#ifdef SPLITSCREEN_MOD_HACKS
34
#ifdef SPLITSCREEN_MOD_HACKS
35
static int32_t g_snum;
35
static int32_t g_snum;
36
#endif
36
#endif
37
37
38
extern int32_t g_levelTextTime, ticrandomseed;
38
extern int32_t g_levelTextTime, ticrandomseed;
39
39
40
int32_t g_numObituaries = 0;
40
int32_t g_numObituaries = 0;
41
int32_t g_numSelfObituaries = 0;
41
int32_t g_numSelfObituaries = 0;
42
42
43
43
44
int const icon_to_inv[ICON_MAX] = { GET_FIRSTAID, GET_FIRSTAID, GET_STEROIDS, GET_HOLODUKE,
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 };
45
                                    GET_JETPACK,  GET_HEATS,    GET_SCUBA,    GET_BOOTS };
46
46
47
int const inv_to_icon[GET_MAX] = { ICON_STEROIDS, ICON_NONE,  ICON_SCUBA, ICON_HOLODUKE, ICON_JETPACK, ICON_NONE,
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 };
48
                                   ICON_NONE,     ICON_HEATS, ICON_NONE,  ICON_FIRSTAID, ICON_BOOTS };
49
49
50
void P_AddKills(DukePlayer_t * const pPlayer, uint16_t kills)
50
void P_AddKills(DukePlayer_t * const pPlayer, uint16_t kills)
51
{
51
{
52
    pPlayer->actors_killed += kills;
52
    pPlayer->actors_killed += kills;
53
}
53
}
54
54
55
void P_UpdateScreenPal(DukePlayer_t * const pPlayer)
55
void P_UpdateScreenPal(DukePlayer_t * const pPlayer)
56
{
56
{
57
    int       inWater       = 0;
57
    int       inWater       = 0;
58
    int const playerSectnum = pPlayer->cursectnum;
58
    int const playerSectnum = pPlayer->cursectnum;
59
59
60
    if (pPlayer->heat_on)
60
    if (pPlayer->heat_on)
61
        pPlayer->palette = SLIMEPAL;
61
        pPlayer->palette = SLIMEPAL;
62
    else if (playerSectnum < 0)
62
    else if (playerSectnum < 0)
63
        pPlayer->palette = BASEPAL;
63
        pPlayer->palette = BASEPAL;
64
    else if (sector[playerSectnum].ceilingpicnum >= FLOORSLIME && sector[playerSectnum].ceilingpicnum <= FLOORSLIME + 2)
64
    else if (sector[playerSectnum].ceilingpicnum >= FLOORSLIME && sector[playerSectnum].ceilingpicnum <= FLOORSLIME + 2)
65
    {
65
    {
66
        pPlayer->palette = SLIMEPAL;
66
        pPlayer->palette = SLIMEPAL;
67
        inWater          = 1;
67
        inWater          = 1;
68
    }
68
    }
69
    else
69
    else
70
    {
70
    {
71
        pPlayer->palette     = (sector[pPlayer->cursectnum].lotag == ST_2_UNDERWATER) ? WATERPAL : BASEPAL;
71
        pPlayer->palette     = (sector[pPlayer->cursectnum].lotag == ST_2_UNDERWATER) ? WATERPAL : BASEPAL;
72
        inWater              = 1;
72
        inWater              = 1;
73
    }
73
    }
74
74
75
    g_restorePalette = 1+inWater;
75
    g_restorePalette = 1+inWater;
76
}
76
}
77
77
78
static void P_IncurDamage(DukePlayer_t * const pPlayer)
78
static void P_IncurDamage(DukePlayer_t * const pPlayer)
79
{
79
{
80
    if (VM_OnEvent(EVENT_INCURDAMAGE, pPlayer->i, P_Get(pPlayer->i)) != 0)
80
    if (VM_OnEvent(EVENT_INCURDAMAGE, pPlayer->i, P_Get(pPlayer->i)) != 0)
81
        return;
81
        return;
82
82
83
    sprite[pPlayer->i].extra -= pPlayer->extra_extra8>>8;
83
    sprite[pPlayer->i].extra -= pPlayer->extra_extra8>>8;
84
84
85
    int playerDamage = sprite[pPlayer->i].extra - pPlayer->last_extra;
85
    int playerDamage = sprite[pPlayer->i].extra - pPlayer->last_extra;
86
86
87
    if (playerDamage >= 0)
87
    if (playerDamage >= 0)
88
        return;
88
        return;
89
89
90
    pPlayer->extra_extra8 = 0;
90
    pPlayer->extra_extra8 = 0;
91
91
92
    if (pPlayer->inv_amount[GET_SHIELD] > 0)
92
    if (pPlayer->inv_amount[GET_SHIELD] > 0)
93
    {
93
    {
94
        int const shieldDamage = playerDamage * (20 + (krand()%30)) / 100;
94
        int const shieldDamage = playerDamage * (20 + (krand()%30)) / 100;
95
95
96
        playerDamage                     -= shieldDamage;
96
        playerDamage                     -= shieldDamage;
97
        pPlayer->inv_amount[GET_SHIELD] += shieldDamage;
97
        pPlayer->inv_amount[GET_SHIELD] += shieldDamage;
98
98
99
        if (pPlayer->inv_amount[GET_SHIELD] < 0)
99
        if (pPlayer->inv_amount[GET_SHIELD] < 0)
100
        {
100
        {
101
            playerDamage += pPlayer->inv_amount[GET_SHIELD];
101
            playerDamage += pPlayer->inv_amount[GET_SHIELD];
102
            pPlayer->inv_amount[GET_SHIELD] = 0;
102
            pPlayer->inv_amount[GET_SHIELD] = 0;
103
        }
103
        }
104
    }
104
    }
105
105
106
    sprite[pPlayer->i].extra = pPlayer->last_extra + playerDamage;
106
    sprite[pPlayer->i].extra = pPlayer->last_extra + playerDamage;
107
}
107
}
108
108
109
void P_QuickKill(DukePlayer_t * const pPlayer)
109
void P_QuickKill(DukePlayer_t * const pPlayer)
110
{
110
{
111
    P_PalFrom(pPlayer, 48, 48,48,48);
111
    P_PalFrom(pPlayer, 48, 48,48,48);
112
112
113
    sprite[pPlayer->i].extra = 0;
113
    sprite[pPlayer->i].extra = 0;
114
    sprite[pPlayer->i].cstat |= 32768;
114
    sprite[pPlayer->i].cstat |= 32768;
115
115
116
#ifndef EDUKE32_STANDALONE
116
#ifndef EDUKE32_STANDALONE
117
    if (!FURY && ud.god == 0)
117
    if (!FURY && ud.god == 0)
118
        A_DoGuts(pPlayer->i,JIBS6,8);
118
        A_DoGuts(pPlayer->i,JIBS6,8);
119
#endif
119
#endif
120
}
120
}
121
121
122
static void Proj_DoWaterTracers(vec3_t startPos, vec3_t const *endPos, int n, int16_t sectNum)
122
static void Proj_DoWaterTracers(vec3_t startPos, vec3_t const *endPos, int n, int16_t sectNum)
123
{
123
{
124
    if ((klabs(startPos.x - endPos->x) + klabs(startPos.y - endPos->y)) < 3084)
124
    if ((klabs(startPos.x - endPos->x) + klabs(startPos.y - endPos->y)) < 3084)
125
        return;
125
        return;
126
126
127
    vec3_t const v_inc = { tabledivide32_noinline(endPos->x - startPos.x, n + 1), tabledivide32_noinline(endPos->y - startPos.y, n + 1),
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) };
128
                           tabledivide32_noinline(endPos->z - startPos.z, n + 1) };
129
129
130
    for (bssize_t i=n; i>0; i--)
130
    for (bssize_t i=n; i>0; i--)
131
    {
131
    {
132
        startPos.x += v_inc.x;
132
        startPos.x += v_inc.x;
133
        startPos.y += v_inc.y;
133
        startPos.y += v_inc.y;
134
        startPos.z += v_inc.z;
134
        startPos.z += v_inc.z;
135
135
136
        updatesector(startPos.x, startPos.y, &sectNum);
136
        updatesector(startPos.x, startPos.y, &sectNum);
137
137
138
        if (sectNum < 0)
138
        if (sectNum < 0)
139
            break;
139
            break;
140
140
141
        A_InsertSprite(sectNum, startPos.x, startPos.y, startPos.z, WATERBUBBLE, -32, 4 + (krand() & 3), 4 + (krand() & 3), krand() & 2047, 0, 0,
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);
142
                       g_player[0].ps->i, 5);
143
    }
143
    }
144
}
144
}
145
145
146
static inline projectile_t *Proj_GetProjectile(int tile)
146
static inline projectile_t *Proj_GetProjectile(int tile)
147
{
147
{
148
    return ((unsigned)tile < MAXTILES && g_tile[tile].proj) ? g_tile[tile].proj : &DefaultProjectile;
148
    return ((unsigned)tile < MAXTILES && g_tile[tile].proj) ? g_tile[tile].proj : &DefaultProjectile;
149
}
149
}
150
150
151
static void A_HitscanProjTrail(const vec3_t *startPos, const vec3_t *endPos, int projAng, int tileNum, int16_t sectNum)
151
static void A_HitscanProjTrail(const vec3_t *startPos, const vec3_t *endPos, int projAng, int tileNum, int16_t sectNum)
152
{
152
{
153
    const projectile_t *const pProj = Proj_GetProjectile(tileNum);
153
    const projectile_t *const pProj = Proj_GetProjectile(tileNum);
154
154
155
    vec3_t        spawnPos = { startPos->x + tabledivide32_noinline(sintable[(348 + projAng + 512) & 2047], pProj->offset),
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),
156
                               startPos->y + tabledivide32_noinline(sintable[(projAng + 348) & 2047], pProj->offset),
157
                               startPos->z + 1024 + (pProj->toffset << 8) };
157
                               startPos->z + 1024 + (pProj->toffset << 8) };
158
158
159
    int32_t      n         = ((FindDistance2D(spawnPos.x - endPos->x, spawnPos.y - endPos->y)) >> 8) + 1;
159
    int32_t      n         = ((FindDistance2D(spawnPos.x - endPos->x, spawnPos.y - endPos->y)) >> 8) + 1;
160
160
161
    vec3_t const increment = { tabledivide32_noinline((endPos->x - spawnPos.x), n),
161
    vec3_t const increment = { tabledivide32_noinline((endPos->x - spawnPos.x), n),
162
                               tabledivide32_noinline((endPos->y - spawnPos.y), n),
162
                               tabledivide32_noinline((endPos->y - spawnPos.y), n),
163
                               tabledivide32_noinline((endPos->z - spawnPos.z), n) };
163
                               tabledivide32_noinline((endPos->z - spawnPos.z), n) };
164
164
165
    spawnPos.x += increment.x >> 2;
165
    spawnPos.x += increment.x >> 2;
166
    spawnPos.y += increment.y >> 2;
166
    spawnPos.y += increment.y >> 2;
167
    spawnPos.z += increment.z >> 2;
167
    spawnPos.z += increment.z >> 2;
168
168
169
    int32_t j;
169
    int32_t j;
170
170
171
    for (bssize_t i = pProj->tnum; i > 0; --i)
171
    for (bssize_t i = pProj->tnum; i > 0; --i)
172
    {
172
    {
173
        spawnPos.x += increment.x;
173
        spawnPos.x += increment.x;
174
        spawnPos.y += increment.y;
174
        spawnPos.y += increment.y;
175
        spawnPos.z += increment.z;
175
        spawnPos.z += increment.z;
176
176
177
        updatesectorz(spawnPos.x, spawnPos.y, spawnPos.z, &sectNum);
177
        updatesectorz(spawnPos.x, spawnPos.y, spawnPos.z, &sectNum);
178
178
179
        if (sectNum < 0)
179
        if (sectNum < 0)
180
            break;
180
            break;
181
181
182
        getzsofslope(sectNum, spawnPos.x, spawnPos.y, &n, &j);
182
        getzsofslope(sectNum, spawnPos.x, spawnPos.y, &n, &j);
183
183
184
        if (spawnPos.z > j || spawnPos.z < n)
184
        if (spawnPos.z > j || spawnPos.z < n)
185
            break;
185
            break;
186
186
187
        j = A_InsertSprite(sectNum, spawnPos.x, spawnPos.y, spawnPos.z, pProj->trail, -32,
187
        j = A_InsertSprite(sectNum, spawnPos.x, spawnPos.y, spawnPos.z, pProj->trail, -32,
188
                           pProj->txrepeat, pProj->tyrepeat, projAng, 0, 0, g_player[0].ps->i, 0);
188
                           pProj->txrepeat, pProj->tyrepeat, projAng, 0, 0, g_player[0].ps->i, 0);
189
        changespritestat(j, STAT_ACTOR);
189
        changespritestat(j, STAT_ACTOR);
190
    }
190
    }
191
}
191
}
192
192
193
int32_t A_GetHitscanRange(int spriteNum)
193
int32_t A_GetHitscanRange(int spriteNum)
194
{
194
{
195
    int const zOffset = (PN(spriteNum) == APLAYER) ? PHEIGHT : 0;
195
    int const zOffset = (PN(spriteNum) == APLAYER) ? PHEIGHT : 0;
196
    hitdata_t hitData;
196
    hitdata_t hitData;
197
197
198
    SZ(spriteNum) -= zOffset;
198
    SZ(spriteNum) -= zOffset;
199
    hitscan((const vec3_t *)&sprite[spriteNum], SECT(spriteNum), sintable[(SA(spriteNum) + 512) & 2047],
199
    hitscan((const vec3_t *)&sprite[spriteNum], SECT(spriteNum), sintable[(SA(spriteNum) + 512) & 2047],
200
            sintable[SA(spriteNum) & 2047], 0, &hitData, CLIPMASK1);
200
            sintable[SA(spriteNum) & 2047], 0, &hitData, CLIPMASK1);
201
    SZ(spriteNum) += zOffset;
201
    SZ(spriteNum) += zOffset;
202
202
203
    return (FindDistance2D(hitData.pos.x - SX(spriteNum), hitData.pos.y - SY(spriteNum)));
203
    return (FindDistance2D(hitData.pos.x - SX(spriteNum), hitData.pos.y - SY(spriteNum)));
204
}
204
}
205
205
206
static int A_FindTargetSprite(const spritetype *pSprite, int projAng, int projecTile)
206
static int A_FindTargetSprite(const spritetype *pSprite, int projAng, int projecTile)
207
{
207
{
208
    static int const aimstats[] = {
208
    static int const aimstats[] = {
209
        STAT_PLAYER, STAT_DUMMYPLAYER, STAT_ACTOR, STAT_ZOMBIEACTOR
209
        STAT_PLAYER, STAT_DUMMYPLAYER, STAT_ACTOR, STAT_ZOMBIEACTOR
210
    };
210
    };
211
211
212
    int const playerNum = pSprite->picnum == APLAYER ? P_GetP(pSprite) : -1;
212
    int const playerNum = pSprite->picnum == APLAYER ? P_GetP(pSprite) : -1;
213
213
214
    if (playerNum != -1)
214
    if (playerNum != -1)
215
    {
215
    {
216
        if (!g_player[playerNum].ps->auto_aim)
216
        if (!g_player[playerNum].ps->auto_aim)
217
            return -1;
217
            return -1;
218
218
219
        if (g_player[playerNum].ps->auto_aim == 2)
219
        if (g_player[playerNum].ps->auto_aim == 2)
220
        {
220
        {
221
            if (A_CheckSpriteTileFlags(projecTile,SFLAG_PROJECTILE) && (Proj_GetProjectile(projecTile)->workslike & PROJECTILE_RPG))
221
            if (A_CheckSpriteTileFlags(projecTile,SFLAG_PROJECTILE) && (Proj_GetProjectile(projecTile)->workslike & PROJECTILE_RPG))
222
                return -1;
222
                return -1;
223
223
224
#ifndef EDUKE32_STANDALONE
224
#ifndef EDUKE32_STANDALONE
225
            if (!FURY)
225
            if (!FURY)
226
            {
226
            {
227
                switch (DYNAMICTILEMAP(projecTile))
227
                switch (DYNAMICTILEMAP(projecTile))
228
                {
228
                {
229
                    case TONGUE__STATIC:
229
                    case TONGUE__STATIC:
230
                    case FREEZEBLAST__STATIC:
230
                    case FREEZEBLAST__STATIC:
231
                    case SHRINKSPARK__STATIC:
231
                    case SHRINKSPARK__STATIC:
232
                    case SHRINKER__STATIC:
232
                    case SHRINKER__STATIC:
233
                    case RPG__STATIC:
233
                    case RPG__STATIC:
234
                    case FIRELASER__STATIC:
234
                    case FIRELASER__STATIC:
235
                    case SPIT__STATIC:
235
                    case SPIT__STATIC:
236
                    case COOLEXPLOSION1__STATIC:
236
                    case COOLEXPLOSION1__STATIC:
237
                        return -1;
237
                        return -1;
238
                    default:
238
                    default:
239
                        break;
239
                        break;
240
                }
240
                }
241
            }
241
            }
242
#endif
242
#endif
243
        }
243
        }
244
    }
244
    }
245
245
246
    int const spriteAng = pSprite->ang;
246
    int const spriteAng = pSprite->ang;
247
247
248
#ifndef EDUKE32_STANDALONE
248
#ifndef EDUKE32_STANDALONE
249
    int const isShrinker = (pSprite->picnum == APLAYER && PWEAPON(playerNum, g_player[playerNum].ps->curr_weapon, WorksLike) == SHRINKER_WEAPON);
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);
250
    int const isFreezer  = (pSprite->picnum == APLAYER && PWEAPON(playerNum, g_player[playerNum].ps->curr_weapon, WorksLike) == FREEZE_WEAPON);
251
#endif
251
#endif
252
252
253
    vec2_t const d1 = { sintable[(spriteAng + 512 - projAng) & 2047], sintable[(spriteAng - projAng) & 2047] };
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] };
254
    vec2_t const d2 = { sintable[(spriteAng + 512 + projAng) & 2047], sintable[(spriteAng + projAng) & 2047] };
255
    vec2_t const d3 = { sintable[(spriteAng + 512) & 2047], sintable[spriteAng & 2047] };
255
    vec2_t const d3 = { sintable[(spriteAng + 512) & 2047], sintable[spriteAng & 2047] };
256
256
257
    int lastDist   = INT32_MAX;
257
    int lastDist   = INT32_MAX;
258
    int bestSprite = -1;
258
    int bestSprite = -1;
259
259
260
    for (bssize_t k=0; k<4; k++)
260
    for (bssize_t k=0; k<4; k++)
261
    {
261
    {
262
        if (bestSprite >= 0)
262
        if (bestSprite >= 0)
263
            break;
263
            break;
264
264
265
        for (bssize_t spriteNum=headspritestat[aimstats[k]]; spriteNum >= 0; spriteNum=nextspritestat[spriteNum])
265
        for (bssize_t spriteNum=headspritestat[aimstats[k]]; spriteNum >= 0; spriteNum=nextspritestat[spriteNum])
266
        {
266
        {
267
            if ((sprite[spriteNum].xrepeat > 0 && sprite[spriteNum].extra >= 0 &&
267
            if ((sprite[spriteNum].xrepeat > 0 && sprite[spriteNum].extra >= 0 &&
268
                 (sprite[spriteNum].cstat & (257 + 32768)) == 257) &&
268
                 (sprite[spriteNum].cstat & (257 + 32768)) == 257) &&
269
                (A_CheckEnemySprite(&sprite[spriteNum]) || k < 2))
269
                (A_CheckEnemySprite(&sprite[spriteNum]) || k < 2))
270
            {
270
            {
271
                if (A_CheckEnemySprite(&sprite[spriteNum]) || PN(spriteNum) == APLAYER)
271
                if (A_CheckEnemySprite(&sprite[spriteNum]) || PN(spriteNum) == APLAYER)
272
                {
272
                {
273
                    if (PN(spriteNum) == APLAYER && pSprite->picnum == APLAYER && pSprite != &sprite[spriteNum] &&
273
                    if (PN(spriteNum) == APLAYER && pSprite->picnum == APLAYER && pSprite != &sprite[spriteNum] &&
274
                        (GTFLAGS(GAMETYPE_PLAYERSFRIENDLY) ||
274
                        (GTFLAGS(GAMETYPE_PLAYERSFRIENDLY) ||
275
                         (GTFLAGS(GAMETYPE_TDM) && g_player[P_Get(spriteNum)].ps->team == g_player[playerNum].ps->team)))
275
                         (GTFLAGS(GAMETYPE_TDM) && g_player[P_Get(spriteNum)].ps->team == g_player[playerNum].ps->team)))
276
                        continue;
276
                        continue;
277
277
278
#ifndef EDUKE32_STANDALONE
278
#ifndef EDUKE32_STANDALONE
279
                    if (!FURY && ((isShrinker && sprite[spriteNum].xrepeat < 30
279
                    if (!FURY && ((isShrinker && sprite[spriteNum].xrepeat < 30
280
                        && (PN(spriteNum) == SHARK || !(PN(spriteNum) >= GREENSLIME && PN(spriteNum) <= GREENSLIME + 7)))
280
                        && (PN(spriteNum) == SHARK || !(PN(spriteNum) >= GREENSLIME && PN(spriteNum) <= GREENSLIME + 7)))
281
                        || (isFreezer && sprite[spriteNum].pal == 1)))
281
                        || (isFreezer && sprite[spriteNum].pal == 1)))
282
                        continue;
282
                        continue;
283
#endif
283
#endif
284
                }
284
                }
285
285
286
                vec2_t const vd = { (SX(spriteNum) - pSprite->x), (SY(spriteNum) - pSprite->y) };
286
                vec2_t const vd = { (SX(spriteNum) - pSprite->x), (SY(spriteNum) - pSprite->y) };
287
287
288
                if ((d1.y * vd.x <= d1.x * vd.y) && (d2.y * vd.x >= d2.x * vd.y))
288
                if ((d1.y * vd.x <= d1.x * vd.y) && (d2.y * vd.x >= d2.x * vd.y))
289
                {
289
                {
290
                    int const spriteDist = mulscale14(d3.x, vd.x) + mulscale14(d3.y, vd.y);
290
                    int const spriteDist = mulscale14(d3.x, vd.x) + mulscale14(d3.y, vd.y);
291
291
292
                    if (spriteDist > 512 && spriteDist < lastDist)
292
                    if (spriteDist > 512 && spriteDist < lastDist)
293
                    {
293
                    {
294
                        int onScreen = 1;
294
                        int onScreen = 1;
295
295
296
                        if (pSprite->picnum == APLAYER)
296
                        if (pSprite->picnum == APLAYER)
297
                        {
297
                        {
298
                            auto const ps = g_player[P_GetP(pSprite)].ps;
298
                            auto const ps = g_player[P_GetP(pSprite)].ps;
299
                            onScreen = (klabs(scale(SZ(spriteNum)-pSprite->z,10,spriteDist)-fix16_to_int(ps->q16horiz+ps->q16horizoff-F16(100))) < 100);
299
                            onScreen = (klabs(scale(SZ(spriteNum)-pSprite->z,10,spriteDist)-fix16_to_int(ps->q16horiz+ps->q16horizoff-F16(100))) < 100);
300
                        }
300
                        }
301
301
302
#ifndef EDUKE32_STANDALONE
302
#ifndef EDUKE32_STANDALONE
303
                        int const zOffset = (!FURY && (PN(spriteNum) == ORGANTIC || PN(spriteNum) == ROTATEGUN)) ? 0 : ZOFFSET5;
303
                        int const zOffset = (!FURY && (PN(spriteNum) == ORGANTIC || PN(spriteNum) == ROTATEGUN)) ? 0 : ZOFFSET5;
304
#else
304
#else
305
                        int const zOffset = ZOFFSET5;
305
                        int const zOffset = ZOFFSET5;
306
#endif
306
#endif
307
                        int const canSee = cansee(SX(spriteNum), SY(spriteNum), SZ(spriteNum) - zOffset, SECT(spriteNum),
307
                        int const canSee = cansee(SX(spriteNum), SY(spriteNum), SZ(spriteNum) - zOffset, SECT(spriteNum),
308
                                                  pSprite->x, pSprite->y, pSprite->z - ZOFFSET5, pSprite->sectnum);
308
                                                  pSprite->x, pSprite->y, pSprite->z - ZOFFSET5, pSprite->sectnum);
309
309
310
                        if (onScreen && canSee)
310
                        if (onScreen && canSee)
311
                        {
311
                        {
312
                            lastDist   = spriteDist;
312
                            lastDist   = spriteDist;
313
                            bestSprite = spriteNum;
313
                            bestSprite = spriteNum;
314
                        }
314
                        }
315
                    }
315
                    }
316
                }
316
                }
317
            }
317
            }
318
        }
318
        }
319
    }
319
    }
320
320
321
    return bestSprite;
321
    return bestSprite;
322
}
322
}
323
323
324
static void A_SetHitData(int spriteNum, const hitdata_t *hitData)
324
static void A_SetHitData(int spriteNum, const hitdata_t *hitData)
325
{
325
{
326
    actor[spriteNum].t_data[6] = hitData->wall;
326
    actor[spriteNum].t_data[6] = hitData->wall;
327
    actor[spriteNum].t_data[7] = hitData->sect;
327
    actor[spriteNum].t_data[7] = hitData->sect;
328
    actor[spriteNum].t_data[8] = hitData->sprite;
328
    actor[spriteNum].t_data[8] = hitData->sprite;
329
}
329
}
330
330
331
#ifndef EDUKE32_STANDALONE
331
#ifndef EDUKE32_STANDALONE
332
static int CheckShootSwitchTile(int tileNum)
332
static int CheckShootSwitchTile(int tileNum)
333
{
333
{
334
    if (FURY)
334
    if (FURY)
335
        return 0;
335
        return 0;
336
336
337
    return tileNum == DIPSWITCH || tileNum == DIPSWITCH + 1 || tileNum == DIPSWITCH2 || tileNum == DIPSWITCH2 + 1 ||
337
    return tileNum == DIPSWITCH || tileNum == DIPSWITCH + 1 || tileNum == DIPSWITCH2 || tileNum == DIPSWITCH2 + 1 ||
338
           tileNum == DIPSWITCH3 || tileNum == DIPSWITCH3 + 1 || tileNum == HANDSWITCH || tileNum == HANDSWITCH + 1;
338
           tileNum == DIPSWITCH3 || tileNum == DIPSWITCH3 + 1 || tileNum == HANDSWITCH || tileNum == HANDSWITCH + 1;
339
}
339
}
340
#endif
340
#endif
341
341
342
static int32_t safeldist(int32_t spriteNum, const void *pSprite)
342
static int32_t safeldist(int32_t spriteNum, const void *pSprite)
343
{
343
{
344
    int32_t distance = ldist(&sprite[spriteNum], pSprite);
344
    int32_t distance = ldist(&sprite[spriteNum], pSprite);
345
    return distance ? distance : 1;
345
    return distance ? distance : 1;
346
}
346
}
347
347
348
// flags:
348
// flags:
349
//  1: do sprite center adjustment (cen-=(8<<8)) for GREENSLIME or ROTATEGUN
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)
350
//  2: do auto getangle only if not RECON (if clear, do unconditionally)
351
static int GetAutoAimAng(int spriteNum, int playerNum, int projecTile, int zAdjust, int aimFlags,
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)
352
                               const vec3_t *startPos, int projVel, int32_t *pZvel, int *pAng)
353
{
353
{
354
    int returnSprite = -1;
354
    int returnSprite = -1;
355
355
356
    Bassert((unsigned)playerNum < MAXPLAYERS);
356
    Bassert((unsigned)playerNum < MAXPLAYERS);
357
357
358
#ifdef LUNATIC
358
#ifdef LUNATIC
359
    g_player[playerNum].ps->autoaimang = g_player[playerNum].ps->auto_aim == 3 ? AUTO_AIM_ANGLE<<1 : AUTO_AIM_ANGLE;
359
    g_player[playerNum].ps->autoaimang = g_player[playerNum].ps->auto_aim == 3 ? AUTO_AIM_ANGLE<<1 : AUTO_AIM_ANGLE;
360
#else
360
#else
361
    Gv_SetVar(g_aimAngleVarID, g_player[playerNum].ps->auto_aim == 3 ? AUTO_AIM_ANGLE<<1 : AUTO_AIM_ANGLE, spriteNum, playerNum);
361
    Gv_SetVar(g_aimAngleVarID, g_player[playerNum].ps->auto_aim == 3 ? AUTO_AIM_ANGLE<<1 : AUTO_AIM_ANGLE, spriteNum, playerNum);
362
#endif
362
#endif
363
363
364
    VM_OnEvent(EVENT_GETAUTOAIMANGLE, spriteNum, playerNum);
364
    VM_OnEvent(EVENT_GETAUTOAIMANGLE, spriteNum, playerNum);
365
365
366
#ifdef LUNATIC
366
#ifdef LUNATIC
367
    int aimang = g_player[playerNum].ps->autoaimang;
367
    int aimang = g_player[playerNum].ps->autoaimang;
368
#else
368
#else
369
    int aimang = Gv_GetVar(g_aimAngleVarID, spriteNum, playerNum);
369
    int aimang = Gv_GetVar(g_aimAngleVarID, spriteNum, playerNum);
370
#endif
370
#endif
371
    if (aimang > 0)
371
    if (aimang > 0)
372
        returnSprite = A_FindTargetSprite(&sprite[spriteNum], aimang, projecTile);
372
        returnSprite = A_FindTargetSprite(&sprite[spriteNum], aimang, projecTile);
373
373
374
    if (returnSprite >= 0)
374
    if (returnSprite >= 0)
375
    {
375
    {
376
        auto const pSprite = (uspriteptr_t)&sprite[returnSprite];
376
        auto const pSprite = (uspriteptr_t)&sprite[returnSprite];
377
        int        zCenter = 2 * (pSprite->yrepeat * tilesiz[pSprite->picnum].y) + zAdjust;
377
        int        zCenter = 2 * (pSprite->yrepeat * tilesiz[pSprite->picnum].y) + zAdjust;
378
378
379
#ifndef EDUKE32_STANDALONE
379
#ifndef EDUKE32_STANDALONE
380
        if (!FURY && aimFlags &&
380
        if (!FURY && aimFlags &&
381
            ((pSprite->picnum >= GREENSLIME && pSprite->picnum <= GREENSLIME + 7) || pSprite->picnum == ROTATEGUN || pSprite->cstat & CSTAT_SPRITE_YCENTER))
381
            ((pSprite->picnum >= GREENSLIME && pSprite->picnum <= GREENSLIME + 7) || pSprite->picnum == ROTATEGUN || pSprite->cstat & CSTAT_SPRITE_YCENTER))
382
#else
382
#else
383
        if (aimFlags && pSprite->cstat & CSTAT_SPRITE_YCENTER)
383
        if (aimFlags && pSprite->cstat & CSTAT_SPRITE_YCENTER)
384
#endif
384
#endif
385
            zCenter -= ZOFFSET3;
385
            zCenter -= ZOFFSET3;
386
386
387
        int spriteDist = safeldist(g_player[playerNum].ps->i, &sprite[returnSprite]);
387
        int spriteDist = safeldist(g_player[playerNum].ps->i, &sprite[returnSprite]);
388
        *pZvel         = tabledivide32_noinline((pSprite->z - startPos->z - zCenter) * projVel, spriteDist);
388
        *pZvel         = tabledivide32_noinline((pSprite->z - startPos->z - zCenter) * projVel, spriteDist);
389
389
390
        if (!(aimFlags&2) || sprite[returnSprite].picnum != RECON)
390
        if (!(aimFlags&2) || sprite[returnSprite].picnum != RECON)
391
            *pAng = getangle(pSprite->x-startPos->x, pSprite->y-startPos->y);
391
            *pAng = getangle(pSprite->x-startPos->x, pSprite->y-startPos->y);
392
    }
392
    }
393
393
394
    return returnSprite;
394
    return returnSprite;
395
}
395
}
396
396
397
static void Proj_MaybeSpawn(int spriteNum, int projecTile, const hitdata_t *hitData)
397
static void Proj_MaybeSpawn(int spriteNum, int projecTile, const hitdata_t *hitData)
398
{
398
{
399
    // atwith < 0 is for hard-coded projectiles
399
    // atwith < 0 is for hard-coded projectiles
400
    projectile_t *const pProj      = Proj_GetProjectile(projecTile);
400
    projectile_t *const pProj      = Proj_GetProjectile(projecTile);
401
    int                 spawnTile  = projecTile < 0 ? -projecTile : pProj->spawns;
401
    int                 spawnTile  = projecTile < 0 ? -projecTile : pProj->spawns;
402
402
403
    if (spawnTile >= 0)
403
    if (spawnTile >= 0)
404
    {
404
    {
405
        int spawned = A_Spawn(spriteNum, spawnTile);
405
        int spawned = A_Spawn(spriteNum, spawnTile);
406
406
407
        if (projecTile >= 0)
407
        if (projecTile >= 0)
408
        {
408
        {
409
            if (pProj->sxrepeat > 4)
409
            if (pProj->sxrepeat > 4)
410
                sprite[spawned].xrepeat = pProj->sxrepeat;
410
                sprite[spawned].xrepeat = pProj->sxrepeat;
411
411
412
            if (pProj->syrepeat > 4)
412
            if (pProj->syrepeat > 4)
413
                sprite[spawned].yrepeat = pProj->syrepeat;
413
                sprite[spawned].yrepeat = pProj->syrepeat;
414
        }
414
        }
415
415
416
        A_SetHitData(spawned, hitData);
416
        A_SetHitData(spawned, hitData);
417
    }
417
    }
418
}
418
}
419
419
420
// <extra>: damage that this shotspark does
420
// <extra>: damage that this shotspark does
421
static int Proj_InsertShotspark(const hitdata_t *hitData, int spriteNum, int projecTile, int sparkSize, int sparkAng, int damage)
421
static int Proj_InsertShotspark(const hitdata_t *hitData, int spriteNum, int projecTile, int sparkSize, int sparkAng, int damage)
422
{
422
{
423
    int returnSprite = A_InsertSprite(hitData->sect, hitData->pos.x, hitData->pos.y, hitData->pos.z, SHOTSPARK1, -15,
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);
424
                                     sparkSize, sparkSize, sparkAng, 0, 0, spriteNum, 4);
425
425
426
    sprite[returnSprite].extra = damage;
426
    sprite[returnSprite].extra = damage;
427
    sprite[returnSprite].yvel  = projecTile;  // This is a hack to allow you to detect which weapon spawned a SHOTSPARK1
427
    sprite[returnSprite].yvel  = projecTile;  // This is a hack to allow you to detect which weapon spawned a SHOTSPARK1
428
428
429
    A_SetHitData(returnSprite, hitData);
429
    A_SetHitData(returnSprite, hitData);
430
430
431
    return returnSprite;
431
    return returnSprite;
432
}
432
}
433
433
434
int Proj_GetDamage(projectile_t const *pProj)
434
int Proj_GetDamage(projectile_t const *pProj)
435
{
435
{
436
    Bassert(pProj);
436
    Bassert(pProj);
437
437
438
    int damage = pProj->extra;
438
    int damage = pProj->extra;
439
439
440
    if (pProj->extra_rand > 0)
440
    if (pProj->extra_rand > 0)
441
        damage += (krand() % pProj->extra_rand);
441
        damage += (krand() % pProj->extra_rand);
442
442
443
    return damage;
443
    return damage;
444
}
444
}
445
445
446
static void Proj_MaybeAddSpread(int doSpread, int32_t *zvel, int *shootAng, int zRange, int angRange)
446
static void Proj_MaybeAddSpread(int doSpread, int32_t *zvel, int *shootAng, int zRange, int angRange)
447
{
447
{
448
    if (doSpread)
448
    if (doSpread)
449
    {
449
    {
450
        // Ranges <= 1 mean no spread at all. A range of 1 calls krand() though.
450
        // Ranges <= 1 mean no spread at all. A range of 1 calls krand() though.
451
        if (zRange > 0)
451
        if (zRange > 0)
452
            *zvel += (zRange >> 1) - krand() % zRange;
452
            *zvel += (zRange >> 1) - krand() % zRange;
453
453
454
        if (angRange > 0)
454
        if (angRange > 0)
455
            *shootAng += (angRange >> 1) - krand() % angRange;
455
            *shootAng += (angRange >> 1) - krand() % angRange;
456
    }
456
    }
457
}
457
}
458
458
459
static int g_overrideShootZvel = 0;  // a boolean
459
static int g_overrideShootZvel = 0;  // a boolean
460
static int g_shootZvel;  // the actual zvel if the above is !=0
460
static int g_shootZvel;  // the actual zvel if the above is !=0
461
461
462
static int A_GetShootZvel(int defaultZvel)
462
static int A_GetShootZvel(int defaultZvel)
463
{
463
{
464
    return g_overrideShootZvel ? g_shootZvel : defaultZvel;
464
    return g_overrideShootZvel ? g_shootZvel : defaultZvel;
465
}
465
}
466
466
467
// Prepare hitscan weapon fired from player p.
467
// Prepare hitscan weapon fired from player p.
468
static void P_PreFireHitscan(int spriteNum, int playerNum, int projecTile, vec3_t *srcVect, int32_t *zvel, int *shootAng,
468
static void P_PreFireHitscan(int spriteNum, int playerNum, int projecTile, vec3_t *srcVect, int32_t *zvel, int *shootAng,
469
                             int accurateAim, int doSpread)
469
                             int accurateAim, int doSpread)
470
{
470
{
471
    int angRange  = 32;
471
    int angRange  = 32;
472
    int zRange    = 256;
472
    int zRange    = 256;
473
    int aimSprite = GetAutoAimAng(spriteNum, playerNum, projecTile, 5 << 8, 0 + 1, srcVect, 256, zvel, shootAng);
473
    int aimSprite = GetAutoAimAng(spriteNum, playerNum, projecTile, 5 << 8, 0 + 1, srcVect, 256, zvel, shootAng);
474
474
475
    auto const pPlayer = g_player[playerNum].ps;
475
    auto const pPlayer = g_player[playerNum].ps;
476
476
477
#ifdef LUNATIC
477
#ifdef LUNATIC
478
    pPlayer->angrange = angRange;
478
    pPlayer->angrange = angRange;
479
    pPlayer->zrange = zRange;
479
    pPlayer->zrange = zRange;
480
#else
480
#else
481
    Gv_SetVar(g_angRangeVarID, angRange, spriteNum, playerNum);
481
    Gv_SetVar(g_angRangeVarID, angRange, spriteNum, playerNum);
482
    Gv_SetVar(g_zRangeVarID, zRange, spriteNum, playerNum);
482
    Gv_SetVar(g_zRangeVarID, zRange, spriteNum, playerNum);
483
#endif
483
#endif
484
484
485
    VM_OnEvent(EVENT_GETSHOTRANGE, spriteNum, playerNum);
485
    VM_OnEvent(EVENT_GETSHOTRANGE, spriteNum, playerNum);
486
486
487
#ifdef LUNATIC
487
#ifdef LUNATIC
488
    angRange = pPlayer->angrange;
488
    angRange = pPlayer->angrange;
489
    zRange   = pPlayer->zrange;
489
    zRange   = pPlayer->zrange;
490
#else
490
#else
491
    angRange = Gv_GetVar(g_angRangeVarID, spriteNum, playerNum);
491
    angRange = Gv_GetVar(g_angRangeVarID, spriteNum, playerNum);
492
    zRange   = Gv_GetVar(g_zRangeVarID, spriteNum, playerNum);
492
    zRange   = Gv_GetVar(g_zRangeVarID, spriteNum, playerNum);
493
#endif
493
#endif
494
494
495
    if (accurateAim)
495
    if (accurateAim)
496
    {
496
    {
497
        if (!pPlayer->auto_aim)
497
        if (!pPlayer->auto_aim)
498
        {
498
        {
499
            hitdata_t hitData;
499
            hitdata_t hitData;
500
500
501
            *zvel = A_GetShootZvel(fix16_to_int(F16(100)-pPlayer->q16horiz-pPlayer->q16horizoff)<<5);
501
            *zvel = A_GetShootZvel(fix16_to_int(F16(100)-pPlayer->q16horiz-pPlayer->q16horizoff)<<5);
502
502
503
            hitscan(srcVect, sprite[spriteNum].sectnum, sintable[(*shootAng + 512) & 2047],
503
            hitscan(srcVect, sprite[spriteNum].sectnum, sintable[(*shootAng + 512) & 2047],
504
                    sintable[*shootAng & 2047], *zvel << 6, &hitData, CLIPMASK1);
504
                    sintable[*shootAng & 2047], *zvel << 6, &hitData, CLIPMASK1);
505
505
506
            if (hitData.sprite != -1)
506
            if (hitData.sprite != -1)
507
            {
507
            {
508
                int const statNumMap = ((1 << STAT_ACTOR) | (1 << STAT_ZOMBIEACTOR) | (1 << STAT_PLAYER) | (1 << STAT_DUMMYPLAYER));
508
                int const statNumMap = ((1 << STAT_ACTOR) | (1 << STAT_ZOMBIEACTOR) | (1 << STAT_PLAYER) | (1 << STAT_DUMMYPLAYER));
509
                int const statNum    = sprite[hitData.sprite].statnum;
509
                int const statNum    = sprite[hitData.sprite].statnum;
510
510
511
                if ((unsigned)statNum <= 30 && (statNumMap & (1 << statNum)))
511
                if ((unsigned)statNum <= 30 && (statNumMap & (1 << statNum)))
512
                    aimSprite = hitData.sprite;
512
                    aimSprite = hitData.sprite;
513
            }
513
            }
514
        }
514
        }
515
515
516
        if (aimSprite == -1)
516
        if (aimSprite == -1)
517
            goto notarget;
517
            goto notarget;
518
    }
518
    }
519
    else
519
    else
520
    {
520
    {
521
        if (aimSprite == -1)  // no target
521
        if (aimSprite == -1)  // no target
522
        {
522
        {
523
notarget:
523
notarget:
524
            *zvel = fix16_to_int(F16(100)-pPlayer->q16horiz-pPlayer->q16horizoff)<<5;
524
            *zvel = fix16_to_int(F16(100)-pPlayer->q16horiz-pPlayer->q16horizoff)<<5;
525
        }
525
        }
526
526
527
        Proj_MaybeAddSpread(doSpread, zvel, shootAng, zRange, angRange);
527
        Proj_MaybeAddSpread(doSpread, zvel, shootAng, zRange, angRange);
528
    }
528
    }
529
529
530
    // ZOFFSET6 is added to this position at the same time as the player's pyoff in A_ShootWithZvel()
530
    // ZOFFSET6 is added to this position at the same time as the player's pyoff in A_ShootWithZvel()
531
    srcVect->z -= ZOFFSET6;
531
    srcVect->z -= ZOFFSET6;
532
}
532
}
533
533
534
// Hitscan weapon fired from actor (sprite s);
534
// Hitscan weapon fired from actor (sprite s);
535
static void A_PreFireHitscan(const spritetype *pSprite, vec3_t * const srcVect, int32_t * const zvel, int * const shootAng, int const doSpread)
535
static void A_PreFireHitscan(const spritetype *pSprite, vec3_t * const srcVect, int32_t * const zvel, int * const shootAng, int const doSpread)
536
{
536
{
537
    int const  playerNum  = A_FindPlayer(pSprite, NULL);
537
    int const  playerNum  = A_FindPlayer(pSprite, NULL);
538
    auto const pPlayer    = g_player[playerNum].ps;
538
    auto const pPlayer    = g_player[playerNum].ps;
539
    int const  playerDist = safeldist(pPlayer->i, pSprite);
539
    int const  playerDist = safeldist(pPlayer->i, pSprite);
540
540
541
    *zvel = tabledivide32_noinline((pPlayer->pos.z - srcVect->z) << 8, playerDist);
541
    *zvel = tabledivide32_noinline((pPlayer->pos.z - srcVect->z) << 8, playerDist);
542
542
543
    srcVect->z -= ZOFFSET6;
543
    srcVect->z -= ZOFFSET6;
544
544
545
    if (pSprite->picnum == BOSS1)
545
    if (pSprite->picnum == BOSS1)
546
        *shootAng = getangle(pPlayer->pos.x - srcVect->x, pPlayer->pos.y - srcVect->y);
546
        *shootAng = getangle(pPlayer->pos.x - srcVect->x, pPlayer->pos.y - srcVect->y);
547
547
548
    Proj_MaybeAddSpread(doSpread, zvel, shootAng, 256, 128 >> (uint8_t)(pSprite->picnum != BOSS1));
548
    Proj_MaybeAddSpread(doSpread, zvel, shootAng, 256, 128 >> (uint8_t)(pSprite->picnum != BOSS1));
549
}
549
}
550
550
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)
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)
552
{
552
{
553
    auto const pSprite = &sprite[spriteNum];
553
    auto const pSprite = &sprite[spriteNum];
554
554
555
    pSprite->cstat &= ~cstatmask;
555
    pSprite->cstat &= ~cstatmask;
556
    zvel = A_GetShootZvel(zvel);
556
    zvel = A_GetShootZvel(zvel);
557
    int16_t sectnum = pSprite->sectnum;
557
    int16_t sectnum = pSprite->sectnum;
558
    updatesector(srcVect->x, srcVect->y, &sectnum);
558
    updatesector(srcVect->x, srcVect->y, &sectnum);
559
    hitscan(srcVect, sectnum, sintable[(shootAng + 512) & 2047], sintable[shootAng & 2047], zvel << 6, hitData, CLIPMASK1);
559
    hitscan(srcVect, sectnum, sintable[(shootAng + 512) & 2047], sintable[shootAng & 2047], zvel << 6, hitData, CLIPMASK1);
560
    pSprite->cstat |= cstatmask;
560
    pSprite->cstat |= cstatmask;
561
561
562
    return (hitData->sect < 0);
562
    return (hitData->sect < 0);
563
}
563
}
564
564
565
static void Proj_DoRandDecalSize(int const spriteNum, int const projecTile)
565
static void Proj_DoRandDecalSize(int const spriteNum, int const projecTile)
566
{
566
{
567
    const projectile_t *const proj    = Proj_GetProjectile(projecTile);
567
    const projectile_t *const proj    = Proj_GetProjectile(projecTile);
568
    auto const         pSprite = &sprite[spriteNum];
568
    auto const         pSprite = &sprite[spriteNum];
569
569
570
    if (proj->workslike & PROJECTILE_RANDDECALSIZE)
570
    if (proj->workslike & PROJECTILE_RANDDECALSIZE)
571
        pSprite->xrepeat = pSprite->yrepeat = clamp((krand() & proj->xrepeat), pSprite->yrepeat, pSprite->xrepeat);
571
        pSprite->xrepeat = pSprite->yrepeat = clamp((krand() & proj->xrepeat), pSprite->yrepeat, pSprite->xrepeat);
572
    else
572
    else
573
    {
573
    {
574
        pSprite->xrepeat = proj->xrepeat;
574
        pSprite->xrepeat = proj->xrepeat;
575
        pSprite->yrepeat = proj->yrepeat;
575
        pSprite->yrepeat = proj->yrepeat;
576
    }
576
    }
577
}
577
}
578
578
579
static int SectorContainsSE13(int const sectNum)
579
static int SectorContainsSE13(int const sectNum)
580
{
580
{
581
    if (sectNum >= 0)
581
    if (sectNum >= 0)
582
    {
582
    {
583
        for (bssize_t SPRITES_OF_SECT(sectNum, i))
583
        for (bssize_t SPRITES_OF_SECT(sectNum, i))
584
        {
584
        {
585
            if (sprite[i].statnum == STAT_EFFECTOR && sprite[i].lotag == SE_13_EXPLOSIVE)
585
            if (sprite[i].statnum == STAT_EFFECTOR && sprite[i].lotag == SE_13_EXPLOSIVE)
586
                return 1;
586
                return 1;
587
        }
587
        }
588
    }
588
    }
589
    return 0;
589
    return 0;
590
}
590
}
591
591
592
// Maybe handle bit 2 (swap wall bottoms).
592
// Maybe handle bit 2 (swap wall bottoms).
593
// (in that case walltype *hitwal may be stale)
593
// (in that case walltype *hitwal may be stale)
594
static inline void HandleHitWall(hitdata_t *hitData)
594
static inline void HandleHitWall(hitdata_t *hitData)
595
{
595
{
596
    auto const hitWall = (uwallptr_t)&wall[hitData->wall];
596
    auto const hitWall = (uwallptr_t)&wall[hitData->wall];
597
597
598
    if ((hitWall->cstat & 2) && redwallp(hitWall) && (hitData->pos.z >= sector[hitWall->nextsector].floorz))
598
    if ((hitWall->cstat & 2) && redwallp(hitWall) && (hitData->pos.z >= sector[hitWall->nextsector].floorz))
599
        hitData->wall = hitWall->nextwall;
599
        hitData->wall = hitWall->nextwall;
600
}
600
}
601
601
602
// Maybe damage a ceiling or floor as the consequence of projectile impact.
602
// Maybe damage a ceiling or floor as the consequence of projectile impact.
603
// Returns 1 if projectile hit a parallaxed ceiling.
603
// Returns 1 if projectile hit a parallaxed ceiling.
604
// NOTE: Compare with Proj_MaybeDamageCF() in actors.c
604
// NOTE: Compare with Proj_MaybeDamageCF() in actors.c
605
static int Proj_MaybeDamageCF2(int const spriteNum, int const zvel, int const hitSect)
605
static int Proj_MaybeDamageCF2(int const spriteNum, int const zvel, int const hitSect)
606
{
606
{
607
    Bassert(hitSect >= 0);
607
    Bassert(hitSect >= 0);
608
608
609
    if (zvel < 0)
609
    if (zvel < 0)
610
    {
610
    {
611
        if (sector[hitSect].ceilingstat&1)
611
        if (sector[hitSect].ceilingstat&1)
612
            return 1;
612
            return 1;
613
613
614
        Sect_DamageCeiling(spriteNum, hitSect);
614
        Sect_DamageCeiling(spriteNum, hitSect);
615
    }
615
    }
616
    else if (zvel > 0)
616
    else if (zvel > 0)
617
    {
617
    {
618
        if (sector[hitSect].floorstat&1)
618
        if (sector[hitSect].floorstat&1)
619
        {
619
        {
620
            // Keep original Duke3D behavior: pass projectiles through
620
            // Keep original Duke3D behavior: pass projectiles through
621
            // parallaxed ceilings, but NOT through such floors.
621
            // parallaxed ceilings, but NOT through such floors.
622
            return 0;
622
            return 0;
623
        }
623
        }
624
624
625
        Sect_DamageFloor(spriteNum, hitSect);
625
        Sect_DamageFloor(spriteNum, hitSect);
626
    }
626
    }
627
627
628
    return 0;
628
    return 0;
629
}
629
}
630
630
631
// Finish shooting hitscan weapon from player <p>. <k> is the inserted SHOTSPARK1.
631
// Finish shooting hitscan weapon from player <p>. <k> is the inserted SHOTSPARK1.
632
// * <spawnObject> is passed to Proj_MaybeSpawn()
632
// * <spawnObject> is passed to Proj_MaybeSpawn()
633
// * <decalTile> and <wallDamage> are for wall impact
633
// * <decalTile> and <wallDamage> are for wall impact
634
// * <wallDamage> is passed to A_DamageWall()
634
// * <wallDamage> is passed to A_DamageWall()
635
// * <decalFlags> is for decals upon wall impact:
635
// * <decalFlags> is for decals upon wall impact:
636
//    1: handle random decal size (tile <atwith>)
636
//    1: handle random decal size (tile <atwith>)
637
//    2: set cstat to wall-aligned + random x/y flip
637
//    2: set cstat to wall-aligned + random x/y flip
638
//
638
//
639
// TODO: maybe split into 3 cases (hit neither wall nor sprite, hit sprite, hit wall)?
639
// TODO: maybe split into 3 cases (hit neither wall nor sprite, hit sprite, hit wall)?
640
static int P_PostFireHitscan(int playerNum, int const spriteNum, hitdata_t *const hitData, int const spriteOwner,
640
static int P_PostFireHitscan(int playerNum, int const spriteNum, hitdata_t *const hitData, int const spriteOwner,
641
                             int const projecTile, int const zvel, int const spawnTile, int const decalTile, int const wallDamage,
641
                             int const projecTile, int const zvel, int const spawnTile, int const decalTile, int const wallDamage,
642
                             int const decalFlags)
642
                             int const decalFlags)
643
{
643
{
644
#ifdef EDUKE32_STANDALONE
644
#ifdef EDUKE32_STANDALONE
645
    UNREFERENCED_PARAMETER(playerNum);
645
    UNREFERENCED_PARAMETER(playerNum);
646
#endif
646
#endif
647
    if (hitData->wall == -1 && hitData->sprite == -1)
647
    if (hitData->wall == -1 && hitData->sprite == -1)
648
    {
648
    {
649
        if (Proj_MaybeDamageCF2(spriteNum, zvel, hitData->sect))
649
        if (Proj_MaybeDamageCF2(spriteNum, zvel, hitData->sect))
650
        {
650
        {
651
            sprite[spriteNum].xrepeat = 0;
651
            sprite[spriteNum].xrepeat = 0;
652
            sprite[spriteNum].yrepeat = 0;
652
            sprite[spriteNum].yrepeat = 0;
653
            return -1;
653
            return -1;
654
        }
654
        }
655
655
656
        Proj_MaybeSpawn(spriteNum, spawnTile, hitData);
656
        Proj_MaybeSpawn(spriteNum, spawnTile, hitData);
657
    }
657
    }
658
    else if (hitData->sprite >= 0)
658
    else if (hitData->sprite >= 0)
659
    {
659
    {
660
        A_DamageObject(hitData->sprite, spriteNum);
660
        A_DamageObject(hitData->sprite, spriteNum);
661
661
662
        if (!FURY && sprite[hitData->sprite].picnum == APLAYER &&
662
        if (!FURY && sprite[hitData->sprite].picnum == APLAYER &&
663
            (ud.ffire == 1 || (!GTFLAGS(GAMETYPE_PLAYERSFRIENDLY) && GTFLAGS(GAMETYPE_TDM) &&
663
            (ud.ffire == 1 || (!GTFLAGS(GAMETYPE_PLAYERSFRIENDLY) && GTFLAGS(GAMETYPE_TDM) &&
664
                               g_player[P_Get(hitData->sprite)].ps->team != g_player[P_Get(spriteOwner)].ps->team)))
664
                               g_player[P_Get(hitData->sprite)].ps->team != g_player[P_Get(spriteOwner)].ps->team)))
665
        {
665
        {
666
#ifndef EDUKE32_STANDALONE
666
#ifndef EDUKE32_STANDALONE
667
            int jibSprite = A_Spawn(spriteNum, JIBS6);
667
            int jibSprite = A_Spawn(spriteNum, JIBS6);
668
668
669
            sprite[spriteNum].xrepeat = sprite[spriteNum].yrepeat = 0;
669
            sprite[spriteNum].xrepeat = sprite[spriteNum].yrepeat = 0;
670
            sprite[jibSprite].z += ZOFFSET6;
670
            sprite[jibSprite].z += ZOFFSET6;
671
            sprite[jibSprite].xvel    = 16;
671
            sprite[jibSprite].xvel    = 16;
672
            sprite[jibSprite].xrepeat = sprite[jibSprite].yrepeat = 24;
672
            sprite[jibSprite].xrepeat = sprite[jibSprite].yrepeat = 24;
673
            sprite[jibSprite].ang += 64 - (krand() & 127);
673
            sprite[jibSprite].ang += 64 - (krand() & 127);
674
#endif
674
#endif
675
        }
675
        }
676
        else
676
        else
677
        {
677
        {
678
            Proj_MaybeSpawn(spriteNum, spawnTile, hitData);
678
            Proj_MaybeSpawn(spriteNum, spawnTile, hitData);
679
        }
679
        }
680
#ifndef EDUKE32_STANDALONE
680
#ifndef EDUKE32_STANDALONE
681
        if (!FURY && playerNum >= 0 && CheckShootSwitchTile(sprite[hitData->sprite].picnum))
681
        if (!FURY && playerNum >= 0 && CheckShootSwitchTile(sprite[hitData->sprite].picnum))
682
        {
682
        {
683
            P_ActivateSwitch(playerNum, hitData->sprite, 1);
683
            P_ActivateSwitch(playerNum, hitData->sprite, 1);
684
            return -1;
684
            return -1;
685
        }
685
        }
686
#endif
686
#endif
687
    }
687
    }
688
    else if (hitData->wall >= 0)
688
    else if (hitData->wall >= 0)
689
    {
689
    {
690
        auto const hitWall = (uwallptr_t)&wall[hitData->wall];
690
        auto const hitWall = (uwallptr_t)&wall[hitData->wall];
691
691
692
        Proj_MaybeSpawn(spriteNum, spawnTile, hitData);
692
        Proj_MaybeSpawn(spriteNum, spawnTile, hitData);
693
693
694
        if (CheckDoorTile(hitWall->picnum) == 1)
694
        if (CheckDoorTile(hitWall->picnum) == 1)
695
            goto SKIPBULLETHOLE;
695
            goto SKIPBULLETHOLE;
696
696
697
#ifndef EDUKE32_STANDALONE
697
#ifndef EDUKE32_STANDALONE
698
        if (!FURY && playerNum >= 0 && CheckShootSwitchTile(hitWall->picnum))
698
        if (!FURY && playerNum >= 0 && CheckShootSwitchTile(hitWall->picnum))
699
        {
699
        {
700
            P_ActivateSwitch(playerNum, hitData->wall, 0);
700
            P_ActivateSwitch(playerNum, hitData->wall, 0);
701
            return -1;
701
            return -1;
702
        }
702
        }
703
#endif
703
#endif
704
704
705
        if (hitWall->hitag != 0 || (hitWall->nextwall >= 0 && wall[hitWall->nextwall].hitag != 0))
705
        if (hitWall->hitag != 0 || (hitWall->nextwall >= 0 && wall[hitWall->nextwall].hitag != 0))
706
            goto SKIPBULLETHOLE;
706
            goto SKIPBULLETHOLE;
707
707
708
        if ((hitData->sect >= 0 && sector[hitData->sect].lotag == 0) &&
708
        if ((hitData->sect >= 0 && sector[hitData->sect].lotag == 0) &&
709
            (hitWall->overpicnum != BIGFORCE && (hitWall->cstat & 16) == 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)))
710
            ((hitWall->nextsector >= 0 && sector[hitWall->nextsector].lotag == 0) || (hitWall->nextsector == -1 && sector[hitData->sect].lotag == 0)))
711
        {
711
        {
712
            int decalSprite;
712
            int decalSprite;
713
713
714
            if (SectorContainsSE13(hitWall->nextsector))
714
            if (SectorContainsSE13(hitWall->nextsector))
715
                goto SKIPBULLETHOLE;
715
                goto SKIPBULLETHOLE;
716
716
717
            for (SPRITES_OF(STAT_MISC, decalSprite))
717
            for (SPRITES_OF(STAT_MISC, decalSprite))
718
                if (sprite[decalSprite].picnum == decalTile && dist(&sprite[decalSprite], &sprite[spriteNum]) < (12 + (krand() & 7)))
718
                if (sprite[decalSprite].picnum == decalTile && dist(&sprite[decalSprite], &sprite[spriteNum]) < (12 + (krand() & 7)))
719
                    goto SKIPBULLETHOLE;
719
                    goto SKIPBULLETHOLE;
720
720
721
            if (decalTile >= 0)
721
            if (decalTile >= 0)
722
            {
722
            {
723
                decalSprite = A_Spawn(spriteNum, decalTile);
723
                decalSprite = A_Spawn(spriteNum, decalTile);
724
724
725
                auto const decal = &sprite[decalSprite];
725
                auto const decal = &sprite[decalSprite];
726
726
727
                A_SetHitData(decalSprite, hitData);
727
                A_SetHitData(decalSprite, hitData);
728
728
729
                if (!A_CheckSpriteFlags(decalSprite, SFLAG_DECAL))
729
                if (!A_CheckSpriteFlags(decalSprite, SFLAG_DECAL))
730
                    actor[decalSprite].flags |= SFLAG_DECAL;
730
                    actor[decalSprite].flags |= SFLAG_DECAL;
731
731
732
                int32_t diffZ;
732
                int32_t diffZ;
733
                spriteheightofs(decalSprite, &diffZ, 0);
733
                spriteheightofs(decalSprite, &diffZ, 0);
734
734
735
                decal->z += diffZ >> 1;
735
                decal->z += diffZ >> 1;
736
                decal->ang = (getangle(hitWall->x - wall[hitWall->point2].x, hitWall->y - wall[hitWall->point2].y) + 1536) & 2047;
736
                decal->ang = (getangle(hitWall->x - wall[hitWall->point2].x, hitWall->y - wall[hitWall->point2].y) + 1536) & 2047;
737
737
738
                if (decalFlags & 1)
738
                if (decalFlags & 1)
739
                    Proj_DoRandDecalSize(decalSprite, projecTile);
739
                    Proj_DoRandDecalSize(decalSprite, projecTile);
740
740
741
                if (decalFlags & 2)
741
                if (decalFlags & 2)
742
                    decal->cstat = 16 + (krand() & (8 + 4));
742
                    decal->cstat = 16 + (krand() & (8 + 4));
743
743
744
                A_SetSprite(decalSprite, CLIPMASK0);
744
                A_SetSprite(decalSprite, CLIPMASK0);
745
745
746
                // BULLETHOLE already adds itself to the deletion queue in
746
                // BULLETHOLE already adds itself to the deletion queue in
747
                // A_Spawn(). However, some other tiles do as well.
747
                // A_Spawn(). However, some other tiles do as well.
748
                if (decalTile != BULLETHOLE)
748
                if (decalTile != BULLETHOLE)
749
                    A_AddToDeleteQueue(decalSprite);
749
                    A_AddToDeleteQueue(decalSprite);
750
            }
750
            }
751
        }
751
        }
752
752
753
SKIPBULLETHOLE:
753
SKIPBULLETHOLE:
754
        HandleHitWall(hitData);
754
        HandleHitWall(hitData);
755
        A_DamageWall(spriteNum, hitData->wall, hitData->pos, wallDamage);
755
        A_DamageWall(spriteNum, hitData->wall, hitData->pos, wallDamage);
756
    }
756
    }
757
757
758
    return 0;
758
    return 0;
759
}
759
}
760
760
761
// Finish shooting hitscan weapon from actor (sprite <i>).
761
// Finish shooting hitscan weapon from actor (sprite <i>).
762
static int A_PostFireHitscan(const hitdata_t *hitData, int const spriteNum, int const projecTile, int const zvel, int const shootAng,
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)
763
                             int const extra, int const spawnTile, int const wallDamage)
764
{
764
{
765
    int const returnSprite = Proj_InsertShotspark(hitData, spriteNum, projecTile, 24, shootAng, extra);
765
    int const returnSprite = Proj_InsertShotspark(hitData, spriteNum, projecTile, 24, shootAng, extra);
766
766
767
    if (hitData->sprite >= 0)
767
    if (hitData->sprite >= 0)
768
    {
768
    {
769
        A_DamageObject(hitData->sprite, returnSprite);
769
        A_DamageObject(hitData->sprite, returnSprite);
770
770
771
        if (sprite[hitData->sprite].picnum != APLAYER)
771
        if (sprite[hitData->sprite].picnum != APLAYER)
772
            Proj_MaybeSpawn(returnSprite, spawnTile, hitData);
772
            Proj_MaybeSpawn(returnSprite, spawnTile, hitData);
773
        else
773
        else
774
            sprite[returnSprite].xrepeat = sprite[returnSprite].yrepeat = 0;
774
            sprite[returnSprite].xrepeat = sprite[returnSprite].yrepeat = 0;
775
    }
775
    }
776
    else if (hitData->wall >= 0)
776
    else if (hitData->wall >= 0)
777
    {
777
    {
778
        A_DamageWall(returnSprite, hitData->wall, hitData->pos, wallDamage);
778
        A_DamageWall(returnSprite, hitData->wall, hitData->pos, wallDamage);
779
        Proj_MaybeSpawn(returnSprite, spawnTile, hitData);
779
        Proj_MaybeSpawn(returnSprite, spawnTile, hitData);
780
    }
780
    }
781
    else
781
    else
782
    {
782
    {
783
        if (Proj_MaybeDamageCF2(returnSprite, zvel, hitData->sect))
783
        if (Proj_MaybeDamageCF2(returnSprite, zvel, hitData->sect))
784
        {
784
        {
785
            sprite[returnSprite].xrepeat = 0;
785
            sprite[returnSprite].xrepeat = 0;
786
            sprite[returnSprite].yrepeat = 0;
786
            sprite[returnSprite].yrepeat = 0;
787
        }
787
        }
788
        else Proj_MaybeSpawn(returnSprite, spawnTile, hitData);
788
        else Proj_MaybeSpawn(returnSprite, spawnTile, hitData);
789
    }
789
    }
790
790
791
    return returnSprite;
791
    return returnSprite;
792
}
792
}
793
793
794
// Common "spawn blood?" predicate.
794
// Common "spawn blood?" predicate.
795
// minzdiff: minimal "step" height for blood to be spawned
795
// minzdiff: minimal "step" height for blood to be spawned
796
static int Proj_CheckBlood(vec3_t const *const srcVect, hitdata_t const *const hitData, int const bloodRange, int const minZdiff)
796
static int Proj_CheckBlood(vec3_t const *const srcVect, hitdata_t const *const hitData, int const bloodRange, int const minZdiff)
797
{
797
{
798
    if (hitData->wall < 0 || hitData->sect < 0)
798
    if (hitData->wall < 0 || hitData->sect < 0)
799
        return 0;
799
        return 0;
800
800
801
    auto const hitWall = (uwallptr_t)&wall[hitData->wall];
801
    auto const hitWall = (uwallptr_t)&wall[hitData->wall];
802
802
803
    if ((FindDistance2D(srcVect->x - hitData->pos.x, srcVect->y - hitData->pos.y) < bloodRange)
803
    if ((FindDistance2D(srcVect->x - hitData->pos.x, srcVect->y - hitData->pos.y) < bloodRange)
804
        && (hitWall->overpicnum != BIGFORCE && (hitWall->cstat & 16) == 0)
804
        && (hitWall->overpicnum != BIGFORCE && (hitWall->cstat & 16) == 0)
805
        && (sector[hitData->sect].lotag == 0)
805
        && (sector[hitData->sect].lotag == 0)
806
        && (hitWall->nextsector < 0 || (sector[hitWall->nextsector].lotag == 0 && 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)))
807
                                        && sector[hitData->sect].floorz - sector[hitWall->nextsector].floorz > minZdiff)))
808
        return 1;
808
        return 1;
809
809
810
    return 0;
810
    return 0;
811
}
811
}
812
812
813
static void Proj_HandleKnee(hitdata_t *const hitData, int const spriteNum, int const playerNum, int const projecTile, int const shootAng,
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,
814
                            const projectile_t *const proj, int const inserttile, int const randomDamage, int const spawnTile,
815
                            int const soundNum)
815
                            int const soundNum)
816
{
816
{
817
    auto const pPlayer = playerNum >= 0 ? g_player[playerNum].ps : NULL;
817
    auto const pPlayer = playerNum >= 0 ? g_player[playerNum].ps : NULL;
818
818
819
    int kneeSprite = A_InsertSprite(hitData->sect,hitData->pos.x,hitData->pos.y,hitData->pos.z,
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);
820
                                    inserttile,-15,0,0,shootAng,32,0,spriteNum,4);
821
821
822
    if (proj != NULL)
822
    if (proj != NULL)
823
    {
823
    {
824
        // Custom projectiles.
824
        // Custom projectiles.
825
        SpriteProjectile[kneeSprite].workslike = Proj_GetProjectile(sprite[kneeSprite].picnum)->workslike;
825
        SpriteProjectile[kneeSprite].workslike = Proj_GetProjectile(sprite[kneeSprite].picnum)->workslike;
826
        sprite[kneeSprite].extra = proj->extra;
826
        sprite[kneeSprite].extra = proj->extra;
827
    }
827
    }
828
828
829
    if (randomDamage > 0)
829
    if (randomDamage > 0)
830
        sprite[kneeSprite].extra += (krand()&randomDamage);
830
        sprite[kneeSprite].extra += (krand()&randomDamage);
831
831
832
    if (playerNum >= 0)
832
    if (playerNum >= 0)
833
    {
833
    {
834
        if (spawnTile >= 0)
834
        if (spawnTile >= 0)
835
        {
835
        {
836
            int k = A_Spawn(kneeSprite, spawnTile);
836
            int k = A_Spawn(kneeSprite, spawnTile);
837
            sprite[k].z -= ZOFFSET3;
837
            sprite[k].z -= ZOFFSET3;
838
            A_SetHitData(k, hitData);
838
            A_SetHitData(k, hitData);
839
        }
839
        }
840
840
841
        if (soundNum >= 0)
841
        if (soundNum >= 0)
842
            A_PlaySound(soundNum, kneeSprite);
842
            A_PlaySound(soundNum, kneeSprite);
843
    }
843
    }
844
844
845
    if (pPlayer != NULL && pPlayer->inv_amount[GET_STEROIDS] > 0 && pPlayer->inv_amount[GET_STEROIDS] < 400)
845
    if (pPlayer != NULL && pPlayer->inv_amount[GET_STEROIDS] > 0 && pPlayer->inv_amount[GET_STEROIDS] < 400)
846
        sprite[kneeSprite].extra += (pPlayer->max_player_health>>2);
846
        sprite[kneeSprite].extra += (pPlayer->max_player_health>>2);
847
847
848
    if (hitData->sprite >= 0 && sprite[hitData->sprite].picnum != ACCESSSWITCH && sprite[hitData->sprite].picnum != ACCESSSWITCH2)
848
    if (hitData->sprite >= 0 && sprite[hitData->sprite].picnum != ACCESSSWITCH && sprite[hitData->sprite].picnum != ACCESSSWITCH2)
849
    {
849
    {
850
        A_DamageObject(hitData->sprite, kneeSprite);
850
        A_DamageObject(hitData->sprite, kneeSprite);
851
        if (playerNum >= 0)
851
        if (playerNum >= 0)
852
            P_ActivateSwitch(playerNum, hitData->sprite,1);
852
            P_ActivateSwitch(playerNum, hitData->sprite,1);
853
    }
853
    }
854
    else if (hitData->wall >= 0)
854
    else if (hitData->wall >= 0)
855
    {
855
    {
856
        HandleHitWall(hitData);
856
        HandleHitWall(hitData);
857
857
858
        if (wall[hitData->wall].picnum != ACCESSSWITCH && wall[hitData->wall].picnum != ACCESSSWITCH2)
858
        if (wall[hitData->wall].picnum != ACCESSSWITCH && wall[hitData->wall].picnum != ACCESSSWITCH2)
859
        {
859
        {
860
            A_DamageWall(kneeSprite, hitData->wall, hitData->pos, projecTile);
860
            A_DamageWall(kneeSprite, hitData->wall, hitData->pos, projecTile);
861
            if (playerNum >= 0)
861
            if (playerNum >= 0)
862
                P_ActivateSwitch(playerNum, hitData->wall,0);
862
                P_ActivateSwitch(playerNum, hitData->wall,0);
863
        }
863
        }
864
    }
864
    }
865
}
865
}
866
866
867
#define MinibossScale(i, s) (((s)*sprite[i].yrepeat)/80)
867
#define MinibossScale(i, s) (((s)*sprite[i].yrepeat)/80)
868
868
869
static int A_ShootCustom(int const spriteNum, int const projecTile, int shootAng, vec3_t * const startPos)
869
static int A_ShootCustom(int const spriteNum, int const projecTile, int shootAng, vec3_t * const startPos)
870
{
870
{
871
    /* Custom projectiles */
871
    /* Custom projectiles */
872
    hitdata_t           hitData;
872
    hitdata_t           hitData;
873
    projectile_t *const pProj     = Proj_GetProjectile(projecTile);
873
    projectile_t *const pProj     = Proj_GetProjectile(projecTile);
874
    auto const   pSprite   = &sprite[spriteNum];
874
    auto const   pSprite   = &sprite[spriteNum];
875
    int const           playerNum = (pSprite->picnum == APLAYER) ? P_GetP(pSprite) : -1;
875
    int const           playerNum = (pSprite->picnum == APLAYER) ? P_GetP(pSprite) : -1;
876
    auto const pPlayer   = playerNum >= 0 ? g_player[playerNum].ps : NULL;
876
    auto const pPlayer   = playerNum >= 0 ? g_player[playerNum].ps : NULL;
877
877
878
#ifdef POLYMER
878
#ifdef POLYMER
879
    if (videoGetRenderMode() == REND_POLYMER && pProj->flashcolor)
879
    if (videoGetRenderMode() == REND_POLYMER && pProj->flashcolor)
880
    {
880
    {
881
        int32_t x = ((sintable[(pSprite->ang + 512) & 2047]) >> 7), y = ((sintable[(pSprite->ang) & 2047]) >> 7);
881
        int32_t x = ((sintable[(pSprite->ang + 512) & 2047]) >> 7), y = ((sintable[(pSprite->ang) & 2047]) >> 7);
882
882
883
        pSprite->x += x;
883
        pSprite->x += x;
884
        pSprite->y += y;
884
        pSprite->y += y;
885
        G_AddGameLight(0, spriteNum, PHEIGHT, 8192, pProj->flashcolor, PR_LIGHT_PRIO_MAX_GAME);
885
        G_AddGameLight(0, spriteNum, PHEIGHT, 8192, pProj->flashcolor, PR_LIGHT_PRIO_MAX_GAME);
886
        actor[spriteNum].lightcount = 2;
886
        actor[spriteNum].lightcount = 2;
887
        pSprite->x -= x;
887
        pSprite->x -= x;
888
        pSprite->y -= y;
888
        pSprite->y -= y;
889
    }
889
    }
890
#endif // POLYMER
890
#endif // POLYMER
891
891
892
    if (pProj->offset == 0)
892
    if (pProj->offset == 0)
893
        pProj->offset = 1;
893
        pProj->offset = 1;
894
894
895
    int     otherSprite = -1;
895
    int     otherSprite = -1;
896
    int32_t zvel = 0;
896
    int32_t zvel = 0;
897
897
898
    switch (pProj->workslike & PROJECTILE_TYPE_MASK)
898
    switch (pProj->workslike & PROJECTILE_TYPE_MASK)
899
    {
899
    {
900
    case PROJECTILE_HITSCAN:
900
    case PROJECTILE_HITSCAN:
901
        if (!(pProj->workslike & PROJECTILE_NOSETOWNERSHADE) && pSprite->extra >= 0)
901
        if (!(pProj->workslike & PROJECTILE_NOSETOWNERSHADE) && pSprite->extra >= 0)
902
            pSprite->shade = pProj->shade;
902
            pSprite->shade = pProj->shade;
903
903
904
        if (playerNum >= 0)
904
        if (playerNum >= 0)
905
            P_PreFireHitscan(spriteNum, playerNum, projecTile, startPos, &zvel, &shootAng,
905
            P_PreFireHitscan(spriteNum, playerNum, projecTile, startPos, &zvel, &shootAng,
906
                             pProj->workslike & PROJECTILE_ACCURATE_AUTOAIM, !(pProj->workslike & PROJECTILE_ACCURATE));
906
                             pProj->workslike & PROJECTILE_ACCURATE_AUTOAIM, !(pProj->workslike & PROJECTILE_ACCURATE));
907
        else
907
        else
908
            A_PreFireHitscan(pSprite, startPos, &zvel, &shootAng, !(pProj->workslike & PROJECTILE_ACCURATE));
908
            A_PreFireHitscan(pSprite, startPos, &zvel, &shootAng, !(pProj->workslike & PROJECTILE_ACCURATE));
909
909
910
        if (Proj_DoHitscan(spriteNum, (pProj->cstat >= 0) ? pProj->cstat : 256 + 1, startPos, zvel, shootAng, &hitData))
910
        if (Proj_DoHitscan(spriteNum, (pProj->cstat >= 0) ? pProj->cstat : 256 + 1, startPos, zvel, shootAng, &hitData))
911
            return -1;
911
            return -1;
912
912
913
        if (pProj->range > 0 && klabs(startPos->x - hitData.pos.x) + klabs(startPos->y - hitData.pos.y) > pProj->range)
913
        if (pProj->range > 0 && klabs(startPos->x - hitData.pos.x) + klabs(startPos->y - hitData.pos.y) > pProj->range)
914
            return -1;
914
            return -1;
915
915
916
        if (pProj->trail >= 0)
916
        if (pProj->trail >= 0)
917
            A_HitscanProjTrail(startPos, &hitData.pos, shootAng, projecTile, pSprite->sectnum);
917
            A_HitscanProjTrail(startPos, &hitData.pos, shootAng, projecTile, pSprite->sectnum);
918
918
919
        if (pProj->workslike & PROJECTILE_WATERBUBBLES)
919
        if (pProj->workslike & PROJECTILE_WATERBUBBLES)
920
        {
920
        {
921
            if ((krand() & 15) == 0 && sector[hitData.sect].lotag == ST_2_UNDERWATER)
921
            if ((krand() & 15) == 0 && sector[hitData.sect].lotag == ST_2_UNDERWATER)
922
                Proj_DoWaterTracers(hitData.pos, startPos, 8 - (ud.multimode >> 1), pSprite->sectnum);
922
                Proj_DoWaterTracers(hitData.pos, startPos, 8 - (ud.multimode >> 1), pSprite->sectnum);
923
        }
923
        }
924
924
925
        if (playerNum >= 0)
925
        if (playerNum >= 0)
926
        {
926
        {
927
            otherSprite = Proj_InsertShotspark(&hitData, spriteNum, projecTile, 10, shootAng, Proj_GetDamage(pProj));
927
            otherSprite = Proj_InsertShotspark(&hitData, spriteNum, projecTile, 10, shootAng, Proj_GetDamage(pProj));
928
928
929
            if (P_PostFireHitscan(playerNum, otherSprite, &hitData, spriteNum, projecTile, zvel, projecTile, pProj->decal,
929
            if (P_PostFireHitscan(playerNum, otherSprite, &hitData, spriteNum, projecTile, zvel, projecTile, pProj->decal,
930
                                  projecTile, 1 + 2) < 0)
930
                                  projecTile, 1 + 2) < 0)
931
                return -1;
931
                return -1;
932
        }
932
        }
933
        else
933
        else
934
        {
934
        {
935
            otherSprite =
935
            otherSprite =
936
            A_PostFireHitscan(&hitData, spriteNum, projecTile, zvel, shootAng, Proj_GetDamage(pProj), projecTile, projecTile);
936
            A_PostFireHitscan(&hitData, spriteNum, projecTile, zvel, shootAng, Proj_GetDamage(pProj), projecTile, projecTile);
937
        }
937
        }
938
938
939
        if ((krand() & 255) < 4 && pProj->isound >= 0)
939
        if ((krand() & 255) < 4 && pProj->isound >= 0)
940
            S_PlaySound3D(pProj->isound, otherSprite, &hitData.pos);
940
            S_PlaySound3D(pProj->isound, otherSprite, &hitData.pos);
941
941
942
        return -1;
942
        return -1;
943
943
944
    case PROJECTILE_RPG:
944
    case PROJECTILE_RPG:
945
        if (!(pProj->workslike & PROJECTILE_NOSETOWNERSHADE) && pSprite->extra >= 0)
945
        if (!(pProj->workslike & PROJECTILE_NOSETOWNERSHADE) && pSprite->extra >= 0)
946
            pSprite->shade = pProj->shade;
946
            pSprite->shade = pProj->shade;
947
947
948
        if (pPlayer != NULL)
948
        if (pPlayer != NULL)
949
        {
949
        {
950
            // NOTE: j is a SPRITE_INDEX
950
            // NOTE: j is a SPRITE_INDEX
951
            otherSprite = GetAutoAimAng(spriteNum, playerNum, projecTile, 8<<8, 0+2, startPos, pProj->vel, &zvel, &shootAng);
951
            otherSprite = GetAutoAimAng(spriteNum, playerNum, projecTile, 8<<8, 0+2, startPos, pProj->vel, &zvel, &shootAng);
952
952
953
            if (otherSprite < 0)
953
            if (otherSprite < 0)
954
                zvel = fix16_to_int(F16(100)-pPlayer->q16horiz-pPlayer->q16horizoff)*(pProj->vel/8);
954
                zvel = fix16_to_int(F16(100)-pPlayer->q16horiz-pPlayer->q16horizoff)*(pProj->vel/8);
955
955
956
            if (pProj->sound >= 0)
956
            if (pProj->sound >= 0)
957
                A_PlaySound(pProj->sound, spriteNum);
957
                A_PlaySound(pProj->sound, spriteNum);
958
        }
958
        }
959
        else
959
        else
960
        {
960
        {
961
            if (!(pProj->workslike & PROJECTILE_NOAIM))
961
            if (!(pProj->workslike & PROJECTILE_NOAIM))
962
            {
962
            {
963
                int const otherPlayer     = A_FindPlayer(pSprite, NULL);
963
                int const otherPlayer     = A_FindPlayer(pSprite, NULL);
964
                int const otherPlayerDist = safeldist(g_player[otherPlayer].ps->i, pSprite);
964
                int const otherPlayerDist = safeldist(g_player[otherPlayer].ps->i, pSprite);
965
965
966
                shootAng = getangle(g_player[otherPlayer].ps->opos.x - startPos->x,
966
                shootAng = getangle(g_player[otherPlayer].ps->opos.x - startPos->x,
967
                                      g_player[otherPlayer].ps->opos.y - startPos->y);
967
                                      g_player[otherPlayer].ps->opos.y - startPos->y);
968
968
969
                zvel = tabledivide32_noinline((g_player[otherPlayer].ps->opos.z - startPos->z) * pProj->vel, otherPlayerDist);
969
                zvel = tabledivide32_noinline((g_player[otherPlayer].ps->opos.z - startPos->z) * pProj->vel, otherPlayerDist);
970
970
971
                if (A_CheckEnemySprite(pSprite) && (AC_MOVFLAGS(pSprite, &actor[spriteNum]) & face_player_smart))
971
                if (A_CheckEnemySprite(pSprite) && (AC_MOVFLAGS(pSprite, &actor[spriteNum]) & face_player_smart))
972
                    shootAng = pSprite->ang + (krand() & 31) - 16;
972
                    shootAng = pSprite->ang + (krand() & 31) - 16;
973
            }
973
            }
974
        }
974
        }
975
975
976
        if (numplayers > 1 && g_netClient) return -1;
976
        if (numplayers > 1 && g_netClient) return -1;
977
        else
977
        else
978
        {
978
        {
979
            // l may be a SPRITE_INDEX, see above
979
            // l may be a SPRITE_INDEX, see above
980
            int const l = (playerNum >= 0 && otherSprite >= 0) ? otherSprite : -1;
980
            int const l = (playerNum >= 0 && otherSprite >= 0) ? otherSprite : -1;
981
981
982
            zvel = A_GetShootZvel(zvel);
982
            zvel = A_GetShootZvel(zvel);
983
            otherSprite = A_InsertSprite(pSprite->sectnum,
983
            otherSprite = A_InsertSprite(pSprite->sectnum,
984
                startPos->x + tabledivide32_noinline(sintable[(348 + shootAng + 512) & 2047], pProj->offset),
984
                startPos->x + tabledivide32_noinline(sintable[(348 + shootAng + 512) & 2047], pProj->offset),
985
                startPos->y + tabledivide32_noinline(sintable[(shootAng + 348) & 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);
986
                startPos->z - (1 << 8), projecTile, 0, 14, 14, shootAng, pProj->vel, zvel, spriteNum, 4);
987
987
988
            sprite[otherSprite].extra = Proj_GetDamage(pProj);
988
            sprite[otherSprite].extra = Proj_GetDamage(pProj);
989
989
990
            if (!(pProj->workslike & PROJECTILE_BOUNCESOFFWALLS))
990
            if (!(pProj->workslike & PROJECTILE_BOUNCESOFFWALLS))
991
                sprite[otherSprite].yvel = l;  // NOT_BOUNCESOFFWALLS_YVEL
991
                sprite[otherSprite].yvel = l;  // NOT_BOUNCESOFFWALLS_YVEL
992
            else
992
            else
993
            {
993
            {
994
                sprite[otherSprite].yvel = (pProj->bounces >= 1) ? pProj->bounces : g_numFreezeBounces;
994
                sprite[otherSprite].yvel = (pProj->bounces >= 1) ? pProj->bounces : g_numFreezeBounces;
995
                sprite[otherSprite].zvel -= (2 << 4);
995
                sprite[otherSprite].zvel -= (2 << 4);
996
            }
996
            }
997
997
998
            sprite[otherSprite].pal       = (pProj->pal >= 0) ? pProj->pal : 0;
998
            sprite[otherSprite].pal       = (pProj->pal >= 0) ? pProj->pal : 0;
999
            sprite[otherSprite].xrepeat   = pProj->xrepeat;
999
            sprite[otherSprite].xrepeat   = pProj->xrepeat;
1000
            sprite[otherSprite].yrepeat   = pProj->yrepeat;
1000
            sprite[otherSprite].yrepeat   = pProj->yrepeat;
1001
            sprite[otherSprite].cstat     = (pProj->cstat >= 0) ? pProj->cstat : 128;
1001
            sprite[otherSprite].cstat     = (pProj->cstat >= 0) ? pProj->cstat : 128;
1002
            sprite[otherSprite].clipdist  = (pProj->clipdist != 255) ? pProj->clipdist : 40;
1002
            sprite[otherSprite].clipdist  = (pProj->clipdist != 255) ? pProj->clipdist : 40;
1003
            SpriteProjectile[otherSprite] = *Proj_GetProjectile(sprite[otherSprite].picnum);
1003
            SpriteProjectile[otherSprite] = *Proj_GetProjectile(sprite[otherSprite].picnum);
1004
1004
1005
            return otherSprite;
1005
            return otherSprite;
1006
        }
1006
        }
1007
1007
1008
    case PROJECTILE_KNEE:
1008
    case PROJECTILE_KNEE:
1009
        if (playerNum >= 0)
1009
        if (playerNum >= 0)
1010
        {
1010
        {
1011
            zvel = fix16_to_int(F16(100) - pPlayer->q16horiz - pPlayer->q16horizoff) << 5;
1011
            zvel = fix16_to_int(F16(100) - pPlayer->q16horiz - pPlayer->q16horizoff) << 5;
1012
            startPos->z += (6 << 8);
1012
            startPos->z += (6 << 8);
1013
            shootAng += 15;
1013
            shootAng += 15;
1014
        }
1014
        }
1015
        else if (!(pProj->workslike & PROJECTILE_NOAIM))
1015
        else if (!(pProj->workslike & PROJECTILE_NOAIM))
1016
        {
1016
        {
1017
            int32_t playerDist;
1017
            int32_t playerDist;
1018
            otherSprite = g_player[A_FindPlayer(pSprite, &playerDist)].ps->i;
1018
            otherSprite = g_player[A_FindPlayer(pSprite, &playerDist)].ps->i;
1019
            zvel = tabledivide32_noinline((sprite[otherSprite].z - startPos->z) << 8, playerDist + 1);
1019
            zvel = tabledivide32_noinline((sprite[otherSprite].z - startPos->z) << 8, playerDist + 1);
1020
            shootAng = getangle(sprite[otherSprite].x - startPos->x, sprite[otherSprite].y - startPos->y);
1020
            shootAng = getangle(sprite[otherSprite].x - startPos->x, sprite[otherSprite].y - startPos->y);
1021
        }
1021
        }
1022
1022
1023
        Proj_DoHitscan(spriteNum, 0, startPos, zvel, shootAng, &hitData);
1023
        Proj_DoHitscan(spriteNum, 0, startPos, zvel, shootAng, &hitData);
1024
1024
1025
        if (hitData.sect < 0) return -1;
1025
        if (hitData.sect < 0) return -1;
1026
1026
1027
        if (pProj->range == 0)
1027
        if (pProj->range == 0)
1028
            pProj->range = 1024;
1028
            pProj->range = 1024;
1029
1029
1030
        if (pProj->range > 0 && klabs(startPos->x - hitData.pos.x) + klabs(startPos->y - hitData.pos.y) > pProj->range)
1030
        if (pProj->range > 0 && klabs(startPos->x - hitData.pos.x) + klabs(startPos->y - hitData.pos.y) > pProj->range)
1031
            return -1;
1031
            return -1;
1032
1032
1033
        Proj_HandleKnee(&hitData, spriteNum, playerNum, projecTile, shootAng,
1033
        Proj_HandleKnee(&hitData, spriteNum, playerNum, projecTile, shootAng,
1034
                        pProj, projecTile, pProj->extra_rand, pProj->spawns, pProj->sound);
1034
                        pProj, projecTile, pProj->extra_rand, pProj->spawns, pProj->sound);
1035
1035
1036
        return -1;
1036
        return -1;
1037
1037
1038
    case PROJECTILE_BLOOD:
1038
    case PROJECTILE_BLOOD:
1039
        shootAng += 64 - (krand() & 127);
1039
        shootAng += 64 - (krand() & 127);
1040
1040
1041
        if (playerNum < 0)
1041
        if (playerNum < 0)
1042
            shootAng += 1024;
1042
            shootAng += 1024;
1043
1043
1044
        zvel = 1024 - (krand() & 2047);
1044
        zvel = 1024 - (krand() & 2047);
1045
1045
1046
        Proj_DoHitscan(spriteNum, 0, startPos, zvel, shootAng, &hitData);
1046
        Proj_DoHitscan(spriteNum, 0, startPos, zvel, shootAng, &hitData);
1047
1047
1048
        if (pProj->range == 0)
1048
        if (pProj->range == 0)
1049
            pProj->range = 1024;
1049
            pProj->range = 1024;
1050
1050
1051
        if (Proj_CheckBlood(startPos, &hitData, pProj->range, mulscale3(pProj->yrepeat, tilesiz[pProj->decal].y) << 8))
1051
        if (Proj_CheckBlood(startPos, &hitData, pProj->range, mulscale3(pProj->yrepeat, tilesiz[pProj->decal].y) << 8))
1052
        {
1052
        {
1053
            uwallptr_t const hitWall = (uwallptr_t)&wall[hitData.wall];
1053
            uwallptr_t const hitWall = (uwallptr_t)&wall[hitData.wall];
1054
1054
1055
            if (FindDistance2D(hitWall->x - wall[hitWall->point2].x, hitWall->y - wall[hitWall->point2].y) >
1055
            if (FindDistance2D(hitWall->x - wall[hitWall->point2].x, hitWall->y - wall[hitWall->point2].y) >
1056
                (mulscale3(pProj->xrepeat + 8, tilesiz[pProj->decal].x)))
1056
                (mulscale3(pProj->xrepeat + 8, tilesiz[pProj->decal].x)))
1057
            {
1057
            {
1058
                if (SectorContainsSE13(hitWall->nextsector))
1058
                if (SectorContainsSE13(hitWall->nextsector))
1059
                    return -1;
1059
                    return -1;
1060
1060
1061
                if (hitWall->nextwall >= 0 && wall[hitWall->nextwall].hitag != 0)
1061
                if (hitWall->nextwall >= 0 && wall[hitWall->nextwall].hitag != 0)
1062
                    return -1;
1062
                    return -1;
1063
1063
1064
                if (hitWall->hitag == 0 && pProj->decal >= 0)
1064
                if (hitWall->hitag == 0 && pProj->decal >= 0)
1065
                {
1065
                {
1066
                    otherSprite = A_Spawn(spriteNum, pProj->decal);
1066
                    otherSprite = A_Spawn(spriteNum, pProj->decal);
1067
1067
1068
                    A_SetHitData(otherSprite, &hitData);
1068
                    A_SetHitData(otherSprite, &hitData);
1069
1069
1070
                    if (!A_CheckSpriteFlags(otherSprite, SFLAG_DECAL))
1070
                    if (!A_CheckSpriteFlags(otherSprite, SFLAG_DECAL))
1071
                        actor[otherSprite].flags |= SFLAG_DECAL;
1071
                        actor[otherSprite].flags |= SFLAG_DECAL;
1072
1072
1073
                    sprite[otherSprite].ang = getangle(hitWall->x - wall[hitWall->point2].x,
1073
                    sprite[otherSprite].ang = getangle(hitWall->x - wall[hitWall->point2].x,
1074
                        hitWall->y - wall[hitWall->point2].y) + 512;
1074
                        hitWall->y - wall[hitWall->point2].y) + 512;
1075
                    Bmemcpy(&sprite[otherSprite], &hitData.pos, sizeof(vec3_t));
1075
                    Bmemcpy(&sprite[otherSprite], &hitData.pos, sizeof(vec3_t));
1076
1076
1077
                    Proj_DoRandDecalSize(otherSprite, projecTile);
1077
                    Proj_DoRandDecalSize(otherSprite, projecTile);
1078
1078
1079
                    sprite[otherSprite].z += sprite[otherSprite].yrepeat << 8;
1079
                    sprite[otherSprite].z += sprite[otherSprite].yrepeat << 8;
1080
1080
1081
                    //                                sprite[spawned].cstat = 16+(krand()&12);
1081
                    //                                sprite[spawned].cstat = 16+(krand()&12);
1082
                    sprite[otherSprite].cstat = 16;
1082
                    sprite[otherSprite].cstat = 16;
1083
1083
1084
                    if (krand() & 1)
1084
                    if (krand() & 1)
1085
                        sprite[otherSprite].cstat |= 4;
1085
                        sprite[otherSprite].cstat |= 4;
1086
1086
1087
                    if (krand() & 1)
1087
                    if (krand() & 1)
1088
                        sprite[otherSprite].cstat |= 8;
1088
                        sprite[otherSprite].cstat |= 8;
1089
1089
1090
                    sprite[otherSprite].shade = sector[sprite[otherSprite].sectnum].floorshade;
1090
                    sprite[otherSprite].shade = sector[sprite[otherSprite].sectnum].floorshade;
1091
1091
1092
                    A_SetSprite(otherSprite, CLIPMASK0);
1092
                    A_SetSprite(otherSprite, CLIPMASK0);
1093
                    A_AddToDeleteQueue(otherSprite);
1093
                    A_AddToDeleteQueue(otherSprite);
1094
                    changespritestat(otherSprite, 5);
1094
                    changespritestat(otherSprite, 5);
1095
                }
1095
                }
1096
            }
1096
            }
1097
        }
1097
        }
1098
1098
1099
        return -1;
1099
        return -1;
1100
1100
1101
    default:
1101
    default:
1102
        return -1;
1102
        return -1;
1103
    }
1103
    }
1104
}
1104
}
1105
1105
1106
#ifndef EDUKE32_STANDALONE
1106
#ifndef EDUKE32_STANDALONE
1107
static int32_t A_ShootHardcoded(int spriteNum, int projecTile, int shootAng, vec3_t startPos,
1107
static int32_t A_ShootHardcoded(int spriteNum, int projecTile, int shootAng, vec3_t startPos,
1108
                                spritetype *pSprite, int const playerNum, DukePlayer_t * const pPlayer)
1108
                                spritetype *pSprite, int const playerNum, DukePlayer_t * const pPlayer)
1109
{
1109
{
1110
    hitdata_t hitData;
1110
    hitdata_t hitData;
1111
    int const spriteSectnum = pSprite->sectnum;
1111
    int const spriteSectnum = pSprite->sectnum;
1112
    int32_t Zvel;
1112
    int32_t Zvel;
1113
    int vel;
1113
    int vel;
1114
1114
1115
    switch (DYNAMICTILEMAP(projecTile))
1115
    switch (DYNAMICTILEMAP(projecTile))
1116
    {
1116
    {
1117
        case BLOODSPLAT1__STATIC:
1117
        case BLOODSPLAT1__STATIC:
1118
        case BLOODSPLAT2__STATIC:
1118
        case BLOODSPLAT2__STATIC:
1119
        case BLOODSPLAT3__STATIC:
1119
        case BLOODSPLAT3__STATIC:
1120
        case BLOODSPLAT4__STATIC:
1120
        case BLOODSPLAT4__STATIC:
1121
            shootAng += 64 - (krand() & 127);
1121
            shootAng += 64 - (krand() & 127);
1122
            if (playerNum < 0)
1122
            if (playerNum < 0)
1123
                shootAng += 1024;
1123
                shootAng += 1024;
1124
            Zvel = 1024 - (krand() & 2047);
1124
            Zvel = 1024 - (krand() & 2047);
1125
            fallthrough__;
1125
            fallthrough__;
1126
        case KNEE__STATIC:
1126
        case KNEE__STATIC:
1127
            if (projecTile == KNEE)
1127
            if (projecTile == KNEE)
1128
            {
1128
            {
1129
                if (playerNum >= 0)
1129
                if (playerNum >= 0)
1130
                {
1130
                {
1131
                    Zvel = fix16_to_int(F16(100) - pPlayer->q16horiz - pPlayer->q16horizoff) << 5;
1131
                    Zvel = fix16_to_int(F16(100) - pPlayer->q16horiz - pPlayer->q16horizoff) << 5;
1132
                    startPos.z += (6 << 8);
1132
                    startPos.z += (6 << 8);
1133
                    shootAng += 15;
1133
                    shootAng += 15;
1134
                }
1134
                }
1135
                else
1135
                else
1136
                {
1136
                {
1137
                    int32_t   playerDist;
1137
                    int32_t   playerDist;
1138
                    int const playerSprite = g_player[A_FindPlayer(pSprite, &playerDist)].ps->i;
1138
                    int const playerSprite = g_player[A_FindPlayer(pSprite, &playerDist)].ps->i;
1139
                    Zvel                   = tabledivide32_noinline((sprite[playerSprite].z - startPos.z) << 8, playerDist + 1);
1139
                    Zvel                   = tabledivide32_noinline((sprite[playerSprite].z - startPos.z) << 8, playerDist + 1);
1140
                    shootAng             = getangle(sprite[playerSprite].x - startPos.x, sprite[playerSprite].y - startPos.y);
1140
                    shootAng             = getangle(sprite[playerSprite].x - startPos.x, sprite[playerSprite].y - startPos.y);
1141
                }
1141
                }
1142
            }
1142
            }
1143
1143
1144
            Proj_DoHitscan(spriteNum, 0, &startPos, Zvel, shootAng, &hitData);
1144
            Proj_DoHitscan(spriteNum, 0, &startPos, Zvel, shootAng, &hitData);
1145
1145
1146
            if (projecTile >= BLOODSPLAT1 && projecTile <= BLOODSPLAT4)
1146
            if (projecTile >= BLOODSPLAT1 && projecTile <= BLOODSPLAT4)
1147
            {
1147
            {
1148
                if (Proj_CheckBlood(&startPos, &hitData, 1024, 16 << 8))
1148
                if (Proj_CheckBlood(&startPos, &hitData, 1024, 16 << 8))
1149
                {
1149
                {
1150
                    uwallptr_t const hitwal = (uwallptr_t)&wall[hitData.wall];
1150
                    uwallptr_t const hitwal = (uwallptr_t)&wall[hitData.wall];
1151
1151
1152
                    if (SectorContainsSE13(hitwal->nextsector))
1152
                    if (SectorContainsSE13(hitwal->nextsector))
1153
                        return -1;
1153
                        return -1;
1154
1154
1155
                    if (hitwal->nextwall >= 0 && wall[hitwal->nextwall].hitag != 0)
1155
                    if (hitwal->nextwall >= 0 && wall[hitwal->nextwall].hitag != 0)
1156
                        return -1;
1156
                        return -1;
1157
1157
1158
                    if (hitwal->hitag == 0)
1158
                    if (hitwal->hitag == 0)
1159
                    {
1159
                    {
1160
                        int const spawnedSprite = A_Spawn(spriteNum, projecTile);
1160
                        int const spawnedSprite = A_Spawn(spriteNum, projecTile);
1161
                        sprite[spawnedSprite].ang
1161
                        sprite[spawnedSprite].ang
1162
                        = (getangle(hitwal->x - wall[hitwal->point2].x, hitwal->y - wall[hitwal->point2].y) + 1536) & 2047;
1162
                        = (getangle(hitwal->x - wall[hitwal->point2].x, hitwal->y - wall[hitwal->point2].y) + 1536) & 2047;
1163
                        sprite[spawnedSprite].pos = hitData.pos;
1163
                        sprite[spawnedSprite].pos = hitData.pos;
1164
                        sprite[spawnedSprite].cstat |= (krand() & 4);
1164
                        sprite[spawnedSprite].cstat |= (krand() & 4);
1165
                        A_SetSprite(spawnedSprite, CLIPMASK0);
1165
                        A_SetSprite(spawnedSprite, CLIPMASK0);
1166
                        setsprite(spawnedSprite, &sprite[spawnedSprite].pos);
1166
                        setsprite(spawnedSprite, &sprite[spawnedSprite].pos);
1167
                        if (PN(spriteNum) == OOZFILTER || PN(spriteNum) == NEWBEAST)
1167
                        if (PN(spriteNum) == OOZFILTER || PN(spriteNum) == NEWBEAST)
1168
                            sprite[spawnedSprite].pal = 6;
1168
                            sprite[spawnedSprite].pal = 6;
1169
                    }
1169
                    }
1170
                }
1170
                }
1171
1171
1172
                return -1;
1172
                return -1;
1173
            }
1173
            }
1174
1174
1175
            if (hitData.sect < 0)
1175
            if (hitData.sect < 0)
1176
                break;
1176
                break;
1177
1177
1178
            if (klabs(startPos.x - hitData.pos.x) + klabs(startPos.y - hitData.pos.y) < 1024)
1178
            if (klabs(startPos.x - hitData.pos.x) + klabs(startPos.y - hitData.pos.y) < 1024)
1179
                Proj_HandleKnee(&hitData, spriteNum, playerNum, projecTile, shootAng, NULL, KNEE, 7, SMALLSMOKE, KICK_HIT);
1179
                Proj_HandleKnee(&hitData, spriteNum, playerNum, projecTile, shootAng, NULL, KNEE, 7, SMALLSMOKE, KICK_HIT);
1180
            break;
1180
            break;
1181
1181
1182
        case SHOTSPARK1__STATIC:
1182
        case SHOTSPARK1__STATIC:
1183
        case SHOTGUN__STATIC:
1183
        case SHOTGUN__STATIC:
1184
        case CHAINGUN__STATIC:
1184
        case CHAINGUN__STATIC:
1185
        {
1185
        {
1186
            if (pSprite->extra >= 0)
1186
            if (pSprite->extra >= 0)
1187
                pSprite->shade = -96;
1187
                pSprite->shade = -96;
1188
1188
1189
            if (playerNum >= 0)
1189
            if (playerNum >= 0)
1190
                P_PreFireHitscan(spriteNum, playerNum, projecTile, &startPos, &Zvel, &shootAng,
1190
                P_PreFireHitscan(spriteNum, playerNum, projecTile, &startPos, &Zvel, &shootAng,
1191
                    projecTile == SHOTSPARK1__STATIC && !WW2GI, 1);
1191
                    projecTile == SHOTSPARK1__STATIC && !WW2GI, 1);
1192
            else
1192
            else
1193
                A_PreFireHitscan(pSprite, &startPos, &Zvel, &shootAng, 1);
1193
                A_PreFireHitscan(pSprite, &startPos, &Zvel, &shootAng, 1);
1194
1194
1195
            if (Proj_DoHitscan(spriteNum, 256 + 1, &startPos, Zvel, shootAng, &hitData))
1195
            if (Proj_DoHitscan(spriteNum, 256 + 1, &startPos, Zvel, shootAng, &hitData))
1196
                return -1;
1196
                return -1;
1197
1197
1198
            if ((krand() & 15) == 0 && sector[hitData.sect].lotag == ST_2_UNDERWATER)
1198
            if ((krand() & 15) == 0 && sector[hitData.sect].lotag == ST_2_UNDERWATER)
1199
                Proj_DoWaterTracers(hitData.pos, &startPos, 8 - (ud.multimode >> 1), pSprite->sectnum);
1199
                Proj_DoWaterTracers(hitData.pos, &startPos, 8 - (ud.multimode >> 1), pSprite->sectnum);
1200
1200
1201
            int spawnedSprite;
1201
            int spawnedSprite;
1202
1202
1203
            if (playerNum >= 0)
1203
            if (playerNum >= 0)
1204
            {
1204
            {
1205
                spawnedSprite = Proj_InsertShotspark(&hitData, spriteNum, projecTile, 10, shootAng, G_DefaultActorHealth(projecTile) + (krand() % 6));
1205
                spawnedSprite = Proj_InsertShotspark(&hitData, spriteNum, projecTile, 10, shootAng, G_DefaultActorHealth(projecTile) + (krand() % 6));
1206
1206
1207
                if (P_PostFireHitscan(playerNum, spawnedSprite, &hitData, spriteNum, projecTile, Zvel, -SMALLSMOKE, BULLETHOLE, SHOTSPARK1, 0) < 0)
1207
                if (P_PostFireHitscan(playerNum, spawnedSprite, &hitData, spriteNum, projecTile, Zvel, -SMALLSMOKE, BULLETHOLE, SHOTSPARK1, 0) < 0)
1208
                    return -1;
1208
                    return -1;
1209
            }
1209
            }
1210
            else
1210
            else
1211
            {
1211
            {
1212
                spawnedSprite = A_PostFireHitscan(&hitData, spriteNum, projecTile, Zvel, shootAng, G_DefaultActorHealth(projecTile), -SMALLSMOKE,
1212
                spawnedSprite = A_PostFireHitscan(&hitData, spriteNum, projecTile, Zvel, shootAng, G_DefaultActorHealth(projecTile), -SMALLSMOKE,
1213
                    SHOTSPARK1);
1213
                    SHOTSPARK1);
1214
            }
1214
            }
1215
1215
1216
            if ((krand() & 255) < 4)
1216
            if ((krand() & 255) < 4)
1217
                S_PlaySound3D(PISTOL_RICOCHET, spawnedSprite, &hitData.pos);
1217
                S_PlaySound3D(PISTOL_RICOCHET, spawnedSprite, &hitData.pos);
1218
1218
1219
            return -1;
1219
            return -1;
1220
        }
1220
        }
1221
1221
1222
        case GROWSPARK__STATIC:
1222
        case GROWSPARK__STATIC:
1223
        {
1223
        {
1224
            if (playerNum >= 0)
1224
            if (playerNum >= 0)
1225
                P_PreFireHitscan(spriteNum, playerNum, projecTile, &startPos, &Zvel, &shootAng, 1, 1);
1225
                P_PreFireHitscan(spriteNum, playerNum, projecTile, &startPos, &Zvel, &shootAng, 1, 1);
1226
            else
1226
            else
1227
                A_PreFireHitscan(pSprite, &startPos, &Zvel, &shootAng, 1);
1227
                A_PreFireHitscan(pSprite, &startPos, &Zvel, &shootAng, 1);
1228
1228
1229
            if (Proj_DoHitscan(spriteNum, 256 + 1, &startPos, Zvel, shootAng, &hitData))
1229
            if (Proj_DoHitscan(spriteNum, 256 + 1, &startPos, Zvel, shootAng, &hitData))
1230
                return -1;
1230
                return -1;
1231
1231
1232
            int const otherSprite = A_InsertSprite(hitData.sect, hitData.pos.x, hitData.pos.y, hitData.pos.z, GROWSPARK, -16, 28, 28,
1232
            int const otherSprite = A_InsertSprite(hitData.sect, hitData.pos.x, hitData.pos.y, hitData.pos.z, GROWSPARK, -16, 28, 28,
1233
                                                   shootAng, 0, 0, spriteNum, 1);
1233
                                                   shootAng, 0, 0, spriteNum, 1);
1234
1234
1235
            sprite[otherSprite].pal = 2;
1235
            sprite[otherSprite].pal = 2;
1236
            sprite[otherSprite].cstat |= 130;
1236
            sprite[otherSprite].cstat |= 130;
1237
            sprite[otherSprite].xrepeat = sprite[otherSprite].yrepeat = 1;
1237
            sprite[otherSprite].xrepeat = sprite[otherSprite].yrepeat = 1;
1238
            A_SetHitData(otherSprite, &hitData);
1238
            A_SetHitData(otherSprite, &hitData);
1239
1239
1240
            if (hitData.wall == -1 && hitData.sprite == -1 && hitData.sect >= 0)
1240
            if (hitData.wall == -1 && hitData.sprite == -1 && hitData.sect >= 0)
1241
            {
1241
            {
1242
                Proj_MaybeDamageCF2(otherSprite, Zvel, hitData.sect);
1242
                Proj_MaybeDamageCF2(otherSprite, Zvel, hitData.sect);
1243
            }
1243
            }
1244
            else if (hitData.sprite >= 0)
1244
            else if (hitData.sprite >= 0)
1245
                A_DamageObject(hitData.sprite, otherSprite);
1245
                A_DamageObject(hitData.sprite, otherSprite);
1246
            else if (hitData.wall >= 0 && wall[hitData.wall].picnum != ACCESSSWITCH && wall[hitData.wall].picnum != ACCESSSWITCH2)
1246
            else if (hitData.wall >= 0 && wall[hitData.wall].picnum != ACCESSSWITCH && wall[hitData.wall].picnum != ACCESSSWITCH2)
1247
                A_DamageWall(otherSprite, hitData.wall, hitData.pos, projecTile);
1247
                A_DamageWall(otherSprite, hitData.wall, hitData.pos, projecTile);
1248
        }
1248
        }
1249
        break;
1249
        break;
1250
1250
1251
        case FIREBALL__STATIC:
1251
        case FIREBALL__STATIC:
1252
            if (!WORLDTOUR)
1252
            if (!WORLDTOUR)
1253
                break;
1253
                break;
1254
            fallthrough__;
1254
            fallthrough__;
1255
        case FIRELASER__STATIC:
1255
        case FIRELASER__STATIC:
1256
        case SPIT__STATIC:
1256
        case SPIT__STATIC:
1257
        case COOLEXPLOSION1__STATIC:
1257
        case COOLEXPLOSION1__STATIC:
1258
        {
1258
        {
1259
            if (pSprite->extra >= 0)
1259
            if (pSprite->extra >= 0)
1260
                pSprite->shade = -96;
1260
                pSprite->shade = -96;
1261
1261
1262
            switch (projecTile)
1262
            switch (projecTile)
1263
            {
1263
            {
1264
                case SPIT__STATIC: vel = 292; break;
1264
                case SPIT__STATIC: vel = 292; break;
1265
                case COOLEXPLOSION1__STATIC:
1265
                case COOLEXPLOSION1__STATIC:
1266
                    vel = (pSprite->picnum == BOSS2) ? 644 : 348;
1266
                    vel = (pSprite->picnum == BOSS2) ? 644 : 348;
1267
                    startPos.z -= (4 << 7);
1267
                    startPos.z -= (4 << 7);
1268
                    break;
1268
                    break;
1269
                case FIREBALL__STATIC:
1269
                case FIREBALL__STATIC:
1270
                    if (pSprite->picnum == BOSS5 || pSprite->picnum == BOSS5STAYPUT)
1270
                    if (pSprite->picnum == BOSS5 || pSprite->picnum == BOSS5STAYPUT)
1271
                    {
1271
                    {
1272
                        vel = 968;
1272
                        vel = 968;
1273
                        startPos.z += 0x1800;
1273
                        startPos.z += 0x1800;
1274
                        break;
1274
                        break;
1275
                    }
1275
                    }
1276
                    fallthrough__;
1276
                    fallthrough__;
1277
                case FIRELASER__STATIC:
1277
                case FIRELASER__STATIC:
1278
                default:
1278
                default:
1279
                    vel = 840;
1279
                    vel = 840;
1280
                    startPos.z -= (4 << 7);
1280
                    startPos.z -= (4 << 7);
1281
                    break;
1281
                    break;
1282
            }
1282
            }
1283
1283
1284
            if (playerNum >= 0)
1284
            if (playerNum >= 0)
1285
            {
1285
            {
1286
                if (projecTile == FIREBALL)
1286
                if (projecTile == FIREBALL)
1287
                {
1287
                {
1288
                    Zvel = fix16_to_int(F16(100) - pPlayer->q16horiz - pPlayer->q16horizoff) * 98;
1288
                    Zvel = fix16_to_int(F16(100) - pPlayer->q16horiz - pPlayer->q16horizoff) * 98;
1289
                    startPos.x += sintable[(348+shootAng+512)&2047]/448;
1289
                    startPos.x += sintable[(348+shootAng+512)&2047]/448;
1290
                    startPos.y += sintable[(348+shootAng)&2047]/448;
1290
                    startPos.y += sintable[(348+shootAng)&2047]/448;
1291
                    startPos.z += 0x300;
1291
                    startPos.z += 0x300;
1292
                }
1292
                }
1293
                else if (GetAutoAimAng(spriteNum, playerNum, projecTile, -ZOFFSET4, 0, &startPos, vel, &Zvel, &shootAng) < 0)
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;
1294
                    Zvel = fix16_to_int(F16(100) - pPlayer->q16horiz - pPlayer->q16horizoff) * 98;
1295
            }
1295
            }
1296
            else
1296
            else
1297
            {
1297
            {
1298
                int const otherPlayer = A_FindPlayer(pSprite, NULL);
1298
                int const otherPlayer = A_FindPlayer(pSprite, NULL);
1299
                shootAng           += 16 - (krand() & 31);
1299
                shootAng           += 16 - (krand() & 31);
1300
                hitData.pos.x         = safeldist(g_player[otherPlayer].ps->i, pSprite);
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);
1301
                Zvel                  = tabledivide32_noinline((g_player[otherPlayer].ps->opos.z - startPos.z + (3 << 8)) * vel, hitData.pos.x);
1302
            }
1302
            }
1303
1303
1304
            Zvel = A_GetShootZvel(Zvel);
1304
            Zvel = A_GetShootZvel(Zvel);
1305
1305
1306
            int spriteSize = (playerNum >= 0) ? 7 : 18;
1306
            int spriteSize = (playerNum >= 0) ? 7 : 18;
1307
1307
1308
            if (projecTile == SPIT)
1308
            if (projecTile == SPIT)
1309
            {
1309
            {
1310
                spriteSize = 18;
1310
                spriteSize = 18;
1311
                startPos.z -= (10 << 8);
1311
                startPos.z -= (10 << 8);
1312
            }
1312
            }
1313
1313
1314
            int const returnSprite = A_InsertSprite(spriteSectnum, startPos.x, startPos.y, startPos.z, projecTile, -127, spriteSize, spriteSize,
1314
            int const returnSprite = A_InsertSprite(spriteSectnum, startPos.x, startPos.y, startPos.z, projecTile, -127, spriteSize, spriteSize,
1315
                                                    shootAng, vel, Zvel, spriteNum, 4);
1315
                                                    shootAng, vel, Zvel, spriteNum, 4);
1316
1316
1317
            sprite[returnSprite].extra += (krand() & 7);
1317
            sprite[returnSprite].extra += (krand() & 7);
1318
1318
1319
            if (projecTile == COOLEXPLOSION1)
1319
            if (projecTile == COOLEXPLOSION1)
1320
            {
1320
            {
1321
                sprite[returnSprite].shade = 0;
1321
                sprite[returnSprite].shade = 0;
1322
1322
1323
                if (PN(spriteNum) == BOSS2)
1323
                if (PN(spriteNum) == BOSS2)
1324
                {
1324
                {
1325
                    int const saveXvel        = sprite[returnSprite].xvel;
1325
                    int const saveXvel        = sprite[returnSprite].xvel;
1326
                    sprite[returnSprite].xvel = MinibossScale(spriteNum, 1024);
1326
                    sprite[returnSprite].xvel = MinibossScale(spriteNum, 1024);
1327
                    A_SetSprite(returnSprite, CLIPMASK0);
1327
                    A_SetSprite(returnSprite, CLIPMASK0);
1328
                    sprite[returnSprite].xvel = saveXvel;
1328
                    sprite[returnSprite].xvel = saveXvel;
1329
                    sprite[returnSprite].ang += 128 - (krand() & 255);
1329
                    sprite[returnSprite].ang += 128 - (krand() & 255);
1330
                }
1330
                }
1331
            }
1331
            }
1332
            else if (projecTile == FIREBALL)
1332
            else if (projecTile == FIREBALL)
1333
            {
1333
            {
1334
                if (PN(spriteNum) == BOSS5 || PN(spriteNum) == BOSS5STAYPUT || playerNum >= 0)
1334
                if (PN(spriteNum) == BOSS5 || PN(spriteNum) == BOSS5STAYPUT || playerNum >= 0)
1335
                {
1335
                {
1336
                    sprite[returnSprite].xrepeat = 40;
1336
                    sprite[returnSprite].xrepeat = 40;
1337
                    sprite[returnSprite].yrepeat = 40;
1337
                    sprite[returnSprite].yrepeat = 40;
1338
                }
1338
                }
1339
                sprite[returnSprite].yvel = playerNum;
1339
                sprite[returnSprite].yvel = playerNum;
1340
                //sprite[returnSprite].cstat |= 0x4000;
1340
                //sprite[returnSprite].cstat |= 0x4000;
1341
            }
1341
            }
1342
1342
1343
            sprite[returnSprite].cstat    = 128;
1343
            sprite[returnSprite].cstat    = 128;
1344
            sprite[returnSprite].clipdist = 4;
1344
            sprite[returnSprite].clipdist = 4;
1345
1345
1346
            return returnSprite;
1346
            return returnSprite;
1347
        }
1347
        }
1348
1348
1349
        case FREEZEBLAST__STATIC:
1349
        case FREEZEBLAST__STATIC:
1350
            startPos.z += (3 << 8);
1350
            startPos.z += (3 << 8);
1351
            fallthrough__;
1351
            fallthrough__;
1352
        case RPG__STATIC:
1352
        case RPG__STATIC:
1353
        {
1353
        {
1354
            // XXX: "CODEDUP"
1354
            // XXX: "CODEDUP"
1355
            if (pSprite->extra >= 0)
1355
            if (pSprite->extra >= 0)
1356
                pSprite->shade = -96;
1356
                pSprite->shade = -96;
1357
1357
1358
            vel = 644;
1358
            vel = 644;
1359
1359
1360
            int j = -1;
1360
            int j = -1;
1361
1361
1362
            if (playerNum >= 0)
1362
            if (playerNum >= 0)
1363
            {
1363
            {
1364
                // NOTE: j is a SPRITE_INDEX
1364
                // NOTE: j is a SPRITE_INDEX
1365
                j = GetAutoAimAng(spriteNum, playerNum, projecTile, 8 << 8, 0 + 2, &startPos, vel, &Zvel, &shootAng);
1365
                j = GetAutoAimAng(spriteNum, playerNum, projecTile, 8 << 8, 0 + 2, &startPos, vel, &Zvel, &shootAng);
1366
1366
1367
                if (j < 0)
1367
                if (j < 0)
1368
                    Zvel = fix16_to_int(F16(100) - pPlayer->q16horiz - pPlayer->q16horizoff) * 81;
1368
                    Zvel = fix16_to_int(F16(100) - pPlayer->q16horiz - pPlayer->q16horizoff) * 81;
1369
1369
1370
                if (projecTile == RPG)
1370
                if (projecTile == RPG)
1371
                    A_PlaySound(RPG_SHOOT, spriteNum);
1371
                    A_PlaySound(RPG_SHOOT, spriteNum);
1372
            }
1372
            }
1373
            else
1373
            else
1374
            {
1374
            {
1375
                // NOTE: j is a player index
1375
                // NOTE: j is a player index
1376
                j          = A_FindPlayer(pSprite, NULL);
1376
                j          = A_FindPlayer(pSprite, NULL);
1377
                shootAng = getangle(g_player[j].ps->opos.x - startPos.x, g_player[j].ps->opos.y - startPos.y);
1377
                shootAng = getangle(g_player[j].ps->opos.x - startPos.x, g_player[j].ps->opos.y - startPos.y);
1378
                if (PN(spriteNum) == BOSS3)
1378
                if (PN(spriteNum) == BOSS3)
1379
                    startPos.z -= MinibossScale(spriteNum, ZOFFSET5);
1379
                    startPos.z -= MinibossScale(spriteNum, ZOFFSET5);
1380
                else if (PN(spriteNum) == BOSS2)
1380
                else if (PN(spriteNum) == BOSS2)
1381
                {
1381
                {
1382
                    vel += 128;
1382
                    vel += 128;
1383
                    startPos.z += MinibossScale(spriteNum, 24 << 8);
1383
                    startPos.z += MinibossScale(spriteNum, 24 << 8);
1384
                }
1384
                }
1385
1385
1386
                Zvel = tabledivide32_noinline((g_player[j].ps->opos.z - startPos.z) * vel, safeldist(g_player[j].ps->i, pSprite));
1386
                Zvel = tabledivide32_noinline((g_player[j].ps->opos.z - startPos.z) * vel, safeldist(g_player[j].ps->i, pSprite));
1387
1387
1388
                if (A_CheckEnemySprite(pSprite) && (AC_MOVFLAGS(pSprite, &actor[spriteNum]) & face_player_smart))
1388
                if (A_CheckEnemySprite(pSprite) && (AC_MOVFLAGS(pSprite, &actor[spriteNum]) & face_player_smart))
1389
                    shootAng = pSprite->ang + (krand() & 31) - 16;
1389
                    shootAng = pSprite->ang + (krand() & 31) - 16;
1390
            }
1390
            }
1391
1391
1392
            if (numplayers > 1 && g_netClient)
1392
            if (numplayers > 1 && g_netClient)
1393
                return -1;
1393
                return -1;
1394
1394
1395
            Zvel                   = A_GetShootZvel(Zvel);
1395
            Zvel                   = A_GetShootZvel(Zvel);
1396
            int const returnSprite = A_InsertSprite(spriteSectnum, startPos.x + (sintable[(348 + shootAng + 512) & 2047] / 448),
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),
1397
                                                    startPos.y + (sintable[(shootAng + 348) & 2047] / 448), startPos.z - (1 << 8),
1398
                                                    projecTile, 0, 14, 14, shootAng, vel, Zvel, spriteNum, 4);
1398
                                                    projecTile, 0, 14, 14, shootAng, vel, Zvel, spriteNum, 4);
1399
            auto const pReturn = &sprite[returnSprite];
1399
            auto const pReturn = &sprite[returnSprite];
1400
1400
1401
            pReturn->extra += (krand() & 7);
1401
            pReturn->extra += (krand() & 7);
1402
            if (projecTile != FREEZEBLAST)
1402
            if (projecTile != FREEZEBLAST)
1403
                pReturn->yvel = (playerNum >= 0 && j >= 0) ? j : -1;  // RPG_YVEL
1403
                pReturn->yvel = (playerNum >= 0 && j >= 0) ? j : -1;  // RPG_YVEL
1404
            else
1404
            else
1405
            {
1405
            {
1406
                pReturn->yvel = g_numFreezeBounces;
1406
                pReturn->yvel = g_numFreezeBounces;
1407
                pReturn->xrepeat >>= 1;
1407
                pReturn->xrepeat >>= 1;
1408
                pReturn->yrepeat >>= 1;
1408
                pReturn->yrepeat >>= 1;
1409
                pReturn->zvel -= (2 << 4);
1409
                pReturn->zvel -= (2 << 4);
1410
            }
1410
            }
1411
1411
1412
            if (playerNum == -1)
1412
            if (playerNum == -1)
1413
            {
1413
            {
1414
                if (PN(spriteNum) == BOSS3)
1414
                if (PN(spriteNum) == BOSS3)
1415
                {
1415
                {
1416
                    if (krand() & 1)
1416
                    if (krand() & 1)
1417
                    {
1417
                    {
1418
                        pReturn->x -= MinibossScale(spriteNum, sintable[shootAng & 2047] >> 6);
1418
                        pReturn->x -= MinibossScale(spriteNum, sintable[shootAng & 2047] >> 6);
1419
                        pReturn->y -= MinibossScale(spriteNum, sintable[(shootAng + 1024 + 512) & 2047] >> 6);
1419
                        pReturn->y -= MinibossScale(spriteNum, sintable[(shootAng + 1024 + 512) & 2047] >> 6);
1420
                        pReturn->ang -= MinibossScale(spriteNum, 8);
1420
                        pReturn->ang -= MinibossScale(spriteNum, 8);
1421
                    }
1421
                    }
1422
                    else
1422
                    else
1423
                    {
1423
                    {
1424
                        pReturn->x += MinibossScale(spriteNum, sintable[shootAng & 2047] >> 6);
1424
                        pReturn->x += MinibossScale(spriteNum, sintable[shootAng & 2047] >> 6);
1425
                        pReturn->y += MinibossScale(spriteNum, sintable[(shootAng + 1024 + 512) & 2047] >> 6);
1425
                        pReturn->y += MinibossScale(spriteNum, sintable[(shootAng + 1024 + 512) & 2047] >> 6);
1426
                        pReturn->ang += MinibossScale(spriteNum, 4);
1426
                        pReturn->ang += MinibossScale(spriteNum, 4);
1427
                    }
1427
                    }
1428
                    pReturn->xrepeat = MinibossScale(spriteNum, 42);
1428
                    pReturn->xrepeat = MinibossScale(spriteNum, 42);
1429
                    pReturn->yrepeat = MinibossScale(spriteNum, 42);
1429
                    pReturn->yrepeat = MinibossScale(spriteNum, 42);
1430
                }
1430
                }
1431
                else if (PN(spriteNum) == BOSS2)
1431
                else if (PN(spriteNum) == BOSS2)
1432
                {
1432
                {
1433
                    pReturn->x -= MinibossScale(spriteNum, sintable[shootAng & 2047] / 56);
1433
                    pReturn->x -= MinibossScale(spriteNum, sintable[shootAng & 2047] / 56);
1434
                    pReturn->y -= MinibossScale(spriteNum, sintable[(shootAng + 1024 + 512) & 2047] / 56);
1434
                    pReturn->y -= MinibossScale(spriteNum, sintable[(shootAng + 1024 + 512) & 2047] / 56);
1435
                    pReturn->ang -= MinibossScale(spriteNum, 8) + (krand() & 255) - 128;
1435
                    pReturn->ang -= MinibossScale(spriteNum, 8) + (krand() & 255) - 128;
1436
                    pReturn->xrepeat = 24;
1436
                    pReturn->xrepeat = 24;
1437
                    pReturn->yrepeat = 24;
1437
                    pReturn->yrepeat = 24;
1438
                }
1438
                }
1439
                else if (projecTile != FREEZEBLAST)
1439
                else if (projecTile != FREEZEBLAST)
1440
                {
1440
                {
1441
                    pReturn->xrepeat = 30;
1441
                    pReturn->xrepeat = 30;
1442
                    pReturn->yrepeat = 30;
1442
                    pReturn->yrepeat = 30;
1443
                    pReturn->extra >>= 2;
1443
                    pReturn->extra >>= 2;
1444
                }
1444
                }
1445
            }
1445
            }
1446
            else if (PWEAPON(playerNum, g_player[playerNum].ps->curr_weapon, WorksLike) == DEVISTATOR_WEAPON)
1446
            else if (PWEAPON(playerNum, g_player[playerNum].ps->curr_weapon, WorksLike) == DEVISTATOR_WEAPON)
1447
            {
1447
            {
1448
                pReturn->extra >>= 2;
1448
                pReturn->extra >>= 2;
1449
                pReturn->ang += 16 - (krand() & 31);
1449
                pReturn->ang += 16 - (krand() & 31);
1450
                pReturn->zvel += 256 - (krand() & 511);
1450
                pReturn->zvel += 256 - (krand() & 511);
1451
1451
1452
                if (g_player[playerNum].ps->hbomb_hold_delay)
1452
                if (g_player[playerNum].ps->hbomb_hold_delay)
1453
                {
1453
                {
1454
                    pReturn->x -= sintable[shootAng & 2047] / 644;
1454
                    pReturn->x -= sintable[shootAng & 2047] / 644;
1455
                    pReturn->y -= sintable[(shootAng + 1024 + 512) & 2047] / 644;
1455
                    pReturn->y -= sintable[(shootAng + 1024 + 512) & 2047] / 644;
1456
                }
1456
                }
1457
                else
1457
                else
1458
                {
1458
                {
1459
                    pReturn->x += sintable[shootAng & 2047] >> 8;
1459
                    pReturn->x += sintable[shootAng & 2047] >> 8;
1460
                    pReturn->y += sintable[(shootAng + 1024 + 512) & 2047] >> 8;
1460
                    pReturn->y += sintable[(shootAng + 1024 + 512) & 2047] >> 8;
1461
                }
1461
                }
1462
                pReturn->xrepeat >>= 1;
1462
                pReturn->xrepeat >>= 1;
1463
                pReturn->yrepeat >>= 1;
1463
                pReturn->yrepeat >>= 1;
1464
            }
1464
            }
1465
1465
1466
            pReturn->cstat    = 128;
1466
            pReturn->cstat    = 128;
1467
            pReturn->clipdist = (projecTile == RPG) ? 4 : 40;
1467
            pReturn->clipdist = (projecTile == RPG) ? 4 : 40;
1468
1468
1469
            return returnSprite;
1469
            return returnSprite;
1470
        }
1470
        }
1471
1471
1472
        case HANDHOLDINGLASER__STATIC:
1472
        case HANDHOLDINGLASER__STATIC:
1473
        {
1473
        {
1474
            int const zOffset     = (playerNum >= 0) ? g_player[playerNum].ps->pyoff : 0;
1474
            int const zOffset     = (playerNum >= 0) ? g_player[playerNum].ps->pyoff : 0;
1475
            Zvel                  = (playerNum >= 0) ? fix16_to_int(F16(100) - pPlayer->q16horiz - pPlayer->q16horizoff) * 32 : 0;
1475
            Zvel                  = (playerNum >= 0) ? fix16_to_int(F16(100) - pPlayer->q16horiz - pPlayer->q16horizoff) * 32 : 0;
1476
1476
1477
            startPos.z -= zOffset;
1477
            startPos.z -= zOffset;
1478
            Proj_DoHitscan(spriteNum, 0, &startPos, Zvel, shootAng, &hitData);
1478
            Proj_DoHitscan(spriteNum, 0, &startPos, Zvel, shootAng, &hitData);
1479
            startPos.z += zOffset;
1479
            startPos.z += zOffset;
1480
1480
1481
            int placeMine = 0;
1481
            int placeMine = 0;
1482
            if (hitData.sprite >= 0)
1482
            if (hitData.sprite >= 0)
1483
                break;
1483
                break;
1484
1484
1485
            if (hitData.wall >= 0 && hitData.sect >= 0)
1485
            if (hitData.wall >= 0 && hitData.sect >= 0)
1486
                if (((hitData.pos.x - startPos.x) * (hitData.pos.x - startPos.x)
1486
                if (((hitData.pos.x - startPos.x) * (hitData.pos.x - startPos.x)
1487
                     + (hitData.pos.y - startPos.y) * (hitData.pos.y - startPos.y))
1487
                     + (hitData.pos.y - startPos.y) * (hitData.pos.y - startPos.y))
1488
                    < (290 * 290))
1488
                    < (290 * 290))
1489
                {
1489
                {
1490
                    // ST_2_UNDERWATER
1490
                    // ST_2_UNDERWATER
1491
                    if (wall[hitData.wall].nextsector >= 0)
1491
                    if (wall[hitData.wall].nextsector >= 0)
1492
                    {
1492
                    {
1493
                        if (sector[wall[hitData.wall].nextsector].lotag <= 2 && sector[hitData.sect].lotag <= 2)
1493
                        if (sector[wall[hitData.wall].nextsector].lotag <= 2 && sector[hitData.sect].lotag <= 2)
1494
                            placeMine = 1;
1494
                            placeMine = 1;
1495
                    }
1495
                    }
1496
                    else if (sector[hitData.sect].lotag <= 2)
1496
                    else if (sector[hitData.sect].lotag <= 2)
1497
                        placeMine = 1;
1497
                        placeMine = 1;
1498
                }
1498
                }
1499
1499
1500
            if (placeMine == 1)
1500
            if (placeMine == 1)
1501
            {
1501
            {
1502
                int const tripBombMode = (playerNum < 0) ? 0 :
1502
                int const tripBombMode = (playerNum < 0) ? 0 :
1503
#ifdef LUNATIC
1503
#ifdef LUNATIC
1504
                                                           g_player[playerNum].ps->tripbombControl;
1504
                                                           g_player[playerNum].ps->tripbombControl;
1505
#else
1505
#else
1506
                                                           Gv_GetVarByLabel("TRIPBOMB_CONTROL", TRIPBOMB_TRIPWIRE,
1506
                                                           Gv_GetVarByLabel("TRIPBOMB_CONTROL", TRIPBOMB_TRIPWIRE,
1507
                                                                            g_player[playerNum].ps->i, playerNum);
1507
                                                                            g_player[playerNum].ps->i, playerNum);
1508
#endif
1508
#endif
1509
                int const spawnedSprite = A_InsertSprite(hitData.sect, hitData.pos.x, hitData.pos.y, hitData.pos.z, TRIPBOMB, -16, 4, 5,
1509
                int const spawnedSprite = A_InsertSprite(hitData.sect, hitData.pos.x, hitData.pos.y, hitData.pos.z, TRIPBOMB, -16, 4, 5,
1510
                                                         shootAng, 0, 0, spriteNum, 6);
1510
                                                         shootAng, 0, 0, spriteNum, 6);
1511
                if (tripBombMode & TRIPBOMB_TIMER)
1511
                if (tripBombMode & TRIPBOMB_TIMER)
1512
                {
1512
                {
1513
#ifdef LUNATIC
1513
#ifdef LUNATIC
1514
                    int32_t lLifetime    = g_player[playerNum].ps->tripbombLifetime;
1514
                    int32_t lLifetime    = g_player[playerNum].ps->tripbombLifetime;
1515
                    int32_t lLifetimeVar = g_player[playerNum].ps->tripbombLifetimeVar;
1515
                    int32_t lLifetimeVar = g_player[playerNum].ps->tripbombLifetimeVar;
1516
#else
1516
#else
1517
                    int32_t lLifetime = Gv_GetVarByLabel("STICKYBOMB_LIFETIME", NAM_GRENADE_LIFETIME, g_player[playerNum].ps->i, playerNum);
1517
                    int32_t lLifetime = Gv_GetVarByLabel("STICKYBOMB_LIFETIME", NAM_GRENADE_LIFETIME, g_player[playerNum].ps->i, playerNum);
1518
                    int32_t lLifetimeVar
1518
                    int32_t lLifetimeVar
1519
                    = Gv_GetVarByLabel("STICKYBOMB_LIFETIME_VAR", NAM_GRENADE_LIFETIME_VAR, g_player[playerNum].ps->i, playerNum);
1519
                    = Gv_GetVarByLabel("STICKYBOMB_LIFETIME_VAR", NAM_GRENADE_LIFETIME_VAR, g_player[playerNum].ps->i, playerNum);
1520
#endif
1520
#endif
1521
                    // set timer.  blows up when at zero....
1521
                    // set timer.  blows up when at zero....
1522
                    actor[spawnedSprite].t_data[7] = lLifetime + mulscale14(krand(), lLifetimeVar) - lLifetimeVar;
1522
                    actor[spawnedSprite].t_data[7] = lLifetime + mulscale14(krand(), lLifetimeVar) - lLifetimeVar;
1523
                    // TIMER_CONTROL
1523
                    // TIMER_CONTROL
1524
                    actor[spawnedSprite].t_data[6] = 1;
1524
                    actor[spawnedSprite].t_data[6] = 1;
1525
                }
1525
                }
1526
                else
1526
                else
1527
                    sprite[spawnedSprite].hitag = spawnedSprite;
1527
                    sprite[spawnedSprite].hitag = spawnedSprite;
1528
1528
1529
                A_PlaySound(LASERTRIP_ONWALL, spawnedSprite);
1529
                A_PlaySound(LASERTRIP_ONWALL, spawnedSprite);
1530
                sprite[spawnedSprite].xvel = -20;
1530
                sprite[spawnedSprite].xvel = -20;
1531
                A_SetSprite(spawnedSprite, CLIPMASK0);
1531
                A_SetSprite(spawnedSprite, CLIPMASK0);
1532
                sprite[spawnedSprite].cstat = 16;
1532
                sprite[spawnedSprite].cstat = 16;
1533
1533
1534
                int const p2      = wall[hitData.wall].point2;
1534
                int const p2      = wall