mirror of
https://github.com/ZDoom/raze-gles.git
synced 2024-12-26 11:40:44 +00:00
9af63dc497
DONT_BUILD. git-svn-id: https://svn.eduke32.com/eduke32@5982 1a8010ca-5511-0410-912e-c29ae57300e0
5439 lines
208 KiB
C
5439 lines
208 KiB
C
//-------------------------------------------------------------------------
|
|
/*
|
|
Copyright (C) 2010 EDuke32 developers and contributors
|
|
|
|
This file is part of EDuke32.
|
|
|
|
EDuke32 is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License version 2
|
|
as published by the Free Software Foundation.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
//-------------------------------------------------------------------------
|
|
|
|
#include "duke3d.h"
|
|
#include "demo.h"
|
|
#include "enet/enet.h"
|
|
|
|
#ifdef __ANDROID__
|
|
#include "android.h"
|
|
#endif
|
|
|
|
int32_t lastvisinc;
|
|
hudweapon_t hudweap;
|
|
|
|
#ifdef SPLITSCREEN_MOD_HACKS
|
|
static int32_t g_snum;
|
|
#endif
|
|
|
|
extern int32_t g_levelTextTime, ticrandomseed;
|
|
|
|
int32_t g_numObituaries = 0;
|
|
int32_t g_numSelfObituaries = 0;
|
|
|
|
void P_UpdateScreenPal(DukePlayer_t * const pPlayer)
|
|
{
|
|
int inWater = 0;
|
|
int const playerSectnum = pPlayer->cursectnum;
|
|
|
|
if (pPlayer->heat_on)
|
|
pPlayer->palette = SLIMEPAL;
|
|
else if (playerSectnum < 0)
|
|
pPlayer->palette = BASEPAL;
|
|
else if (sector[playerSectnum].ceilingpicnum >= FLOORSLIME && sector[playerSectnum].ceilingpicnum <= FLOORSLIME + 2)
|
|
{
|
|
pPlayer->palette = SLIMEPAL;
|
|
inWater = 1;
|
|
}
|
|
else
|
|
{
|
|
pPlayer->palette = (sector[pPlayer->cursectnum].lotag == ST_2_UNDERWATER) ? WATERPAL : BASEPAL;
|
|
inWater = 1;
|
|
}
|
|
|
|
g_restorePalette = 1+inWater;
|
|
}
|
|
|
|
static void P_IncurDamage(DukePlayer_t * const pPlayer)
|
|
{
|
|
if (VM_OnEvent(EVENT_INCURDAMAGE, pPlayer->i, P_Get(pPlayer->i)) != 0)
|
|
return;
|
|
|
|
sprite[pPlayer->i].extra -= pPlayer->extra_extra8>>8;
|
|
|
|
int playerDamage = sprite[pPlayer->i].extra - pPlayer->last_extra;
|
|
|
|
if (playerDamage >= 0)
|
|
return;
|
|
|
|
pPlayer->extra_extra8 = 0;
|
|
|
|
if (pPlayer->inv_amount[GET_SHIELD] > 0)
|
|
{
|
|
int const shieldDamage = playerDamage * (20 + (krand()%30)) / 100;
|
|
|
|
playerDamage -= shieldDamage;
|
|
pPlayer->inv_amount[GET_SHIELD] += shieldDamage;
|
|
|
|
if (pPlayer->inv_amount[GET_SHIELD] < 0)
|
|
{
|
|
playerDamage += pPlayer->inv_amount[GET_SHIELD];
|
|
pPlayer->inv_amount[GET_SHIELD] = 0;
|
|
}
|
|
}
|
|
|
|
sprite[pPlayer->i].extra = pPlayer->last_extra + playerDamage;
|
|
}
|
|
|
|
void P_QuickKill(DukePlayer_t * const pPlayer)
|
|
{
|
|
P_PalFrom(pPlayer, 48, 48,48,48);
|
|
|
|
sprite[pPlayer->i].extra = 0;
|
|
sprite[pPlayer->i].cstat |= 32768;
|
|
|
|
if (ud.god == 0)
|
|
A_DoGuts(pPlayer->i,JIBS6,8);
|
|
}
|
|
|
|
static void A_DoWaterTracers(int x1, int y1, int z1, int x2, int y2, int z2, int n)
|
|
{
|
|
if ((klabs(x1-x2)+klabs(y1-y2)) < 3084)
|
|
return;
|
|
|
|
int16_t sectNum = -1;
|
|
vec3_t const v = { tabledivide32_noinline(x2 - x1, n + 1), tabledivide32_noinline(y2 - y1, n + 1),
|
|
tabledivide32_noinline(z2 - z1, n + 1) };
|
|
|
|
for (bssize_t i=n; i>0; i--)
|
|
{
|
|
x1 += v.x;
|
|
y1 += v.y;
|
|
z1 += v.z;
|
|
|
|
updatesector(x1, y1, §Num);
|
|
|
|
if (sectNum < 0)
|
|
break;
|
|
|
|
if (sector[sectNum].lotag == ST_2_UNDERWATER)
|
|
A_InsertSprite(sectNum,x1,y1,z1,WATERBUBBLE,-32,4+(krand()&3),4+(krand()&3),krand()&2047,0,0,g_player[0].ps->i,5);
|
|
else
|
|
A_InsertSprite(sectNum,x1,y1,z1,SMALLSMOKE,-32,14,14,0,0,0,g_player[0].ps->i,5);
|
|
}
|
|
}
|
|
|
|
static inline projectile_t *Proj_GetProjectile(int tile)
|
|
{
|
|
return ((unsigned)tile < MAXTILES && g_tile[tile].proj) ? g_tile[tile].proj : &DefaultProjectile;
|
|
}
|
|
|
|
static void A_HitscanProjTrail(const vec3_t *startPos, const vec3_t *endPos, int projAng, int tileNum, int16_t sectNum)
|
|
{
|
|
const projectile_t *const pProj = Proj_GetProjectile(tileNum);
|
|
|
|
vec3_t spawnPos = { startPos->x + tabledivide32_noinline(sintable[(348 + projAng + 512) & 2047], pProj->offset),
|
|
startPos->y + tabledivide32_noinline(sintable[(projAng + 348) & 2047], pProj->offset),
|
|
startPos->z + 1024 + (pProj->toffset << 8) };
|
|
|
|
int32_t n = ((FindDistance2D(spawnPos.x - endPos->x, spawnPos.y - endPos->y)) >> 8) + 1;
|
|
|
|
vec3_t const increment = { tabledivide32_noinline((endPos->x - spawnPos.x), n),
|
|
tabledivide32_noinline((endPos->y - spawnPos.y), n),
|
|
tabledivide32_noinline((endPos->z - spawnPos.z), n) };
|
|
|
|
spawnPos.x += increment.x >> 2;
|
|
spawnPos.y += increment.y >> 2;
|
|
spawnPos.z += increment.z >> 2;
|
|
|
|
int32_t j;
|
|
|
|
for (bssize_t i = pProj->tnum; i > 0; --i)
|
|
{
|
|
spawnPos.x += increment.x;
|
|
spawnPos.y += increment.y;
|
|
spawnPos.z += increment.z;
|
|
|
|
updatesectorz(spawnPos.x, spawnPos.y, spawnPos.z, §Num);
|
|
|
|
if (sectNum < 0)
|
|
break;
|
|
|
|
getzsofslope(sectNum, spawnPos.x, spawnPos.y, &n, &j);
|
|
|
|
if (spawnPos.z > j || spawnPos.z < n)
|
|
break;
|
|
|
|
j = A_InsertSprite(sectNum, spawnPos.x, spawnPos.y, spawnPos.z, pProj->trail, -32,
|
|
pProj->txrepeat, pProj->tyrepeat, projAng, 0, 0, g_player[0].ps->i, 0);
|
|
changespritestat(j, STAT_ACTOR);
|
|
}
|
|
}
|
|
|
|
int32_t A_GetHitscanRange(int spriteNum)
|
|
{
|
|
int const zOffset = (PN(spriteNum) == APLAYER) ? PHEIGHT : 0;
|
|
hitdata_t hitData;
|
|
|
|
SZ(spriteNum) -= zOffset;
|
|
hitscan((const vec3_t *)&sprite[spriteNum], SECT(spriteNum), sintable[(SA(spriteNum) + 512) & 2047],
|
|
sintable[SA(spriteNum) & 2047], 0, &hitData, CLIPMASK1);
|
|
SZ(spriteNum) += zOffset;
|
|
|
|
return (FindDistance2D(hitData.pos.x - SX(spriteNum), hitData.pos.y - SY(spriteNum)));
|
|
}
|
|
|
|
static int A_FindTargetSprite(const spritetype *pSprite, int projAng, int projecTile)
|
|
{
|
|
static int const aimstats[] = {
|
|
STAT_PLAYER, STAT_DUMMYPLAYER, STAT_ACTOR, STAT_ZOMBIEACTOR
|
|
};
|
|
|
|
int const playerNum = pSprite->picnum == APLAYER ? P_GetP(pSprite) : -1;
|
|
|
|
if (pSprite->picnum == APLAYER)
|
|
{
|
|
if (!g_player[playerNum].ps->auto_aim)
|
|
return -1;
|
|
|
|
if (g_player[playerNum].ps->auto_aim == 2)
|
|
{
|
|
if (A_CheckSpriteTileFlags(projecTile,SFLAG_PROJECTILE) && (Proj_GetProjectile(projecTile)->workslike & PROJECTILE_RPG))
|
|
return -1;
|
|
|
|
switch (DYNAMICTILEMAP(projecTile))
|
|
{
|
|
case TONGUE__STATIC:
|
|
case FREEZEBLAST__STATIC:
|
|
case SHRINKSPARK__STATIC:
|
|
case SHRINKER__STATIC:
|
|
case RPG__STATIC:
|
|
case FIRELASER__STATIC:
|
|
case SPIT__STATIC:
|
|
case COOLEXPLOSION1__STATIC:
|
|
return -1;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
int const spriteAng = pSprite->ang;
|
|
|
|
int const gotShrinker =
|
|
(pSprite->picnum == APLAYER && PWEAPON(playerNum, g_player[playerNum].ps->curr_weapon, WorksLike) == SHRINKER_WEAPON);
|
|
int const gotFreezer =
|
|
(pSprite->picnum == APLAYER && PWEAPON(playerNum, g_player[playerNum].ps->curr_weapon, WorksLike) == FREEZE_WEAPON);
|
|
|
|
vec2_t const d1 = { sintable[(spriteAng + 512 - projAng) & 2047], sintable[(spriteAng - projAng) & 2047] };
|
|
vec2_t const d2 = { sintable[(spriteAng + 512 + projAng) & 2047], sintable[(spriteAng + projAng) & 2047] };
|
|
vec2_t const d3 = { sintable[(spriteAng + 512) & 2047], sintable[spriteAng & 2047] };
|
|
|
|
int lastDist = INT32_MAX;
|
|
int returnSprite = -1;
|
|
|
|
for (bssize_t k=0; k<4; k++)
|
|
{
|
|
if (returnSprite >= 0)
|
|
break;
|
|
|
|
for (bssize_t spriteNum=headspritestat[aimstats[k]]; spriteNum >= 0; spriteNum=nextspritestat[spriteNum])
|
|
{
|
|
if ((sprite[spriteNum].xrepeat > 0 && sprite[spriteNum].extra >= 0 &&
|
|
(sprite[spriteNum].cstat & (257 + 32768)) == 257) &&
|
|
(A_CheckEnemySprite(&sprite[spriteNum]) || k < 2))
|
|
{
|
|
if (A_CheckEnemySprite(&sprite[spriteNum]) || PN(spriteNum) == APLAYER || PN(spriteNum) == SHARK)
|
|
{
|
|
if (PN(spriteNum) == APLAYER && pSprite->picnum == APLAYER && pSprite != &sprite[spriteNum] &&
|
|
(GTFLAGS(GAMETYPE_PLAYERSFRIENDLY) ||
|
|
(GTFLAGS(GAMETYPE_TDM) && g_player[P_Get(spriteNum)].ps->team == g_player[playerNum].ps->team)))
|
|
continue;
|
|
|
|
if ((gotShrinker && sprite[spriteNum].xrepeat < 30
|
|
&& (PN(spriteNum) == SHARK || !(PN(spriteNum) >= GREENSLIME && PN(spriteNum) <= GREENSLIME + 7)))
|
|
|| (gotFreezer && sprite[spriteNum].pal == 1))
|
|
continue;
|
|
}
|
|
|
|
vec2_t vd = { (SX(spriteNum) - pSprite->x), (SY(spriteNum) - pSprite->y) };
|
|
|
|
if ((d1.y * vd.x <= d1.x * vd.y) && (d2.y * vd.x >= d2.x * vd.y))
|
|
{
|
|
int const spriteDist = mulscale(d3.x, vd.x, 14) + mulscale(d3.y, vd.y, 14);
|
|
|
|
if (spriteDist > 512 && spriteDist < lastDist)
|
|
{
|
|
int onScreen = 1;
|
|
|
|
if (pSprite->picnum == APLAYER)
|
|
{
|
|
const DukePlayer_t *const ps = g_player[P_GetP(pSprite)].ps;
|
|
onScreen = (klabs(scale(SZ(spriteNum)-pSprite->z,10,spriteDist)-(ps->horiz+ps->horizoff-100)) < 100);
|
|
}
|
|
|
|
int const canSee = (PN(spriteNum) == ORGANTIC || PN(spriteNum) == ROTATEGUN)
|
|
? cansee(SX(spriteNum), SY(spriteNum), SZ(spriteNum), SECT(spriteNum), pSprite->x, pSprite->y,
|
|
pSprite->z - ZOFFSET5, pSprite->sectnum)
|
|
: cansee(SX(spriteNum), SY(spriteNum), SZ(spriteNum) - ZOFFSET5, SECT(spriteNum), pSprite->x, pSprite->y,
|
|
pSprite->z - ZOFFSET5, pSprite->sectnum);
|
|
|
|
if (onScreen && canSee)
|
|
{
|
|
lastDist = spriteDist;
|
|
returnSprite = spriteNum;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return returnSprite;
|
|
}
|
|
|
|
static void A_SetHitData(int spriteNum, const hitdata_t *hitData)
|
|
{
|
|
actor[spriteNum].t_data[6] = hitData->wall;
|
|
actor[spriteNum].t_data[7] = hitData->sect;
|
|
actor[spriteNum].t_data[8] = hitData->sprite;
|
|
}
|
|
|
|
static int CheckShootSwitchTile(int tileNum)
|
|
{
|
|
return tileNum == DIPSWITCH || tileNum == DIPSWITCH + 1 || tileNum == DIPSWITCH2 || tileNum == DIPSWITCH2 + 1 ||
|
|
tileNum == DIPSWITCH3 || tileNum == DIPSWITCH3 + 1 || tileNum == HANDSWITCH || tileNum == HANDSWITCH + 1;
|
|
}
|
|
|
|
static int32_t safeldist(int32_t spriteNum, const void *pSprite)
|
|
{
|
|
int32_t distance = ldist(&sprite[spriteNum], pSprite);
|
|
return distance ? distance : 1;
|
|
}
|
|
|
|
// flags:
|
|
// 1: do sprite center adjustment (cen-=(8<<8)) for GREENSLIME or ROTATEGUN
|
|
// 2: do auto getangle only if not RECON (if clear, do unconditionally)
|
|
static int GetAutoAimAng(int spriteNum, int playerNum, int projecTile, int zAdjust, int aimFlags,
|
|
const vec3_t *startPos, int projVel, int32_t *pZvel, int *pAng)
|
|
{
|
|
int returnSprite = -1;
|
|
|
|
Bassert((unsigned)playerNum < MAXPLAYERS);
|
|
|
|
#ifdef LUNATIC
|
|
g_player[playerNum].ps->autoaimang = g_player[playerNum].ps->auto_aim == 3 ? AUTO_AIM_ANGLE<<1 : AUTO_AIM_ANGLE;
|
|
#else
|
|
Gv_SetVar(g_aimAngleVarID, g_player[playerNum].ps->auto_aim == 3 ? AUTO_AIM_ANGLE<<1 : AUTO_AIM_ANGLE, spriteNum, playerNum);
|
|
#endif
|
|
|
|
VM_OnEvent(EVENT_GETAUTOAIMANGLE, spriteNum, playerNum);
|
|
|
|
#ifdef LUNATIC
|
|
int aimang = g_player[playerNum].ps->autoaimang;
|
|
#else
|
|
int aimang = Gv_GetVar(g_aimAngleVarID, spriteNum, playerNum);
|
|
#endif
|
|
if (aimang > 0)
|
|
returnSprite = A_FindTargetSprite(&sprite[spriteNum], aimang, projecTile);
|
|
|
|
if (returnSprite >= 0)
|
|
{
|
|
const uspritetype *const pSprite = (uspritetype *)&sprite[returnSprite];
|
|
int zCenter = 2 * (pSprite->yrepeat * tilesiz[pSprite->picnum].y) + zAdjust;
|
|
|
|
if (aimFlags &&
|
|
((pSprite->picnum >= GREENSLIME && pSprite->picnum <= GREENSLIME + 7) || pSprite->picnum == ROTATEGUN))
|
|
zCenter -= ZOFFSET3;
|
|
|
|
int spriteDist = safeldist(g_player[playerNum].ps->i, &sprite[returnSprite]);
|
|
*pZvel = tabledivide32_noinline((pSprite->z - startPos->z - zCenter) * projVel, spriteDist);
|
|
|
|
if (!(aimFlags&2) || sprite[returnSprite].picnum != RECON)
|
|
*pAng = getangle(pSprite->x-startPos->x, pSprite->y-startPos->y);
|
|
}
|
|
|
|
return returnSprite;
|
|
}
|
|
|
|
static void Proj_MaybeSpawn(int spriteNum, int projecTile, const hitdata_t *hitData)
|
|
{
|
|
// atwith < 0 is for hard-coded projectiles
|
|
projectile_t *const pProj = Proj_GetProjectile(projecTile);
|
|
int spawnTile = projecTile < 0 ? -projecTile : pProj->spawns;
|
|
|
|
if (spawnTile >= 0)
|
|
{
|
|
int spawned = A_Spawn(spriteNum, spawnTile);
|
|
|
|
if (projecTile >= 0)
|
|
{
|
|
if (pProj->sxrepeat > 4)
|
|
sprite[spawned].xrepeat = pProj->sxrepeat;
|
|
|
|
if (pProj->syrepeat > 4)
|
|
sprite[spawned].yrepeat = pProj->syrepeat;
|
|
}
|
|
|
|
A_SetHitData(spawned, hitData);
|
|
}
|
|
}
|
|
|
|
// <extra>: damage that this shotspark does
|
|
static int Proj_InsertShotspark(const hitdata_t *hitData, int spriteNum, int projecTile, int sparkSize, int sparkAng, int damage)
|
|
{
|
|
int returnSprite = A_InsertSprite(hitData->sect, hitData->pos.x, hitData->pos.y, hitData->pos.z, SHOTSPARK1, -15,
|
|
sparkSize, sparkSize, sparkAng, 0, 0, spriteNum, 4);
|
|
|
|
sprite[returnSprite].extra = damage;
|
|
sprite[returnSprite].yvel = projecTile; // This is a hack to allow you to detect which weapon spawned a SHOTSPARK1
|
|
|
|
A_SetHitData(returnSprite, hitData);
|
|
|
|
return returnSprite;
|
|
}
|
|
|
|
int Proj_GetDamage(projectile_t const *pProj)
|
|
{
|
|
Bassert(pProj);
|
|
|
|
int damage = pProj->extra;
|
|
|
|
if (pProj->extra_rand > 0)
|
|
damage += (krand() % pProj->extra_rand);
|
|
|
|
return damage;
|
|
}
|
|
|
|
static void Proj_MaybeAddSpread(int doSpread, int32_t *zvel, int *shootAng, int zRange, int angRange)
|
|
{
|
|
if (doSpread)
|
|
{
|
|
// Ranges <= 1 mean no spread at all. A range of 1 calls krand() though.
|
|
if (zRange > 0)
|
|
*zvel += (zRange >> 1) - krand() % zRange;
|
|
|
|
if (angRange > 0)
|
|
*shootAng += (angRange >> 1) - krand() % angRange;
|
|
}
|
|
}
|
|
|
|
static int g_overrideShootZvel = 0; // a boolean
|
|
static int g_shootZvel; // the actual zvel if the above is !=0
|
|
|
|
static int A_GetShootZvel(int defaultZvel)
|
|
{
|
|
return g_overrideShootZvel ? g_shootZvel : defaultZvel;
|
|
}
|
|
|
|
// Prepare hitscan weapon fired from player p.
|
|
static void P_PreFireHitscan(int spriteNum, int playerNum, int projecTile, vec3_t *srcVect, int32_t *zvel, int *shootAng,
|
|
int accurateAim, int doSpread)
|
|
{
|
|
int angRange = 32;
|
|
int zRange = 256;
|
|
int aimSprite = GetAutoAimAng(spriteNum, playerNum, projecTile, 5 << 8, 0 + 1, srcVect, 256, zvel, shootAng);
|
|
|
|
DukePlayer_t *const pPlayer = g_player[playerNum].ps;
|
|
|
|
#ifdef LUNATIC
|
|
pPlayer->angrange = angRange;
|
|
pPlayer->zrange = zRange;
|
|
#else
|
|
Gv_SetVar(g_angRangeVarID,angRange, spriteNum,playerNum);
|
|
Gv_SetVar(g_zRangeVarID,zRange,spriteNum,playerNum);
|
|
#endif
|
|
|
|
VM_OnEvent(EVENT_GETSHOTRANGE, spriteNum, playerNum);
|
|
|
|
#ifdef LUNATIC
|
|
angRange = pPlayer->angrange;
|
|
zRange = pPlayer->zrange;
|
|
#else
|
|
angRange = Gv_GetVar(g_angRangeVarID, spriteNum, playerNum);
|
|
zRange = Gv_GetVar(g_zRangeVarID, spriteNum, playerNum);
|
|
#endif
|
|
|
|
if (accurateAim)
|
|
{
|
|
if (!pPlayer->auto_aim)
|
|
{
|
|
hitdata_t hitData;
|
|
|
|
*zvel = A_GetShootZvel((100-pPlayer->horiz-pPlayer->horizoff)<<5);
|
|
|
|
hitscan(srcVect, sprite[spriteNum].sectnum, sintable[(*shootAng + 512) & 2047],
|
|
sintable[*shootAng & 2047], *zvel << 6, &hitData, CLIPMASK1);
|
|
|
|
if (hitData.sprite != -1)
|
|
{
|
|
int const statNumMap = ((1 << STAT_ACTOR) | (1 << STAT_ZOMBIEACTOR) | (1 << STAT_PLAYER) | (1 << STAT_DUMMYPLAYER));
|
|
int const statNum = sprite[hitData.sprite].statnum;
|
|
|
|
if (statNum >= 0 && statNum <= 30 && (statNumMap & (1 << statNum)))
|
|
aimSprite = hitData.sprite;
|
|
}
|
|
}
|
|
|
|
if (aimSprite == -1)
|
|
{
|
|
*zvel = (100-pPlayer->horiz-pPlayer->horizoff)<<5;
|
|
Proj_MaybeAddSpread(doSpread, zvel, shootAng, zRange, angRange);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (aimSprite == -1) // no target
|
|
*zvel = (100-pPlayer->horiz-pPlayer->horizoff)<<5;
|
|
Proj_MaybeAddSpread(doSpread, zvel, shootAng, zRange, angRange);
|
|
}
|
|
|
|
srcVect->z -= (2<<8);
|
|
}
|
|
|
|
// Hitscan weapon fired from actor (sprite s);
|
|
static void A_PreFireHitscan(const spritetype *pSprite, vec3_t *srcVect, int32_t *zvel, int *shootAng, int doSpread)
|
|
{
|
|
int const playerNum = A_FindPlayer(pSprite, NULL);
|
|
const DukePlayer_t *pPlayer = g_player[playerNum].ps;
|
|
int const playerDist = safeldist(pPlayer->i, pSprite);
|
|
|
|
*zvel = tabledivide32_noinline((pPlayer->pos.z - srcVect->z) << 8, playerDist);
|
|
|
|
srcVect->z -= ZOFFSET6;
|
|
|
|
if (pSprite->picnum != BOSS1)
|
|
{
|
|
Proj_MaybeAddSpread(doSpread, zvel, shootAng, 256, 64);
|
|
}
|
|
else
|
|
{
|
|
*shootAng = getangle(pPlayer->pos.x-srcVect->x, pPlayer->pos.y-srcVect->y);
|
|
|
|
Proj_MaybeAddSpread(doSpread, zvel, shootAng, 256, 128);
|
|
}
|
|
}
|
|
|
|
static int Proj_DoHitscan(int spriteNum, int32_t cstatmask, const vec3_t *srcVect, int zvel, int shootAng, hitdata_t *hitData)
|
|
{
|
|
spritetype *const pSprite = &sprite[spriteNum];
|
|
|
|
pSprite->cstat &= ~cstatmask;
|
|
zvel = A_GetShootZvel(zvel);
|
|
hitscan(srcVect, pSprite->sectnum, sintable[(shootAng + 512) & 2047], sintable[shootAng & 2047], zvel << 6, hitData, CLIPMASK1);
|
|
pSprite->cstat |= cstatmask;
|
|
|
|
return (hitData->sect < 0);
|
|
}
|
|
|
|
static void Proj_DoRandDecalSize(int spriteNum, int projecTile)
|
|
{
|
|
const projectile_t *const proj = Proj_GetProjectile(projecTile);
|
|
spritetype *const pSprite = &sprite[spriteNum];
|
|
|
|
if (proj->workslike & PROJECTILE_RANDDECALSIZE)
|
|
{
|
|
int projSize = (krand() & proj->xrepeat);
|
|
|
|
if (projSize < proj->yrepeat)
|
|
projSize = proj->yrepeat;
|
|
|
|
pSprite->xrepeat = projSize;
|
|
pSprite->yrepeat = projSize;
|
|
}
|
|
else
|
|
{
|
|
pSprite->xrepeat = proj->xrepeat;
|
|
pSprite->yrepeat = proj->yrepeat;
|
|
}
|
|
}
|
|
|
|
static int SectorContainsSE13(int sectNum)
|
|
{
|
|
if (sectNum >= 0)
|
|
{
|
|
for (bssize_t SPRITES_OF_SECT(sectNum, i))
|
|
{
|
|
if (sprite[i].statnum == STAT_EFFECTOR && sprite[i].lotag == SE_13_EXPLOSIVE)
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Maybe handle bit 2 (swap wall bottoms).
|
|
// (in that case walltype *hitwal may be stale)
|
|
static inline void HandleHitWall(hitdata_t *hitData)
|
|
{
|
|
uwalltype const * const hitWall = (uwalltype *)&wall[hitData->wall];
|
|
|
|
if ((hitWall->cstat & 2) && redwallp(hitWall) && (hitData->pos.z >= sector[hitWall->nextsector].floorz))
|
|
hitData->wall = hitWall->nextwall;
|
|
}
|
|
|
|
// Maybe damage a ceiling or floor as the consequence of projectile impact.
|
|
// Returns 1 if projectile hit a parallaxed ceiling.
|
|
// NOTE: Compare with Proj_MaybeDamageCF() in actors.c
|
|
static int Proj_MaybeDamageCF2(int zvel, int hitSect)
|
|
{
|
|
Bassert(hitSect >= 0);
|
|
|
|
if (zvel < 0)
|
|
{
|
|
if (sector[hitSect].ceilingstat&1)
|
|
return 1;
|
|
|
|
Sect_DamageCeilingOrFloor(0, hitSect);
|
|
}
|
|
else if (zvel > 0)
|
|
{
|
|
if (sector[hitSect].floorstat&1)
|
|
{
|
|
// Keep original Duke3D behavior: pass projectiles through
|
|
// parallaxed ceilings, but NOT through such floors.
|
|
return 0;
|
|
}
|
|
|
|
Sect_DamageCeilingOrFloor(1, hitSect);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Finish shooting hitscan weapon from player <p>. <k> is the inserted SHOTSPARK1.
|
|
// * <spawnObject> is passed to Proj_MaybeSpawn()
|
|
// * <decalTile> and <wallDamage> are for wall impact
|
|
// * <wallDamage> is passed to A_DamageWall()
|
|
// * <decalFlags> is for decals upon wall impact:
|
|
// 1: handle random decal size (tile <atwith>)
|
|
// 2: set cstat to wall-aligned + random x/y flip
|
|
//
|
|
// TODO: maybe split into 3 cases (hit neither wall nor sprite, hit sprite, hit wall)?
|
|
static int P_PostFireHitscan(int playerNum, int spriteNum, hitdata_t *hitData, int spriteOwner, int projecTile, int zvel,
|
|
int spawnTile, int decalTile, int wallDamage, int decalFlags)
|
|
{
|
|
if (hitData->wall == -1 && hitData->sprite == -1)
|
|
{
|
|
if (Proj_MaybeDamageCF2(zvel, hitData->sect))
|
|
{
|
|
sprite[spriteNum].xrepeat = 0;
|
|
sprite[spriteNum].yrepeat = 0;
|
|
return -1;
|
|
}
|
|
|
|
Proj_MaybeSpawn(spriteNum, spawnTile, hitData);
|
|
}
|
|
else if (hitData->sprite >= 0)
|
|
{
|
|
A_DamageObject(hitData->sprite, spriteNum);
|
|
|
|
if (sprite[hitData->sprite].picnum == APLAYER &&
|
|
(ud.ffire == 1 || (!GTFLAGS(GAMETYPE_PLAYERSFRIENDLY) && GTFLAGS(GAMETYPE_TDM) &&
|
|
g_player[P_Get(hitData->sprite)].ps->team != g_player[P_Get(spriteOwner)].ps->team)))
|
|
{
|
|
int jibSprite = A_Spawn(spriteNum, JIBS6);
|
|
|
|
sprite[spriteNum].xrepeat = sprite[spriteNum].yrepeat = 0;
|
|
sprite[jibSprite].z += ZOFFSET6;
|
|
sprite[jibSprite].xvel = 16;
|
|
sprite[jibSprite].xrepeat = sprite[jibSprite].yrepeat = 24;
|
|
sprite[jibSprite].ang += 64 - (krand() & 127);
|
|
}
|
|
else
|
|
{
|
|
Proj_MaybeSpawn(spriteNum, spawnTile, hitData);
|
|
}
|
|
|
|
if (playerNum >= 0 && CheckShootSwitchTile(sprite[hitData->sprite].picnum))
|
|
{
|
|
P_ActivateSwitch(playerNum, hitData->sprite, 1);
|
|
return -1;
|
|
}
|
|
}
|
|
else if (hitData->wall >= 0)
|
|
{
|
|
uwalltype const * const hitWall = (uwalltype *)&wall[hitData->wall];
|
|
|
|
Proj_MaybeSpawn(spriteNum, spawnTile, hitData);
|
|
|
|
if (CheckDoorTile(hitWall->picnum) == 1)
|
|
goto SKIPBULLETHOLE;
|
|
|
|
if (playerNum >= 0 && CheckShootSwitchTile(hitWall->picnum))
|
|
{
|
|
P_ActivateSwitch(playerNum, hitData->wall, 0);
|
|
return -1;
|
|
}
|
|
|
|
if (hitWall->hitag != 0 || (hitWall->nextwall >= 0 && wall[hitWall->nextwall].hitag != 0))
|
|
goto SKIPBULLETHOLE;
|
|
|
|
if ((hitData->sect >= 0 && sector[hitData->sect].lotag == 0) &&
|
|
(hitWall->overpicnum != BIGFORCE && (hitWall->cstat & 16) == 0) &&
|
|
((hitWall->nextsector >= 0 && sector[hitWall->nextsector].lotag == 0) || (hitWall->nextsector == -1 && sector[hitData->sect].lotag == 0)))
|
|
{
|
|
int decalSprite;
|
|
|
|
if (SectorContainsSE13(hitWall->nextsector))
|
|
goto SKIPBULLETHOLE;
|
|
|
|
for (SPRITES_OF(STAT_MISC, decalSprite))
|
|
if (sprite[decalSprite].picnum == decalTile && dist(&sprite[decalSprite], &sprite[spriteNum]) < (12 + (krand() & 7)))
|
|
goto SKIPBULLETHOLE;
|
|
|
|
if (decalTile >= 0)
|
|
{
|
|
decalSprite = A_Spawn(spriteNum, decalTile);
|
|
|
|
A_SetHitData(decalSprite, hitData);
|
|
|
|
if (!A_CheckSpriteFlags(decalSprite, SFLAG_DECAL))
|
|
actor[decalSprite].flags |= SFLAG_DECAL;
|
|
|
|
sprite[decalSprite].ang
|
|
= (getangle(hitWall->x - wall[hitWall->point2].x, hitWall->y - wall[hitWall->point2].y) + 1536) & 2047;
|
|
|
|
if (decalFlags & 1)
|
|
Proj_DoRandDecalSize(decalSprite, projecTile);
|
|
|
|
if (decalFlags & 2)
|
|
sprite[decalSprite].cstat = 16 + (krand() & (8 + 4));
|
|
|
|
A_SetSprite(decalSprite, CLIPMASK0);
|
|
|
|
// BULLETHOLE already adds itself to the deletion queue in
|
|
// A_Spawn(). However, some other tiles do as well.
|
|
if (decalTile != BULLETHOLE)
|
|
A_AddToDeleteQueue(decalSprite);
|
|
}
|
|
}
|
|
|
|
SKIPBULLETHOLE:
|
|
HandleHitWall(hitData);
|
|
|
|
A_DamageWall(spriteNum, hitData->wall, &hitData->pos, wallDamage);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Finish shooting hitscan weapon from actor (sprite <i>).
|
|
static int A_PostFireHitscan(const hitdata_t *hitData, int spriteNum, int projecTile, int zvel, int shootAng, int extra,
|
|
int spawnTile, int wallDamage)
|
|
{
|
|
int returnSprite = Proj_InsertShotspark(hitData, spriteNum, projecTile, 24, shootAng, extra);
|
|
|
|
if (hitData->sprite >= 0)
|
|
{
|
|
A_DamageObject(hitData->sprite, returnSprite);
|
|
|
|
if (sprite[hitData->sprite].picnum != APLAYER)
|
|
Proj_MaybeSpawn(returnSprite, spawnTile, hitData);
|
|
else
|
|
sprite[returnSprite].xrepeat = sprite[returnSprite].yrepeat = 0;
|
|
}
|
|
else if (hitData->wall >= 0)
|
|
{
|
|
A_DamageWall(returnSprite, hitData->wall, &hitData->pos, wallDamage);
|
|
Proj_MaybeSpawn(returnSprite, spawnTile, hitData);
|
|
}
|
|
else
|
|
{
|
|
if (Proj_MaybeDamageCF2(zvel, hitData->sect))
|
|
{
|
|
sprite[returnSprite].xrepeat = 0;
|
|
sprite[returnSprite].yrepeat = 0;
|
|
}
|
|
else Proj_MaybeSpawn(returnSprite, spawnTile, hitData);
|
|
}
|
|
|
|
return returnSprite;
|
|
}
|
|
|
|
// Common "spawn blood?" predicate.
|
|
// minzdiff: minimal "step" height for blood to be spawned
|
|
static int Proj_CheckBlood(vec3_t const *const srcVect, hitdata_t const *const hitData, int bloodRange, int minZdiff)
|
|
{
|
|
if (hitData->wall < 0 || hitData->sect < 0)
|
|
return 0;
|
|
|
|
uwalltype const *const hitWall = (uwalltype *)&wall[hitData->wall];
|
|
|
|
if ((FindDistance2D(srcVect->x - hitData->pos.x, srcVect->y - hitData->pos.y) < bloodRange)
|
|
&& (hitWall->overpicnum != BIGFORCE && (hitWall->cstat & 16) == 0)
|
|
&& (sector[hitData->sect].lotag == 0)
|
|
&& (hitWall->nextsector < 0 || (sector[hitWall->nextsector].lotag == 0 && sector[hitData->sect].lotag == 0
|
|
&& sector[hitData->sect].floorz - sector[hitWall->nextsector].floorz > minZdiff)))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void Proj_HandleKnee(hitdata_t *hitData, int spriteNum, int playerNum, int projecTile, int shootAng,
|
|
const projectile_t *proj, int inserttile,
|
|
int randomDamage, int spawnTile, int soundNum)
|
|
{
|
|
const DukePlayer_t *const pPlayer = playerNum >= 0 ? g_player[playerNum].ps : NULL;
|
|
|
|
int kneeSprite = A_InsertSprite(hitData->sect,hitData->pos.x,hitData->pos.y,hitData->pos.z,
|
|
inserttile,-15,0,0,shootAng,32,0,spriteNum,4);
|
|
|
|
if (proj != NULL)
|
|
{
|
|
// Custom projectiles.
|
|
SpriteProjectile[kneeSprite].workslike = Proj_GetProjectile(sprite[kneeSprite].picnum)->workslike;
|
|
sprite[kneeSprite].extra = proj->extra;
|
|
}
|
|
|
|
if (randomDamage > 0)
|
|
sprite[kneeSprite].extra += (krand()&randomDamage);
|
|
|
|
if (playerNum >= 0)
|
|
{
|
|
if (spawnTile >= 0)
|
|
{
|
|
int k = A_Spawn(kneeSprite, spawnTile);
|
|
sprite[k].z -= ZOFFSET3;
|
|
A_SetHitData(k, hitData);
|
|
}
|
|
|
|
if (soundNum >= 0)
|
|
A_PlaySound(soundNum, kneeSprite);
|
|
}
|
|
|
|
if (playerNum >= 0 && pPlayer->inv_amount[GET_STEROIDS] > 0 && pPlayer->inv_amount[GET_STEROIDS] < 400)
|
|
sprite[kneeSprite].extra += (pPlayer->max_player_health>>2);
|
|
|
|
if (hitData->sprite >= 0 && sprite[hitData->sprite].picnum != ACCESSSWITCH && sprite[hitData->sprite].picnum != ACCESSSWITCH2)
|
|
{
|
|
A_DamageObject(hitData->sprite, kneeSprite);
|
|
if (playerNum >= 0)
|
|
P_ActivateSwitch(playerNum, hitData->sprite,1);
|
|
}
|
|
else if (hitData->wall >= 0)
|
|
{
|
|
HandleHitWall(hitData);
|
|
|
|
if (wall[hitData->wall].picnum != ACCESSSWITCH && wall[hitData->wall].picnum != ACCESSSWITCH2)
|
|
{
|
|
A_DamageWall(kneeSprite, hitData->wall, &hitData->pos, projecTile);
|
|
if (playerNum >= 0)
|
|
P_ActivateSwitch(playerNum, hitData->wall,0);
|
|
}
|
|
}
|
|
}
|
|
|
|
#define MinibossScale(i, s) (((s)*sprite[i].yrepeat)/80)
|
|
|
|
static int A_ShootCustom(int const spriteNum, int const projecTile, int shootAng, vec3_t * const startPos)
|
|
{
|
|
/* Custom projectiles */
|
|
hitdata_t hitData;
|
|
projectile_t *const pProj = Proj_GetProjectile(projecTile);
|
|
spritetype *const pSprite = &sprite[spriteNum];
|
|
int const playerNum = (pSprite->picnum == APLAYER) ? P_GetP(pSprite) : -1;
|
|
DukePlayer_t *const pPlayer = playerNum >= 0 ? g_player[playerNum].ps : NULL;
|
|
|
|
#ifdef POLYMER
|
|
if (getrendermode() == REND_POLYMER && pProj->flashcolor)
|
|
{
|
|
int32_t x = ((sintable[(pSprite->ang + 512) & 2047]) >> 7), y = ((sintable[(pSprite->ang) & 2047]) >> 7);
|
|
|
|
pSprite->x += x;
|
|
pSprite->y += y;
|
|
G_AddGameLight(0, spriteNum, PHEIGHT, 8192, pProj->flashcolor, PR_LIGHT_PRIO_MAX_GAME);
|
|
actor[spriteNum].lightcount = 2;
|
|
pSprite->x -= x;
|
|
pSprite->y -= y;
|
|
}
|
|
#endif // POLYMER
|
|
|
|
if (pProj->offset == 0)
|
|
pProj->offset = 1;
|
|
|
|
int otherSprite;
|
|
int32_t zvel = 0;
|
|
|
|
switch (pProj->workslike & PROJECTILE_TYPE_MASK)
|
|
{
|
|
case PROJECTILE_HITSCAN:
|
|
if (!(pProj->workslike & PROJECTILE_NOSETOWNERSHADE) && pSprite->extra >= 0)
|
|
pSprite->shade = pProj->shade;
|
|
|
|
if (playerNum >= 0)
|
|
P_PreFireHitscan(spriteNum, playerNum, projecTile, startPos, &zvel, &shootAng,
|
|
pProj->workslike & PROJECTILE_ACCURATE_AUTOAIM, !(pProj->workslike & PROJECTILE_ACCURATE));
|
|
else
|
|
A_PreFireHitscan(pSprite, startPos, &zvel, &shootAng, !(pProj->workslike & PROJECTILE_ACCURATE));
|
|
|
|
if (Proj_DoHitscan(spriteNum, (pProj->cstat >= 0) ? pProj->cstat : 256 + 1, startPos, zvel, shootAng, &hitData))
|
|
return -1;
|
|
|
|
if (pProj->range > 0 && klabs(startPos->x - hitData.pos.x) + klabs(startPos->y - hitData.pos.y) > pProj->range)
|
|
return -1;
|
|
|
|
if (pProj->trail >= 0)
|
|
A_HitscanProjTrail(startPos, &hitData.pos, shootAng, projecTile, pSprite->sectnum);
|
|
|
|
if (pProj->workslike & PROJECTILE_WATERBUBBLES)
|
|
{
|
|
if ((krand() & 15) == 0 && sector[hitData.sect].lotag == ST_2_UNDERWATER)
|
|
A_DoWaterTracers(hitData.pos.x, hitData.pos.y, hitData.pos.z, startPos->x, startPos->y, startPos->z,
|
|
8 - (ud.multimode >> 1));
|
|
}
|
|
|
|
if (playerNum >= 0)
|
|
{
|
|
otherSprite = Proj_InsertShotspark(&hitData, spriteNum, projecTile, 10, shootAng, Proj_GetDamage(pProj));
|
|
|
|
if (P_PostFireHitscan(playerNum, otherSprite, &hitData, spriteNum, projecTile, zvel, projecTile, pProj->decal,
|
|
projecTile, 1 + 2) < 0)
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
otherSprite =
|
|
A_PostFireHitscan(&hitData, spriteNum, projecTile, zvel, shootAng, Proj_GetDamage(pProj), projecTile, projecTile);
|
|
}
|
|
|
|
if ((krand() & 255) < 4 && pProj->isound >= 0)
|
|
S_PlaySound3D(pProj->isound, otherSprite, &hitData.pos);
|
|
|
|
return -1;
|
|
|
|
case PROJECTILE_RPG:
|
|
if (!(pProj->workslike & PROJECTILE_NOSETOWNERSHADE) && pSprite->extra >= 0)
|
|
pSprite->shade = pProj->shade;
|
|
|
|
if (playerNum >= 0)
|
|
{
|
|
// NOTE: j is a SPRITE_INDEX
|
|
otherSprite = GetAutoAimAng(spriteNum, playerNum, projecTile, 8<<8, 0+2, startPos, pProj->vel, &zvel, &shootAng);
|
|
|
|
if (otherSprite < 0)
|
|
zvel = (100-pPlayer->horiz-pPlayer->horizoff)*(pProj->vel/8);
|
|
|
|
if (pProj->sound >= 0)
|
|
A_PlaySound(pProj->sound, spriteNum);
|
|
}
|
|
else
|
|
{
|
|
if (!(pProj->workslike & PROJECTILE_NOAIM))
|
|
{
|
|
int const otherPlayer = A_FindPlayer(pSprite, NULL);
|
|
int const otherPlayerDist = safeldist(g_player[otherPlayer].ps->i, pSprite);
|
|
|
|
shootAng = getangle(g_player[otherPlayer].ps->opos.x - startPos->x,
|
|
g_player[otherPlayer].ps->opos.y - startPos->y);
|
|
|
|
zvel = tabledivide32_noinline((g_player[otherPlayer].ps->opos.z - startPos->z) * pProj->vel, otherPlayerDist);
|
|
|
|
if (A_CheckEnemySprite(pSprite) && (AC_MOVFLAGS(pSprite, &actor[spriteNum]) & face_player_smart))
|
|
shootAng = pSprite->ang + (krand() & 31) - 16;
|
|
}
|
|
}
|
|
|
|
if (numplayers > 1 && g_netClient) return -1;
|
|
else
|
|
{
|
|
// l may be a SPRITE_INDEX, see above
|
|
int const l = (playerNum >= 0 && otherSprite >= 0) ? otherSprite : -1;
|
|
|
|
zvel = A_GetShootZvel(zvel);
|
|
otherSprite = A_InsertSprite(pSprite->sectnum,
|
|
startPos->x + tabledivide32_noinline(sintable[(348 + shootAng + 512) & 2047], pProj->offset),
|
|
startPos->y + tabledivide32_noinline(sintable[(shootAng + 348) & 2047], pProj->offset),
|
|
startPos->z - (1 << 8), projecTile, 0, 14, 14, shootAng, pProj->vel, zvel, spriteNum, 4);
|
|
|
|
|
|
if (pProj->workslike & PROJECTILE_RPG_IMPACT)
|
|
sprite[otherSprite].extra = Proj_GetDamage(pProj);
|
|
|
|
if (!(pProj->workslike & PROJECTILE_BOUNCESOFFWALLS))
|
|
sprite[otherSprite].yvel = l; // NOT_BOUNCESOFFWALLS_YVEL
|
|
else
|
|
{
|
|
sprite[otherSprite].yvel = (pProj->bounces >= 1) ? pProj->bounces : g_numFreezeBounces;
|
|
sprite[otherSprite].zvel -= (2 << 4);
|
|
}
|
|
|
|
sprite[otherSprite].pal = (pProj->pal >= 0) ? pProj->pal : 0;
|
|
sprite[otherSprite].xrepeat = pProj->xrepeat;
|
|
sprite[otherSprite].yrepeat = pProj->yrepeat;
|
|
sprite[otherSprite].cstat = (pProj->cstat >= 0) ? pProj->cstat : 128;
|
|
sprite[otherSprite].clipdist = (pProj->clipdist != 255) ? pProj->clipdist : 40;
|
|
SpriteProjectile[otherSprite] = *Proj_GetProjectile(sprite[otherSprite].picnum);
|
|
|
|
return otherSprite;
|
|
}
|
|
|
|
case PROJECTILE_KNEE:
|
|
if (playerNum >= 0)
|
|
{
|
|
zvel = (100 - pPlayer->horiz - pPlayer->horizoff) << 5;
|
|
startPos->z += (6 << 8);
|
|
shootAng += 15;
|
|
}
|
|
else if (!(pProj->workslike & PROJECTILE_NOAIM))
|
|
{
|
|
int32_t playerDist;
|
|
otherSprite = g_player[A_FindPlayer(pSprite, &playerDist)].ps->i;
|
|
zvel = tabledivide32_noinline((sprite[otherSprite].z - startPos->z) << 8, playerDist + 1);
|
|
shootAng = getangle(sprite[otherSprite].x - startPos->x, sprite[otherSprite].y - startPos->y);
|
|
}
|
|
|
|
Proj_DoHitscan(spriteNum, 0, startPos, zvel, shootAng, &hitData);
|
|
|
|
if (hitData.sect < 0) return -1;
|
|
|
|
if (pProj->range == 0)
|
|
pProj->range = 1024;
|
|
|
|
if (pProj->range > 0 && klabs(startPos->x - hitData.pos.x) + klabs(startPos->y - hitData.pos.y) > pProj->range)
|
|
return -1;
|
|
|
|
Proj_HandleKnee(&hitData, spriteNum, playerNum, projecTile, shootAng,
|
|
pProj, projecTile, pProj->extra_rand, pProj->spawns, pProj->sound);
|
|
|
|
return -1;
|
|
|
|
case PROJECTILE_BLOOD:
|
|
shootAng += 64 - (krand() & 127);
|
|
|
|
if (playerNum < 0)
|
|
shootAng += 1024;
|
|
|
|
zvel = 1024 - (krand() & 2047);
|
|
|
|
Proj_DoHitscan(spriteNum, 0, startPos, zvel, shootAng, &hitData);
|
|
|
|
if (pProj->range == 0)
|
|
pProj->range = 1024;
|
|
|
|
if (Proj_CheckBlood(startPos, &hitData, pProj->range, mulscale3(pProj->yrepeat, tilesiz[pProj->decal].y) << 8))
|
|
{
|
|
const uwalltype *const hitWall = (uwalltype *)&wall[hitData.wall];
|
|
|
|
if (FindDistance2D(hitWall->x - wall[hitWall->point2].x, hitWall->y - wall[hitWall->point2].y) >
|
|
(mulscale3(pProj->xrepeat + 8, tilesiz[pProj->decal].x)))
|
|
{
|
|
if (SectorContainsSE13(hitWall->nextsector))
|
|
return -1;
|
|
|
|
if (hitWall->nextwall >= 0 && wall[hitWall->nextwall].hitag != 0)
|
|
return -1;
|
|
|
|
if (hitWall->hitag == 0 && pProj->decal >= 0)
|
|
{
|
|
otherSprite = A_Spawn(spriteNum, pProj->decal);
|
|
|
|
A_SetHitData(otherSprite, &hitData);
|
|
|
|
if (!A_CheckSpriteFlags(otherSprite, SFLAG_DECAL))
|
|
actor[otherSprite].flags |= SFLAG_DECAL;
|
|
|
|
sprite[otherSprite].ang = getangle(hitWall->x - wall[hitWall->point2].x,
|
|
hitWall->y - wall[hitWall->point2].y) + 512;
|
|
Bmemcpy(&sprite[otherSprite], &hitData.pos, sizeof(vec3_t));
|
|
|
|
Proj_DoRandDecalSize(otherSprite, projecTile);
|
|
|
|
sprite[otherSprite].z += sprite[otherSprite].yrepeat << 8;
|
|
|
|
// sprite[spawned].cstat = 16+(krand()&12);
|
|
sprite[otherSprite].cstat = 16;
|
|
|
|
if (krand() & 1)
|
|
sprite[otherSprite].cstat |= 4;
|
|
|
|
if (krand() & 1)
|
|
sprite[otherSprite].cstat |= 8;
|
|
|
|
sprite[otherSprite].shade = sector[sprite[otherSprite].sectnum].floorshade;
|
|
|
|
A_SetSprite(otherSprite, CLIPMASK0);
|
|
A_AddToDeleteQueue(otherSprite);
|
|
changespritestat(otherSprite, 5);
|
|
}
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static int32_t A_ShootHardcoded(int spriteNum, int projecTile, int shootAng, vec3_t startPos,
|
|
spritetype *pSprite, int const playerNum, DukePlayer_t * const pPlayer)
|
|
{
|
|
hitdata_t hitData;
|
|
int const spriteSectnum = pSprite->sectnum;
|
|
int32_t Zvel;
|
|
int vel;
|
|
|
|
switch (DYNAMICTILEMAP(projecTile))
|
|
{
|
|
case BLOODSPLAT1__STATIC:
|
|
case BLOODSPLAT2__STATIC:
|
|
case BLOODSPLAT3__STATIC:
|
|
case BLOODSPLAT4__STATIC:
|
|
shootAng += 64 - (krand() & 127);
|
|
if (playerNum < 0)
|
|
shootAng += 1024;
|
|
Zvel = 1024 - (krand() & 2047);
|
|
// fall-through
|
|
case KNEE__STATIC:
|
|
if (projecTile == KNEE)
|
|
{
|
|
if (playerNum >= 0)
|
|
{
|
|
Zvel = (100 - pPlayer->horiz - pPlayer->horizoff) << 5;
|
|
startPos.z += (6 << 8);
|
|
shootAng += 15;
|
|
}
|
|
else
|
|
{
|
|
int32_t playerDist;
|
|
int const playerSprite = g_player[A_FindPlayer(pSprite, &playerDist)].ps->i;
|
|
Zvel = tabledivide32_noinline((sprite[playerSprite].z - startPos.z) << 8, playerDist + 1);
|
|
shootAng = getangle(sprite[playerSprite].x - startPos.x, sprite[playerSprite].y - startPos.y);
|
|
}
|
|
}
|
|
|
|
Proj_DoHitscan(spriteNum, 0, &startPos, Zvel, shootAng, &hitData);
|
|
|
|
if (projecTile >= BLOODSPLAT1 && projecTile <= BLOODSPLAT4)
|
|
{
|
|
if (Proj_CheckBlood(&startPos, &hitData, 1024, 16 << 8))
|
|
{
|
|
const uwalltype *const hitwal = (uwalltype *)&wall[hitData.wall];
|
|
|
|
if (SectorContainsSE13(hitwal->nextsector))
|
|
return -1;
|
|
|
|
if (hitwal->nextwall >= 0 && wall[hitwal->nextwall].hitag != 0)
|
|
return -1;
|
|
|
|
if (hitwal->hitag == 0)
|
|
{
|
|
int const spawnedSprite = A_Spawn(spriteNum, projecTile);
|
|
sprite[spawnedSprite].ang
|
|
= (getangle(hitwal->x - wall[hitwal->point2].x, hitwal->y - wall[hitwal->point2].y) + 1536) & 2047;
|
|
*(vec3_t *)&sprite[spawnedSprite] = hitData.pos;
|
|
sprite[spawnedSprite].cstat |= (krand() & 4);
|
|
A_SetSprite(spawnedSprite, CLIPMASK0);
|
|
setsprite(spawnedSprite, (vec3_t *)&sprite[spawnedSprite]);
|
|
if (PN(spriteNum) == OOZFILTER || PN(spriteNum) == NEWBEAST)
|
|
sprite[spawnedSprite].pal = 6;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
if (hitData.sect < 0)
|
|
break;
|
|
|
|
if (klabs(startPos.x - hitData.pos.x) + klabs(startPos.y - hitData.pos.y) < 1024)
|
|
Proj_HandleKnee(&hitData, spriteNum, playerNum, projecTile, shootAng, NULL, KNEE, 7, SMALLSMOKE, KICK_HIT);
|
|
break;
|
|
|
|
case SHOTSPARK1__STATIC:
|
|
case SHOTGUN__STATIC:
|
|
case CHAINGUN__STATIC:
|
|
{
|
|
if (pSprite->extra >= 0)
|
|
pSprite->shade = -96;
|
|
|
|
if (playerNum >= 0)
|
|
P_PreFireHitscan(spriteNum, playerNum, projecTile, &startPos, &Zvel, &shootAng,
|
|
projecTile == SHOTSPARK1__STATIC && !NAM_WW2GI, 1);
|
|
else
|
|
A_PreFireHitscan(pSprite, &startPos, &Zvel, &shootAng, 1);
|
|
|
|
if (Proj_DoHitscan(spriteNum, 256 + 1, &startPos, Zvel, shootAng, &hitData))
|
|
return -1;
|
|
|
|
if ((krand() & 15) == 0 && sector[hitData.sect].lotag == ST_2_UNDERWATER)
|
|
A_DoWaterTracers(hitData.pos.x, hitData.pos.y, hitData.pos.z, startPos.x, startPos.y, startPos.z, 8 - (ud.multimode >> 1));
|
|
|
|
int spawnedSprite;
|
|
|
|
if (playerNum >= 0)
|
|
{
|
|
spawnedSprite = Proj_InsertShotspark(&hitData, spriteNum, projecTile, 10, shootAng, G_DefaultActorHealth(projecTile) + (krand() % 6));
|
|
|
|
if (P_PostFireHitscan(playerNum, spawnedSprite, &hitData, spriteNum, projecTile, Zvel, -SMALLSMOKE, BULLETHOLE, SHOTSPARK1, 0) < 0)
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
spawnedSprite = A_PostFireHitscan(&hitData, spriteNum, projecTile, Zvel, shootAng, G_DefaultActorHealth(projecTile), -SMALLSMOKE,
|
|
SHOTSPARK1);
|
|
}
|
|
|
|
if ((krand() & 255) < 4)
|
|
S_PlaySound3D(PISTOL_RICOCHET, spawnedSprite, &hitData.pos);
|
|
|
|
return -1;
|
|
}
|
|
|
|
case GROWSPARK__STATIC:
|
|
{
|
|
if (playerNum >= 0)
|
|
P_PreFireHitscan(spriteNum, playerNum, projecTile, &startPos, &Zvel, &shootAng, 1, 1);
|
|
else
|
|
A_PreFireHitscan(pSprite, &startPos, &Zvel, &shootAng, 1);
|
|
|
|
if (Proj_DoHitscan(spriteNum, 256 + 1, &startPos, Zvel, shootAng, &hitData))
|
|
return -1;
|
|
|
|
int const otherSprite = A_InsertSprite(hitData.sect, hitData.pos.x, hitData.pos.y, hitData.pos.z, GROWSPARK, -16, 28, 28,
|
|
shootAng, 0, 0, spriteNum, 1);
|
|
|
|
sprite[otherSprite].pal = 2;
|
|
sprite[otherSprite].cstat |= 130;
|
|
sprite[otherSprite].xrepeat = sprite[otherSprite].yrepeat = 1;
|
|
A_SetHitData(otherSprite, &hitData);
|
|
|
|
if (hitData.wall == -1 && hitData.sprite == -1 && hitData.sect >= 0)
|
|
{
|
|
Proj_MaybeDamageCF2(Zvel, hitData.sect);
|
|
}
|
|
else if (hitData.sprite >= 0)
|
|
A_DamageObject(hitData.sprite, otherSprite);
|
|
else if (hitData.wall >= 0 && wall[hitData.wall].picnum != ACCESSSWITCH && wall[hitData.wall].picnum != ACCESSSWITCH2)
|
|
A_DamageWall(otherSprite, hitData.wall, &hitData.pos, projecTile);
|
|
}
|
|
break;
|
|
|
|
case FIRELASER__STATIC:
|
|
case SPIT__STATIC:
|
|
case COOLEXPLOSION1__STATIC:
|
|
{
|
|
if (pSprite->extra >= 0)
|
|
pSprite->shade = -96;
|
|
|
|
switch (projecTile)
|
|
{
|
|
case SPIT__STATIC: vel = 292; break;
|
|
case COOLEXPLOSION1__STATIC:
|
|
vel = (pSprite->picnum == BOSS2) ? 644 : 348;
|
|
startPos.z -= (4 << 7);
|
|
break;
|
|
case FIRELASER__STATIC:
|
|
default:
|
|
vel = 840;
|
|
startPos.z -= (4 << 7);
|
|
break;
|
|
}
|
|
|
|
if (playerNum >= 0)
|
|
{
|
|
if (GetAutoAimAng(spriteNum, playerNum, projecTile, -ZOFFSET4, 0, &startPos, vel, &Zvel, &shootAng) < 0)
|
|
Zvel = (100 - pPlayer->horiz - pPlayer->horizoff) * 98;
|
|
}
|
|
else
|
|
{
|
|
int const otherPlayer = A_FindPlayer(pSprite, NULL);
|
|
shootAng += 16 - (krand() & 31);
|
|
hitData.pos.x = safeldist(g_player[otherPlayer].ps->i, pSprite);
|
|
Zvel = tabledivide32_noinline((g_player[otherPlayer].ps->opos.z - startPos.z + (3 << 8)) * vel, hitData.pos.x);
|
|
}
|
|
|
|
Zvel = A_GetShootZvel(Zvel);
|
|
|
|
int spriteSize = (playerNum >= 0) ? 7 : 18;
|
|
|
|
if (projecTile == SPIT)
|
|
{
|
|
spriteSize = 18;
|
|
startPos.z -= (10 << 8);
|
|
}
|
|
|
|
int const returnSprite = A_InsertSprite(spriteSectnum, startPos.x, startPos.y, startPos.z, projecTile, -127, spriteSize, spriteSize,
|
|
shootAng, vel, Zvel, spriteNum, 4);
|
|
|
|
sprite[returnSprite].extra += (krand() & 7);
|
|
|
|
if (projecTile == COOLEXPLOSION1)
|
|
{
|
|
sprite[returnSprite].shade = 0;
|
|
|
|
if (PN(spriteNum) == BOSS2)
|
|
{
|
|
int const saveXvel = sprite[returnSprite].xvel;
|
|
sprite[returnSprite].xvel = MinibossScale(spriteNum, 1024);
|
|
A_SetSprite(returnSprite, CLIPMASK0);
|
|
sprite[returnSprite].xvel = saveXvel;
|
|
sprite[returnSprite].ang += 128 - (krand() & 255);
|
|
}
|
|
}
|
|
|
|
sprite[returnSprite].cstat = 128;
|
|
sprite[returnSprite].clipdist = 4;
|
|
|
|
shootAng = pSprite->ang + 32 - (krand() & 63);
|
|
Zvel += 512 - (krand() & 1023);
|
|
|
|
return returnSprite;
|
|
}
|
|
|
|
case FREEZEBLAST__STATIC: startPos.z += (3 << 8);
|
|
case RPG__STATIC:
|
|
{
|
|
// XXX: "CODEDUP"
|
|
if (pSprite->extra >= 0)
|
|
pSprite->shade = -96;
|
|
|
|
vel = 644;
|
|
|
|
int j = -1;
|
|
|
|
if (playerNum >= 0)
|
|
{
|
|
// NOTE: j is a SPRITE_INDEX
|
|
j = GetAutoAimAng(spriteNum, playerNum, projecTile, 8 << 8, 0 + 2, &startPos, vel, &Zvel, &shootAng);
|
|
|
|
if (j < 0)
|
|
Zvel = (100 - pPlayer->horiz - pPlayer->horizoff) * 81;
|
|
|
|
if (projecTile == RPG)
|
|
A_PlaySound(RPG_SHOOT, spriteNum);
|
|
}
|
|
else
|
|
{
|
|
// NOTE: j is a player index
|
|
j = A_FindPlayer(pSprite, NULL);
|
|
shootAng = getangle(g_player[j].ps->opos.x - startPos.x, g_player[j].ps->opos.y - startPos.y);
|
|
if (PN(spriteNum) == BOSS3)
|
|
startPos.z -= MinibossScale(spriteNum, ZOFFSET5);
|
|
else if (PN(spriteNum) == BOSS2)
|
|
{
|
|
vel += 128;
|
|
startPos.z += MinibossScale(spriteNum, 24 << 8);
|
|
}
|
|
|
|
Zvel = tabledivide32_noinline((g_player[j].ps->opos.z - startPos.z) * vel, safeldist(g_player[j].ps->i, pSprite));
|
|
|
|
if (A_CheckEnemySprite(pSprite) && (AC_MOVFLAGS(pSprite, &actor[spriteNum]) & face_player_smart))
|
|
shootAng = pSprite->ang + (krand() & 31) - 16;
|
|
}
|
|
|
|
if (numplayers > 1 && g_netClient)
|
|
return -1;
|
|
|
|
Zvel = A_GetShootZvel(Zvel);
|
|
int const returnSprite = A_InsertSprite(spriteSectnum, startPos.x + (sintable[(348 + shootAng + 512) & 2047] / 448),
|
|
startPos.y + (sintable[(shootAng + 348) & 2047] / 448), startPos.z - (1 << 8),
|
|
projecTile, 0, 14, 14, shootAng, vel, Zvel, spriteNum, 4);
|
|
spritetype *const pReturn = &sprite[returnSprite];
|
|
|
|
pReturn->extra += (krand() & 7);
|
|
if (projecTile != FREEZEBLAST)
|
|
pReturn->yvel = (playerNum >= 0 && j >= 0) ? j : -1; // RPG_YVEL
|
|
else
|
|
{
|
|
pReturn->yvel = g_numFreezeBounces;
|
|
pReturn->xrepeat >>= 1;
|
|
pReturn->yrepeat >>= 1;
|
|
pReturn->zvel -= (2 << 4);
|
|
}
|
|
|
|
if (playerNum == -1)
|
|
{
|
|
if (PN(spriteNum) == BOSS3)
|
|
{
|
|
if (krand() & 1)
|
|
{
|
|
pReturn->x -= MinibossScale(spriteNum, sintable[shootAng & 2047] >> 6);
|
|
pReturn->y -= MinibossScale(spriteNum, sintable[(shootAng + 1024 + 512) & 2047] >> 6);
|
|
pReturn->ang -= MinibossScale(spriteNum, 8);
|
|
}
|
|
else
|
|
{
|
|
pReturn->x += MinibossScale(spriteNum, sintable[shootAng & 2047] >> 6);
|
|
pReturn->y += MinibossScale(spriteNum, sintable[(shootAng + 1024 + 512) & 2047] >> 6);
|
|
pReturn->ang += MinibossScale(spriteNum, 4);
|
|
}
|
|
pReturn->xrepeat = MinibossScale(spriteNum, 42);
|
|
pReturn->yrepeat = MinibossScale(spriteNum, 42);
|
|
}
|
|
else if (PN(spriteNum) == BOSS2)
|
|
{
|
|
pReturn->x -= MinibossScale(spriteNum, sintable[shootAng & 2047] / 56);
|
|
pReturn->y -= MinibossScale(spriteNum, sintable[(shootAng + 1024 + 512) & 2047] / 56);
|
|
pReturn->ang -= MinibossScale(spriteNum, 8) + (krand() & 255) - 128;
|
|
pReturn->xrepeat = 24;
|
|
pReturn->yrepeat = 24;
|
|
}
|
|
else if (projecTile != FREEZEBLAST)
|
|
{
|
|
pReturn->xrepeat = 30;
|
|
pReturn->yrepeat = 30;
|
|
pReturn->extra >>= 2;
|
|
}
|
|
}
|
|
else if (PWEAPON(playerNum, g_player[playerNum].ps->curr_weapon, WorksLike) == DEVISTATOR_WEAPON)
|
|
{
|
|
pReturn->extra >>= 2;
|
|
pReturn->ang += 16 - (krand() & 31);
|
|
pReturn->zvel += 256 - (krand() & 511);
|
|
|
|
if (g_player[playerNum].ps->hbomb_hold_delay)
|
|
{
|
|
pReturn->x -= sintable[shootAng & 2047] / 644;
|
|
pReturn->y -= sintable[(shootAng + 1024 + 512) & 2047] / 644;
|
|
}
|
|
else
|
|
{
|
|
pReturn->x += sintable[shootAng & 2047] >> 8;
|
|
pReturn->y += sintable[(shootAng + 1024 + 512) & 2047] >> 8;
|
|
}
|
|
pReturn->xrepeat >>= 1;
|
|
pReturn->yrepeat >>= 1;
|
|
}
|
|
|
|
pReturn->cstat = 128;
|
|
pReturn->clipdist = (projecTile == RPG) ? 4 : 40;
|
|
|
|
return returnSprite;
|
|
}
|
|
|
|
case HANDHOLDINGLASER__STATIC:
|
|
{
|
|
int const zOffset = (playerNum >= 0) ? g_player[playerNum].ps->pyoff : 0;
|
|
Zvel = (playerNum >= 0) ? (100 - pPlayer->horiz - pPlayer->horizoff) * 32 : 0;
|
|
|
|
startPos.z -= zOffset;
|
|
Proj_DoHitscan(spriteNum, 0, &startPos, Zvel, shootAng, &hitData);
|
|
startPos.z += zOffset;
|
|
|
|
int placeMine = 0;
|
|
if (hitData.sprite >= 0)
|
|
break;
|
|
|
|
if (hitData.wall >= 0 && hitData.sect >= 0)
|
|
if (((hitData.pos.x - startPos.x) * (hitData.pos.x - startPos.x)
|
|
+ (hitData.pos.y - startPos.y) * (hitData.pos.y - startPos.y))
|
|
< (290 * 290))
|
|
{
|
|
// ST_2_UNDERWATER
|
|
if (wall[hitData.wall].nextsector >= 0)
|
|
{
|
|
if (sector[wall[hitData.wall].nextsector].lotag <= 2 && sector[hitData.sect].lotag <= 2)
|
|
placeMine = 1;
|
|
}
|
|
else if (sector[hitData.sect].lotag <= 2)
|
|
placeMine = 1;
|
|
}
|
|
|
|
if (placeMine == 1)
|
|
{
|
|
int32_t lTripBombControl = (playerNum < 0) ? 0 :
|
|
#ifdef LUNATIC
|
|
g_player[playerNum].ps->tripbombControl;
|
|
#else
|
|
Gv_GetVarByLabel("TRIPBOMB_CONTROL", TRIPBOMB_TRIPWIRE,
|
|
g_player[playerNum].ps->i, playerNum);
|
|
#endif
|
|
int const spawnedSprite = A_InsertSprite(hitData.sect, hitData.pos.x, hitData.pos.y, hitData.pos.z, TRIPBOMB, -16, 4, 5,
|
|
shootAng, 0, 0, spriteNum, 6);
|
|
if (lTripBombControl & TRIPBOMB_TIMER)
|
|
{
|
|
#ifdef LUNATIC
|
|
int32_t lLifetime = g_player[playerNum].ps->tripbombLifetime;
|
|
int32_t lLifetimeVar = g_player[playerNum].ps->tripbombLifetimeVar;
|
|
#else
|
|
int32_t lLifetime = Gv_GetVarByLabel("STICKYBOMB_LIFETIME", NAM_GRENADE_LIFETIME, g_player[playerNum].ps->i, playerNum);
|
|
int32_t lLifetimeVar
|
|
= Gv_GetVarByLabel("STICKYBOMB_LIFETIME_VAR", NAM_GRENADE_LIFETIME_VAR, g_player[playerNum].ps->i, playerNum);
|
|
#endif
|
|
// set timer. blows up when at zero....
|
|
actor[spawnedSprite].t_data[7] = lLifetime + mulscale(krand(), lLifetimeVar, 14) - lLifetimeVar;
|
|
// TIMER_CONTROL
|
|
actor[spawnedSprite].t_data[6] = 1;
|
|
}
|
|
else
|
|
sprite[spawnedSprite].hitag = spawnedSprite;
|
|
|
|
A_PlaySound(LASERTRIP_ONWALL, spawnedSprite);
|
|
sprite[spawnedSprite].xvel = -20;
|
|
A_SetSprite(spawnedSprite, CLIPMASK0);
|
|
sprite[spawnedSprite].cstat = 16;
|
|
|
|
{
|
|
int32_t p2 = wall[hitData.wall].point2;
|
|
int32_t a = getangle(wall[hitData.wall].x - wall[p2].x, wall[hitData.wall].y - wall[p2].y) - 512;
|
|
actor[spawnedSprite].t_data[5] = sprite[spawnedSprite].ang = a;
|
|
}
|
|
|
|
return spawnedSprite;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
case BOUNCEMINE__STATIC:
|
|
case MORTER__STATIC:
|
|
{
|
|
if (pSprite->extra >= 0)
|
|
pSprite->shade = -96;
|
|
|
|
int const playerSprite = g_player[A_FindPlayer(pSprite, NULL)].ps->i;
|
|
int const playerDist = ldist(&sprite[playerSprite], pSprite);
|
|
|
|
Zvel = -playerDist >> 1;
|
|
|
|
if (Zvel < -4096)
|
|
Zvel = -2048;
|
|
|
|
vel = playerDist >> 4;
|
|
Zvel = A_GetShootZvel(Zvel);
|
|
|
|
A_InsertSprite(spriteSectnum, startPos.x + (sintable[(512 + shootAng + 512) & 2047] >> 8),
|
|
startPos.y + (sintable[(shootAng + 512) & 2047] >> 8), startPos.z + (6 << 8), projecTile, -64, 32, 32,
|
|
shootAng, vel, Zvel, spriteNum, 1);
|
|
break;
|
|
}
|
|
|
|
case SHRINKER__STATIC:
|
|
{
|
|
if (pSprite->extra >= 0)
|
|
pSprite->shade = -96;
|
|
|
|
if (playerNum >= 0)
|
|
{
|
|
if (GetAutoAimAng(spriteNum, playerNum, projecTile, ZOFFSET6, 0, &startPos, 768, &Zvel, &shootAng) < 0)
|
|
Zvel = (100 - pPlayer->horiz - pPlayer->horizoff) * 98;
|
|
}
|
|
else if (pSprite->statnum != STAT_EFFECTOR)
|
|
{
|
|
int const otherPlayer = A_FindPlayer(pSprite, NULL);
|
|
Zvel = tabledivide32_noinline((g_player[otherPlayer].ps->opos.z - startPos.z) * 512,
|
|
safeldist(g_player[otherPlayer].ps->i, pSprite));
|
|
}
|
|
else
|
|
Zvel = 0;
|
|
|
|
Zvel = A_GetShootZvel(Zvel);
|
|
int const returnSprite = A_InsertSprite(spriteSectnum, startPos.x + (sintable[(512 + shootAng + 512) & 2047] >> 12),
|
|
startPos.y + (sintable[(shootAng + 512) & 2047] >> 12), startPos.z + (2 << 8),
|
|
SHRINKSPARK, -16, 28, 28, shootAng, 768, Zvel, spriteNum, 4);
|
|
sprite[returnSprite].cstat = 128;
|
|
sprite[returnSprite].clipdist = 32;
|
|
|
|
return returnSprite;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int A_ShootWithZvel(int spriteNum, int projecTile, int forceZvel)
|
|
{
|
|
Bassert(projecTile >= 0);
|
|
|
|
spritetype *const pSprite = &sprite[spriteNum];
|
|
int const playerNum = (pSprite->picnum == APLAYER) ? P_GetP(pSprite) : -1;
|
|
DukePlayer_t *const pPlayer = playerNum >= 0 ? g_player[playerNum].ps : NULL;
|
|
|
|
if (forceZvel != SHOOT_HARDCODED_ZVEL)
|
|
{
|
|
g_overrideShootZvel = 1;
|
|
g_shootZvel = forceZvel;
|
|
}
|
|
else
|
|
g_overrideShootZvel = 0;
|
|
|
|
int shootAng;
|
|
vec3_t startPos;
|
|
|
|
if (pSprite->picnum == APLAYER)
|
|
{
|
|
startPos = *(vec3_t *)pPlayer;
|
|
startPos.z += pPlayer->pyoff + ZOFFSET6;
|
|
shootAng = pPlayer->ang;
|
|
pPlayer->crack_time = 777;
|
|
}
|
|
else
|
|
{
|
|
shootAng = pSprite->ang;
|
|
startPos = *(vec3_t *)pSprite;
|
|
startPos.z -= (((pSprite->yrepeat * tilesiz[pSprite->picnum].y)<<1) - ZOFFSET6);
|
|
|
|
if (pSprite->picnum != ROTATEGUN)
|
|
{
|
|
startPos.z -= (7<<8);
|
|
|
|
if (A_CheckEnemySprite(pSprite) && PN(spriteNum) != COMMANDER)
|
|
{
|
|
startPos.x += (sintable[(shootAng+1024+96)&2047]>>7);
|
|
startPos.y += (sintable[(shootAng+512+96)&2047]>>7);
|
|
}
|
|
}
|
|
|
|
#ifdef POLYMER
|
|
switch (DYNAMICTILEMAP(projecTile))
|
|
{
|
|
case FIRELASER__STATIC:
|
|
case SHOTGUN__STATIC:
|
|
case SHOTSPARK1__STATIC:
|
|
case CHAINGUN__STATIC:
|
|
case RPG__STATIC:
|
|
case MORTER__STATIC:
|
|
{
|
|
vec2_t const v = { ((sintable[(pSprite->ang + 512) & 2047]) >> 7),
|
|
((sintable[(pSprite->ang) & 2047]) >> 7) };
|
|
|
|
pSprite->x += v.x;
|
|
pSprite->y += v.y;
|
|
G_AddGameLight(0, spriteNum, PHEIGHT, 8192, 255 + (95 << 8), PR_LIGHT_PRIO_MAX_GAME);
|
|
actor[spriteNum].lightcount = 2;
|
|
pSprite->x -= v.x;
|
|
pSprite->y -= v.y;
|
|
}
|
|
|
|
break;
|
|
}
|
|
#endif // POLYMER
|
|
}
|
|
|
|
return A_CheckSpriteTileFlags(projecTile, SFLAG_PROJECTILE)
|
|
? A_ShootCustom(spriteNum, projecTile, shootAng, &startPos)
|
|
: A_ShootHardcoded(spriteNum, projecTile, shootAng, startPos, pSprite, playerNum, pPlayer);
|
|
}
|
|
|
|
|
|
//////////////////// HUD WEAPON / MISC. DISPLAY CODE ////////////////////
|
|
|
|
static void P_DisplaySpit(void)
|
|
{
|
|
DukePlayer_t *const pPlayer = g_player[screenpeek].ps;
|
|
int const loogCounter = pPlayer->loogcnt;
|
|
|
|
if (loogCounter == 0)
|
|
return;
|
|
|
|
if (VM_OnEvent(EVENT_DISPLAYSPIT, pPlayer->i, screenpeek) != 0)
|
|
return;
|
|
|
|
int const rotY = loogCounter<<2;
|
|
|
|
for (bssize_t i=0; i < pPlayer->numloogs; i++)
|
|
{
|
|
int const rotAng = klabs(sintable[((loogCounter + i) << 5) & 2047]) >> 5;
|
|
int const rotZoom = 4096 + ((loogCounter + i) << 9);
|
|
int const rotX = (-g_player[screenpeek].inputBits->avel >> 1) + (sintable[((loogCounter + i) << 6) & 2047] >> 10);
|
|
|
|
rotatesprite_fs((pPlayer->loogiex[i] + rotX) << 16, (200 + pPlayer->loogiey[i] - rotY) << 16, rotZoom - (i << 8),
|
|
256 - rotAng, LOOGIE, 0, 0, 2);
|
|
}
|
|
}
|
|
|
|
int P_GetHudPal(const DukePlayer_t *p)
|
|
{
|
|
if (sprite[p->i].pal == 1)
|
|
return 1;
|
|
|
|
if (p->cursectnum >= 0)
|
|
{
|
|
int const hudPal = sector[p->cursectnum].floorpal;
|
|
if (!g_noFloorPal[hudPal])
|
|
return hudPal;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int P_DisplayFist(int const fistShade)
|
|
{
|
|
DukePlayer_t const *const pPlayer = g_player[screenpeek].ps;
|
|
int fistInc = pPlayer->fist_incs;
|
|
|
|
if (fistInc > 32)
|
|
fistInc = 32;
|
|
|
|
if (fistInc <= 0)
|
|
return 0;
|
|
|
|
switch (VM_OnEvent(EVENT_DISPLAYFIST, pPlayer->i, screenpeek))
|
|
{
|
|
case 1: return 1;
|
|
case -1: return 0;
|
|
}
|
|
|
|
int const fistY = klabs(pPlayer->look_ang) / 9;
|
|
int const fistZoom = clamp(65536 - (sintable[(512 + (fistInc << 6)) & 2047] << 2), 40920, 90612);
|
|
int const fistYOffset = 194 + (sintable[((6 + fistInc) << 7) & 2047] >> 9);
|
|
int const fistPal = P_GetHudPal(pPlayer);
|
|
int wx[2] = { windowxy1.x, windowxy2.x };
|
|
|
|
#ifdef SPLITSCREEN_MOD_HACKS
|
|
// XXX: this is outdated, doesn't handle above/below split.
|
|
if (g_fakeMultiMode==2)
|
|
wx[(g_snum==0)] = (wx[0]+wx[1])/2+1;
|
|
#endif
|
|
|
|
rotatesprite((-fistInc + 222 + (g_player[screenpeek].inputBits->avel >> 5)) << 16, (fistY + fistYOffset) << 16,
|
|
fistZoom, 0, FIST, fistShade, fistPal, 2, wx[0], windowxy1.y, wx[1], windowxy2.y);
|
|
|
|
return 1;
|
|
}
|
|
|
|
#define DRAWEAP_CENTER 262144
|
|
#define weapsc(sc) scale(sc, ud.weaponscale, 100)
|
|
|
|
static int32_t g_dts_yadd;
|
|
|
|
static void G_DrawTileScaled(int drawX, int drawY, int tileNum, int drawShade, int drawBits, int drawPal)
|
|
{
|
|
int32_t wx[2] = { windowxy1.x, windowxy2.x };
|
|
int32_t wy[2] = { windowxy1.y, windowxy2.y };
|
|
|
|
int drawYOffset = 0;
|
|
int drawXOffset = 192;
|
|
|
|
switch (hudweap.cur)
|
|
{
|
|
case DEVISTATOR_WEAPON:
|
|
case TRIPBOMB_WEAPON:
|
|
drawXOffset = 160;
|
|
break;
|
|
default:
|
|
if (drawBits & DRAWEAP_CENTER)
|
|
{
|
|
drawXOffset = 160;
|
|
drawBits &= ~DRAWEAP_CENTER;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// bit 4 means "flip x" for G_DrawTileScaled
|
|
int const drawAng = (drawBits & 4) ? 1024 : 0;
|
|
|
|
#ifdef SPLITSCREEN_MOD_HACKS
|
|
if (g_fakeMultiMode==2)
|
|
{
|
|
int const sideBySide = (ud.screen_size!=0);
|
|
|
|
// splitscreen HACK
|
|
drawBits &= ~(1024|512|256);
|
|
if (sideBySide)
|
|
{
|
|
drawBits &= ~8;
|
|
wx[(g_snum==0)] = (wx[0]+wx[1])/2 + 2;
|
|
}
|
|
else
|
|
{
|
|
drawBits |= 8;
|
|
if (g_snum==0)
|
|
drawYOffset = -(100<<16);
|
|
wy[(g_snum==0)] = (wy[0]+wy[1])/2 + 2;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef USE_OPENGL
|
|
if (getrendermode() >= REND_POLYMOST && usemodels && md_tilehasmodel(tileNum,drawPal) >= 0)
|
|
drawY += (224-weapsc(224));
|
|
#endif
|
|
rotatesprite(weapsc(drawX<<16) + ((drawXOffset-weapsc(drawXOffset))<<16),
|
|
weapsc((drawY<<16) + g_dts_yadd) + ((200-weapsc(200))<<16) + drawYOffset,
|
|
weapsc(65536L),drawAng,tileNum,drawShade,drawPal,(2|drawBits),
|
|
wx[0],wy[0], wx[1],wy[1]);
|
|
}
|
|
|
|
static void G_DrawWeaponTile(int weaponX, int weaponY, int weaponTile, int weaponShade, int weaponBits, int weaponPal,
|
|
uint8_t drawSlot)
|
|
{
|
|
static int shadef[2] = { 0, 0 };
|
|
static int palf[2] = { 0, 0 };
|
|
|
|
// sanity checking the slot value
|
|
if (drawSlot > 1)
|
|
drawSlot = 1;
|
|
|
|
// basic fading between player weapon shades
|
|
if (shadef[drawSlot] != weaponShade && (!weaponPal || palf[drawSlot] == weaponPal))
|
|
{
|
|
shadef[drawSlot] += (weaponShade - shadef[drawSlot]) >> 2;
|
|
|
|
if (!((weaponShade - shadef[drawSlot]) >> 2))
|
|
shadef[drawSlot] = logapproach(shadef[drawSlot], weaponShade);
|
|
}
|
|
else
|
|
shadef[drawSlot] = weaponShade;
|
|
|
|
palf[drawSlot] = weaponPal;
|
|
|
|
#ifdef USE_OPENGL
|
|
if (getrendermode() >= REND_POLYMOST)
|
|
{
|
|
if (weaponTile >= CHAINGUN + 1 && weaponTile <= CHAINGUN + 4)
|
|
{
|
|
if (!usemodels || md_tilehasmodel(weaponTile, weaponPal) < 0)
|
|
{
|
|
// HACK: Draw the upper part of the chaingun two screen
|
|
// pixels (not texels; multiplied by weapon scale) lower
|
|
// first, preventing ugly horizontal seam.
|
|
g_dts_yadd = tabledivide32_noinline(65536 * 2 * 200, ydim);
|
|
G_DrawTileScaled(weaponX, weaponY, weaponTile, shadef[drawSlot], weaponBits, weaponPal);
|
|
g_dts_yadd = 0;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
G_DrawTileScaled(weaponX, weaponY, weaponTile, shadef[drawSlot], weaponBits, weaponPal);
|
|
}
|
|
|
|
static inline void G_DrawWeaponTileWithID(int uniqueID, int weaponX, int weaponY, int weaponTile, int weaponShade,
|
|
int weaponBits, int p, uint8_t slot)
|
|
{
|
|
int lastUniqueID = guniqhudid;
|
|
guniqhudid = uniqueID;
|
|
|
|
G_DrawWeaponTile(weaponX, weaponY, weaponTile, weaponShade, weaponBits, p, slot);
|
|
|
|
guniqhudid = lastUniqueID;
|
|
}
|
|
|
|
static int P_DisplayKnee(int kneeShade)
|
|
{
|
|
static int8_t const knee_y[] = { 0, -8, -16, -32, -64, -84, -108, -108, -108, -72, -32, -8 };
|
|
const DukePlayer_t *const ps = g_player[screenpeek].ps;
|
|
|
|
if (ps->knee_incs == 0)
|
|
return 0;
|
|
|
|
switch (VM_OnEvent(EVENT_DISPLAYKNEE, ps->i, screenpeek))
|
|
{
|
|
case 1: return 1;
|
|
case -1: return 0;
|
|
}
|
|
|
|
if (ps->knee_incs >= ARRAY_SIZE(knee_y) || sprite[ps->i].extra <= 0)
|
|
return 0;
|
|
|
|
int const kneeY = knee_y[ps->knee_incs] + (klabs(ps->look_ang) / 9) - (ps->hard_landing << 3);
|
|
int kneePal = P_GetHudPal(ps);
|
|
|
|
if (kneePal == 0)
|
|
kneePal = ps->palookup;
|
|
|
|
G_DrawTileScaled(105+(g_player[screenpeek].inputBits->avel>>5)-(ps->look_ang>>1)+(knee_y[ps->knee_incs]>>2),
|
|
kneeY+280-((ps->horiz-ps->horizoff)>>4),KNEE,kneeShade,4+DRAWEAP_CENTER,kneePal);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int P_DisplayKnuckles(int knuckleShade)
|
|
{
|
|
if (WW2GI)
|
|
return 0;
|
|
|
|
const DukePlayer_t *const pPlayer = g_player[screenpeek].ps;
|
|
|
|
if (pPlayer->knuckle_incs == 0)
|
|
return 0;
|
|
|
|
static int8_t const knuckleFrames[] = { 0, 1, 2, 2, 3, 3, 3, 2, 2, 1, 0 };
|
|
|
|
switch (VM_OnEvent(EVENT_DISPLAYKNUCKLES, pPlayer->i, screenpeek))
|
|
{
|
|
case 1: return 1;
|
|
case -1: return 0;
|
|
}
|
|
|
|
if ((unsigned) (pPlayer->knuckle_incs>>1) >= ARRAY_SIZE(knuckleFrames) || sprite[pPlayer->i].extra <= 0)
|
|
return 0;
|
|
|
|
int const knuckleY = (klabs(pPlayer->look_ang) / 9) - (pPlayer->hard_landing << 3);
|
|
int const knucklePal = P_GetHudPal(pPlayer);
|
|
|
|
G_DrawTileScaled(160 + (g_player[screenpeek].inputBits->avel >> 5) - (pPlayer->look_ang >> 1),
|
|
knuckleY + 180 - ((pPlayer->horiz - pPlayer->horizoff) >> 4),
|
|
CRACKKNUCKLES + knuckleFrames[pPlayer->knuckle_incs >> 1], knuckleShade, 4 + DRAWEAP_CENTER,
|
|
knucklePal);
|
|
|
|
return 1;
|
|
}
|
|
|
|
#if !defined LUNATIC
|
|
// Set C-CON's WEAPON and WORKSLIKE gamevars.
|
|
void P_SetWeaponGamevars(int playerNum, const DukePlayer_t * const pPlayer)
|
|
{
|
|
Gv_SetVar(g_weaponVarID, pPlayer->curr_weapon, pPlayer->i, playerNum);
|
|
Gv_SetVar(g_worksLikeVarID,
|
|
((unsigned)pPlayer->curr_weapon < MAX_WEAPONS) ? PWEAPON(playerNum, pPlayer->curr_weapon, WorksLike) : -1,
|
|
pPlayer->i, playerNum);
|
|
}
|
|
#endif
|
|
|
|
static void P_FireWeapon(int playerNum)
|
|
{
|
|
DukePlayer_t *const pPlayer = g_player[playerNum].ps;
|
|
|
|
if (VM_OnEvent(EVENT_DOFIRE, pPlayer->i, playerNum) || pPlayer->weapon_pos != 0)
|
|
return;
|
|
|
|
if (PWEAPON(playerNum, pPlayer->curr_weapon, WorksLike) != KNEE_WEAPON)
|
|
pPlayer->ammo_amount[pPlayer->curr_weapon]--;
|
|
|
|
if (PWEAPON(playerNum, pPlayer->curr_weapon, FireSound) > 0)
|
|
A_PlaySound(PWEAPON(playerNum, pPlayer->curr_weapon, FireSound), pPlayer->i);
|
|
|
|
P_SetWeaponGamevars(playerNum, pPlayer);
|
|
// OSD_Printf("doing %d %d %d\n",PWEAPON(snum, p->curr_weapon, Shoots),p->curr_weapon,snum);
|
|
A_Shoot(pPlayer->i, PWEAPON(playerNum, pPlayer->curr_weapon, Shoots));
|
|
|
|
for (bssize_t burstFire = PWEAPON(playerNum, pPlayer->curr_weapon, ShotsPerBurst) - 1; burstFire > 0; --burstFire)
|
|
{
|
|
if (PWEAPON(playerNum, pPlayer->curr_weapon, Flags) & WEAPON_FIREEVERYOTHER)
|
|
{
|
|
// devastator hack to make the projectiles fire on a delay from player code
|
|
actor[pPlayer->i].t_data[7] = (PWEAPON(playerNum, pPlayer->curr_weapon, ShotsPerBurst)) << 1;
|
|
}
|
|
else
|
|
{
|
|
if (PWEAPON(playerNum, pPlayer->curr_weapon, Flags) & WEAPON_AMMOPERSHOT &&
|
|
PWEAPON(playerNum, pPlayer->curr_weapon, WorksLike) != KNEE_WEAPON)
|
|
{
|
|
if (pPlayer->ammo_amount[pPlayer->curr_weapon] > 0)
|
|
pPlayer->ammo_amount[pPlayer->curr_weapon]--;
|
|
else
|
|
break;
|
|
}
|
|
|
|
A_Shoot(pPlayer->i, PWEAPON(playerNum, pPlayer->curr_weapon, Shoots));
|
|
}
|
|
}
|
|
|
|
if (!(PWEAPON(playerNum, pPlayer->curr_weapon, Flags) & WEAPON_NOVISIBLE))
|
|
{
|
|
#ifdef POLYMER
|
|
spritetype *s = &sprite[pPlayer->i];
|
|
int32_t x = ((sintable[(s->ang + 512) & 2047]) >> 7), y = ((sintable[(s->ang) & 2047]) >> 7);
|
|
|
|
s->x += x;
|
|
s->y += y;
|
|
G_AddGameLight(0, pPlayer->i, PHEIGHT, 8192, PWEAPON(playerNum, pPlayer->curr_weapon, FlashColor),
|
|
PR_LIGHT_PRIO_MAX_GAME);
|
|
actor[pPlayer->i].lightcount = 2;
|
|
s->x -= x;
|
|
s->y -= y;
|
|
#endif // POLYMER
|
|
pPlayer->visibility = 0;
|
|
}
|
|
}
|
|
|
|
static void P_DoWeaponSpawn(int playerNum)
|
|
{
|
|
const DukePlayer_t *const pPlayer = g_player[playerNum].ps;
|
|
|
|
// NOTE: For the 'Spawn' member, 0 means 'none', too (originally so,
|
|
// i.e. legacy). The check for <0 was added to the check because mod
|
|
// authors (rightly) assumed that -1 is the no-op value.
|
|
if (PWEAPON(playerNum, pPlayer->curr_weapon, Spawn) <= 0) // <=0 : AMC TC beta/RC2 has WEAPONx_SPAWN -1
|
|
return;
|
|
|
|
int newSprite = A_Spawn(pPlayer->i, PWEAPON(playerNum, pPlayer->curr_weapon, Spawn));
|
|
|
|
if ((PWEAPON(playerNum, pPlayer->curr_weapon, Flags) & WEAPON_SPAWNTYPE3))
|
|
{
|
|
// like chaingun shells
|
|
sprite[newSprite].ang += 1024;
|
|
sprite[newSprite].ang &= 2047;
|
|
sprite[newSprite].xvel += 32;
|
|
sprite[newSprite].z += (3<<8);
|
|
}
|
|
|
|
A_SetSprite(newSprite,CLIPMASK0);
|
|
}
|
|
|
|
void P_DisplayScuba(void)
|
|
{
|
|
if (g_player[screenpeek].ps->scuba_on)
|
|
{
|
|
const DukePlayer_t *const pPlayer = g_player[screenpeek].ps;
|
|
|
|
if (VM_OnEvent(EVENT_DISPLAYSCUBA, pPlayer->i, screenpeek) != 0)
|
|
return;
|
|
|
|
int const scubaPal = P_GetHudPal(pPlayer);
|
|
|
|
#ifdef SPLITSCREEN_MOD_HACKS
|
|
g_snum = screenpeek;
|
|
#endif
|
|
|
|
// this is a hack to hide the seam that appears between the two halves of the mask in GL
|
|
#ifdef USE_OPENGL
|
|
if (getrendermode() >= REND_POLYMOST)
|
|
G_DrawTileScaled(44, (200 - tilesiz[SCUBAMASK].y), SCUBAMASK, 0, 2 + 16 + DRAWEAP_CENTER, scubaPal);
|
|
#endif
|
|
G_DrawTileScaled(43, (200 - tilesiz[SCUBAMASK].y), SCUBAMASK, 0, 2 + 16 + DRAWEAP_CENTER, scubaPal);
|
|
G_DrawTileScaled(320 - 43, (200 - tilesiz[SCUBAMASK].y), SCUBAMASK, 0, 2 + 4 + 16 + DRAWEAP_CENTER, scubaPal);
|
|
}
|
|
}
|
|
|
|
static int8_t const access_tip_y [] ={
|
|
0, -8, -16, -32, -64, -84, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -96, -72, -64, -32, -16,
|
|
/* EDuke32: */ 0, 16, 32, 48,
|
|
// At y coord 64, the hand is already not shown.
|
|
};
|
|
|
|
static int P_DisplayTip(int tipShade)
|
|
{
|
|
const DukePlayer_t *const pPlayer = g_player[screenpeek].ps;
|
|
|
|
if (pPlayer->tipincs == 0)
|
|
return 0;
|
|
|
|
switch (VM_OnEvent(EVENT_DISPLAYTIP, pPlayer->i, screenpeek))
|
|
{
|
|
case 1: return 1;
|
|
case -1: return 0;
|
|
}
|
|
|
|
// Report that the tipping hand has been drawn so that the otherwise
|
|
// selected weapon is not drawn.
|
|
if ((unsigned)pPlayer->tipincs >= ARRAY_SIZE(access_tip_y))
|
|
return 1;
|
|
|
|
int const tipY = (klabs(pPlayer->look_ang) / 9) - (pPlayer->hard_landing << 3);
|
|
int const tipPal = P_GetHudPal(pPlayer);
|
|
int const tipYOffset = access_tip_y[pPlayer->tipincs] >> 1;
|
|
|
|
guniqhudid = 201;
|
|
|
|
G_DrawTileScaled(170 + (g_player[screenpeek].inputBits->avel >> 5) - (pPlayer->look_ang >> 1),
|
|
tipYOffset + tipY + 240 - ((pPlayer->horiz - pPlayer->horizoff) >> 4),
|
|
TIP + ((26 - pPlayer->tipincs) >> 4), tipShade, DRAWEAP_CENTER, tipPal);
|
|
|
|
guniqhudid = 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int P_DisplayAccess(int accessShade)
|
|
{
|
|
const DukePlayer_t *const pSprite = g_player[screenpeek].ps;
|
|
|
|
if (pSprite->access_incs == 0)
|
|
return 0;
|
|
|
|
switch (VM_OnEvent(EVENT_DISPLAYACCESS, pSprite->i, screenpeek))
|
|
{
|
|
case 1: return 1;
|
|
case -1: return 0;
|
|
}
|
|
|
|
if ((unsigned)pSprite->access_incs >= ARRAY_SIZE(access_tip_y)-4 || sprite[pSprite->i].extra <= 0)
|
|
return 1;
|
|
|
|
int const accessX = access_tip_y[pSprite->access_incs] >> 2;
|
|
int const accessY = access_tip_y[pSprite->access_incs] + (klabs(pSprite->look_ang) / 9) - (pSprite->hard_landing << 3);
|
|
int const accessPal = (pSprite->access_spritenum >= 0) ? sprite[pSprite->access_spritenum].pal : 0;
|
|
|
|
guniqhudid = 200;
|
|
|
|
if ((pSprite->access_incs - 3) > 0 && (pSprite->access_incs - 3) >> 3)
|
|
{
|
|
G_DrawTileScaled(170 + (g_player[screenpeek].inputBits->avel >> 5) - (pSprite->look_ang >> 1) + accessX,
|
|
accessY + 266 - ((pSprite->horiz - pSprite->horizoff) >> 4),
|
|
HANDHOLDINGLASER + (pSprite->access_incs >> 3), accessShade, DRAWEAP_CENTER, accessPal);
|
|
}
|
|
else
|
|
{
|
|
G_DrawTileScaled(170 + (g_player[screenpeek].inputBits->avel >> 5) - (pSprite->look_ang >> 1) + accessX,
|
|
accessY + 266 - ((pSprite->horiz - pSprite->horizoff) >> 4), HANDHOLDINGACCESS, accessShade,
|
|
4 + DRAWEAP_CENTER, accessPal);
|
|
}
|
|
|
|
guniqhudid = 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
static int32_t fistPos;
|
|
|
|
void P_DisplayWeapon(void)
|
|
{
|
|
DukePlayer_t *const pPlayer = g_player[screenpeek].ps;
|
|
const uint8_t *const weaponFrame = &pPlayer->kickback_pic;
|
|
|
|
int currentWeapon;
|
|
int32_t weaponBits = 0;
|
|
|
|
#ifdef SPLITSCREEN_MOD_HACKS
|
|
g_snum = screenpeek;
|
|
#endif
|
|
|
|
if (pPlayer->newowner >= 0 || ud.camerasprite >= 0 || pPlayer->over_shoulder_on > 0
|
|
|| (sprite[pPlayer->i].pal != 1 && sprite[pPlayer->i].extra <= 0))
|
|
return;
|
|
|
|
int weaponX = (160) - 90;
|
|
int weaponY = klabs(pPlayer->look_ang) / 9;
|
|
int weaponYOffset = 80 - (pPlayer->weapon_pos * pPlayer->weapon_pos);
|
|
int weaponShade = sprite[pPlayer->i].shade <= 24 ? sprite[pPlayer->i].shade : 24;
|
|
|
|
if (P_DisplayFist(weaponShade) || P_DisplayKnuckles(weaponShade) || P_DisplayTip(weaponShade) || P_DisplayAccess(weaponShade))
|
|
goto enddisplayweapon;
|
|
|
|
P_DisplayKnee(weaponShade);
|
|
|
|
if (ud.weaponsway)
|
|
{
|
|
weaponX -= (sintable[((pPlayer->weapon_sway>>1)+512)&2047]/(1024+512));
|
|
weaponYOffset -= (sprite[pPlayer->i].xrepeat < 32) ? klabs(sintable[(pPlayer->weapon_sway << 2) & 2047] >> 9)
|
|
: klabs(sintable[(pPlayer->weapon_sway >> 1) & 2047] >> 10);
|
|
}
|
|
else weaponYOffset -= 16;
|
|
|
|
weaponX -= 58 + pPlayer->weapon_ang;
|
|
weaponYOffset -= (pPlayer->hard_landing << 3);
|
|
|
|
currentWeapon = PWEAPON(screenpeek, (pPlayer->last_weapon >= 0) ? pPlayer->last_weapon
|
|
: pPlayer->curr_weapon, WorksLike);
|
|
hudweap.gunposy = weaponYOffset;
|
|
hudweap.lookhoriz = weaponY;
|
|
hudweap.cur = currentWeapon;
|
|
hudweap.gunposx = weaponX;
|
|
hudweap.shade = weaponShade;
|
|
hudweap.count = *weaponFrame;
|
|
hudweap.lookhalfang = pPlayer->look_ang >> 1;
|
|
|
|
if (VM_OnEvent(EVENT_DISPLAYWEAPON, pPlayer->i, screenpeek) == 0)
|
|
{
|
|
int const quickKickFrame = 14 - pPlayer->quick_kick;
|
|
|
|
if ((quickKickFrame != 14 || pPlayer->last_quick_kick) && ud.drawweapon == 1)
|
|
{
|
|
int weaponPal = P_GetHudPal(pPlayer);
|
|
|
|
if (weaponPal == 0)
|
|
weaponPal = pPlayer->palookup;
|
|
|
|
guniqhudid = 100;
|
|
|
|
if (quickKickFrame < 6 || quickKickFrame > 12)
|
|
G_DrawTileScaled(weaponX + 80 - (pPlayer->look_ang >> 1), weaponY + 250 - weaponYOffset, KNEE, weaponShade,
|
|
weaponBits | 4 | DRAWEAP_CENTER, weaponPal);
|
|
else
|
|
G_DrawTileScaled(weaponX + 160 - 16 - (pPlayer->look_ang >> 1), weaponY + 214 - weaponYOffset, KNEE + 1,
|
|
weaponShade, weaponBits | 4 | DRAWEAP_CENTER, weaponPal);
|
|
guniqhudid = 0;
|
|
}
|
|
|
|
if (sprite[pPlayer->i].xrepeat < 40)
|
|
{
|
|
int const weaponPal = P_GetHudPal(pPlayer);
|
|
|
|
if (pPlayer->jetpack_on == 0)
|
|
{
|
|
int const playerXvel = sprite[pPlayer->i].xvel;
|
|
weaponY += 32 - (playerXvel >> 3);
|
|
fistPos += playerXvel >> 3;
|
|
}
|
|
|
|
currentWeapon = weaponX;
|
|
weaponX += sintable[(fistPos)&2047] >> 10;
|
|
G_DrawTileScaled(weaponX + 250 - (pPlayer->look_ang >> 1), weaponY + 258 - (klabs(sintable[(fistPos)&2047] >> 8)),
|
|
FIST, weaponShade, weaponBits, weaponPal);
|
|
weaponX = currentWeapon - (sintable[(fistPos)&2047] >> 10);
|
|
G_DrawTileScaled(weaponX + 40 - (pPlayer->look_ang >> 1), weaponY + 200 + (klabs(sintable[(fistPos)&2047] >> 8)), FIST,
|
|
weaponShade, weaponBits | 4, weaponPal);
|
|
}
|
|
else
|
|
{
|
|
switch (ud.drawweapon)
|
|
{
|
|
case 1: break;
|
|
case 2:
|
|
if ((unsigned)hudweap.cur < MAX_WEAPONS && hudweap.cur != KNEE_WEAPON)
|
|
rotatesprite_win(160 << 16, (180 + (pPlayer->weapon_pos * pPlayer->weapon_pos)) << 16, scale(65536, ud.statusbarscale, 100), 0,
|
|
hudweap.cur == GROW_WEAPON ? GROWSPRITEICON : WeaponPickupSprites[hudweap.cur], 0,
|
|
0, 2);
|
|
default: goto enddisplayweapon;
|
|
}
|
|
|
|
if (VM_OnEvent(EVENT_DRAWWEAPON, g_player[screenpeek].ps->i, screenpeek)||(currentWeapon == KNEE_WEAPON && *weaponFrame == 0))
|
|
goto enddisplayweapon;
|
|
|
|
int const doAnim = !(sprite[pPlayer->i].pal == 1 || ud.pause_on || g_player[myconnectindex].ps->gm & MODE_MENU);
|
|
int const halfLookAng = pPlayer->look_ang >> 1;
|
|
|
|
int weaponPal = P_GetHudPal(pPlayer);
|
|
|
|
switch (currentWeapon)
|
|
{
|
|
case KNEE_WEAPON:
|
|
if (weaponPal == 0)
|
|
weaponPal = pPlayer->palookup;
|
|
|
|
guniqhudid = currentWeapon;
|
|
if (*weaponFrame < 5 || *weaponFrame > 9)
|
|
G_DrawTileScaled(weaponX + 220 - halfLookAng, weaponY + 250 - weaponYOffset, KNEE,
|
|
weaponShade, weaponBits, weaponPal);
|
|
else
|
|
G_DrawTileScaled(weaponX + 160 - halfLookAng, weaponY + 214 - weaponYOffset, KNEE + 1,
|
|
weaponShade, weaponBits, weaponPal);
|
|
guniqhudid = 0;
|
|
break;
|
|
|
|
case TRIPBOMB_WEAPON:
|
|
weaponX += 8;
|
|
weaponYOffset -= 10;
|
|
|
|
if ((*weaponFrame) > 6)
|
|
weaponY += ((*weaponFrame) << 3);
|
|
else if ((*weaponFrame) < 4)
|
|
G_DrawWeaponTileWithID(currentWeapon << 2, weaponX + 142 - halfLookAng,
|
|
weaponY + 234 - weaponYOffset, HANDHOLDINGLASER + 3, weaponShade, weaponBits, weaponPal, 0);
|
|
|
|
G_DrawWeaponTileWithID(currentWeapon, weaponX + 130 - halfLookAng, weaponY + 249 - weaponYOffset,
|
|
HANDHOLDINGLASER + ((*weaponFrame) >> 2), weaponShade, weaponBits, weaponPal, 0);
|
|
|
|
G_DrawWeaponTileWithID(currentWeapon << 1, weaponX + 152 - halfLookAng,
|
|
weaponY + 249 - weaponYOffset, HANDHOLDINGLASER + ((*weaponFrame) >> 2), weaponShade, weaponBits | 4,
|
|
weaponPal, 0);
|
|
break;
|
|
|
|
case RPG_WEAPON:
|
|
weaponX -= sintable[(768 + ((*weaponFrame) << 7)) & 2047] >> 11;
|
|
weaponYOffset += sintable[(768 + ((*weaponFrame) << 7)) & 2047] >> 11;
|
|
|
|
if (!(duke3d_globalflags & DUKE3D_NO_WIDESCREEN_PINNING))
|
|
weaponBits |= 512;
|
|
|
|
if (*weaponFrame > 0)
|
|
{
|
|
if (*weaponFrame < 8)
|
|
G_DrawWeaponTileWithID(currentWeapon << 1, weaponX + 164, (weaponY << 1) + 176 - weaponYOffset,
|
|
RPGGUN + ((*weaponFrame) >> 1), weaponShade, weaponBits, weaponPal, 0);
|
|
else if (WW2GI)
|
|
{
|
|
int const totalTime = PWEAPON(screenpeek, pPlayer->curr_weapon, TotalTime);
|
|
if (*weaponFrame >= totalTime)
|
|
{
|
|
int const reloadTime = PWEAPON(screenpeek, pPlayer->curr_weapon, Reload);
|
|
|
|
weaponYOffset -= (*weaponFrame < ((reloadTime - totalTime) / 2 + totalTime))
|
|
? 10 * ((*weaponFrame) - totalTime) // down
|
|
: 10 * (reloadTime - (*weaponFrame)); // up
|
|
}
|
|
}
|
|
}
|
|
|
|
G_DrawWeaponTileWithID(currentWeapon, weaponX + 164, (weaponY << 1) + 176 - weaponYOffset, RPGGUN, weaponShade,
|
|
weaponBits, weaponPal, 0);
|
|
break;
|
|
|
|
case SHOTGUN_WEAPON:
|
|
weaponX -= 8;
|
|
|
|
if (WW2GI)
|
|
{
|
|
int const totalTime = PWEAPON(screenpeek, pPlayer->curr_weapon, TotalTime);
|
|
int const reloadTime = PWEAPON(screenpeek, pPlayer->curr_weapon, Reload);
|
|
|
|
if (*weaponFrame > 0)
|
|
weaponYOffset -= sintable[(*weaponFrame)<<7]>>12;
|
|
|
|
if (*weaponFrame > 0 && doAnim)
|
|
weaponX += 1-(krand()&3);
|
|
|
|
if (*weaponFrame == 0)
|
|
{
|
|
G_DrawWeaponTileWithID(currentWeapon, weaponX + 146 - halfLookAng, weaponY + 202 - weaponYOffset,
|
|
SHOTGUN, weaponShade, weaponBits, weaponPal, 0);
|
|
}
|
|
else if (*weaponFrame <= totalTime)
|
|
{
|
|
G_DrawWeaponTileWithID(currentWeapon, weaponX + 146 - halfLookAng, weaponY + 202 - weaponYOffset,
|
|
SHOTGUN + 1, weaponShade, weaponBits, weaponPal, 0);
|
|
}
|
|
// else we are in 'reload time'
|
|
else
|
|
{
|
|
weaponYOffset -= (*weaponFrame < ((reloadTime - totalTime) / 2 + totalTime))
|
|
? 10 * ((*weaponFrame) - totalTime) // D
|
|
: 10 * (reloadTime - (*weaponFrame)); // U
|
|
|
|
G_DrawWeaponTileWithID(currentWeapon, weaponX + 146 - halfLookAng, weaponY + 202 - weaponYOffset,
|
|
SHOTGUN, weaponShade, weaponBits, weaponPal, 0);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
switch (*weaponFrame)
|
|
{
|
|
case 1:
|
|
case 2:
|
|
G_DrawWeaponTileWithID(currentWeapon << 1, weaponX + 168 - halfLookAng, weaponY + 201 - weaponYOffset,
|
|
SHOTGUN + 2, -128, weaponBits, weaponPal, 0);
|
|
case 0:
|
|
case 6:
|
|
case 7:
|
|
case 8:
|
|
G_DrawWeaponTileWithID(currentWeapon, weaponX + 146 - halfLookAng, weaponY + 202 - weaponYOffset,
|
|
SHOTGUN, weaponShade, weaponBits, weaponPal, 0);
|
|
break;
|
|
|
|
case 3:
|
|
case 4:
|
|
weaponYOffset -= 40;
|
|
weaponX += 20;
|
|
|
|
G_DrawWeaponTileWithID(currentWeapon << 1, weaponX + 178 - halfLookAng, weaponY + 194 - weaponYOffset,
|
|
SHOTGUN + 1 + ((*(weaponFrame)-1) >> 1), -128, weaponBits, weaponPal, 0);
|
|
case 5:
|
|
case 9:
|
|
case 10:
|
|
case 11:
|
|
case 12:
|
|
G_DrawWeaponTileWithID(currentWeapon, weaponX + 158 - halfLookAng, weaponY + 220 - weaponYOffset,
|
|
SHOTGUN + 3, weaponShade, weaponBits, weaponPal, 0);
|
|
break;
|
|
|
|
case 13:
|
|
case 14:
|
|
case 15:
|
|
G_DrawWeaponTileWithID(currentWeapon, 32 + weaponX + 166 - halfLookAng, weaponY + 210 - weaponYOffset,
|
|
SHOTGUN + 4, weaponShade, weaponBits, weaponPal, 0);
|
|
break;
|
|
|
|
case 16:
|
|
case 17:
|
|
case 18:
|
|
case 19:
|
|
case 24:
|
|
case 25:
|
|
case 26:
|
|
case 27:
|
|
G_DrawWeaponTileWithID(currentWeapon, 64 + weaponX + 170 - halfLookAng, weaponY + 196 - weaponYOffset,
|
|
SHOTGUN + 5, weaponShade, weaponBits, weaponPal, 0);
|
|
break;
|
|
|
|
case 20:
|
|
case 21:
|
|
case 22:
|
|
case 23:
|
|
G_DrawWeaponTileWithID(currentWeapon, 64 + weaponX + 176 - halfLookAng, weaponY + 196 - weaponYOffset,
|
|
SHOTGUN + 6, weaponShade, weaponBits, weaponPal, 0);
|
|
break;
|
|
|
|
|
|
case 28:
|
|
case 29:
|
|
case 30:
|
|
G_DrawWeaponTileWithID(currentWeapon, 32 + weaponX + 156 - halfLookAng, weaponY + 206 - weaponYOffset,
|
|
SHOTGUN + 4, weaponShade, weaponBits, weaponPal, 0);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case CHAINGUN_WEAPON:
|
|
if (*weaponFrame > 0)
|
|
{
|
|
weaponYOffset -= sintable[(*weaponFrame)<<7]>>12;
|
|
|
|
if (doAnim)
|
|
weaponX += 1-(rand()&3);
|
|
}
|
|
|
|
if (WW2GI)
|
|
{
|
|
int const totalTime = PWEAPON(screenpeek, pPlayer->curr_weapon, TotalTime);
|
|
int const reloadTime = PWEAPON(screenpeek, pPlayer->curr_weapon, Reload);
|
|
|
|
if (*weaponFrame == 0)
|
|
{
|
|
G_DrawWeaponTileWithID(currentWeapon, weaponX + 178 - halfLookAng,weaponY+233-weaponYOffset,
|
|
CHAINGUN+1,weaponShade,weaponBits,weaponPal,0);
|
|
}
|
|
else if (*weaponFrame <= totalTime)
|
|
{
|
|
G_DrawWeaponTileWithID(currentWeapon, weaponX + 188 - halfLookAng,weaponY+243-weaponYOffset,
|
|
CHAINGUN+2,weaponShade,weaponBits,weaponPal,0);
|
|
}
|
|
// else we are in 'reload time'
|
|
// divide reload time into fifths..
|
|
// 1) move weapon up/right, hand on clip (CHAINGUN - 17)
|
|
// 2) move weapon up/right, hand removing clip (CHAINGUN - 18)
|
|
// 3) hold weapon up/right, hand removed clip (CHAINGUN - 19)
|
|
// 4) hold weapon up/right, hand inserting clip (CHAINGUN - 18)
|
|
// 5) move weapon down/left, clip inserted (CHAINGUN - 17)
|
|
else
|
|
{
|
|
int iFifths = (reloadTime - totalTime) / 5;
|
|
if (iFifths < 1)
|
|
iFifths = 1;
|
|
|
|
if (*weaponFrame < iFifths + totalTime)
|
|
{
|
|
// first segment
|
|
int const weaponOffset = 80 - 10 * (totalTime + iFifths - (*weaponFrame));
|
|
weaponYOffset += weaponOffset;
|
|
weaponX += weaponOffset;
|
|
G_DrawWeaponTileWithID(currentWeapon, weaponX + 168 - halfLookAng, weaponY + 260 - weaponYOffset, CHAINGUN - 17,
|
|
weaponShade, weaponBits, weaponPal, 0);
|
|
}
|
|
else if (*weaponFrame < (iFifths * 2 + totalTime))
|
|
{
|
|
// second segment
|
|
weaponYOffset += 80; // D
|
|
weaponX += 80;
|
|
G_DrawWeaponTileWithID(currentWeapon, weaponX + 168 - halfLookAng, weaponY + 260 - weaponYOffset, CHAINGUN - 18,
|
|
weaponShade, weaponBits, weaponPal, 0);
|
|
}
|
|
else if (*weaponFrame < (iFifths * 3 + totalTime))
|
|
{
|
|
// third segment
|
|
// up
|
|
weaponYOffset += 80;
|
|
weaponX += 80;
|
|
G_DrawWeaponTileWithID(currentWeapon, weaponX + 168 - halfLookAng, weaponY + 260 - weaponYOffset, CHAINGUN - 19,
|
|
weaponShade, weaponBits, weaponPal, 0);
|
|
}
|
|
else if (*weaponFrame < (iFifths * 4 + totalTime))
|
|
{
|
|
// fourth segment
|
|
// down
|
|
weaponYOffset += 80; // D
|
|
weaponX += 80;
|
|
G_DrawWeaponTileWithID(currentWeapon, weaponX + 168 - halfLookAng, weaponY + 260 - weaponYOffset, CHAINGUN - 18,
|
|
weaponShade, weaponBits, weaponPal, 0);
|
|
}
|
|
else
|
|
{
|
|
// up and left
|
|
int const weaponOffset = 10 * (reloadTime - (*weaponFrame));
|
|
weaponYOffset += weaponOffset; // U
|
|
weaponX += weaponOffset;
|
|
G_DrawWeaponTileWithID(currentWeapon, weaponX + 168 - halfLookAng, weaponY + 260 - weaponYOffset, CHAINGUN - 17,
|
|
weaponShade, weaponBits, weaponPal, 0);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
switch (*weaponFrame)
|
|
{
|
|
case 0:
|
|
G_DrawWeaponTileWithID(currentWeapon, weaponX + 178 - (pPlayer->look_ang >> 1), weaponY + 233 - weaponYOffset,
|
|
CHAINGUN + 1, weaponShade, weaponBits, weaponPal, 0);
|
|
break;
|
|
|
|
default:
|
|
if (*weaponFrame > PWEAPON(screenpeek, CHAINGUN_WEAPON, FireDelay) &&
|
|
*weaponFrame < PWEAPON(screenpeek, CHAINGUN_WEAPON, TotalTime))
|
|
{
|
|
int randomOffset = doAnim ? rand()&7 : 0;
|
|
G_DrawWeaponTileWithID(currentWeapon << 2, randomOffset + weaponX - 4 + 140 - (pPlayer->look_ang >> 1),
|
|
randomOffset + weaponY - ((*weaponFrame) >> 1) + 208 - weaponYOffset,
|
|
CHAINGUN + 5 + ((*weaponFrame - 4) / 5), weaponShade, weaponBits, weaponPal, 0);
|
|
if (doAnim) randomOffset = rand()&7;
|
|
G_DrawWeaponTileWithID(currentWeapon << 2, randomOffset + weaponX - 4 + 184 - (pPlayer->look_ang >> 1),
|
|
randomOffset + weaponY - ((*weaponFrame) >> 1) + 208 - weaponYOffset,
|
|
CHAINGUN + 5 + ((*weaponFrame - 4) / 5), weaponShade, weaponBits, weaponPal, 0);
|
|
}
|
|
|
|
if (*weaponFrame < PWEAPON(screenpeek, CHAINGUN_WEAPON, TotalTime)-4)
|
|
{
|
|
int const randomOffset = doAnim ? rand()&7 : 0;
|
|
G_DrawWeaponTileWithID(currentWeapon << 2, randomOffset + weaponX - 4 + 162 - (pPlayer->look_ang >> 1),
|
|
randomOffset + weaponY - ((*weaponFrame) >> 1) + 208 - weaponYOffset,
|
|
CHAINGUN + 5 + ((*weaponFrame - 2) / 5), weaponShade, weaponBits, weaponPal, 0);
|
|
G_DrawWeaponTileWithID(currentWeapon, weaponX + 178 - (pPlayer->look_ang >> 1), weaponY + 233 - weaponYOffset,
|
|
CHAINGUN + 1 + ((*weaponFrame) >> 1), weaponShade, weaponBits, weaponPal, 0);
|
|
}
|
|
else
|
|
G_DrawWeaponTileWithID(currentWeapon, weaponX + 178 - (pPlayer->look_ang >> 1), weaponY + 233 - weaponYOffset,
|
|
CHAINGUN + 1, weaponShade, weaponBits, weaponPal, 0);
|
|
|
|
break;
|
|
}
|
|
|
|
G_DrawWeaponTileWithID(currentWeapon << 1, weaponX + 168 - (pPlayer->look_ang >> 1), weaponY + 260 - weaponYOffset,
|
|
CHAINGUN, weaponShade, weaponBits, weaponPal, 0);
|
|
break;
|
|
|
|
case PISTOL_WEAPON:
|
|
if ((*weaponFrame) < PWEAPON(screenpeek, PISTOL_WEAPON, TotalTime)+1)
|
|
{
|
|
static uint8_t pistolFrames[] = { 0, 1, 2 };
|
|
int pistolOffset = 195-12+weaponX;
|
|
|
|
if ((*weaponFrame) == PWEAPON(screenpeek, PISTOL_WEAPON, FireDelay))
|
|
pistolOffset -= 3;
|
|
|
|
G_DrawWeaponTileWithID(currentWeapon, (pistolOffset - (pPlayer->look_ang >> 1)), (weaponY + 244 - weaponYOffset),
|
|
FIRSTGUN + pistolFrames[*weaponFrame > 2 ? 0 : *weaponFrame], weaponShade, 2,
|
|
weaponPal, 0);
|
|
|
|
break;
|
|
}
|
|
|
|
if (!(duke3d_globalflags & DUKE3D_NO_WIDESCREEN_PINNING) && DUKE)
|
|
weaponBits |= 512;
|
|
|
|
if ((*weaponFrame) < PWEAPON(screenpeek, PISTOL_WEAPON, Reload) - 17)
|
|
G_DrawWeaponTileWithID(currentWeapon, 194 - (pPlayer->look_ang >> 1), weaponY + 230 - weaponYOffset, FIRSTGUN + 4,
|
|
weaponShade, weaponBits, weaponPal, 0);
|
|
else if ((*weaponFrame) < PWEAPON(screenpeek, PISTOL_WEAPON, Reload) - 12)
|
|
{
|
|
G_DrawWeaponTileWithID(currentWeapon << 1, 244 - ((*weaponFrame) << 3) - (pPlayer->look_ang >> 1),
|
|
weaponY + 130 - weaponYOffset + ((*weaponFrame) << 4), FIRSTGUN + 6, weaponShade,
|
|
weaponBits, weaponPal, 0);
|
|
G_DrawWeaponTileWithID(currentWeapon, 224 - (pPlayer->look_ang >> 1), weaponY + 220 - weaponYOffset, FIRSTGUN + 5,
|
|
weaponShade, weaponBits, weaponPal, 0);
|
|
}
|
|
else if ((*weaponFrame) < PWEAPON(screenpeek, PISTOL_WEAPON, Reload) - 7)
|
|
{
|
|
G_DrawWeaponTileWithID(currentWeapon << 1, 124 + ((*weaponFrame) << 1) - (pPlayer->look_ang >> 1),
|
|
weaponY + 430 - weaponYOffset - ((*weaponFrame) << 3), FIRSTGUN + 6, weaponShade,
|
|
weaponBits, weaponPal, 0);
|
|
G_DrawWeaponTileWithID(currentWeapon, 224 - (pPlayer->look_ang >> 1), weaponY + 220 - weaponYOffset, FIRSTGUN + 5,
|
|
weaponShade, weaponBits, weaponPal, 0);
|
|
}
|
|
|
|
else if ((*weaponFrame) < PWEAPON(screenpeek, PISTOL_WEAPON, Reload) - (WW2GI ? 12 : 4))
|
|
{
|
|
G_DrawWeaponTileWithID(currentWeapon << 2, 184 - (pPlayer->look_ang >> 1), weaponY + 235 - weaponYOffset,
|
|
FIRSTGUN + 8, weaponShade, weaponBits, weaponPal, 0);
|
|
G_DrawWeaponTileWithID(currentWeapon, 224 - (pPlayer->look_ang >> 1), weaponY + 210 - weaponYOffset, FIRSTGUN + 5,
|
|
weaponShade, weaponBits, weaponPal, 0);
|
|
}
|
|
else if ((*weaponFrame) < PWEAPON(screenpeek, PISTOL_WEAPON, Reload) - (WW2GI ? 6 : 2))
|
|
{
|
|
G_DrawWeaponTileWithID(currentWeapon << 2, 164 - (pPlayer->look_ang >> 1), weaponY + 245 - weaponYOffset,
|
|
FIRSTGUN + 8, weaponShade, weaponBits, weaponPal, 0);
|
|
G_DrawWeaponTileWithID(currentWeapon, 224 - (pPlayer->look_ang >> 1), weaponY + 220 - weaponYOffset, FIRSTGUN + 5,
|
|
weaponShade, weaponBits, weaponPal, 0);
|
|
}
|
|
else if ((*weaponFrame) < PWEAPON(screenpeek, PISTOL_WEAPON, Reload))
|
|
G_DrawWeaponTileWithID(currentWeapon, 194 - (pPlayer->look_ang >> 1), weaponY + 235 - weaponYOffset, FIRSTGUN + 5,
|
|
weaponShade, weaponBits, weaponPal, 0);
|
|
|
|
break;
|
|
|
|
case HANDBOMB_WEAPON:
|
|
{
|
|
static uint8_t pipebombFrames [] ={ 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2 };
|
|
|
|
if (*weaponFrame >= PWEAPON(screenpeek, pPlayer->curr_weapon, TotalTime) || *weaponFrame >= ARRAY_SIZE(pipebombFrames))
|
|
break;
|
|
|
|
if (*weaponFrame)
|
|
{
|
|
if (WW2GI)
|
|
{
|
|
int const fireDelay = PWEAPON(screenpeek, pPlayer->curr_weapon, FireDelay);
|
|
int const totalTime = PWEAPON(screenpeek, pPlayer->curr_weapon, TotalTime);
|
|
|
|
if (*weaponFrame <= fireDelay)
|
|
{
|
|
// it holds here
|
|
weaponYOffset -= 5 * (*weaponFrame); // D
|
|
}
|
|
else if (*weaponFrame < ((totalTime - fireDelay) / 2 + fireDelay))
|
|
{
|
|
// up and left
|
|
int const weaponOffset = (*weaponFrame) - fireDelay;
|
|
weaponYOffset += 10 * weaponOffset; // U
|
|
weaponX += 80 * weaponOffset;
|
|
}
|
|
else if (*weaponFrame < totalTime)
|
|
{
|
|
// start high
|
|
weaponYOffset += 240;
|
|
weaponYOffset -= 12 * ((*weaponFrame) - fireDelay); // D
|
|
// move left
|
|
weaponX += 90 - 5 * (totalTime - (*weaponFrame));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (*weaponFrame < 7) weaponYOffset -= 10 * (*weaponFrame); // D
|
|
else if (*weaponFrame < 12) weaponYOffset += 20 * ((*weaponFrame) - 10); // U
|
|
else if (*weaponFrame < 20) weaponYOffset -= 9 * ((*weaponFrame) - 14); // D
|
|
}
|
|
|
|
weaponYOffset += 10;
|
|
}
|
|
|
|
G_DrawWeaponTileWithID(currentWeapon, weaponX + 190 - halfLookAng, weaponY + 260 - weaponYOffset,
|
|
HANDTHROW + pipebombFrames[(*weaponFrame)], weaponShade, weaponBits, weaponPal, 0);
|
|
}
|
|
break;
|
|
|
|
case HANDREMOTE_WEAPON:
|
|
{
|
|
static uint8_t remoteFrames[] = { 0, 1, 1, 2, 1, 1, 0, 0, 0, 0, 0 };
|
|
|
|
if (*weaponFrame >= ARRAY_SIZE(remoteFrames))
|
|
break;
|
|
|
|
weaponX = -48;
|
|
G_DrawWeaponTileWithID(currentWeapon, weaponX + 150 - halfLookAng, weaponY + 258 - weaponYOffset,
|
|
HANDREMOTE + remoteFrames[(*weaponFrame)], weaponShade, weaponBits, weaponPal, 0);
|
|
}
|
|
break;
|
|
|
|
case DEVISTATOR_WEAPON:
|
|
if (WW2GI)
|
|
{
|
|
if (*weaponFrame)
|
|
{
|
|
int32_t const totalTime = PWEAPON(screenpeek, pPlayer->curr_weapon, TotalTime);
|
|
int32_t const reloadTime = PWEAPON(screenpeek, pPlayer->curr_weapon, Reload);
|
|
|
|
if (*weaponFrame < totalTime)
|
|
{
|
|
int const tileOffset = ksgn((*weaponFrame) >> 2);
|
|
|
|
if (pPlayer->ammo_amount[pPlayer->curr_weapon] & 1)
|
|
{
|
|
G_DrawWeaponTileWithID(currentWeapon << 1, weaponX + 30 - halfLookAng, weaponY + 240 - weaponYOffset,
|
|
DEVISTATOR, weaponShade, weaponBits | 4, weaponPal, 0);
|
|
G_DrawWeaponTileWithID(currentWeapon, weaponX + 268 - halfLookAng, weaponY + 238 - weaponYOffset,
|
|
DEVISTATOR + tileOffset, -32, weaponBits, weaponPal, 0);
|
|
}
|
|
else
|
|
{
|
|
G_DrawWeaponTileWithID(currentWeapon << 1, weaponX + 30 - halfLookAng, weaponY + 240 - weaponYOffset,
|
|
DEVISTATOR + tileOffset, -32, weaponBits | 4, weaponPal, 0);
|
|
G_DrawWeaponTileWithID(currentWeapon, weaponX + 268 - halfLookAng, weaponY + 238 - weaponYOffset, DEVISTATOR,
|
|
weaponShade, weaponBits, weaponPal, 0);
|
|
}
|
|
}
|
|
// else we are in 'reload time'
|
|
else
|
|
{
|
|
weaponYOffset -= (*weaponFrame < ((reloadTime - totalTime) / 2 + totalTime))
|
|
? 10 * ((*weaponFrame) - totalTime)
|
|
: 10 * (reloadTime - (*weaponFrame));
|
|
|
|
G_DrawWeaponTileWithID(currentWeapon, weaponX + 268 - halfLookAng, weaponY + 238 - weaponYOffset, DEVISTATOR,
|
|
weaponShade, weaponBits, weaponPal, 0);
|
|
G_DrawWeaponTileWithID(currentWeapon << 1, weaponX + 30 - halfLookAng, weaponY + 240 - weaponYOffset, DEVISTATOR,
|
|
weaponShade, weaponBits | 4, weaponPal, 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
G_DrawWeaponTileWithID(currentWeapon, weaponX + 268 - halfLookAng, weaponY + 238 - weaponYOffset, DEVISTATOR,
|
|
weaponShade, weaponBits, weaponPal, 0);
|
|
G_DrawWeaponTileWithID(currentWeapon << 1, weaponX + 30 - halfLookAng, weaponY + 240 - weaponYOffset, DEVISTATOR,
|
|
weaponShade, weaponBits | 4, weaponPal, 0);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (*weaponFrame <= PWEAPON(screenpeek, DEVISTATOR_WEAPON, TotalTime) && *weaponFrame > 0)
|
|
{
|
|
static uint8_t const devastatorFrames[] = { 0, 4, 12, 24, 12, 4, 0 };
|
|
|
|
if (*weaponFrame >= ARRAY_SIZE(devastatorFrames))
|
|
break;
|
|
|
|
int const tileOffset = ksgn((*weaponFrame) >> 2);
|
|
|
|
if (pPlayer->hbomb_hold_delay)
|
|
{
|
|
G_DrawWeaponTileWithID(currentWeapon, (devastatorFrames[*weaponFrame] >> 1) + weaponX + 268 - halfLookAng,
|
|
devastatorFrames[*weaponFrame] + weaponY + 238 - weaponYOffset,
|
|
DEVISTATOR + tileOffset, -32, weaponBits, weaponPal, 0);
|
|
G_DrawWeaponTileWithID(currentWeapon << 1, weaponX + 30 - halfLookAng, weaponY + 240 - weaponYOffset, DEVISTATOR,
|
|
weaponShade, weaponBits | 4, weaponPal, 0);
|
|
}
|
|
else
|
|
{
|
|
G_DrawWeaponTileWithID(currentWeapon << 1, -(devastatorFrames[*weaponFrame] >> 1) + weaponX + 30 - halfLookAng,
|
|
devastatorFrames[*weaponFrame] + weaponY + 240 - weaponYOffset,
|
|
DEVISTATOR + tileOffset, -32, weaponBits | 4, weaponPal, 0);
|
|
G_DrawWeaponTileWithID(currentWeapon, weaponX + 268 - halfLookAng, weaponY + 238 - weaponYOffset, DEVISTATOR,
|
|
weaponShade, weaponBits, weaponPal, 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
G_DrawWeaponTileWithID(currentWeapon, weaponX + 268 - halfLookAng, weaponY + 238 - weaponYOffset, DEVISTATOR, weaponShade,
|
|
weaponBits, weaponPal, 0);
|
|
G_DrawWeaponTileWithID(currentWeapon << 1, weaponX + 30 - halfLookAng, weaponY + 240 - weaponYOffset, DEVISTATOR,
|
|
weaponShade, weaponBits | 4, weaponPal, 0);
|
|
}
|
|
break;
|
|
|
|
case FREEZE_WEAPON:
|
|
if (!(duke3d_globalflags & DUKE3D_NO_WIDESCREEN_PINNING) && DUKE)
|
|
weaponBits |= 512;
|
|
|
|
if ((*weaponFrame) < (PWEAPON(screenpeek, pPlayer->curr_weapon, TotalTime) + 1) && (*weaponFrame) > 0)
|
|
{
|
|
static uint8_t freezerFrames[] = { 0, 0, 1, 1, 2, 2 };
|
|
|
|
if (*weaponFrame % 6 >= ARRAY_SIZE(freezerFrames))
|
|
break;
|
|
|
|
if (doAnim)
|
|
{
|
|
weaponX += rand() & 3;
|
|
weaponY += rand() & 3;
|
|
}
|
|
weaponYOffset -= 16;
|
|
G_DrawWeaponTileWithID(currentWeapon << 1, weaponX + 210 - (pPlayer->look_ang >> 1), weaponY + 261 - weaponYOffset,
|
|
FREEZE + 2, -32, weaponBits, weaponPal, 0);
|
|
G_DrawWeaponTileWithID(currentWeapon, weaponX + 210 - (pPlayer->look_ang >> 1), weaponY + 235 - weaponYOffset,
|
|
FREEZE + 3 + freezerFrames[*weaponFrame % 6], -32, weaponBits, weaponPal, 0);
|
|
}
|
|
else
|
|
G_DrawWeaponTileWithID(currentWeapon, weaponX + 210 - (pPlayer->look_ang >> 1), weaponY + 261 - weaponYOffset,
|
|
FREEZE, weaponShade, weaponBits, weaponPal, 0);
|
|
break;
|
|
|
|
case GROW_WEAPON:
|
|
case SHRINKER_WEAPON:
|
|
weaponX += 28;
|
|
weaponY += 18;
|
|
|
|
if (WW2GI)
|
|
{
|
|
if (*weaponFrame == 0)
|
|
{
|
|
// the 'at rest' display
|
|
if (currentWeapon == GROW_WEAPON)
|
|
{
|
|
G_DrawWeaponTileWithID(currentWeapon, weaponX + 188 - halfLookAng, weaponY + 240 - weaponYOffset, SHRINKER - 2,
|
|
weaponShade, weaponBits, weaponPal, 0);
|
|
break;
|
|
}
|
|
else if (pPlayer->ammo_amount[currentWeapon] > 0)
|
|
{
|
|
G_DrawWeaponTileWithID(currentWeapon << 1, weaponX + 184 - halfLookAng, weaponY + 240 - weaponYOffset, SHRINKER + 2,
|
|
16 - (sintable[pPlayer->random_club_frame & 2047] >> 10), weaponBits, 0, 1);
|
|
G_DrawWeaponTileWithID(currentWeapon, weaponX + 188 - halfLookAng, weaponY + 240 - weaponYOffset, SHRINKER,
|
|
weaponShade, weaponBits, weaponPal, 0);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// the 'active' display.
|
|
if (doAnim)
|
|
{
|
|
weaponX += rand() & 3;
|
|
weaponYOffset += rand() & 3;
|
|
}
|
|
|
|
int const totalTime = PWEAPON(screenpeek, pPlayer->curr_weapon, TotalTime);
|
|
int const reloadTime = PWEAPON(screenpeek, pPlayer->curr_weapon, Reload);
|
|
|
|
if (*weaponFrame < totalTime)
|
|
{
|
|
if (*weaponFrame >= PWEAPON(screenpeek, pPlayer->curr_weapon, FireDelay))
|
|
{
|
|
// after fire time.
|
|
// lower weapon to reload cartridge (not clip)
|
|
weaponYOffset -= (currentWeapon == GROW_WEAPON ? 15 : 10) * (totalTime - (*weaponFrame));
|
|
}
|
|
}
|
|
// else we are in 'reload time'
|
|
weaponYOffset -= (*weaponFrame < ((reloadTime - totalTime) / 2 + totalTime))
|
|
? (currentWeapon == GROW_WEAPON ? 5 : 10) * ((*weaponFrame) - totalTime) // D
|
|
: 10 * (reloadTime - (*weaponFrame)); // U
|
|
}
|
|
|
|
G_DrawWeaponTileWithID(currentWeapon << 1, weaponX + 184 - halfLookAng, weaponY + 240 - weaponYOffset,
|
|
SHRINKER + 3 + ((*weaponFrame) & 3), -32, weaponBits, currentWeapon == GROW_WEAPON ? 2 : 0, 1);
|
|
|
|
G_DrawWeaponTileWithID(currentWeapon, weaponX + 188 - halfLookAng, weaponY + 240 - weaponYOffset,
|
|
SHRINKER + (currentWeapon == GROW_WEAPON ? -1 : 1), weaponShade, weaponBits, weaponPal, 0);
|
|
|
|
break;
|
|
}
|
|
|
|
if ((*weaponFrame) < PWEAPON(screenpeek, pPlayer->curr_weapon, TotalTime) && (*weaponFrame) > 0)
|
|
{
|
|
if (doAnim)
|
|
{
|
|
weaponX += rand() & 3;
|
|
weaponYOffset += (rand() & 3);
|
|
}
|
|
|
|
G_DrawWeaponTileWithID(currentWeapon << 1, weaponX + 184 - halfLookAng, weaponY + 240 - weaponYOffset,
|
|
SHRINKER + 3 + ((*weaponFrame) & 3), -32, weaponBits, currentWeapon == GROW_WEAPON ? 2 : 0, 1);
|
|
G_DrawWeaponTileWithID(currentWeapon, weaponX + 188 - halfLookAng, weaponY + 240 - weaponYOffset,
|
|
currentWeapon == GROW_WEAPON ? SHRINKER - 1 : SHRINKER + 1, weaponShade, weaponBits, weaponPal, 0);
|
|
}
|
|
else
|
|
{
|
|
G_DrawWeaponTileWithID(currentWeapon << 1, weaponX + 184 - halfLookAng, weaponY + 240 - weaponYOffset,
|
|
SHRINKER + 2, 16 - (sintable[pPlayer->random_club_frame & 2047] >> 10), weaponBits,
|
|
currentWeapon == GROW_WEAPON ? 2 : 0, 1);
|
|
G_DrawWeaponTileWithID(currentWeapon, weaponX + 188 - halfLookAng, weaponY + 240 - weaponYOffset,
|
|
currentWeapon == GROW_WEAPON ? SHRINKER - 2 : SHRINKER, weaponShade, weaponBits, weaponPal, 0);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
enddisplayweapon:
|
|
P_DisplaySpit();
|
|
}
|
|
|
|
#define TURBOTURNTIME (TICRATE/8) // 7
|
|
#define NORMALTURN 15
|
|
#define PREAMBLETURN 5
|
|
#define NORMALKEYMOVE 40
|
|
#define MAXVEL ((NORMALKEYMOVE*2)+10)
|
|
#define MAXSVEL ((NORMALKEYMOVE*2)+10)
|
|
#define MAXANGVEL 255
|
|
#define MAXHORIZ 127
|
|
|
|
int32_t g_myAimMode = 0, g_myAimStat = 0, g_oldAimStat = 0;
|
|
int32_t mouseyaxismode = -1;
|
|
int32_t g_emuJumpTics = 0;
|
|
|
|
void P_GetInput(int playerNum)
|
|
{
|
|
static ControlInfo info[2];
|
|
static int32_t turnHeldTime = 0; // MED
|
|
static int32_t lastControlInput = 0; // MED
|
|
DukePlayer_t * pPlayer = g_player[playerNum].ps;
|
|
static input_t staticInput;
|
|
|
|
if ((pPlayer->gm & (MODE_MENU|MODE_TYPE)) || (ud.pause_on && !KB_KeyPressed(sc_Pause)))
|
|
{
|
|
if (!(pPlayer->gm&MODE_MENU))
|
|
CONTROL_GetInput(&info[0]);
|
|
|
|
Bmemset(&info[1], 0, sizeof(input_t));
|
|
Bmemset(&localInput, 0, sizeof(input_t));
|
|
|
|
localInput.bits = (((int32_t)g_gameQuit) << SK_GAMEQUIT);
|
|
localInput.extbits = (g_player[playerNum].pteam != g_player[playerNum].ps->team) << 6;
|
|
localInput.extbits |= (1 << 7);
|
|
|
|
return;
|
|
}
|
|
|
|
if (ud.mouseaiming)
|
|
g_myAimMode = BUTTON(gamefunc_Mouse_Aiming);
|
|
else
|
|
{
|
|
g_oldAimStat = g_myAimStat;
|
|
g_myAimStat = BUTTON(gamefunc_Mouse_Aiming);
|
|
|
|
if (g_myAimStat > g_oldAimStat)
|
|
{
|
|
g_myAimMode ^= 1;
|
|
P_DoQuote(QUOTE_MOUSE_AIMING_OFF + g_myAimMode, pPlayer);
|
|
}
|
|
}
|
|
|
|
int32_t const aimMode = (g_myAimMode) ? (int32_t)analog_lookingupanddown : ud.config.MouseAnalogueAxes[1];
|
|
|
|
if (aimMode != mouseyaxismode)
|
|
{
|
|
CONTROL_MapAnalogAxis(1, aimMode, controldevice_mouse);
|
|
mouseyaxismode = aimMode;
|
|
}
|
|
|
|
CONTROL_GetInput(&info[0]);
|
|
|
|
if (ud.config.MouseDeadZone)
|
|
{
|
|
if (info[0].dpitch > 0)
|
|
info[0].dpitch = (info[0].dpitch > ud.config.MouseDeadZone) ? info[0].dpitch - ud.config.MouseDeadZone : 0;
|
|
else if (info[0].dpitch < 0)
|
|
info[0].dpitch = (info[0].dpitch < -ud.config.MouseDeadZone) ? info[0].dpitch + ud.config.MouseDeadZone : 0;
|
|
|
|
if (info[0].dyaw > 0)
|
|
info[0].dyaw = (info[0].dyaw > ud.config.MouseDeadZone) ? info[0].dyaw - ud.config.MouseDeadZone : 0;
|
|
else if (info[0].dyaw < 0)
|
|
info[0].dyaw = (info[0].dyaw < -ud.config.MouseDeadZone) ? info[0].dyaw + ud.config.MouseDeadZone : 0;
|
|
}
|
|
|
|
if (ud.config.MouseBias)
|
|
{
|
|
if (klabs(info[0].dyaw) > klabs(info[0].dpitch))
|
|
info[0].dpitch = tabledivide32_noinline(info[0].dpitch, ud.config.MouseBias);
|
|
else info[0].dyaw = tabledivide32_noinline(info[0].dyaw, ud.config.MouseBias);
|
|
}
|
|
|
|
int const elapsedTics = totalclock-lastControlInput;
|
|
lastControlInput = totalclock;
|
|
|
|
// JBF: Run key behaviour is selectable
|
|
int const playerRunning = (ud.runkey_mode) ? (BUTTON(gamefunc_Run) | ud.auto_run) : (ud.auto_run ^ BUTTON(gamefunc_Run));
|
|
|
|
staticInput.svel = staticInput.fvel = staticInput.avel = staticInput.horz = 0;
|
|
|
|
if (BUTTON(gamefunc_Strafe))
|
|
{
|
|
staticInput.svel = -(info[0].dyaw + info[1].dyaw) / 8;
|
|
staticInput.svel = -(info[0].dyaw + info[1].dyaw) / 8;
|
|
info[1].dyaw = (info[1].dyaw + info[0].dyaw) % 8;
|
|
}
|
|
else
|
|
{
|
|
staticInput.avel = (info[0].dyaw + info[1].dyaw) / 32;
|
|
info[1].dyaw = (info[1].dyaw + info[0].dyaw) % 32;
|
|
}
|
|
|
|
staticInput.horz = (ud.mouseflip) ? -(info[0].dpitch + info[1].dpitch) / (314 - 128) : (info[0].dpitch + info[1].dpitch) / (314 - 128);
|
|
|
|
info[1].dpitch = (info[1].dpitch + info[0].dpitch) % (314 - 128);
|
|
staticInput.svel -= info[0].dx;
|
|
info[1].dz = info[0].dz % (1 << 6);
|
|
staticInput.fvel = -info[0].dz >> 6;
|
|
|
|
// OSD_Printf("running: %d\n", running);
|
|
|
|
int const turnAmount = playerRunning ? (NORMALTURN << 1) : NORMALTURN;
|
|
int const keyMove = playerRunning ? (NORMALKEYMOVE << 1) : NORMALKEYMOVE;
|
|
|
|
if (BUTTON(gamefunc_Strafe))
|
|
{
|
|
if (BUTTON(gamefunc_Turn_Left) && !(g_player[playerNum].ps->movement_lock&4))
|
|
staticInput.svel -= -keyMove;
|
|
if (BUTTON(gamefunc_Turn_Right) && !(g_player[playerNum].ps->movement_lock&8))
|
|
staticInput.svel -= keyMove;
|
|
}
|
|
else
|
|
{
|
|
if (BUTTON(gamefunc_Turn_Left))
|
|
{
|
|
turnHeldTime += elapsedTics;
|
|
staticInput.avel -= (turnHeldTime>=TURBOTURNTIME) ? (turnAmount<<1) : (PREAMBLETURN<<1);
|
|
}
|
|
else if (BUTTON(gamefunc_Turn_Right))
|
|
{
|
|
turnHeldTime += elapsedTics;
|
|
staticInput.avel += (turnHeldTime>=TURBOTURNTIME) ? (turnAmount<<1) : (PREAMBLETURN<<1);
|
|
}
|
|
else
|
|
turnHeldTime=0;
|
|
}
|
|
|
|
if (BUTTON(gamefunc_Strafe_Left) && !(g_player[playerNum].ps->movement_lock & 4))
|
|
staticInput.svel += keyMove;
|
|
|
|
if (BUTTON(gamefunc_Strafe_Right) && !(g_player[playerNum].ps->movement_lock & 8))
|
|
staticInput.svel += -keyMove;
|
|
|
|
if (BUTTON(gamefunc_Move_Forward) && !(g_player[playerNum].ps->movement_lock & 1))
|
|
staticInput.fvel += keyMove;
|
|
|
|
if (BUTTON(gamefunc_Move_Backward) && !(g_player[playerNum].ps->movement_lock & 2))
|
|
staticInput.fvel += -keyMove;
|
|
|
|
staticInput.fvel = clamp(staticInput.fvel, -MAXVEL, MAXVEL);
|
|
staticInput.svel = clamp(staticInput.svel, -MAXSVEL, MAXSVEL);
|
|
staticInput.avel = clamp(staticInput.avel, -MAXANGVEL, MAXANGVEL);
|
|
staticInput.horz = clamp(staticInput.horz, -MAXHORIZ, MAXHORIZ);
|
|
|
|
int weaponSelection;
|
|
|
|
for (weaponSelection = gamefunc_Weapon_10; weaponSelection >= gamefunc_Weapon_1; --weaponSelection)
|
|
{
|
|
if (BUTTON(weaponSelection))
|
|
{
|
|
weaponSelection -= (gamefunc_Weapon_1 - 1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (weaponSelection == gamefunc_Weapon_1-1)
|
|
weaponSelection = 0;
|
|
|
|
if (BUTTON(gamefunc_Previous_Weapon) || (BUTTON(gamefunc_Dpad_Select) && staticInput.fvel < 0))
|
|
weaponSelection = 11;
|
|
|
|
if (BUTTON(gamefunc_Next_Weapon) || (BUTTON(gamefunc_Dpad_Select) && staticInput.fvel > 0))
|
|
weaponSelection = 12;
|
|
|
|
if (BUTTON(gamefunc_Jump) && pPlayer->on_ground)
|
|
g_emuJumpTics = 4;
|
|
|
|
localInput.bits = (g_emuJumpTics > 0 || BUTTON(gamefunc_Jump))<<SK_JUMP;
|
|
|
|
if (g_emuJumpTics > 0)
|
|
g_emuJumpTics--;
|
|
|
|
localInput.bits |= BUTTON(gamefunc_Crouch) << SK_CROUCH;
|
|
localInput.bits |= BUTTON(gamefunc_Fire) << SK_FIRE;
|
|
localInput.bits |= (BUTTON(gamefunc_Aim_Up) || (BUTTON(gamefunc_Dpad_Aiming) && staticInput.fvel > 0)) << SK_AIM_UP;
|
|
localInput.bits |= (BUTTON(gamefunc_Aim_Down) || (BUTTON(gamefunc_Dpad_Aiming) && staticInput.fvel < 0)) << SK_AIM_DOWN;
|
|
localInput.bits |= ((ud.runkey_mode) ? (ud.auto_run | BUTTON(gamefunc_Run)) : (BUTTON(gamefunc_Run) ^ ud.auto_run)) << SK_RUN;
|
|
localInput.bits |= BUTTON(gamefunc_Look_Left) << SK_LOOK_LEFT;
|
|
localInput.bits |= BUTTON(gamefunc_Look_Right) << SK_LOOK_RIGHT;
|
|
localInput.bits |= weaponSelection << SK_WEAPON_BITS;
|
|
localInput.bits |= BUTTON(gamefunc_Steroids) << SK_STEROIDS;
|
|
localInput.bits |= BUTTON(gamefunc_Look_Up) << SK_LOOK_UP;
|
|
localInput.bits |= BUTTON(gamefunc_Look_Down) << SK_LOOK_DOWN;
|
|
localInput.bits |= BUTTON(gamefunc_NightVision) << SK_NIGHTVISION;
|
|
localInput.bits |= BUTTON(gamefunc_MedKit) << SK_MEDKIT;
|
|
localInput.bits |= BUTTON(gamefunc_Center_View) << SK_CENTER_VIEW;
|
|
localInput.bits |= BUTTON(gamefunc_Holster_Weapon) << SK_HOLSTER;
|
|
localInput.bits |= (BUTTON(gamefunc_Inventory_Left) ||
|
|
(BUTTON(gamefunc_Dpad_Select) && (staticInput.svel > 0 || staticInput.avel < 0))) << SK_INV_LEFT;
|
|
localInput.bits |= KB_KeyPressed(sc_Pause) << SK_PAUSE;
|
|
localInput.bits |= BUTTON(gamefunc_Quick_Kick) << SK_QUICK_KICK;
|
|
localInput.bits |= g_myAimMode << SK_AIMMODE;
|
|
localInput.bits |= BUTTON(gamefunc_Holo_Duke) << SK_HOLODUKE;
|
|
localInput.bits |= BUTTON(gamefunc_Jetpack) << SK_JETPACK;
|
|
localInput.bits |= (g_gameQuit << SK_GAMEQUIT);
|
|
localInput.bits |= (BUTTON(gamefunc_Inventory_Right) ||
|
|
(BUTTON(gamefunc_Dpad_Select) && (staticInput.svel < 0 || staticInput.avel > 0))) << SK_INV_RIGHT;
|
|
localInput.bits |= BUTTON(gamefunc_TurnAround) << SK_TURNAROUND;
|
|
localInput.bits |= BUTTON(gamefunc_Open) << SK_OPEN;
|
|
localInput.bits |= BUTTON(gamefunc_Inventory) << SK_INVENTORY;
|
|
localInput.bits |= ((uint32_t)KB_KeyPressed(sc_Escape)) << SK_ESCAPE;
|
|
|
|
if (BUTTON(gamefunc_Dpad_Select))
|
|
{
|
|
staticInput.fvel = 0;
|
|
staticInput.svel = 0;
|
|
staticInput.avel = 0;
|
|
}
|
|
|
|
if (BUTTON(gamefunc_Dpad_Aiming))
|
|
staticInput.fvel = 0;
|
|
|
|
if (PWEAPON(playerNum, g_player[playerNum].ps->curr_weapon, Flags) & WEAPON_SEMIAUTO && BUTTON(gamefunc_Fire))
|
|
CONTROL_ClearButton(gamefunc_Fire);
|
|
|
|
localInput.extbits = (BUTTON(gamefunc_Move_Forward) || (staticInput.fvel > 0));
|
|
localInput.extbits |= (BUTTON(gamefunc_Move_Backward) || (staticInput.fvel < 0))<<1;
|
|
localInput.extbits |= (BUTTON(gamefunc_Strafe_Left) || (staticInput.svel > 0))<<2;
|
|
localInput.extbits |= (BUTTON(gamefunc_Strafe_Right) || (staticInput.svel < 0))<<3;
|
|
|
|
if (VM_HaveEvent(EVENT_PROCESSINPUT) || VM_HaveEvent(EVENT_TURNLEFT))
|
|
localInput.extbits |= BUTTON(gamefunc_Turn_Left)<<4;
|
|
|
|
if (VM_HaveEvent(EVENT_PROCESSINPUT) || VM_HaveEvent(EVENT_TURNRIGHT))
|
|
localInput.extbits |= BUTTON(gamefunc_Turn_Right)<<5;
|
|
|
|
// used for changing team
|
|
localInput.extbits |= (g_player[playerNum].pteam != g_player[playerNum].ps->team)<<6;
|
|
|
|
if (ud.scrollmode && ud.overhead_on)
|
|
{
|
|
ud.folfvel = staticInput.fvel;
|
|
ud.folavel = staticInput.avel;
|
|
localInput.fvel = 0;
|
|
localInput.svel = 0;
|
|
localInput.avel = 0;
|
|
localInput.horz = 0;
|
|
return;
|
|
}
|
|
|
|
localInput.fvel = mulscale9(staticInput.fvel, sintable[(pPlayer->ang + 2560) & 2047]) +
|
|
(mulscale9(staticInput.svel, sintable[(pPlayer->ang + 2048) & 2047]));
|
|
|
|
localInput.svel = mulscale9(staticInput.fvel, sintable[(pPlayer->ang + 2048) & 2047]) +
|
|
(mulscale9(staticInput.svel, sintable[(pPlayer->ang + 1536) & 2047]));
|
|
|
|
localInput.avel = staticInput.avel;
|
|
localInput.horz = staticInput.horz;
|
|
}
|
|
|
|
static int32_t P_DoCounters(int playerNum)
|
|
{
|
|
DukePlayer_t *const pPlayer = g_player[playerNum].ps;
|
|
|
|
if (pPlayer->invdisptime > 0)
|
|
pPlayer->invdisptime--;
|
|
|
|
if (pPlayer->tipincs > 0)
|
|
pPlayer->tipincs--;
|
|
|
|
if (pPlayer->last_pissed_time > 0)
|
|
{
|
|
switch (--pPlayer->last_pissed_time)
|
|
{
|
|
case GAMETICSPERSEC * 219:
|
|
{
|
|
A_PlaySound(FLUSH_TOILET, pPlayer->i);
|
|
if (playerNum == screenpeek || GTFLAGS(GAMETYPE_COOPSOUND))
|
|
A_PlaySound(DUKE_PISSRELIEF, pPlayer->i);
|
|
}
|
|
break;
|
|
case GAMETICSPERSEC * 218:
|
|
{
|
|
pPlayer->holster_weapon = 0;
|
|
pPlayer->weapon_pos = WEAPON_POS_RAISE;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pPlayer->crack_time > 0)
|
|
{
|
|
if (--pPlayer->crack_time == 0)
|
|
{
|
|
pPlayer->knuckle_incs = 1;
|
|
pPlayer->crack_time = 777;
|
|
}
|
|
}
|
|
|
|
if (pPlayer->inv_amount[GET_STEROIDS] > 0 && pPlayer->inv_amount[GET_STEROIDS] < 400)
|
|
{
|
|
if (--pPlayer->inv_amount[GET_STEROIDS] == 0)
|
|
P_SelectNextInvItem(pPlayer);
|
|
|
|
if (!(pPlayer->inv_amount[GET_STEROIDS] & 7))
|
|
if (playerNum == screenpeek || GTFLAGS(GAMETYPE_COOPSOUND))
|
|
A_PlaySound(DUKE_HARTBEAT, pPlayer->i);
|
|
}
|
|
|
|
if (pPlayer->heat_on && pPlayer->inv_amount[GET_HEATS] > 0)
|
|
{
|
|
if (--pPlayer->inv_amount[GET_HEATS] == 0)
|
|
{
|
|
pPlayer->heat_on = 0;
|
|
P_SelectNextInvItem(pPlayer);
|
|
A_PlaySound(NITEVISION_ONOFF, pPlayer->i);
|
|
P_UpdateScreenPal(pPlayer);
|
|
}
|
|
}
|
|
|
|
if (pPlayer->holoduke_on >= 0)
|
|
{
|
|
if (--pPlayer->inv_amount[GET_HOLODUKE] <= 0)
|
|
{
|
|
A_PlaySound(TELEPORTER, pPlayer->i);
|
|
pPlayer->holoduke_on = -1;
|
|
P_SelectNextInvItem(pPlayer);
|
|
}
|
|
}
|
|
|
|
if (pPlayer->jetpack_on && pPlayer->inv_amount[GET_JETPACK] > 0)
|
|
{
|
|
if (--pPlayer->inv_amount[GET_JETPACK] <= 0)
|
|
{
|
|
pPlayer->jetpack_on = 0;
|
|
P_SelectNextInvItem(pPlayer);
|
|
A_PlaySound(DUKE_JETPACK_OFF, pPlayer->i);
|
|
S_StopEnvSound(DUKE_JETPACK_IDLE, pPlayer->i);
|
|
S_StopEnvSound(DUKE_JETPACK_ON, pPlayer->i);
|
|
}
|
|
}
|
|
|
|
if (pPlayer->quick_kick > 0 && sprite[pPlayer->i].pal != 1)
|
|
{
|
|
pPlayer->last_quick_kick = pPlayer->quick_kick + 1;
|
|
|
|
if (--pPlayer->quick_kick == 8)
|
|
A_Shoot(pPlayer->i, KNEE);
|
|
}
|
|
else if (pPlayer->last_quick_kick > 0)
|
|
--pPlayer->last_quick_kick;
|
|
|
|
if (pPlayer->access_incs && sprite[pPlayer->i].pal != 1)
|
|
{
|
|
++pPlayer->access_incs;
|
|
|
|
if (sprite[pPlayer->i].extra <= 0)
|
|
pPlayer->access_incs = 12;
|
|
|
|
if (pPlayer->access_incs == 12)
|
|
{
|
|
if (pPlayer->access_spritenum >= 0)
|
|
{
|
|
P_ActivateSwitch(playerNum, pPlayer->access_spritenum, 1);
|
|
switch (sprite[pPlayer->access_spritenum].pal)
|
|
{
|
|
case 0: pPlayer->got_access &= (0xffff - 0x1); break;
|
|
case 21: pPlayer->got_access &= (0xffff - 0x2); break;
|
|
case 23: pPlayer->got_access &= (0xffff - 0x4); break;
|
|
}
|
|
pPlayer->access_spritenum = -1;
|
|
}
|
|
else
|
|
{
|
|
P_ActivateSwitch(playerNum,pPlayer->access_wallnum,0);
|
|
switch (wall[pPlayer->access_wallnum].pal)
|
|
{
|
|
case 0: pPlayer->got_access &= (0xffff - 0x1); break;
|
|
case 21: pPlayer->got_access &= (0xffff - 0x2); break;
|
|
case 23: pPlayer->got_access &= (0xffff - 0x4); break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pPlayer->access_incs > 20)
|
|
{
|
|
pPlayer->access_incs = 0;
|
|
pPlayer->weapon_pos = WEAPON_POS_RAISE;
|
|
pPlayer->kickback_pic = 0;
|
|
}
|
|
}
|
|
|
|
if (pPlayer->cursectnum >= 0 && pPlayer->scuba_on == 0 && sector[pPlayer->cursectnum].lotag == ST_2_UNDERWATER)
|
|
{
|
|
if (pPlayer->inv_amount[GET_SCUBA] > 0)
|
|
{
|
|
pPlayer->scuba_on = 1;
|
|
pPlayer->inven_icon = ICON_SCUBA;
|
|
P_DoQuote(QUOTE_SCUBA_ON, pPlayer);
|
|
}
|
|
else
|
|
{
|
|
if (pPlayer->airleft > 0)
|
|
--pPlayer->airleft;
|
|
else
|
|
{
|
|
pPlayer->extra_extra8 += 32;
|
|
if (pPlayer->last_extra < (pPlayer->max_player_health >> 1) && (pPlayer->last_extra & 3) == 0)
|
|
A_PlaySound(DUKE_LONGTERM_PAIN, pPlayer->i);
|
|
}
|
|
}
|
|
}
|
|
else if (pPlayer->inv_amount[GET_SCUBA] > 0 && pPlayer->scuba_on)
|
|
{
|
|
pPlayer->inv_amount[GET_SCUBA]--;
|
|
if (pPlayer->inv_amount[GET_SCUBA] == 0)
|
|
{
|
|
pPlayer->scuba_on = 0;
|
|
P_SelectNextInvItem(pPlayer);
|
|
}
|
|
}
|
|
|
|
if (pPlayer->knuckle_incs)
|
|
{
|
|
if (++pPlayer->knuckle_incs == 10)
|
|
{
|
|
if (!WW2GI)
|
|
{
|
|
if (totalclock > 1024)
|
|
if (playerNum == screenpeek || GTFLAGS(GAMETYPE_COOPSOUND))
|
|
{
|
|
if (rand()&1)
|
|
A_PlaySound(DUKE_CRACK,pPlayer->i);
|
|
else A_PlaySound(DUKE_CRACK2,pPlayer->i);
|
|
}
|
|
|
|
A_PlaySound(DUKE_CRACK_FIRST,pPlayer->i);
|
|
}
|
|
}
|
|
else if (pPlayer->knuckle_incs == 22 || TEST_SYNC_KEY(g_player[playerNum].inputBits->bits, SK_FIRE))
|
|
pPlayer->knuckle_incs=0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int16_t WeaponPickupSprites[MAX_WEAPONS] = { KNEE__STATIC, FIRSTGUNSPRITE__STATIC, SHOTGUNSPRITE__STATIC,
|
|
CHAINGUNSPRITE__STATIC, RPGSPRITE__STATIC, HEAVYHBOMB__STATIC, SHRINKERSPRITE__STATIC, DEVISTATORSPRITE__STATIC,
|
|
TRIPBOMBSPRITE__STATIC, FREEZESPRITE__STATIC, HEAVYHBOMB__STATIC, SHRINKERSPRITE__STATIC
|
|
};
|
|
// this is used for player deaths
|
|
void P_DropWeapon(int playerNum)
|
|
{
|
|
const DukePlayer_t *const pPlayer = g_player[playerNum].ps;
|
|
int const currentWeapon = PWEAPON(playerNum, pPlayer->curr_weapon, WorksLike);
|
|
|
|
if ((unsigned)currentWeapon >= MAX_WEAPONS)
|
|
return;
|
|
|
|
if (krand() & 1)
|
|
A_Spawn(pPlayer->i, WeaponPickupSprites[currentWeapon]);
|
|
else
|
|
switch (currentWeapon)
|
|
{
|
|
case RPG_WEAPON:
|
|
case HANDBOMB_WEAPON: A_Spawn(pPlayer->i, EXPLOSION2); break;
|
|
}
|
|
}
|
|
|
|
void P_AddAmmo(DukePlayer_t *pPlayer, int weaponNum, int addAmount)
|
|
{
|
|
pPlayer->ammo_amount[weaponNum] += addAmount;
|
|
|
|
if (pPlayer->ammo_amount[weaponNum] > pPlayer->max_ammo_amount[weaponNum])
|
|
pPlayer->ammo_amount[weaponNum] = pPlayer->max_ammo_amount[weaponNum];
|
|
}
|
|
|
|
static void P_AddWeaponNoSwitch(DukePlayer_t *p, int weaponNum)
|
|
{
|
|
int const playerNum = P_Get(p->i); // PASS_SNUM?
|
|
|
|
if ((p->gotweapon & (1<<weaponNum)) == 0)
|
|
{
|
|
p->gotweapon |= (1<<weaponNum);
|
|
|
|
if (weaponNum == SHRINKER_WEAPON)
|
|
p->gotweapon |= (1<<GROW_WEAPON);
|
|
}
|
|
|
|
if (PWEAPON(playerNum, p->curr_weapon, SelectSound) > 0)
|
|
S_StopEnvSound(PWEAPON(playerNum, p->curr_weapon, SelectSound), p->i);
|
|
|
|
if (PWEAPON(playerNum, weaponNum, SelectSound) > 0)
|
|
A_PlaySound(PWEAPON(playerNum, weaponNum, SelectSound), p->i);
|
|
}
|
|
|
|
static void P_ChangeWeapon(DukePlayer_t *pPlayer, int weaponNum)
|
|
{
|
|
int const playerNum = P_Get(pPlayer->i); // PASS_SNUM?
|
|
int8_t const currentWeapon = pPlayer->curr_weapon;
|
|
|
|
if (pPlayer->reloading)
|
|
return;
|
|
|
|
int eventReturn = 0;
|
|
|
|
if (pPlayer->curr_weapon != weaponNum && VM_HaveEvent(EVENT_CHANGEWEAPON))
|
|
eventReturn = VM_OnEventWithReturn(EVENT_CHANGEWEAPON,pPlayer->i, playerNum, weaponNum);
|
|
|
|
if (eventReturn == -1)
|
|
return;
|
|
|
|
if (eventReturn != -2)
|
|
pPlayer->curr_weapon = weaponNum;
|
|
|
|
pPlayer->random_club_frame = 0;
|
|
|
|
if (pPlayer->weapon_pos == 0)
|
|
{
|
|
pPlayer->weapon_pos = -1;
|
|
pPlayer->last_weapon = currentWeapon;
|
|
}
|
|
else if ((unsigned)pPlayer->weapon_pos < WEAPON_POS_RAISE)
|
|
{
|
|
pPlayer->weapon_pos = -pPlayer->weapon_pos;
|
|
pPlayer->last_weapon = currentWeapon;
|
|
}
|
|
else if (pPlayer->last_weapon == weaponNum)
|
|
{
|
|
pPlayer->last_weapon = -1;
|
|
pPlayer->weapon_pos = -pPlayer->weapon_pos;
|
|
}
|
|
|
|
if (pPlayer->holster_weapon)
|
|
{
|
|
pPlayer->weapon_pos = WEAPON_POS_RAISE;
|
|
pPlayer->holster_weapon = 0;
|
|
pPlayer->last_weapon = -1;
|
|
}
|
|
|
|
#ifdef __ANDROID__
|
|
if (currentWeapon != pPlayer->curr_weapon &&
|
|
// p->last_weapon != -1 &&
|
|
!(PWEAPON(playerNum, currentWeapon, WorksLike) == HANDREMOTE_WEAPON && PWEAPON(playerNum, pPlayer->curr_weapon, WorksLike) == HANDBOMB_WEAPON) &&
|
|
!(PWEAPON(playerNum, currentWeapon, WorksLike) == HANDBOMB_WEAPON && PWEAPON(playerNum, pPlayer->curr_weapon, WorksLike) == HANDREMOTE_WEAPON))
|
|
CONTROL_Android_SetLastWeapon(PWEAPON(playerNum, currentWeapon, WorksLike) == HANDREMOTE_WEAPON ? (int)HANDBOMB_WEAPON : currentWeapon);
|
|
#endif
|
|
|
|
pPlayer->kickback_pic = 0;
|
|
|
|
P_SetWeaponGamevars(playerNum, pPlayer);
|
|
}
|
|
|
|
void P_AddWeapon(DukePlayer_t *pPlayer, int weaponNum, int switchWeapon)
|
|
{
|
|
P_AddWeaponNoSwitch(pPlayer, weaponNum);
|
|
|
|
if (switchWeapon)
|
|
P_ChangeWeapon(pPlayer, weaponNum);
|
|
}
|
|
|
|
void P_SelectNextInvItem(DukePlayer_t *pPlayer)
|
|
{
|
|
if (pPlayer->inv_amount[GET_FIRSTAID] > 0)
|
|
pPlayer->inven_icon = ICON_FIRSTAID;
|
|
else if (pPlayer->inv_amount[GET_STEROIDS] > 0)
|
|
pPlayer->inven_icon = ICON_STEROIDS;
|
|
else if (pPlayer->inv_amount[GET_JETPACK] > 0)
|
|
pPlayer->inven_icon = ICON_JETPACK;
|
|
else if (pPlayer->inv_amount[GET_HOLODUKE] > 0)
|
|
pPlayer->inven_icon = ICON_HOLODUKE;
|
|
else if (pPlayer->inv_amount[GET_HEATS] > 0)
|
|
pPlayer->inven_icon = ICON_HEATS;
|
|
else if (pPlayer->inv_amount[GET_SCUBA] > 0)
|
|
pPlayer->inven_icon = ICON_SCUBA;
|
|
else if (pPlayer->inv_amount[GET_BOOTS] > 0)
|
|
pPlayer->inven_icon = ICON_BOOTS;
|
|
else
|
|
pPlayer->inven_icon = ICON_NONE;
|
|
}
|
|
|
|
void P_CheckWeapon(DukePlayer_t *pPlayer)
|
|
{
|
|
int playerNum;
|
|
int weaponNum;
|
|
|
|
if (pPlayer->reloading)
|
|
return;
|
|
|
|
if (pPlayer->wantweaponfire >= 0)
|
|
{
|
|
weaponNum = pPlayer->wantweaponfire;
|
|
pPlayer->wantweaponfire = -1;
|
|
|
|
if (weaponNum == pPlayer->curr_weapon)
|
|
return;
|
|
|
|
if ((pPlayer->gotweapon & (1<<weaponNum)) && pPlayer->ammo_amount[weaponNum] > 0)
|
|
{
|
|
P_AddWeapon(pPlayer, weaponNum, 1);
|
|
return;
|
|
}
|
|
}
|
|
|
|
weaponNum = pPlayer->curr_weapon;
|
|
|
|
if ((pPlayer->gotweapon & (1<<weaponNum)) && (pPlayer->ammo_amount[weaponNum] > 0 || !(pPlayer->weaponswitch & 2)))
|
|
return;
|
|
|
|
playerNum = P_Get(pPlayer->i);
|
|
|
|
int wpnInc = 0;
|
|
|
|
for (wpnInc = 0; wpnInc <= FREEZE_WEAPON; ++wpnInc)
|
|
{
|
|
weaponNum = g_player[playerNum].wchoice[wpnInc];
|
|
if (VOLUMEONE && weaponNum > SHRINKER_WEAPON)
|
|
continue;
|
|
|
|
if (weaponNum == KNEE_WEAPON)
|
|
weaponNum = FREEZE_WEAPON;
|
|
else weaponNum--;
|
|
|
|
if (weaponNum == KNEE_WEAPON || ((pPlayer->gotweapon & (1<<weaponNum)) && pPlayer->ammo_amount[weaponNum] > 0))
|
|
break;
|
|
}
|
|
|
|
if (wpnInc == HANDREMOTE_WEAPON)
|
|
weaponNum = KNEE_WEAPON;
|
|
|
|
// Found the weapon
|
|
|
|
P_ChangeWeapon(pPlayer, weaponNum);
|
|
}
|
|
|
|
#ifdef LUNATIC
|
|
void P_CheckWeaponI(int playerNum)
|
|
{
|
|
P_CheckWeapon(g_player[playerNum].ps);
|
|
}
|
|
#endif
|
|
|
|
static void DoWallTouchDamage(const DukePlayer_t *pPlayer, int32_t wallNum)
|
|
{
|
|
vec3_t const davect = { pPlayer->pos.x + (sintable[(pPlayer->ang + 512) & 2047] >> 9),
|
|
pPlayer->pos.y + (sintable[pPlayer->ang & 2047] >> 9), pPlayer->pos.z };
|
|
|
|
A_DamageWall(pPlayer->i, wallNum, &davect, -1);
|
|
}
|
|
|
|
static void P_CheckTouchDamage(DukePlayer_t *pPlayer, int touchObject)
|
|
{
|
|
if ((touchObject = VM_OnEventWithReturn(EVENT_CHECKTOUCHDAMAGE, pPlayer->i, P_Get(pPlayer->i), touchObject)) == -1)
|
|
return;
|
|
|
|
if ((touchObject & 49152) == 49152)
|
|
{
|
|
int const touchSprite = touchObject & (MAXSPRITES - 1);
|
|
|
|
if (sprite[touchSprite].picnum == CACTUS)
|
|
{
|
|
if (pPlayer->hurt_delay < 8)
|
|
{
|
|
sprite[pPlayer->i].extra -= 5;
|
|
|
|
pPlayer->hurt_delay = 16;
|
|
P_PalFrom(pPlayer, 32, 32, 0, 0);
|
|
A_PlaySound(DUKE_LONGTERM_PAIN, pPlayer->i);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
if ((touchObject & 49152) != 32768)
|
|
return;
|
|
|
|
int const touchWall = touchObject & (MAXWALLS-1);
|
|
|
|
if (pPlayer->hurt_delay > 0)
|
|
pPlayer->hurt_delay--;
|
|
else if (wall[touchWall].cstat & FORCEFIELD_CSTAT)
|
|
{
|
|
int const forcePic = G_GetForcefieldPicnum(touchWall);
|
|
|
|
switch (DYNAMICTILEMAP(forcePic))
|
|
{
|
|
case W_FORCEFIELD__STATIC:
|
|
sprite[pPlayer->i].extra -= 5;
|
|
|
|
pPlayer->hurt_delay = 16;
|
|
P_PalFrom(pPlayer, 32, 32,0,0);
|
|
|
|
pPlayer->vel.x = -(sintable[(pPlayer->ang+512)&2047]<<8);
|
|
pPlayer->vel.y = -(sintable[(pPlayer->ang)&2047]<<8);
|
|
A_PlaySound(DUKE_LONGTERM_PAIN,pPlayer->i);
|
|
|
|
DoWallTouchDamage(pPlayer, touchWall);
|
|
break;
|
|
|
|
case BIGFORCE__STATIC:
|
|
pPlayer->hurt_delay = GAMETICSPERSEC;
|
|
DoWallTouchDamage(pPlayer, touchWall);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int P_CheckFloorDamage(DukePlayer_t *pPlayer, int floorTexture)
|
|
{
|
|
spritetype * const pSprite = &sprite[pPlayer->i];
|
|
|
|
if ((unsigned)(floorTexture = VM_OnEventWithReturn(EVENT_CHECKFLOORDAMAGE, pPlayer->i, P_Get(pPlayer->i), floorTexture)) >= MAXTILES)
|
|
return 0;
|
|
|
|
switch (DYNAMICTILEMAP(floorTexture))
|
|
{
|
|
case HURTRAIL__STATIC:
|
|
if (rnd(32))
|
|
{
|
|
if (pPlayer->inv_amount[GET_BOOTS] > 0)
|
|
return 1;
|
|
else
|
|
{
|
|
if (!A_CheckSoundPlaying(pPlayer->i, DUKE_LONGTERM_PAIN))
|
|
A_PlaySound(DUKE_LONGTERM_PAIN, pPlayer->i);
|
|
|
|
P_PalFrom(pPlayer, 32, 64, 64, 64);
|
|
|
|
pSprite->extra -= 1 + (krand() & 3);
|
|
if (!A_CheckSoundPlaying(pPlayer->i, SHORT_CIRCUIT))
|
|
A_PlaySound(SHORT_CIRCUIT, pPlayer->i);
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case FLOORSLIME__STATIC:
|
|
if (rnd(16))
|
|
{
|
|
if (pPlayer->inv_amount[GET_BOOTS] > 0)
|
|
return 1;
|
|
else
|
|
{
|
|
if (!A_CheckSoundPlaying(pPlayer->i, DUKE_LONGTERM_PAIN))
|
|
A_PlaySound(DUKE_LONGTERM_PAIN, pPlayer->i);
|
|
|
|
P_PalFrom(pPlayer, 32, 0, 8, 0);
|
|
pSprite->extra -= 1 + (krand() & 3);
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case FLOORPLASMA__STATIC:
|
|
if (rnd(32))
|
|
{
|
|
if (pPlayer->inv_amount[GET_BOOTS] > 0)
|
|
return 1;
|
|
else
|
|
{
|
|
if (!A_CheckSoundPlaying(pPlayer->i, DUKE_LONGTERM_PAIN))
|
|
A_PlaySound(DUKE_LONGTERM_PAIN, pPlayer->i);
|
|
|
|
P_PalFrom(pPlayer, 32, 8, 0, 0);
|
|
pSprite->extra -= 1 + (krand() & 3);
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int P_FindOtherPlayer(int playerNum, int32_t *pDist)
|
|
{
|
|
int closestPlayer = playerNum;
|
|
int closestPlayerDist = INT32_MAX;
|
|
|
|
for (bssize_t TRAVERSE_CONNECT(otherPlayer))
|
|
{
|
|
if (playerNum != otherPlayer && sprite[g_player[otherPlayer].ps->i].extra > 0)
|
|
{
|
|
int otherPlayerDist = klabs(g_player[otherPlayer].ps->opos.x - g_player[playerNum].ps->pos.x) +
|
|
klabs(g_player[otherPlayer].ps->opos.y - g_player[playerNum].ps->pos.y) +
|
|
(klabs(g_player[otherPlayer].ps->opos.z - g_player[playerNum].ps->pos.z) >> 4);
|
|
|
|
if (otherPlayerDist < closestPlayerDist)
|
|
{
|
|
closestPlayer = otherPlayer;
|
|
closestPlayerDist = otherPlayerDist;
|
|
}
|
|
}
|
|
}
|
|
|
|
*pDist = closestPlayerDist;
|
|
|
|
return closestPlayer;
|
|
}
|
|
|
|
void P_FragPlayer(int playerNum)
|
|
{
|
|
DukePlayer_t *const pPlayer = g_player[playerNum].ps;
|
|
spritetype *const pSprite = &sprite[pPlayer->i];
|
|
|
|
if (g_netServer || g_netClient)
|
|
randomseed = ticrandomseed;
|
|
|
|
if (pSprite->pal != 1)
|
|
{
|
|
P_PalFrom(pPlayer, 63, 63, 0, 0);
|
|
|
|
pPlayer->pos.z -= ZOFFSET2;
|
|
pSprite->z -= ZOFFSET2;
|
|
|
|
pPlayer->dead_flag = (512 - ((krand() & 1) << 10) + (krand() & 255) - 512) & 2047;
|
|
|
|
if (pPlayer->dead_flag == 0)
|
|
pPlayer->dead_flag++;
|
|
|
|
#ifndef NETCODE_DISABLE
|
|
if (g_netServer)
|
|
{
|
|
packbuf[0] = PACKET_FRAG;
|
|
packbuf[1] = playerNum;
|
|
packbuf[2] = pPlayer->frag_ps;
|
|
packbuf[3] = actor[pPlayer->i].picnum;
|
|
B_BUF32(&packbuf[4], ticrandomseed);
|
|
packbuf[8] = myconnectindex;
|
|
|
|
enet_host_broadcast(g_netServer, CHAN_GAMESTATE, enet_packet_create(packbuf, 9, ENET_PACKET_FLAG_RELIABLE));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
pPlayer->jetpack_on = 0;
|
|
pPlayer->holoduke_on = -1;
|
|
|
|
S_StopEnvSound(DUKE_JETPACK_IDLE, pPlayer->i);
|
|
|
|
if (pPlayer->scream_voice > FX_Ok)
|
|
{
|
|
FX_StopSound(pPlayer->scream_voice);
|
|
S_Cleanup();
|
|
pPlayer->scream_voice = -1;
|
|
}
|
|
|
|
if (pSprite->pal != 1 && (pSprite->cstat & 32768) == 0)
|
|
pSprite->cstat = 0;
|
|
|
|
if ((g_netServer || ud.multimode > 1) && (pSprite->pal != 1 || (pSprite->cstat & 32768)))
|
|
{
|
|
if (pPlayer->frag_ps != playerNum)
|
|
{
|
|
if (GTFLAGS(GAMETYPE_TDM) && g_player[pPlayer->frag_ps].ps->team == g_player[playerNum].ps->team)
|
|
g_player[pPlayer->frag_ps].ps->fraggedself++;
|
|
else
|
|
{
|
|
g_player[pPlayer->frag_ps].ps->frag++;
|
|
g_player[pPlayer->frag_ps].frags[playerNum]++;
|
|
g_player[playerNum].frags[playerNum]++; // deaths
|
|
}
|
|
|
|
if (playerNum == screenpeek)
|
|
{
|
|
Bsprintf(apStrings[QUOTE_RESERVED], "Killed by %s", &g_player[pPlayer->frag_ps].user_name[0]);
|
|
P_DoQuote(QUOTE_RESERVED, pPlayer);
|
|
}
|
|
else
|
|
{
|
|
Bsprintf(apStrings[QUOTE_RESERVED2], "Killed %s", &g_player[playerNum].user_name[0]);
|
|
P_DoQuote(QUOTE_RESERVED2, g_player[pPlayer->frag_ps].ps);
|
|
}
|
|
|
|
if (ud.obituaries)
|
|
{
|
|
Bsprintf(tempbuf, apStrings[OBITQUOTEINDEX + (krand() % g_numObituaries)],
|
|
&g_player[pPlayer->frag_ps].user_name[0], &g_player[playerNum].user_name[0]);
|
|
G_AddUserQuote(tempbuf);
|
|
}
|
|
else
|
|
krand();
|
|
}
|
|
else
|
|
{
|
|
if (actor[pPlayer->i].picnum != APLAYERTOP)
|
|
{
|
|
pPlayer->fraggedself++;
|
|
if ((unsigned)pPlayer->wackedbyactor < MAXTILES && A_CheckEnemyTile(sprite[pPlayer->wackedbyactor].picnum))
|
|
Bsprintf(tempbuf, apStrings[OBITQUOTEINDEX + (krand() % g_numObituaries)], "A monster",
|
|
&g_player[playerNum].user_name[0]);
|
|
else if (actor[pPlayer->i].picnum == NUKEBUTTON)
|
|
Bsprintf(tempbuf, "^02%s^02 tried to leave", &g_player[playerNum].user_name[0]);
|
|
else
|
|
{
|
|
// random suicide death string
|
|
Bsprintf(tempbuf, apStrings[SUICIDEQUOTEINDEX + (krand() % g_numSelfObituaries)],
|
|
&g_player[playerNum].user_name[0]);
|
|
}
|
|
}
|
|
else
|
|
Bsprintf(tempbuf, "^02%s^02 switched to team %d", &g_player[playerNum].user_name[0], pPlayer->team + 1);
|
|
|
|
if (ud.obituaries)
|
|
G_AddUserQuote(tempbuf);
|
|
}
|
|
pPlayer->frag_ps = playerNum;
|
|
pus = NUMPAGES;
|
|
}
|
|
}
|
|
|
|
#ifdef LUNATIC
|
|
# define PIPEBOMB_CONTROL(playerNum) (g_player[playerNum].ps->pipebombControl)
|
|
#else
|
|
# define PIPEBOMB_CONTROL(playerNum) (Gv_GetVarByLabel("PIPEBOMB_CONTROL", PIPEBOMB_REMOTE, -1, playerNum))
|
|
#endif
|
|
|
|
static void P_ProcessWeapon(int playerNum)
|
|
{
|
|
DukePlayer_t *const pPlayer = g_player[playerNum].ps;
|
|
uint8_t *const weaponFrame = &pPlayer->kickback_pic;
|
|
int const playerShrunk = (sprite[pPlayer->i].yrepeat < 32);
|
|
uint32_t playerBits = g_player[playerNum].inputBits->bits;
|
|
|
|
switch (pPlayer->weapon_pos)
|
|
{
|
|
case WEAPON_POS_LOWER:
|
|
if (pPlayer->last_weapon >= 0)
|
|
{
|
|
pPlayer->weapon_pos = WEAPON_POS_RAISE;
|
|
pPlayer->last_weapon = -1;
|
|
}
|
|
else if (pPlayer->holster_weapon == 0)
|
|
pPlayer->weapon_pos = WEAPON_POS_RAISE;
|
|
break;
|
|
case 0: break;
|
|
default: pPlayer->weapon_pos--; break;
|
|
}
|
|
|
|
if (TEST_SYNC_KEY(playerBits, SK_FIRE))
|
|
{
|
|
P_SetWeaponGamevars(playerNum, pPlayer);
|
|
|
|
if (VM_OnEvent(EVENT_PRESSEDFIRE, pPlayer->i, playerNum) != 0)
|
|
playerBits &= ~BIT(SK_FIRE);
|
|
}
|
|
|
|
if (TEST_SYNC_KEY(playerBits, SK_HOLSTER)) // 'Holster Weapon
|
|
{
|
|
P_SetWeaponGamevars(playerNum, pPlayer);
|
|
|
|
if (VM_OnEvent(EVENT_HOLSTER, pPlayer->i, playerNum) == 0)
|
|
{
|
|
if (PWEAPON(playerNum, pPlayer->curr_weapon, WorksLike) != KNEE_WEAPON)
|
|
{
|
|
if (pPlayer->holster_weapon == 0 && pPlayer->weapon_pos == 0)
|
|
{
|
|
pPlayer->holster_weapon = 1;
|
|
pPlayer->weapon_pos = -1;
|
|
P_DoQuote(QUOTE_WEAPON_LOWERED, pPlayer);
|
|
}
|
|
else if (pPlayer->holster_weapon == 1 && pPlayer->weapon_pos == WEAPON_POS_LOWER)
|
|
{
|
|
pPlayer->holster_weapon = 0;
|
|
pPlayer->weapon_pos = WEAPON_POS_RAISE;
|
|
P_DoQuote(QUOTE_WEAPON_RAISED,pPlayer);
|
|
}
|
|
}
|
|
|
|
if (PWEAPON(playerNum, pPlayer->curr_weapon, Flags) & WEAPON_HOLSTER_CLEARS_CLIP)
|
|
{
|
|
int const weap = pPlayer->curr_weapon, clipcnt = PWEAPON(playerNum, weap, Clip);
|
|
|
|
if (pPlayer->ammo_amount[weap] > clipcnt && (pPlayer->ammo_amount[weap] % clipcnt) != 0)
|
|
{
|
|
pPlayer->ammo_amount[weap] -= pPlayer->ammo_amount[weap] % clipcnt;
|
|
*weaponFrame = PWEAPON(playerNum, weap, TotalTime);
|
|
playerBits &= ~BIT(SK_FIRE); // not firing...
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (PWEAPON(playerNum, pPlayer->curr_weapon, Flags) & WEAPON_GLOWS)
|
|
{
|
|
pPlayer->random_club_frame += 64; // Glowing
|
|
|
|
#ifdef POLYMER
|
|
if (pPlayer->kickback_pic == 0)
|
|
{
|
|
spritetype *const pSprite = &sprite[pPlayer->i];
|
|
int const glowXOffset = ((sintable[(pSprite->ang + 512) & 2047]) >> 7);
|
|
int const glowYOffset = ((sintable[(pSprite->ang) & 2047]) >> 7);
|
|
int const glowRange = 1024 + (sintable[pPlayer->random_club_frame & 2047] >> 3);
|
|
|
|
pSprite->x += glowXOffset;
|
|
pSprite->y += glowYOffset;
|
|
|
|
G_AddGameLight(0, pPlayer->i, PHEIGHT, max(glowRange, 0),
|
|
PWEAPON(playerNum, pPlayer->curr_weapon, FlashColor), PR_LIGHT_PRIO_HIGH_GAME);
|
|
|
|
actor[pPlayer->i].lightcount = 2;
|
|
|
|
pSprite->x -= glowXOffset;
|
|
pSprite->y -= glowYOffset;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// this is a hack for WEAPON_FIREEVERYOTHER
|
|
if (actor[pPlayer->i].t_data[7])
|
|
{
|
|
actor[pPlayer->i].t_data[7]--;
|
|
if (pPlayer->last_weapon == -1 && actor[pPlayer->i].t_data[7] != 0 && ((actor[pPlayer->i].t_data[7] & 1) == 0))
|
|
{
|
|
if (PWEAPON(playerNum, pPlayer->curr_weapon, Flags) & WEAPON_AMMOPERSHOT)
|
|
{
|
|
if (pPlayer->ammo_amount[pPlayer->curr_weapon] > 0)
|
|
pPlayer->ammo_amount[pPlayer->curr_weapon]--;
|
|
else
|
|
{
|
|
actor[pPlayer->i].t_data[7] = 0;
|
|
P_CheckWeapon(pPlayer);
|
|
}
|
|
}
|
|
|
|
if (actor[pPlayer->i].t_data[7] != 0)
|
|
A_Shoot(pPlayer->i,PWEAPON(playerNum, pPlayer->curr_weapon, Shoots));
|
|
}
|
|
}
|
|
|
|
if (pPlayer->rapid_fire_hold == 1)
|
|
{
|
|
if (TEST_SYNC_KEY(playerBits, SK_FIRE))
|
|
return;
|
|
pPlayer->rapid_fire_hold = 0;
|
|
}
|
|
|
|
if (playerShrunk || pPlayer->tipincs || pPlayer->access_incs)
|
|
playerBits &= ~BIT(SK_FIRE);
|
|
else if (playerShrunk == 0 && (playerBits & (1 << 2)) && (*weaponFrame) == 0 && pPlayer->fist_incs == 0 &&
|
|
pPlayer->last_weapon == -1 && (pPlayer->weapon_pos == 0 || pPlayer->holster_weapon == 1))
|
|
{
|
|
pPlayer->crack_time = 777;
|
|
|
|
if (pPlayer->holster_weapon == 1)
|
|
{
|
|
if (pPlayer->last_pissed_time <= (GAMETICSPERSEC * 218) && pPlayer->weapon_pos == WEAPON_POS_LOWER)
|
|
{
|
|
pPlayer->holster_weapon = 0;
|
|
pPlayer->weapon_pos = WEAPON_POS_RAISE;
|
|
P_DoQuote(QUOTE_WEAPON_RAISED, pPlayer);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
P_SetWeaponGamevars(playerNum, pPlayer);
|
|
|
|
if (VM_OnEvent(EVENT_FIRE, pPlayer->i, playerNum) == 0)
|
|
{
|
|
// this event is deprecated
|
|
VM_OnEvent(EVENT_FIREWEAPON, pPlayer->i, playerNum);
|
|
|
|
int spriteNum;
|
|
|
|
switch (PWEAPON(playerNum, pPlayer->curr_weapon, WorksLike))
|
|
{
|
|
case HANDBOMB_WEAPON:
|
|
pPlayer->hbomb_hold_delay = 0;
|
|
if (pPlayer->ammo_amount[pPlayer->curr_weapon] > 0)
|
|
{
|
|
(*weaponFrame) = 1;
|
|
if (PWEAPON(playerNum, pPlayer->curr_weapon, InitialSound) > 0)
|
|
A_PlaySound(PWEAPON(playerNum, pPlayer->curr_weapon, InitialSound), pPlayer->i);
|
|
}
|
|
break;
|
|
|
|
case HANDREMOTE_WEAPON:
|
|
pPlayer->hbomb_hold_delay = 0;
|
|
(*weaponFrame) = 1;
|
|
if (PWEAPON(playerNum, pPlayer->curr_weapon, InitialSound) > 0)
|
|
A_PlaySound(PWEAPON(playerNum, pPlayer->curr_weapon, InitialSound), pPlayer->i);
|
|
break;
|
|
|
|
case SHOTGUN_WEAPON:
|
|
if (pPlayer->ammo_amount[pPlayer->curr_weapon] > 0 && pPlayer->random_club_frame == 0)
|
|
{
|
|
(*weaponFrame) = 1;
|
|
if (PWEAPON(playerNum, pPlayer->curr_weapon, InitialSound) > 0)
|
|
A_PlaySound(PWEAPON(playerNum, pPlayer->curr_weapon, InitialSound), pPlayer->i);
|
|
}
|
|
break;
|
|
|
|
case TRIPBOMB_WEAPON:
|
|
if (pPlayer->ammo_amount[pPlayer->curr_weapon] > 0)
|
|
{
|
|
hitdata_t hitData;
|
|
|
|
hitscan((const vec3_t *)pPlayer, pPlayer->cursectnum, sintable[(pPlayer->ang + 512) & 2047],
|
|
sintable[pPlayer->ang & 2047], (100 - pPlayer->horiz - pPlayer->horizoff) * 32, &hitData,
|
|
CLIPMASK1);
|
|
|
|
if ((hitData.sect < 0 || hitData.sprite >= 0) ||
|
|
(hitData.wall >= 0 && sector[hitData.sect].lotag > 2))
|
|
break;
|
|
|
|
if (hitData.wall >= 0 && wall[hitData.wall].overpicnum >= 0)
|
|
if (wall[hitData.wall].overpicnum == BIGFORCE)
|
|
break;
|
|
|
|
spriteNum = headspritesect[hitData.sect];
|
|
while (spriteNum >= 0)
|
|
{
|
|
if (sprite[spriteNum].picnum == TRIPBOMB && klabs(sprite[spriteNum].z - hitData.pos.z) < ZOFFSET4 &&
|
|
((sprite[spriteNum].x - hitData.pos.x) * (sprite[spriteNum].x - hitData.pos.x) +
|
|
(sprite[spriteNum].y - hitData.pos.y) * (sprite[spriteNum].y - hitData.pos.y)) < (290 * 290))
|
|
break;
|
|
spriteNum = nextspritesect[spriteNum];
|
|
}
|
|
|
|
// ST_2_UNDERWATER
|
|
if (spriteNum == -1 && hitData.wall >= 0 && (wall[hitData.wall].cstat & 16) == 0)
|
|
if ((wall[hitData.wall].nextsector >= 0 && sector[wall[hitData.wall].nextsector].lotag <= 2) ||
|
|
(wall[hitData.wall].nextsector == -1 && sector[hitData.sect].lotag <= 2))
|
|
if (((hitData.pos.x - pPlayer->pos.x) * (hitData.pos.x - pPlayer->pos.x) +
|
|
(hitData.pos.y - pPlayer->pos.y) * (hitData.pos.y - pPlayer->pos.y)) < (290 * 290))
|
|
{
|
|
pPlayer->pos.z = pPlayer->opos.z;
|
|
pPlayer->vel.z = 0;
|
|
(*weaponFrame) = 1;
|
|
if (PWEAPON(playerNum, pPlayer->curr_weapon, InitialSound) > 0)
|
|
{
|
|
A_PlaySound(PWEAPON(playerNum, pPlayer->curr_weapon, InitialSound), pPlayer->i);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case PISTOL_WEAPON:
|
|
case CHAINGUN_WEAPON:
|
|
case SHRINKER_WEAPON:
|
|
case GROW_WEAPON:
|
|
case FREEZE_WEAPON:
|
|
case RPG_WEAPON:
|
|
if (pPlayer->ammo_amount[pPlayer->curr_weapon] > 0)
|
|
{
|
|
(*weaponFrame) = 1;
|
|
if (PWEAPON(playerNum, pPlayer->curr_weapon, InitialSound) > 0)
|
|
A_PlaySound(PWEAPON(playerNum, pPlayer->curr_weapon, InitialSound), pPlayer->i);
|
|
}
|
|
break;
|
|
|
|
case DEVISTATOR_WEAPON:
|
|
if (pPlayer->ammo_amount[pPlayer->curr_weapon] > 0)
|
|
{
|
|
(*weaponFrame) = 1;
|
|
pPlayer->hbomb_hold_delay = !pPlayer->hbomb_hold_delay;
|
|
if (PWEAPON(playerNum, pPlayer->curr_weapon, InitialSound) > 0)
|
|
A_PlaySound(PWEAPON(playerNum, pPlayer->curr_weapon, InitialSound), pPlayer->i);
|
|
}
|
|
break;
|
|
|
|
case KNEE_WEAPON:
|
|
if (pPlayer->quick_kick == 0)
|
|
{
|
|
(*weaponFrame) = 1;
|
|
if (PWEAPON(playerNum, pPlayer->curr_weapon, InitialSound) > 0)
|
|
A_PlaySound(PWEAPON(playerNum, pPlayer->curr_weapon, InitialSound), pPlayer->i);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (*weaponFrame)
|
|
{
|
|
if (PWEAPON(playerNum, pPlayer->curr_weapon, WorksLike) == HANDBOMB_WEAPON)
|
|
{
|
|
if (PWEAPON(playerNum, pPlayer->curr_weapon, HoldDelay) && ((*weaponFrame) == PWEAPON(playerNum, pPlayer->curr_weapon, FireDelay)) && TEST_SYNC_KEY(playerBits, SK_FIRE))
|
|
{
|
|
pPlayer->rapid_fire_hold = 1;
|
|
return;
|
|
}
|
|
|
|
if (++(*weaponFrame) == PWEAPON(playerNum, pPlayer->curr_weapon, HoldDelay))
|
|
{
|
|
pPlayer->ammo_amount[pPlayer->curr_weapon]--;
|
|
|
|
if (numplayers < 2 || g_netServer)
|
|
{
|
|
int pipeBombType;
|
|
int pipeBombZvel;
|
|
int pipeBombFwdVel;
|
|
|
|
if (pPlayer->on_ground && TEST_SYNC_KEY(playerBits, SK_CROUCH))
|
|
{
|
|
pipeBombFwdVel = 15;
|
|
pipeBombZvel = ((pPlayer->horiz + pPlayer->horizoff - 100) * 20);
|
|
}
|
|
else
|
|
{
|
|
pipeBombFwdVel = 140;
|
|
pipeBombZvel = -512 - ((pPlayer->horiz + pPlayer->horizoff - 100) * 20);
|
|
}
|
|
|
|
int pipeSpriteNum = A_InsertSprite(pPlayer->cursectnum,
|
|
pPlayer->pos.x+(sintable[(pPlayer->ang+512)&2047]>>6),
|
|
pPlayer->pos.y+(sintable[pPlayer->ang&2047]>>6),
|
|
pPlayer->pos.z,PWEAPON(playerNum, pPlayer->curr_weapon, Shoots),-16,9,9,
|
|
pPlayer->ang,(pipeBombFwdVel+(pPlayer->hbomb_hold_delay<<5)),pipeBombZvel,pPlayer->i,1);
|
|
|
|
pipeBombType = PIPEBOMB_CONTROL(playerNum);
|
|
|
|
if (pipeBombType & PIPEBOMB_TIMER)
|
|
{
|
|
#ifdef LUNATIC
|
|
int pipeLifeTime = g_player[playerNum].ps->pipebombLifetime;
|
|
int pipeLifeVariance = g_player[playerNum].ps->pipebombLifetimeVar;
|
|
#else
|
|
int pipeLifeTime = Gv_GetVarByLabel("GRENADE_LIFETIME", NAM_GRENADE_LIFETIME, -1, playerNum);
|
|
int pipeLifeVariance = Gv_GetVarByLabel("GRENADE_LIFETIME_VAR", NAM_GRENADE_LIFETIME_VAR, -1, playerNum);
|
|
#endif
|
|
actor[pipeSpriteNum].t_data[7]= pipeLifeTime
|
|
+ mulscale(krand(),pipeLifeVariance, 14)
|
|
- pipeLifeVariance;
|
|
// TIMER_CONTROL
|
|
actor[pipeSpriteNum].t_data[6]=1;
|
|
}
|
|
else actor[pipeSpriteNum].t_data[6]=2;
|
|
|
|
if (pipeBombFwdVel == 15)
|
|
{
|
|
sprite[pipeSpriteNum].yvel = 3;
|
|
sprite[pipeSpriteNum].z += ZOFFSET3;
|
|
}
|
|
|
|
if (A_GetHitscanRange(pPlayer->i) < 512)
|
|
{
|
|
sprite[pipeSpriteNum].ang += 1024;
|
|
sprite[pipeSpriteNum].zvel /= 3;
|
|
sprite[pipeSpriteNum].xvel /= 3;
|
|
}
|
|
}
|
|
|
|
pPlayer->hbomb_on = 1;
|
|
}
|
|
else if ((*weaponFrame) < PWEAPON(playerNum, pPlayer->curr_weapon, HoldDelay) && TEST_SYNC_KEY(playerBits, SK_FIRE))
|
|
pPlayer->hbomb_hold_delay++;
|
|
else if ((*weaponFrame) > PWEAPON(playerNum, pPlayer->curr_weapon, TotalTime))
|
|
{
|
|
(*weaponFrame) = 0;
|
|
pPlayer->weapon_pos = WEAPON_POS_RAISE;
|
|
if (PIPEBOMB_CONTROL(playerNum) == PIPEBOMB_REMOTE)
|
|
{
|
|
pPlayer->curr_weapon = HANDREMOTE_WEAPON;
|
|
pPlayer->last_weapon = -1;
|
|
}
|
|
else P_CheckWeapon(pPlayer);
|
|
}
|
|
}
|
|
else if (PWEAPON(playerNum, pPlayer->curr_weapon, WorksLike) == HANDREMOTE_WEAPON)
|
|
{
|
|
if (++(*weaponFrame) == PWEAPON(playerNum, pPlayer->curr_weapon, FireDelay))
|
|
{
|
|
if (PWEAPON(playerNum, pPlayer->curr_weapon, Flags) & WEAPON_BOMB_TRIGGER)
|
|
pPlayer->hbomb_on = 0;
|
|
|
|
if (PWEAPON(playerNum, pPlayer->curr_weapon, Shoots) != 0)
|
|
{
|
|
if (!(PWEAPON(playerNum, pPlayer->curr_weapon, Flags) & WEAPON_NOVISIBLE))
|
|
{
|
|
lastvisinc = totalclock+32;
|
|
pPlayer->visibility = 0;
|
|
}
|
|
|
|
P_SetWeaponGamevars(playerNum, pPlayer);
|
|
A_Shoot(pPlayer->i, PWEAPON(playerNum, pPlayer->curr_weapon, Shoots));
|
|
}
|
|
}
|
|
|
|
if ((*weaponFrame) >= PWEAPON(playerNum, pPlayer->curr_weapon, TotalTime))
|
|
{
|
|
(*weaponFrame) = 0;
|
|
if ((pPlayer->ammo_amount[HANDBOMB_WEAPON] > 0) && PIPEBOMB_CONTROL(playerNum) == PIPEBOMB_REMOTE)
|
|
P_AddWeapon(pPlayer, HANDBOMB_WEAPON, 1);
|
|
else P_CheckWeapon(pPlayer);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// the basic weapon...
|
|
(*weaponFrame)++;
|
|
|
|
if (PWEAPON(playerNum, pPlayer->curr_weapon, Flags) & WEAPON_CHECKATRELOAD)
|
|
{
|
|
if (PWEAPON(playerNum, pPlayer->curr_weapon, WorksLike) == TRIPBOMB_WEAPON)
|
|
{
|
|
if ((*weaponFrame) >= PWEAPON(playerNum, pPlayer->curr_weapon, TotalTime))
|
|
{
|
|
(*weaponFrame) = 0;
|
|
P_CheckWeapon(pPlayer);
|
|
pPlayer->weapon_pos = WEAPON_POS_LOWER;
|
|
}
|
|
}
|
|
else if (*weaponFrame >= PWEAPON(playerNum, pPlayer->curr_weapon, Reload))
|
|
P_CheckWeapon(pPlayer);
|
|
}
|
|
else if (PWEAPON(playerNum, pPlayer->curr_weapon, WorksLike)!=KNEE_WEAPON && *weaponFrame >= PWEAPON(playerNum, pPlayer->curr_weapon, FireDelay))
|
|
P_CheckWeapon(pPlayer);
|
|
|
|
if (PWEAPON(playerNum, pPlayer->curr_weapon, Flags) & WEAPON_STANDSTILL
|
|
&& *weaponFrame < (PWEAPON(playerNum, pPlayer->curr_weapon, FireDelay)+1))
|
|
{
|
|
pPlayer->pos.z = pPlayer->opos.z;
|
|
pPlayer->vel.z = 0;
|
|
}
|
|
|
|
if (*weaponFrame == PWEAPON(playerNum, pPlayer->curr_weapon, Sound2Time))
|
|
if (PWEAPON(playerNum, pPlayer->curr_weapon, Sound2Sound) > 0)
|
|
A_PlaySound(PWEAPON(playerNum, pPlayer->curr_weapon, Sound2Sound),pPlayer->i);
|
|
|
|
if (*weaponFrame == PWEAPON(playerNum, pPlayer->curr_weapon, SpawnTime))
|
|
P_DoWeaponSpawn(playerNum);
|
|
|
|
if ((*weaponFrame) >= PWEAPON(playerNum, pPlayer->curr_weapon, TotalTime))
|
|
{
|
|
if (/*!(PWEAPON(snum, p->curr_weapon, Flags) & WEAPON_CHECKATRELOAD) && */ pPlayer->reloading == 1 ||
|
|
(PWEAPON(playerNum, pPlayer->curr_weapon, Reload) > PWEAPON(playerNum, pPlayer->curr_weapon, TotalTime) && pPlayer->ammo_amount[pPlayer->curr_weapon] > 0
|
|
&& (PWEAPON(playerNum, pPlayer->curr_weapon, Clip)) && (((pPlayer->ammo_amount[pPlayer->curr_weapon]%(PWEAPON(playerNum, pPlayer->curr_weapon, Clip)))==0))))
|
|
{
|
|
int const weaponReloadTime = PWEAPON(playerNum, pPlayer->curr_weapon, Reload)
|
|
- PWEAPON(playerNum, pPlayer->curr_weapon, TotalTime);
|
|
|
|
pPlayer->reloading = 1;
|
|
|
|
if ((*weaponFrame) != (PWEAPON(playerNum, pPlayer->curr_weapon, TotalTime)))
|
|
{
|
|
if ((*weaponFrame) == (PWEAPON(playerNum, pPlayer->curr_weapon, TotalTime)+1))
|
|
{
|
|
if (PWEAPON(playerNum, pPlayer->curr_weapon, ReloadSound1) > 0)
|
|
A_PlaySound(PWEAPON(playerNum, pPlayer->curr_weapon, ReloadSound1), pPlayer->i);
|
|
}
|
|
else if (((*weaponFrame) ==
|
|
(PWEAPON(playerNum, pPlayer->curr_weapon, Reload) - (weaponReloadTime / 3)) &&
|
|
!(PWEAPON(playerNum, pPlayer->curr_weapon, Flags) & WEAPON_RELOAD_TIMING)) ||
|
|
((*weaponFrame) ==
|
|
(PWEAPON(playerNum, pPlayer->curr_weapon, Reload) - weaponReloadTime + 4) &&
|
|
(PWEAPON(playerNum, pPlayer->curr_weapon, Flags) & WEAPON_RELOAD_TIMING)))
|
|
{
|
|
if (PWEAPON(playerNum, pPlayer->curr_weapon, ReloadSound2) > 0)
|
|
A_PlaySound(PWEAPON(playerNum, pPlayer->curr_weapon, ReloadSound2), pPlayer->i);
|
|
}
|
|
else if ((*weaponFrame) >= (PWEAPON(playerNum, pPlayer->curr_weapon, Reload)))
|
|
{
|
|
*weaponFrame = 0;
|
|
pPlayer->reloading = 0;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (PWEAPON(playerNum, pPlayer->curr_weapon, Flags) & WEAPON_AUTOMATIC &&
|
|
(PWEAPON(playerNum, pPlayer->curr_weapon, WorksLike)==KNEE_WEAPON?1:pPlayer->ammo_amount[pPlayer->curr_weapon] > 0))
|
|
{
|
|
if (TEST_SYNC_KEY(playerBits, SK_FIRE))
|
|
{
|
|
*weaponFrame =
|
|
(PWEAPON(playerNum, pPlayer->curr_weapon, Flags) & WEAPON_RANDOMRESTART) ? 1 + (krand() & 3) : 1;
|
|
}
|
|
else *weaponFrame = 0;
|
|
}
|
|
else *weaponFrame = 0;
|
|
|
|
if (PWEAPON(playerNum, pPlayer->curr_weapon, Flags) & WEAPON_RESET &&
|
|
((PWEAPON(playerNum, pPlayer->curr_weapon, WorksLike) == KNEE_WEAPON)
|
|
? 1
|
|
: pPlayer->ammo_amount[pPlayer->curr_weapon] > 0))
|
|
{
|
|
*weaponFrame = !!(TEST_SYNC_KEY(playerBits, SK_FIRE));
|
|
}
|
|
}
|
|
}
|
|
else if (*weaponFrame >= PWEAPON(playerNum, pPlayer->curr_weapon, FireDelay) && (*weaponFrame) < PWEAPON(playerNum, pPlayer->curr_weapon, TotalTime)
|
|
&& ((PWEAPON(playerNum, pPlayer->curr_weapon, WorksLike) == KNEE_WEAPON)?1:pPlayer->ammo_amount[pPlayer->curr_weapon] > 0))
|
|
{
|
|
if (PWEAPON(playerNum, pPlayer->curr_weapon, Flags) & WEAPON_AUTOMATIC)
|
|
{
|
|
if (!(PWEAPON(playerNum, pPlayer->curr_weapon, Flags) & WEAPON_SEMIAUTO))
|
|
{
|
|
if (TEST_SYNC_KEY(playerBits, SK_FIRE) == 0 && PWEAPON(playerNum, pPlayer->curr_weapon, Flags) & WEAPON_RESET)
|
|
*weaponFrame = 0;
|
|
if (PWEAPON(playerNum, pPlayer->curr_weapon, Flags) & WEAPON_FIREEVERYTHIRD)
|
|
{
|
|
if (((*(weaponFrame))%3) == 0)
|
|
{
|
|
P_FireWeapon(playerNum);
|
|
P_DoWeaponSpawn(playerNum);
|
|
}
|
|
}
|
|
else if (PWEAPON(playerNum, pPlayer->curr_weapon, Flags) & WEAPON_FIREEVERYOTHER)
|
|
{
|
|
P_FireWeapon(playerNum);
|
|
P_DoWeaponSpawn(playerNum);
|
|
}
|
|
else
|
|
{
|
|
if (*weaponFrame == PWEAPON(playerNum, pPlayer->curr_weapon, FireDelay))
|
|
{
|
|
P_FireWeapon(playerNum);
|
|
// P_DoWeaponSpawn(snum);
|
|
}
|
|
}
|
|
if (PWEAPON(playerNum, pPlayer->curr_weapon, Flags) & WEAPON_RESET
|
|
&& (*weaponFrame) > PWEAPON(playerNum, pPlayer->curr_weapon, TotalTime)
|
|
- PWEAPON(playerNum, pPlayer->curr_weapon, HoldDelay)
|
|
&& ((PWEAPON(playerNum, pPlayer->curr_weapon, WorksLike) == KNEE_WEAPON)
|
|
|| pPlayer->ammo_amount[pPlayer->curr_weapon] > 0))
|
|
{
|
|
*weaponFrame = !!(TEST_SYNC_KEY(playerBits, SK_FIRE));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (PWEAPON(playerNum, pPlayer->curr_weapon, Flags) & WEAPON_FIREEVERYOTHER)
|
|
{
|
|
P_FireWeapon(playerNum);
|
|
P_DoWeaponSpawn(playerNum);
|
|
}
|
|
else
|
|
{
|
|
if (*weaponFrame == PWEAPON(playerNum, pPlayer->curr_weapon, FireDelay))
|
|
P_FireWeapon(playerNum);
|
|
}
|
|
}
|
|
}
|
|
else if (*weaponFrame == PWEAPON(playerNum, pPlayer->curr_weapon, FireDelay))
|
|
P_FireWeapon(playerNum);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void P_EndLevel(void)
|
|
{
|
|
for (bssize_t TRAVERSE_CONNECT(playerNum))
|
|
g_player[playerNum].ps->gm = MODE_EOL;
|
|
|
|
if (ud.from_bonus)
|
|
{
|
|
ud.level_number = ud.from_bonus;
|
|
ud.m_level_number = ud.level_number;
|
|
ud.from_bonus = 0;
|
|
}
|
|
else
|
|
{
|
|
ud.level_number = (++ud.level_number < MAXLEVELS) ? ud.level_number : 0;
|
|
ud.m_level_number = ud.level_number;
|
|
}
|
|
}
|
|
|
|
static int P_DoFist(DukePlayer_t *pPlayer)
|
|
{
|
|
// the fist punching NUKEBUTTON
|
|
|
|
if (++(pPlayer->fist_incs) == 28)
|
|
{
|
|
if (ud.recstat == 1)
|
|
G_CloseDemoWrite();
|
|
|
|
S_PlaySound(PIPEBOMB_EXPLODE);
|
|
P_PalFrom(pPlayer, 48, 64, 64, 64);
|
|
}
|
|
|
|
if (pPlayer->fist_incs > 42)
|
|
{
|
|
if (pPlayer->buttonpalette && ud.from_bonus == 0)
|
|
{
|
|
for (bssize_t TRAVERSE_CONNECT(playerNum))
|
|
g_player[playerNum].ps->gm = MODE_EOL;
|
|
|
|
ud.from_bonus = ud.level_number + 1;
|
|
|
|
if ((unsigned)ud.secretlevel <= MAXLEVELS)
|
|
ud.level_number = ud.secretlevel - 1;
|
|
|
|
ud.m_level_number = ud.level_number;
|
|
}
|
|
else
|
|
P_EndLevel();
|
|
|
|
pPlayer->fist_incs = 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef YAX_ENABLE
|
|
static void getzsofslope_player(int sectNum, int playerX, int playerY, int32_t *pCeilZ, int32_t *pFloorZ)
|
|
{
|
|
int didCeiling = 0;
|
|
|
|
if ((sector[sectNum].ceilingstat & 512) == 0)
|
|
{
|
|
int const neighborSect = yax_getneighborsect(playerX, playerY, sectNum, YAX_CEILING);
|
|
|
|
if (neighborSect >= 0)
|
|
{
|
|
*pCeilZ = getceilzofslope(neighborSect, playerX, playerY);
|
|
didCeiling = 1;
|
|
}
|
|
}
|
|
|
|
int didFloor = 0;
|
|
|
|
if ((sector[sectNum].floorstat & 512) == 0)
|
|
{
|
|
int const neighborSect = yax_getneighborsect(playerX, playerY, sectNum, YAX_FLOOR);
|
|
|
|
if (neighborSect >= 0)
|
|
{
|
|
*pFloorZ = getflorzofslope(neighborSect, playerX, playerY);
|
|
didFloor = 1;
|
|
}
|
|
}
|
|
|
|
if (!didCeiling || !didFloor)
|
|
{
|
|
int32_t ceilingZ, floorZ;
|
|
getzsofslope(sectNum, playerX, playerY, &ceilingZ, &floorZ);
|
|
|
|
if (!didCeiling)
|
|
*pCeilZ = ceilingZ;
|
|
|
|
if (!didFloor)
|
|
*pFloorZ = floorZ;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void P_UpdatePosWhenViewingCam(DukePlayer_t *pPlayer)
|
|
{
|
|
int const newOwner = pPlayer->newowner;
|
|
pPlayer->pos = *(vec3_t *)&sprite[newOwner];
|
|
pPlayer->ang = SA(newOwner);
|
|
pPlayer->vel.x = 0;
|
|
pPlayer->vel.y = 0;
|
|
sprite[pPlayer->i].xvel = 0;
|
|
pPlayer->look_ang = 0;
|
|
pPlayer->rotscrnang = 0;
|
|
}
|
|
|
|
static void P_DoWater(int const playerNum, int const playerBits, int const floorZ, int const ceilZ)
|
|
{
|
|
DukePlayer_t *const pPlayer = g_player[playerNum].ps;
|
|
|
|
// under water
|
|
pPlayer->pycount += 32;
|
|
pPlayer->pycount &= 2047;
|
|
pPlayer->jumping_counter = 0;
|
|
pPlayer->pyoff = sintable[pPlayer->pycount] >> 7;
|
|
|
|
if (!A_CheckSoundPlaying(pPlayer->i, DUKE_UNDERWATER))
|
|
A_PlaySound(DUKE_UNDERWATER, pPlayer->i);
|
|
|
|
if (TEST_SYNC_KEY(playerBits, SK_JUMP))
|
|
{
|
|
if (VM_OnEvent(EVENT_SWIMUP, pPlayer->i, playerNum) == 0)
|
|
pPlayer->vel.z = max(min(-348, pPlayer->vel.z - 348), -(256 * 6));
|
|
}
|
|
else if (TEST_SYNC_KEY(playerBits, SK_CROUCH))
|
|
{
|
|
if (VM_OnEvent(EVENT_SWIMDOWN, pPlayer->i, playerNum) == 0)
|
|
pPlayer->vel.z = min(max(348, pPlayer->vel.z + 348), (256 * 6));
|
|
}
|
|
else
|
|
{
|
|
// normal view
|
|
if (pPlayer->vel.z < 0)
|
|
pPlayer->vel.z = min(0, pPlayer->vel.z + 256);
|
|
|
|
if (pPlayer->vel.z > 0)
|
|
pPlayer->vel.z = max(0, pPlayer->vel.z - 256);
|
|
}
|
|
|
|
if (pPlayer->vel.z > 2048)
|
|
pPlayer->vel.z >>= 1;
|
|
|
|
pPlayer->pos.z += pPlayer->vel.z;
|
|
|
|
if (pPlayer->pos.z > (floorZ-(15<<8)))
|
|
pPlayer->pos.z += ((floorZ-(15<<8))-pPlayer->pos.z)>>1;
|
|
|
|
if (pPlayer->pos.z < ceilZ)
|
|
{
|
|
pPlayer->pos.z = ceilZ;
|
|
pPlayer->vel.z = 0;
|
|
}
|
|
|
|
if (pPlayer->scuba_on && (krand()&255) < 8)
|
|
{
|
|
int const spriteNum = A_Spawn(pPlayer->i, WATERBUBBLE);
|
|
|
|
sprite[spriteNum].x += sintable[(pPlayer->ang + 512 + 64 - (g_globalRandom & 128)) & 2047] >> 6;
|
|
sprite[spriteNum].y += sintable[(pPlayer->ang + 64 - (g_globalRandom & 128)) & 2047] >> 6;
|
|
sprite[spriteNum].xrepeat = 3;
|
|
sprite[spriteNum].yrepeat = 2;
|
|
sprite[spriteNum].z = pPlayer->pos.z + ZOFFSET3;
|
|
}
|
|
}
|
|
static void P_DoJetpack(int const playerNum, int const playerBits, int const playerShrunk, int const sectorLotag, int const floorZ)
|
|
{
|
|
DukePlayer_t *const pPlayer = g_player[playerNum].ps;
|
|
|
|
pPlayer->on_ground = 0;
|
|
pPlayer->jumping_counter = 0;
|
|
pPlayer->hard_landing = 0;
|
|
pPlayer->falling_counter = 0;
|
|
pPlayer->pycount += 32;
|
|
pPlayer->pycount &= 2047;
|
|
pPlayer->pyoff = sintable[pPlayer->pycount] >> 7;
|
|
|
|
if (pPlayer->jetpack_on < 11)
|
|
{
|
|
pPlayer->jetpack_on++;
|
|
pPlayer->pos.z -= (pPlayer->jetpack_on<<7); //Goin up
|
|
}
|
|
else if (pPlayer->jetpack_on == 11 && !A_CheckSoundPlaying(pPlayer->i, DUKE_JETPACK_IDLE))
|
|
A_PlaySound(DUKE_JETPACK_IDLE, pPlayer->i);
|
|
|
|
int const zAdjust = playerShrunk ? 512 : 2048;
|
|
|
|
if (TEST_SYNC_KEY(playerBits, SK_JUMP)) // jumping, flying up
|
|
{
|
|
if (VM_OnEvent(EVENT_SOARUP, pPlayer->i, playerNum) == 0)
|
|
{
|
|
pPlayer->pos.z -= zAdjust;
|
|
pPlayer->crack_time = 777;
|
|
}
|
|
}
|
|
|
|
if (TEST_SYNC_KEY(playerBits, SK_CROUCH)) // crouching, flying down
|
|
{
|
|
if (VM_OnEvent(EVENT_SOARDOWN, pPlayer->i, playerNum) == 0)
|
|
{
|
|
pPlayer->pos.z += zAdjust;
|
|
pPlayer->crack_time = 777;
|
|
}
|
|
}
|
|
|
|
int const Zdiff = (playerShrunk == 0 && (sectorLotag == 0 || sectorLotag == ST_2_UNDERWATER)) ? 32 : 16;
|
|
|
|
if (sectorLotag != ST_2_UNDERWATER && pPlayer->scuba_on == 1)
|
|
pPlayer->scuba_on = 0;
|
|
|
|
if (pPlayer->pos.z > (floorZ - (Zdiff << 8)))
|
|
pPlayer->pos.z += ((floorZ - (Zdiff << 8)) - pPlayer->pos.z) >> 1;
|
|
|
|
if (pPlayer->pos.z < (actor[pPlayer->i].ceilingz + (18 << 8)))
|
|
pPlayer->pos.z = actor[pPlayer->i].ceilingz + (18 << 8);
|
|
}
|
|
|
|
static void P_Dead(int const playerNum, int const sectorLotag, int const floorZ, int const ceilZ)
|
|
{
|
|
DukePlayer_t *const pPlayer = g_player[playerNum].ps;
|
|
spritetype *const pSprite = &sprite[pPlayer->i];
|
|
|
|
if (ud.recstat == 1 && (!g_netServer && ud.multimode < 2))
|
|
G_CloseDemoWrite();
|
|
|
|
if ((numplayers < 2 || g_netServer) && pPlayer->dead_flag == 0)
|
|
P_FragPlayer(playerNum);
|
|
|
|
if (sectorLotag == ST_2_UNDERWATER)
|
|
{
|
|
if (pPlayer->on_warping_sector == 0)
|
|
{
|
|
if (klabs(pPlayer->pos.z-floorZ) >(PHEIGHT>>1))
|
|
pPlayer->pos.z += 348;
|
|
}
|
|
else
|
|
{
|
|
pSprite->z -= 512;
|
|
pSprite->zvel = -348;
|
|
}
|
|
|
|
clipmove((vec3_t *) pPlayer, &pPlayer->cursectnum,
|
|
0, 0, 164L, (4L<<8), (4L<<8), CLIPMASK0);
|
|
// p->bobcounter += 32;
|
|
}
|
|
|
|
Bmemcpy(&pPlayer->opos, &pPlayer->pos, sizeof(vec3_t));
|
|
pPlayer->oang = pPlayer->ang;
|
|
pPlayer->opyoff = pPlayer->pyoff;
|
|
|
|
pPlayer->horiz = 100;
|
|
pPlayer->horizoff = 0;
|
|
|
|
updatesector(pPlayer->pos.x, pPlayer->pos.y, &pPlayer->cursectnum);
|
|
|
|
pushmove((vec3_t *) pPlayer, &pPlayer->cursectnum, 128L, (4L<<8), (20L<<8), CLIPMASK0);
|
|
|
|
if (floorZ > ceilZ + ZOFFSET2 && pSprite->pal != 1)
|
|
pPlayer->rotscrnang = (pPlayer->dead_flag + ((floorZ+pPlayer->pos.z)>>7))&2047;
|
|
|
|
pPlayer->on_warping_sector = 0;
|
|
}
|
|
|
|
|
|
static void P_HandlePal(DukePlayer_t *const pPlayer)
|
|
{
|
|
#if !defined LUNATIC
|
|
pPlayer->pals.f--;
|
|
#else
|
|
if (pPlayer->palsfadespeed > 0)
|
|
{
|
|
// <palsfadespeed> is the tint fade speed is in
|
|
// decrements/P_ProcessInput() calls.
|
|
pPlayer->pals.f = max(pPlayer->pals.f - pPlayer->palsfadespeed, 0);
|
|
}
|
|
else
|
|
{
|
|
// <palsfadespeed> is a negated count of how many times we
|
|
// (P_ProcessInput()) should be called before decrementing the tint
|
|
// fading by one. <palsfadenext> is the live counter.
|
|
if (pPlayer->palsfadenext < 0)
|
|
pPlayer->palsfadenext++;
|
|
|
|
if (pPlayer->palsfadenext == 0)
|
|
{
|
|
pPlayer->palsfadenext = pPlayer->palsfadespeed;
|
|
pPlayer->pals.f--;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
void P_ProcessInput(int playerNum)
|
|
{
|
|
if (g_player[playerNum].playerquitflag == 0)
|
|
return;
|
|
|
|
DukePlayer_t *const pPlayer = g_player[playerNum].ps;
|
|
spritetype *const pSprite = &sprite[pPlayer->i];
|
|
uint32_t playerBits = g_player[playerNum].inputBits->bits;
|
|
|
|
++pPlayer->player_par;
|
|
|
|
VM_OnEvent(EVENT_PROCESSINPUT, pPlayer->i, playerNum);
|
|
|
|
if (pPlayer->cheat_phase > 0)
|
|
playerBits = 0;
|
|
|
|
if (pPlayer->cursectnum == -1)
|
|
{
|
|
if (pSprite->extra > 0 && ud.noclip == 0)
|
|
{
|
|
P_QuickKill(pPlayer);
|
|
A_PlaySound(SQUISHED, pPlayer->i);
|
|
}
|
|
|
|
pPlayer->cursectnum = 0;
|
|
}
|
|
|
|
int sectorLotag = sector[pPlayer->cursectnum].lotag;
|
|
pPlayer->spritebridge = 0;
|
|
pPlayer->sbs = 0;
|
|
|
|
int32_t floorZ, ceilZ, highZhit, lowZhit;
|
|
getzrange((vec3_t *)pPlayer, pPlayer->cursectnum, &ceilZ, &highZhit, &floorZ, &lowZhit, 163L, CLIPMASK0);
|
|
|
|
#ifdef YAX_ENABLE
|
|
getzsofslope_player(pPlayer->cursectnum, pPlayer->pos.x, pPlayer->pos.y, &pPlayer->truecz, &pPlayer->truefz);
|
|
#else
|
|
getzsofslope(pPlayer->cursectnum, pPlayer->pos.x, pPlayer->pos.y, &pPlayer->truecz, &pPlayer->truefz);
|
|
#endif
|
|
int const trueFloorZ = pPlayer->truefz;
|
|
int const trueFloorDist = klabs(pPlayer->pos.z - trueFloorZ);
|
|
|
|
if ((lowZhit & 49152) == 16384 && sectorLotag == 1 && trueFloorDist > PHEIGHT + ZOFFSET2)
|
|
sectorLotag = 0;
|
|
|
|
actor[pPlayer->i].floorz = floorZ;
|
|
actor[pPlayer->i].ceilingz = ceilZ;
|
|
|
|
pPlayer->ohoriz = pPlayer->horiz;
|
|
pPlayer->ohorizoff = pPlayer->horizoff;
|
|
|
|
// calculates automatic view angle for playing without a mouse
|
|
if (pPlayer->aim_mode == 0 && pPlayer->on_ground && sectorLotag != ST_2_UNDERWATER
|
|
&& (sector[pPlayer->cursectnum].floorstat & 2))
|
|
{
|
|
vec2_t const adjustedPlayer = { pPlayer->pos.x + (sintable[(pPlayer->ang + 512) & 2047] >> 5),
|
|
pPlayer->pos.y + (sintable[pPlayer->ang & 2047] >> 5) };
|
|
int16_t curSectNum = pPlayer->cursectnum;
|
|
|
|
updatesector(adjustedPlayer.x, adjustedPlayer.y, &curSectNum);
|
|
|
|
if (curSectNum >= 0)
|
|
{
|
|
int const slopeZ = getflorzofslope(pPlayer->cursectnum, adjustedPlayer.x, adjustedPlayer.y);
|
|
if ((pPlayer->cursectnum == curSectNum) ||
|
|
(klabs(getflorzofslope(curSectNum, adjustedPlayer.x, adjustedPlayer.y) - slopeZ) <= ZOFFSET6))
|
|
pPlayer->horizoff += mulscale16(trueFloorZ - slopeZ, 160);
|
|
}
|
|
}
|
|
|
|
if (pPlayer->horizoff > 0)
|
|
pPlayer->horizoff -= ((pPlayer->horizoff >> 3) + 1);
|
|
else if (pPlayer->horizoff < 0)
|
|
pPlayer->horizoff += (((-pPlayer->horizoff) >> 3) + 1);
|
|
|
|
if (highZhit >= 0 && (highZhit&49152) == 49152)
|
|
{
|
|
highZhit &= (MAXSPRITES-1);
|
|
|
|
if (sprite[highZhit].statnum == STAT_ACTOR && sprite[highZhit].extra >= 0)
|
|
{
|
|
highZhit = 0;
|
|
ceilZ = pPlayer->truecz;
|
|
}
|
|
}
|
|
|
|
int spriteNum = -1;
|
|
|
|
if (lowZhit >= 0 && (lowZhit&49152) == 49152)
|
|
{
|
|
spriteNum = lowZhit&(MAXSPRITES-1);
|
|
|
|
if ((sprite[spriteNum].cstat&33) == 33 || (sprite[spriteNum].cstat&17) == 17 ||
|
|
clipshape_idx_for_sprite((uspritetype *)&sprite[spriteNum], -1) >= 0)
|
|
{
|
|
// EDuke32 extension: xvel of 1 makes a sprite be never regarded as a bridge.
|
|
if ((sprite[spriteNum].xvel&1) == 0)
|
|
{
|
|
sectorLotag = 0;
|
|
pPlayer->footprintcount = 0;
|
|
pPlayer->spritebridge = 1;
|
|
pPlayer->sbs = spriteNum;
|
|
}
|
|
}
|
|
else if (A_CheckEnemySprite(&sprite[spriteNum]) && sprite[spriteNum].xrepeat > 24
|
|
&& klabs(pSprite->z - sprite[spriteNum].z) < (84 << 8))
|
|
{
|
|
// TX: I think this is what makes the player slide off enemies... might
|
|
// be a good sprite flag to add later.
|
|
// Helix: there's also SLIDE_ABOVE_ENEMY.
|
|
int spriteAng = getangle(sprite[spriteNum].x - pPlayer->pos.x,
|
|
sprite[spriteNum].y - pPlayer->pos.y);
|
|
pPlayer->vel.x -= sintable[(spriteAng + 512) & 2047] << 4;
|
|
pPlayer->vel.y -= sintable[spriteAng & 2047] << 4;
|
|
}
|
|
}
|
|
|
|
if (pSprite->extra > 0)
|
|
P_IncurDamage(pPlayer);
|
|
else
|
|
{
|
|
pSprite->extra = 0;
|
|
pPlayer->inv_amount[GET_SHIELD] = 0;
|
|
}
|
|
|
|
pPlayer->last_extra = pSprite->extra;
|
|
pPlayer->loogcnt = (pPlayer->loogcnt > 0) ? pPlayer->loogcnt - 1 : 0;
|
|
|
|
if (pPlayer->fist_incs && P_DoFist(pPlayer)) return;
|
|
|
|
if (pPlayer->timebeforeexit > 1 && pPlayer->last_extra > 0)
|
|
{
|
|
if (--pPlayer->timebeforeexit == GAMETICSPERSEC*5)
|
|
{
|
|
FX_StopAllSounds();
|
|
S_ClearSoundLocks();
|
|
|
|
if (pPlayer->customexitsound >= 0)
|
|
{
|
|
S_PlaySound(pPlayer->customexitsound);
|
|
P_DoQuote(QUOTE_WEREGONNAFRYYOURASS,pPlayer);
|
|
}
|
|
}
|
|
else if (pPlayer->timebeforeexit == 1)
|
|
{
|
|
P_EndLevel();
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (pPlayer->pals.f > 0)
|
|
P_HandlePal(pPlayer);
|
|
|
|
if (pPlayer->fta > 0 && --pPlayer->fta == 0)
|
|
{
|
|
pub = pus = NUMPAGES;
|
|
pPlayer->ftq = 0;
|
|
}
|
|
|
|
if (g_levelTextTime > 0)
|
|
g_levelTextTime--;
|
|
|
|
if (pSprite->extra <= 0)
|
|
{
|
|
P_Dead(playerNum, sectorLotag, floorZ, ceilZ);
|
|
return;
|
|
}
|
|
|
|
if (pPlayer->transporter_hold > 0)
|
|
{
|
|
pPlayer->transporter_hold--;
|
|
if (pPlayer->transporter_hold == 0 && pPlayer->on_warping_sector)
|
|
pPlayer->transporter_hold = 2;
|
|
}
|
|
else if (pPlayer->transporter_hold < 0)
|
|
pPlayer->transporter_hold++;
|
|
|
|
if (pPlayer->newowner >= 0)
|
|
{
|
|
P_UpdatePosWhenViewingCam(pPlayer);
|
|
P_DoCounters(playerNum);
|
|
|
|
if (PWEAPON(playerNum, pPlayer->curr_weapon, WorksLike) == HANDREMOTE_WEAPON)
|
|
P_ProcessWeapon(playerNum);
|
|
|
|
return;
|
|
}
|
|
|
|
pPlayer->rotscrnang -= (pPlayer->rotscrnang >> 1);
|
|
|
|
if (pPlayer->rotscrnang && !(pPlayer->rotscrnang >> 1))
|
|
pPlayer->rotscrnang -= ksgn(pPlayer->rotscrnang);
|
|
|
|
pPlayer->look_ang -= (pPlayer->look_ang >> 2);
|
|
|
|
if (pPlayer->look_ang && !(pPlayer->look_ang >> 2))
|
|
pPlayer->look_ang -= ksgn(pPlayer->look_ang);
|
|
|
|
if (TEST_SYNC_KEY(playerBits, SK_LOOK_LEFT))
|
|
{
|
|
// look_left
|
|
if (VM_OnEvent(EVENT_LOOKLEFT,pPlayer->i,playerNum) == 0)
|
|
{
|
|
pPlayer->look_ang -= 152;
|
|
pPlayer->rotscrnang += 24;
|
|
}
|
|
}
|
|
|
|
if (TEST_SYNC_KEY(playerBits, SK_LOOK_RIGHT))
|
|
{
|
|
// look_right
|
|
if (VM_OnEvent(EVENT_LOOKRIGHT,pPlayer->i,playerNum) == 0)
|
|
{
|
|
pPlayer->look_ang += 152;
|
|
pPlayer->rotscrnang -= 24;
|
|
}
|
|
}
|
|
|
|
int velocityModifier = TICSPERFRAME;
|
|
const uint8_t *const weaponFrame = &pPlayer->kickback_pic;
|
|
int floorZOffset = 40;
|
|
int const playerShrunk = (pSprite->yrepeat < 32);
|
|
|
|
if (pPlayer->on_crane >= 0)
|
|
goto HORIZONLY;
|
|
|
|
pPlayer->weapon_sway = (pSprite->xvel < 32 || pPlayer->on_ground == 0 || pPlayer->bobcounter == 1024)
|
|
? (((pPlayer->weapon_sway & 2047) > (1024 + 96))
|
|
? (pPlayer->weapon_sway - 96)
|
|
: (((pPlayer->weapon_sway & 2047) < (1024 - 96)))
|
|
? (pPlayer->weapon_sway + 96)
|
|
: 1024)
|
|
: pPlayer->bobcounter;
|
|
|
|
// NOTE: This silently wraps if the difference is too great, e.g. used to do
|
|
// that when teleported by silent SE7s.
|
|
pSprite->xvel = ksqrt(uhypsq(pPlayer->pos.x - pPlayer->bobpos.x, pPlayer->pos.y - pPlayer->bobpos.y));
|
|
|
|
if (pPlayer->on_ground)
|
|
pPlayer->bobcounter += sprite[pPlayer->i].xvel>>1;
|
|
|
|
if (ud.noclip == 0 && ((uint16_t)pPlayer->cursectnum >= MAXSECTORS || sector[pPlayer->cursectnum].floorpicnum == MIRROR))
|
|
{
|
|
pPlayer->pos.x = pPlayer->opos.x;
|
|
pPlayer->pos.y = pPlayer->opos.y;
|
|
}
|
|
else
|
|
{
|
|
pPlayer->opos.x = pPlayer->pos.x;
|
|
pPlayer->opos.y = pPlayer->pos.y;
|
|
}
|
|
|
|
pPlayer->bobpos.x = pPlayer->pos.x;
|
|
pPlayer->bobpos.y = pPlayer->pos.y;
|
|
pPlayer->opos.z = pPlayer->pos.z;
|
|
pPlayer->opyoff = pPlayer->pyoff;
|
|
pPlayer->oang = pPlayer->ang;
|
|
|
|
if (pPlayer->one_eighty_count < 0)
|
|
{
|
|
pPlayer->one_eighty_count += 128;
|
|
pPlayer->ang += 128;
|
|
}
|
|
|
|
// Shrinking code
|
|
|
|
if (sectorLotag == ST_2_UNDERWATER)
|
|
P_DoWater(playerNum, playerBits, floorZ, ceilZ);
|
|
else if (pPlayer->jetpack_on)
|
|
P_DoJetpack(playerNum, playerBits, playerShrunk, sectorLotag, floorZ);
|
|
else
|
|
{
|
|
pPlayer->airleft = 15 * GAMETICSPERSEC; // 13 seconds
|
|
pPlayer->scuba_on = 0;
|
|
|
|
if (sectorLotag == ST_1_ABOVE_WATER && pPlayer->spritebridge == 0)
|
|
{
|
|
floorZOffset = 12;
|
|
|
|
if (playerShrunk == 0)
|
|
{
|
|
floorZOffset = 34;
|
|
pPlayer->pycount += 32;
|
|
pPlayer->pycount &= 2047;
|
|
pPlayer->pyoff = sintable[pPlayer->pycount] >> 6;
|
|
}
|
|
|
|
if (playerShrunk == 0 && trueFloorDist <= PHEIGHT)
|
|
{
|
|
if (pPlayer->on_ground == 1)
|
|
{
|
|
if (pPlayer->dummyplayersprite < 0)
|
|
pPlayer->dummyplayersprite = A_Spawn(pPlayer->i,PLAYERONWATER);
|
|
|
|
sprite[pPlayer->dummyplayersprite].cstat |= 32768;
|
|
sprite[pPlayer->dummyplayersprite].pal = sprite[pPlayer->i].pal;
|
|
pPlayer->footprintpal = (sector[pPlayer->cursectnum].floorpicnum == FLOORSLIME) ? 8 : 0;
|
|
pPlayer->footprintshade = 0;
|
|
}
|
|
}
|
|
}
|
|
else if (pPlayer->footprintcount > 0 && pPlayer->on_ground)
|
|
{
|
|
if (pPlayer->cursectnum >= 0 && (sector[pPlayer->cursectnum].floorstat & 2) != 2)
|
|
{
|
|
for (spriteNum = headspritesect[pPlayer->cursectnum]; spriteNum >= 0; spriteNum = nextspritesect[spriteNum])
|
|
{
|
|
if (sprite[spriteNum].picnum == FOOTPRINTS || sprite[spriteNum].picnum == FOOTPRINTS2 ||
|
|
sprite[spriteNum].picnum == FOOTPRINTS3 || sprite[spriteNum].picnum == FOOTPRINTS4)
|
|
{
|
|
if (klabs(sprite[spriteNum].x - pPlayer->pos.x) < 384 &&
|
|
klabs(sprite[spriteNum].y - pPlayer->pos.y) < 384)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (spriteNum < 0)
|
|
{
|
|
if (pPlayer->cursectnum >= 0 && sector[pPlayer->cursectnum].lotag == 0 &&
|
|
sector[pPlayer->cursectnum].hitag == 0)
|
|
#ifdef YAX_ENABLE
|
|
if (yax_getbunch(pPlayer->cursectnum, YAX_FLOOR) < 0 || (sector[pPlayer->cursectnum].floorstat & 512))
|
|
#endif
|
|
{
|
|
switch (krand() & 3)
|
|
{
|
|
case 0: spriteNum = A_Spawn(pPlayer->i, FOOTPRINTS); break;
|
|
case 1: spriteNum = A_Spawn(pPlayer->i, FOOTPRINTS2); break;
|
|
case 2: spriteNum = A_Spawn(pPlayer->i, FOOTPRINTS3); break;
|
|
default: spriteNum = A_Spawn(pPlayer->i, FOOTPRINTS4); break;
|
|
}
|
|
sprite[spriteNum].pal = pPlayer->footprintpal;
|
|
sprite[spriteNum].shade = pPlayer->footprintshade;
|
|
pPlayer->footprintcount--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pPlayer->pos.z < (floorZ-(floorZOffset<<8))) //falling
|
|
{
|
|
// not jumping or crouching
|
|
|
|
if ((!TEST_SYNC_KEY(playerBits, SK_JUMP) && !TEST_SYNC_KEY(playerBits, SK_CROUCH)) && pPlayer->on_ground &&
|
|
(sector[pPlayer->cursectnum].floorstat & 2) && pPlayer->pos.z >= (floorZ - (floorZOffset << 8) - ZOFFSET2))
|
|
pPlayer->pos.z = floorZ - (floorZOffset << 8);
|
|
else
|
|
{
|
|
pPlayer->on_ground = 0;
|
|
pPlayer->vel.z += (g_spriteGravity + 80); // (TICSPERFRAME<<6);
|
|
|
|
if (pPlayer->vel.z >= (4096 + 2048))
|
|
pPlayer->vel.z = (4096 + 2048);
|
|
|
|
if (pPlayer->vel.z > 2400 && pPlayer->falling_counter < 255)
|
|
{
|
|
pPlayer->falling_counter++;
|
|
if (pPlayer->falling_counter >= 38 && pPlayer->scream_voice <= FX_Ok)
|
|
{
|
|
int32_t voice = A_PlaySound(DUKE_SCREAM,pPlayer->i);
|
|
if (voice <= 127) // XXX: p->scream_voice is an int8_t
|
|
pPlayer->scream_voice = voice;
|
|
}
|
|
}
|
|
|
|
if ((pPlayer->pos.z + pPlayer->vel.z) >= (floorZ - (floorZOffset << 8)) && pPlayer->cursectnum >= 0) // hit the ground
|
|
{
|
|
if (sector[pPlayer->cursectnum].lotag != ST_1_ABOVE_WATER)
|
|
{
|
|
if (pPlayer->falling_counter > 62)
|
|
P_QuickKill(pPlayer);
|
|
else if (pPlayer->falling_counter > 9)
|
|
{
|
|
// Falling damage.
|
|
pSprite->extra -= pPlayer->falling_counter - (krand() & 3);
|
|
|
|
if (pSprite->extra <= 0)
|
|
A_PlaySound(SQUISHED, pPlayer->i);
|
|
else
|
|
{
|
|
A_PlaySound(DUKE_LAND, pPlayer->i);
|
|
A_PlaySound(DUKE_LAND_HURT, pPlayer->i);
|
|
}
|
|
|
|
P_PalFrom(pPlayer, 32, 16, 0, 0);
|
|
}
|
|
else if (pPlayer->vel.z > 2048)
|
|
A_PlaySound(DUKE_LAND, pPlayer->i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pPlayer->falling_counter = 0;
|
|
|
|
if (pPlayer->scream_voice > FX_Ok)
|
|
{
|
|
FX_StopSound(pPlayer->scream_voice);
|
|
S_Cleanup();
|
|
pPlayer->scream_voice = -1;
|
|
}
|
|
|
|
if ((sectorLotag != ST_1_ABOVE_WATER && sectorLotag != ST_2_UNDERWATER) &&
|
|
(pPlayer->on_ground == 0 && pPlayer->vel.z > (6144 >> 1)))
|
|
pPlayer->hard_landing = pPlayer->vel.z>>10;
|
|
|
|
pPlayer->on_ground = 1;
|
|
|
|
if (floorZOffset==40)
|
|
{
|
|
//Smooth on the ground
|
|
int Zdiff = ((floorZ - (floorZOffset << 8)) - pPlayer->pos.z) >> 1;
|
|
|
|
if (klabs(Zdiff) < 256)
|
|
Zdiff = 0;
|
|
|
|
pPlayer->pos.z += ((klabs(Zdiff) >= 256) ? (((floorZ - (floorZOffset << 8)) - pPlayer->pos.z) >> 1) : 0);
|
|
pPlayer->vel.z -= 768;
|
|
|
|
if (pPlayer->vel.z < 0)
|
|
pPlayer->vel.z = 0;
|
|
}
|
|
else if (pPlayer->jumping_counter == 0)
|
|
{
|
|
pPlayer->pos.z += ((floorZ - (floorZOffset << 7)) - pPlayer->pos.z) >> 1; // Smooth on the water
|
|
|
|
if (pPlayer->on_warping_sector == 0 && pPlayer->pos.z > floorZ - ZOFFSET2)
|
|
{
|
|
pPlayer->pos.z = floorZ - ZOFFSET2;
|
|
pPlayer->vel.z >>= 1;
|
|
}
|
|
}
|
|
|
|
pPlayer->on_warping_sector = 0;
|
|
|
|
if (TEST_SYNC_KEY(playerBits, SK_CROUCH))
|
|
{
|
|
// crouching
|
|
if (VM_OnEvent(EVENT_CROUCH,pPlayer->i,playerNum) == 0)
|
|
{
|
|
pPlayer->pos.z += (2048+768);
|
|
pPlayer->crack_time = 777;
|
|
}
|
|
}
|
|
|
|
// jumping
|
|
if (!TEST_SYNC_KEY(playerBits, SK_JUMP) && pPlayer->jumping_toggle == 1)
|
|
pPlayer->jumping_toggle = 0;
|
|
else if (TEST_SYNC_KEY(playerBits, SK_JUMP) && pPlayer->jumping_toggle == 0)
|
|
{
|
|
if (pPlayer->jumping_counter == 0)
|
|
if ((floorZ-ceilZ) > (56<<8))
|
|
{
|
|
if (VM_OnEvent(EVENT_JUMP,pPlayer->i,playerNum) == 0)
|
|
{
|
|
pPlayer->jumping_counter = 1;
|
|
pPlayer->jumping_toggle = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pPlayer->jumping_counter && !TEST_SYNC_KEY(playerBits, SK_JUMP))
|
|
pPlayer->jumping_toggle = 0;
|
|
}
|
|
|
|
if (pPlayer->jumping_counter)
|
|
{
|
|
if (!TEST_SYNC_KEY(playerBits, SK_JUMP) && pPlayer->jumping_toggle == 1)
|
|
pPlayer->jumping_toggle = 0;
|
|
|
|
if (pPlayer->jumping_counter < (1024+256))
|
|
{
|
|
if (sectorLotag == ST_1_ABOVE_WATER && pPlayer->jumping_counter > 768)
|
|
{
|
|
pPlayer->jumping_counter = 0;
|
|
pPlayer->vel.z = -512;
|
|
}
|
|
else
|
|
{
|
|
pPlayer->vel.z -= (sintable[(2048-128+pPlayer->jumping_counter)&2047])/12;
|
|
pPlayer->jumping_counter += 180;
|
|
pPlayer->on_ground = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pPlayer->jumping_counter = 0;
|
|
pPlayer->vel.z = 0;
|
|
}
|
|
}
|
|
|
|
pPlayer->pos.z += pPlayer->vel.z;
|
|
|
|
if ((sectorLotag != ST_2_UNDERWATER || ceilZ != sector[pPlayer->cursectnum].ceilingz) && pPlayer->pos.z < (ceilZ+ZOFFSET6))
|
|
{
|
|
pPlayer->jumping_counter = 0;
|
|
if (pPlayer->vel.z < 0)
|
|
pPlayer->vel.x = pPlayer->vel.y = 0;
|
|
pPlayer->vel.z = 128;
|
|
pPlayer->pos.z = ceilZ+ZOFFSET6;
|
|
}
|
|
}
|
|
|
|
if (pPlayer->fist_incs || pPlayer->transporter_hold > 2 || pPlayer->hard_landing || pPlayer->access_incs > 0 ||
|
|
pPlayer->knee_incs > 0 || (PWEAPON(playerNum, pPlayer->curr_weapon, WorksLike) == TRIPBOMB_WEAPON &&
|
|
*weaponFrame > 1 && *weaponFrame < PWEAPON(playerNum, pPlayer->curr_weapon, FireDelay)))
|
|
{
|
|
velocityModifier = 0;
|
|
pPlayer->vel.x = 0;
|
|
pPlayer->vel.y = 0;
|
|
}
|
|
else if (g_player[playerNum].inputBits->avel) //p->ang += syncangvel * constant
|
|
{
|
|
int const inputAng = g_player[playerNum].inputBits->avel;
|
|
|
|
pPlayer->angvel = (sectorLotag == ST_2_UNDERWATER) ? (inputAng - (inputAng >> 3)) * ksgn(velocityModifier)
|
|
: inputAng * ksgn(velocityModifier);
|
|
pPlayer->ang += pPlayer->angvel;
|
|
pPlayer->ang &= 2047;
|
|
pPlayer->crack_time = 777;
|
|
}
|
|
|
|
if (pPlayer->spritebridge == 0)
|
|
{
|
|
int const floorPicnum = sector[pSprite->sectnum].floorpicnum;
|
|
|
|
if (floorPicnum == PURPLELAVA || sector[pSprite->sectnum].ceilingpicnum == PURPLELAVA)
|
|
{
|
|
if (pPlayer->inv_amount[GET_BOOTS] > 0)
|
|
{
|
|
pPlayer->inv_amount[GET_BOOTS]--;
|
|
pPlayer->inven_icon = ICON_BOOTS;
|
|
if (pPlayer->inv_amount[GET_BOOTS] <= 0)
|
|
P_SelectNextInvItem(pPlayer);
|
|
}
|
|
else
|
|
{
|
|
if (!A_CheckSoundPlaying(pPlayer->i,DUKE_LONGTERM_PAIN))
|
|
A_PlaySound(DUKE_LONGTERM_PAIN,pPlayer->i);
|
|
P_PalFrom(pPlayer, 32, 0, 8, 0);
|
|
pSprite->extra--;
|
|
}
|
|
}
|
|
|
|
if (pPlayer->on_ground && trueFloorDist <= PHEIGHT+ZOFFSET2 && P_CheckFloorDamage(pPlayer, floorPicnum))
|
|
{
|
|
P_DoQuote(QUOTE_BOOTS_ON, pPlayer);
|
|
pPlayer->inv_amount[GET_BOOTS] -= 2;
|
|
if (pPlayer->inv_amount[GET_BOOTS] <= 0)
|
|
{
|
|
pPlayer->inv_amount[GET_BOOTS] = 0;
|
|
P_SelectNextInvItem(pPlayer);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (g_player[playerNum].inputBits->extbits & (1)) VM_OnEvent(EVENT_MOVEFORWARD, pPlayer->i, playerNum);
|
|
if (g_player[playerNum].inputBits->extbits & (1 << 1)) VM_OnEvent(EVENT_MOVEBACKWARD, pPlayer->i, playerNum);
|
|
if (g_player[playerNum].inputBits->extbits & (1 << 2)) VM_OnEvent(EVENT_STRAFELEFT, pPlayer->i, playerNum);
|
|
if (g_player[playerNum].inputBits->extbits & (1 << 3)) VM_OnEvent(EVENT_STRAFERIGHT, pPlayer->i, playerNum);
|
|
|
|
if (g_player[playerNum].inputBits->extbits & (1 << 4) || g_player[playerNum].inputBits->avel < 0)
|
|
VM_OnEvent(EVENT_TURNLEFT, pPlayer->i, playerNum);
|
|
|
|
if (g_player[playerNum].inputBits->extbits & (1 << 5) || g_player[playerNum].inputBits->avel > 0)
|
|
VM_OnEvent(EVENT_TURNRIGHT, pPlayer->i, playerNum);
|
|
|
|
if (pPlayer->vel.x || pPlayer->vel.y || g_player[playerNum].inputBits->fvel || g_player[playerNum].inputBits->svel)
|
|
{
|
|
pPlayer->crack_time = 777;
|
|
|
|
int const checkWalkSound = sintable[pPlayer->bobcounter & 2047] >> 12;
|
|
|
|
if ((trueFloorDist < PHEIGHT + ZOFFSET3) && (checkWalkSound == 1 || checkWalkSound == 3))
|
|
{
|
|
if (pPlayer->walking_snd_toggle == 0 && pPlayer->on_ground)
|
|
{
|
|
switch (sectorLotag)
|
|
{
|
|
case 0:
|
|
{
|
|
int const walkPicnum = (lowZhit >= 0 && (lowZhit & 49152) == 49152)
|
|
? TrackerCast(sprite[lowZhit & (MAXSPRITES - 1)].picnum)
|
|
: TrackerCast(sector[pPlayer->cursectnum].floorpicnum);
|
|
|
|
switch (DYNAMICTILEMAP(walkPicnum))
|
|
{
|
|
case PANNEL1__STATIC:
|
|
case PANNEL2__STATIC:
|
|
A_PlaySound(DUKE_WALKINDUCTS, pPlayer->i);
|
|
pPlayer->walking_snd_toggle = 1;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ST_1_ABOVE_WATER:
|
|
if (!pPlayer->spritebridge)
|
|
{
|
|
if ((krand() & 1) == 0)
|
|
A_PlaySound(DUKE_ONWATER, pPlayer->i);
|
|
pPlayer->walking_snd_toggle = 1;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (pPlayer->walking_snd_toggle > 0)
|
|
pPlayer->walking_snd_toggle--;
|
|
|
|
if (pPlayer->jetpack_on == 0 && pPlayer->inv_amount[GET_STEROIDS] > 0 && pPlayer->inv_amount[GET_STEROIDS] < 400)
|
|
velocityModifier <<= 1;
|
|
|
|
pPlayer->vel.x += (((g_player[playerNum].inputBits->fvel) * velocityModifier) << 6);
|
|
pPlayer->vel.y += (((g_player[playerNum].inputBits->svel) * velocityModifier) << 6);
|
|
|
|
int playerSpeedReduction = 0;
|
|
|
|
if (sectorLotag == ST_2_UNDERWATER)
|
|
playerSpeedReduction = 0x1400;
|
|
else if (((pPlayer->on_ground && (TEST_SYNC_KEY(playerBits, SK_CROUCH)))
|
|
|| (*weaponFrame > 10 && PWEAPON(playerNum, pPlayer->curr_weapon, WorksLike) == KNEE_WEAPON)))
|
|
playerSpeedReduction = 0x2000;
|
|
|
|
pPlayer->vel.x = mulscale16(pPlayer->vel.x, pPlayer->runspeed - playerSpeedReduction);
|
|
pPlayer->vel.y = mulscale16(pPlayer->vel.y, pPlayer->runspeed - playerSpeedReduction);
|
|
|
|
if (klabs(pPlayer->vel.x) < 2048 && klabs(pPlayer->vel.y) < 2048)
|
|
pPlayer->vel.x = pPlayer->vel.y = 0;
|
|
|
|
if (playerShrunk)
|
|
{
|
|
pPlayer->vel.x = mulscale16(pPlayer->vel.x, pPlayer->runspeed - (pPlayer->runspeed >> 1) + (pPlayer->runspeed >> 2));
|
|
pPlayer->vel.y = mulscale16(pPlayer->vel.y, pPlayer->runspeed - (pPlayer->runspeed >> 1) + (pPlayer->runspeed >> 2));
|
|
}
|
|
}
|
|
|
|
HORIZONLY:;
|
|
int stepHeight = (sectorLotag == ST_1_ABOVE_WATER || pPlayer->spritebridge == 1) ? pPlayer->autostep_sbw : pPlayer->autostep;
|
|
|
|
#ifdef EDUKE32_TOUCH_DEVICES
|
|
if (TEST_SYNC_KEY(playerBits, SK_CROUCH))
|
|
stepHeight = pPlayer->autostep_sbw;
|
|
#endif
|
|
|
|
if (ud.noclip)
|
|
{
|
|
pPlayer->pos.x += pPlayer->vel.x >> 14;
|
|
pPlayer->pos.y += pPlayer->vel.y >> 14;
|
|
updatesector(pPlayer->pos.x, pPlayer->pos.y, &pPlayer->cursectnum);
|
|
changespritesect(pPlayer->i, pPlayer->cursectnum);
|
|
}
|
|
else
|
|
{
|
|
#ifdef YAX_ENABLE
|
|
int const playerSectNum = pPlayer->cursectnum;
|
|
int16_t ceilingBunch, floorBunch;
|
|
|
|
if (playerSectNum >= 0)
|
|
yax_getbunches(playerSectNum, &ceilingBunch, &floorBunch);
|
|
|
|
// This updatesectorz conflicts with Duke3D's way of teleporting through water,
|
|
// so make it a bit conditional... OTOH, this way we have an ugly z jump when
|
|
// changing from above water to underwater
|
|
|
|
if ((playerSectNum >= 0 && !(sector[playerSectNum].lotag == ST_1_ABOVE_WATER && pPlayer->on_ground && floorBunch >= 0))
|
|
&& ((floorBunch >= 0 && !(sector[playerSectNum].floorstat & 512))
|
|
|| (ceilingBunch >= 0 && !(sector[playerSectNum].ceilingstat & 512))))
|
|
{
|
|
pPlayer->cursectnum += MAXSECTORS; // skip initial z check, restored by updatesectorz
|
|
updatesectorz(pPlayer->pos.x, pPlayer->pos.y, pPlayer->pos.z, &pPlayer->cursectnum);
|
|
}
|
|
#endif
|
|
if ((spriteNum = clipmove((vec3_t *)pPlayer, &pPlayer->cursectnum, pPlayer->vel.x + (pPlayer->fric.x << 9),
|
|
pPlayer->vel.y + (pPlayer->fric.y << 9), 164L, (4L << 8), stepHeight, CLIPMASK0)))
|
|
P_CheckTouchDamage(pPlayer, spriteNum);
|
|
|
|
pPlayer->fric.x = pPlayer->fric.y = 0;
|
|
}
|
|
|
|
// This makes the player view lower when shrunk. NOTE that it can get the
|
|
// view below the sector floor (and does, when on the ground).
|
|
if (pPlayer->jetpack_on == 0 && sectorLotag != ST_2_UNDERWATER && sectorLotag != ST_1_ABOVE_WATER && playerShrunk)
|
|
pPlayer->pos.z += ZOFFSET5;
|
|
|
|
if (pPlayer->jetpack_on == 0)
|
|
{
|
|
if (pSprite->xvel > 16)
|
|
{
|
|
if (sectorLotag != ST_1_ABOVE_WATER && sectorLotag != ST_2_UNDERWATER && pPlayer->on_ground)
|
|
{
|
|
pPlayer->pycount += 52;
|
|
pPlayer->pycount &= 2047;
|
|
pPlayer->pyoff = klabs(pSprite->xvel * sintable[pPlayer->pycount]) / 1596;
|
|
}
|
|
}
|
|
else if (sectorLotag != ST_2_UNDERWATER && sectorLotag != ST_1_ABOVE_WATER)
|
|
pPlayer->pyoff = 0;
|
|
}
|
|
|
|
pPlayer->pos.z += PHEIGHT;
|
|
setsprite(pPlayer->i, (vec3_t *)&pPlayer->pos.x);
|
|
pPlayer->pos.z -= PHEIGHT;
|
|
|
|
// ST_2_UNDERWATER
|
|
if (pPlayer->cursectnum >= 0 && sectorLotag < 3)
|
|
{
|
|
usectortype const *pSector = (usectortype *)§or[pPlayer->cursectnum];
|
|
|
|
// TRAIN_SECTOR_TO_SE_INDEX
|
|
if ((!ud.noclip && pSector->lotag == ST_31_TWO_WAY_TRAIN) &&
|
|
((unsigned)pSector->hitag < MAXSPRITES && sprite[pSector->hitag].xvel && actor[pSector->hitag].t_data[0] == 0))
|
|
{
|
|
P_QuickKill(pPlayer);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ((pPlayer->cursectnum >= 0 && trueFloorDist < PHEIGHT && pPlayer->on_ground && sectorLotag != ST_1_ABOVE_WATER &&
|
|
playerShrunk == 0 && sector[pPlayer->cursectnum].lotag == ST_1_ABOVE_WATER) && (!A_CheckSoundPlaying(pPlayer->i, DUKE_ONWATER)))
|
|
A_PlaySound(DUKE_ONWATER, pPlayer->i);
|
|
|
|
if (pPlayer->cursectnum >= 0 && pPlayer->cursectnum != pSprite->sectnum)
|
|
changespritesect(pPlayer->i, pPlayer->cursectnum);
|
|
|
|
if (pPlayer->cursectnum >= 0 && ud.noclip == 0)
|
|
{
|
|
int const squishPlayer = (pushmove((vec3_t *)pPlayer, &pPlayer->cursectnum, 164L, (4L << 8), (4L << 8), CLIPMASK0) < 0 &&
|
|
A_GetFurthestAngle(pPlayer->i, 8) < 512);
|
|
|
|
if (squishPlayer || klabs(actor[pPlayer->i].floorz-actor[pPlayer->i].ceilingz) < (48<<8))
|
|
{
|
|
if (!(sector[pSprite->sectnum].lotag & 0x8000) &&
|
|
(isanunderoperator(sector[pSprite->sectnum].lotag) || isanearoperator(sector[pSprite->sectnum].lotag)))
|
|
G_ActivateBySector(pSprite->sectnum, pPlayer->i);
|
|
|
|
if (squishPlayer)
|
|
{
|
|
P_QuickKill(pPlayer);
|
|
return;
|
|
}
|
|
}
|
|
else if (klabs(floorZ - ceilZ) < ZOFFSET5 && isanunderoperator(sector[pPlayer->cursectnum].lotag))
|
|
G_ActivateBySector(pPlayer->cursectnum, pPlayer->i);
|
|
}
|
|
|
|
int centerHoriz = 0;
|
|
|
|
if (TEST_SYNC_KEY(playerBits, SK_CENTER_VIEW) || pPlayer->hard_landing)
|
|
if (VM_OnEvent(EVENT_RETURNTOCENTER,pPlayer->i,playerNum) == 0)
|
|
pPlayer->return_to_center = 9;
|
|
|
|
if (TEST_SYNC_KEY(playerBits, SK_LOOK_UP))
|
|
{
|
|
if (VM_OnEvent(EVENT_LOOKUP,pPlayer->i,playerNum) == 0)
|
|
{
|
|
pPlayer->return_to_center = 9;
|
|
pPlayer->horiz += 12<<(int)(TEST_SYNC_KEY(playerBits, SK_RUN));
|
|
centerHoriz++;
|
|
}
|
|
}
|
|
|
|
if (TEST_SYNC_KEY(playerBits, SK_LOOK_DOWN))
|
|
{
|
|
if (VM_OnEvent(EVENT_LOOKDOWN,pPlayer->i,playerNum) == 0)
|
|
{
|
|
pPlayer->return_to_center = 9;
|
|
pPlayer->horiz -= 12<<(int)(TEST_SYNC_KEY(playerBits, SK_RUN));
|
|
centerHoriz++;
|
|
}
|
|
}
|
|
|
|
if (TEST_SYNC_KEY(playerBits, SK_AIM_UP))
|
|
{
|
|
if (VM_OnEvent(EVENT_AIMUP,pPlayer->i,playerNum) == 0)
|
|
{
|
|
pPlayer->horiz += 6<<(int)(TEST_SYNC_KEY(playerBits, SK_RUN));
|
|
centerHoriz++;
|
|
}
|
|
}
|
|
|
|
if (TEST_SYNC_KEY(playerBits, SK_AIM_DOWN))
|
|
{
|
|
if (VM_OnEvent(EVENT_AIMDOWN,pPlayer->i,playerNum) == 0)
|
|
{
|
|
pPlayer->horiz -= 6<<(int)(TEST_SYNC_KEY(playerBits, SK_RUN));
|
|
centerHoriz++;
|
|
}
|
|
}
|
|
|
|
if (pPlayer->return_to_center > 0 && !TEST_SYNC_KEY(playerBits, SK_LOOK_UP) && !TEST_SYNC_KEY(playerBits, SK_LOOK_DOWN))
|
|
{
|
|
pPlayer->return_to_center--;
|
|
pPlayer->horiz += 33-(pPlayer->horiz/3);
|
|
centerHoriz++;
|
|
}
|
|
|
|
if (pPlayer->hard_landing > 0)
|
|
{
|
|
pPlayer->hard_landing--;
|
|
pPlayer->horiz -= (pPlayer->hard_landing<<4);
|
|
}
|
|
|
|
if (centerHoriz)
|
|
{
|
|
if (pPlayer->horiz > 95 && pPlayer->horiz < 105) pPlayer->horiz = 100;
|
|
if (pPlayer->horizoff > -5 && pPlayer->horizoff < 5) pPlayer->horizoff = 0;
|
|
}
|
|
|
|
pPlayer->horiz = clamp(pPlayer->horiz + g_player[playerNum].inputBits->horz, HORIZ_MIN, HORIZ_MAX);
|
|
|
|
//Shooting code/changes
|
|
|
|
if (pPlayer->show_empty_weapon > 0)
|
|
{
|
|
--pPlayer->show_empty_weapon;
|
|
|
|
if (pPlayer->show_empty_weapon == 0 && (pPlayer->weaponswitch & 2) && pPlayer->ammo_amount[pPlayer->curr_weapon] <= 0)
|
|
{
|
|
if (pPlayer->last_full_weapon == GROW_WEAPON)
|
|
pPlayer->subweapon |= (1 << GROW_WEAPON);
|
|
else if (pPlayer->last_full_weapon == SHRINKER_WEAPON)
|
|
pPlayer->subweapon &= ~(1 << GROW_WEAPON);
|
|
|
|
P_AddWeapon(pPlayer, pPlayer->last_full_weapon, 1);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (pPlayer->knee_incs > 0)
|
|
{
|
|
pPlayer->horiz -= 48;
|
|
pPlayer->return_to_center = 9;
|
|
|
|
if (++pPlayer->knee_incs > 15)
|
|
{
|
|
pPlayer->knee_incs = 0;
|
|
pPlayer->holster_weapon = 0;
|
|
pPlayer->weapon_pos = klabs(pPlayer->weapon_pos);
|
|
|
|
if (pPlayer->actorsqu >= 0 && sprite[pPlayer->actorsqu].statnum != MAXSTATUS &&
|
|
dist(&sprite[pPlayer->i], &sprite[pPlayer->actorsqu]) < 1400)
|
|
{
|
|
A_DoGuts(pPlayer->actorsqu, JIBS6, 7);
|
|
A_Spawn(pPlayer->actorsqu, BLOODPOOL);
|
|
A_PlaySound(SQUISHED, pPlayer->actorsqu);
|
|
|
|
switch (DYNAMICTILEMAP(sprite[pPlayer->actorsqu].picnum))
|
|
{
|
|
case FEM1__STATIC:
|
|
case FEM2__STATIC:
|
|
case FEM3__STATIC:
|
|
case FEM4__STATIC:
|
|
case FEM5__STATIC:
|
|
case FEM6__STATIC:
|
|
case FEM7__STATIC:
|
|
case FEM8__STATIC:
|
|
case FEM9__STATIC:
|
|
case FEM10__STATIC:
|
|
case PODFEM1__STATIC:
|
|
case NAKED1__STATIC:
|
|
case STATUE__STATIC:
|
|
if (sprite[pPlayer->actorsqu].yvel)
|
|
G_OperateRespawns(sprite[pPlayer->actorsqu].yvel);
|
|
A_DeleteSprite(pPlayer->actorsqu);
|
|
break;
|
|
case APLAYER__STATIC:
|
|
{
|
|
int playerNum = P_Get(pPlayer->actorsqu);
|
|
P_QuickKill(g_player[playerNum].ps);
|
|
g_player[playerNum].ps->frag_ps = playerNum;
|
|
break;
|
|
}
|
|
default:
|
|
if (A_CheckEnemySprite(&sprite[pPlayer->actorsqu]))
|
|
pPlayer->actors_killed++;
|
|
A_DeleteSprite(pPlayer->actorsqu);
|
|
break;
|
|
}
|
|
}
|
|
pPlayer->actorsqu = -1;
|
|
}
|
|
else if (pPlayer->actorsqu >= 0)
|
|
pPlayer->ang += G_GetAngleDelta(pPlayer->ang, getangle(sprite[pPlayer->actorsqu].x - pPlayer->pos.x,
|
|
sprite[pPlayer->actorsqu].y - pPlayer->pos.y)) >> 2;
|
|
}
|
|
|
|
if (P_DoCounters(playerNum))
|
|
return;
|
|
|
|
P_ProcessWeapon(playerNum);
|
|
}
|