raze/source/games/exhumed/src/grenade.cpp

449 lines
12 KiB
C++
Raw Normal View History

//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 sirlemonhead, Nuke.YKT
This file is part of PCExhumed.
PCExhumed 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 "ns.h"
#include "aistuff.h"
#include "engine.h"
#include "player.h"
#include "exhumed.h"
#include "sound.h"
#include "sequence.h"
#include <assert.h>
BEGIN_PS_NS
struct Grenade
{
short nCount;
short nHealth;
short nSprite;
short nPhase;
short nRun;
short nIndex;
short nFrame;
short nIndex2;
int nTurn;
int x;
int y;
};
2020-11-30 00:10:52 +00:00
FreeListArray<Grenade, kMaxGrenades> GrenadeList;
2020-11-30 00:10:52 +00:00
FSerializer& Serialize(FSerializer& arc, const char* keyname, Grenade& w, Grenade* def)
{
static Grenade nul;
if (!def)
{
def = &nul;
if (arc.isReading()) w = {};
}
if (arc.BeginObject(keyname))
{
arc("sprite", w.nSprite, def->nSprite)
("at0", w.nCount, def->nCount)
("at2", w.nHealth, def->nHealth)
("at6", w.nPhase, def->nPhase)
("at8", w.nRun, def->nRun)
("ata", w.nIndex, def->nIndex)
("atc", w.nFrame, def->nFrame)
("ate", w.nIndex2, def->nIndex2)
("at10", w.nTurn, def->nTurn)
2020-11-30 00:10:52 +00:00
("x", w.x, def->x)
("y", w.y, def->y)
.EndObject();
}
return arc;
}
2020-11-30 00:10:52 +00:00
void SerializeGrenade(FSerializer& arc)
{
2020-11-30 00:10:52 +00:00
arc("grenades", GrenadeList);
}
2020-11-30 00:10:52 +00:00
void InitGrenades()
{
GrenadeList.Clear();
}
short GrabGrenade()
{
2020-11-30 00:10:52 +00:00
return GrenadeList.Get();
}
void DestroyGrenade(short nGrenade)
{
auto pActor = &GrenadeList[nGrenade];
2021-10-20 21:33:08 +00:00
runlist_DoSubRunRec(pActor->nPhase);
runlist_SubRunRec(pActor->nRun);
runlist_DoSubRunRec(sprite[pActor->nSprite].lotag - 1);
2021-10-20 21:33:08 +00:00
mydeletesprite(pActor->nSprite);
2020-11-30 00:10:52 +00:00
GrenadeList.Release(nGrenade);
}
void BounceGrenade(short nGrenade, short nAngle)
{
auto pActor = &GrenadeList[nGrenade];
2021-10-20 21:33:08 +00:00
pActor->nTurn >>= 1;
2021-10-20 21:33:08 +00:00
pActor->x = bcos(nAngle, -5) * pActor->nTurn;
pActor->y = bsin(nAngle, -5) * pActor->nTurn;
2021-10-20 21:33:08 +00:00
D3PlayFX(StaticSound[kSound3], pActor->nSprite);
}
int ThrowGrenade(short nPlayer, int, int, int ecx, int push1)
{
if (nPlayerGrenade[nPlayer] < 0)
return -1;
short nGrenade = nPlayerGrenade[nPlayer];
auto pActor = &GrenadeList[nGrenade];
2021-10-20 21:33:08 +00:00
short nGrenadeSprite = pActor->nSprite;
short nPlayerSprite = PlayerList[nPlayer].nSprite;
auto pGrenadeSprite = &sprite[nGrenadeSprite];
auto pPlayerSprite = &sprite[nPlayerSprite];
short nAngle = pPlayerSprite->ang;
mychangespritesect(nGrenadeSprite, nPlayerViewSect[nPlayer]);
pGrenadeSprite->x = pPlayerSprite->x;
pGrenadeSprite->y = pPlayerSprite->y;
pGrenadeSprite->z = pPlayerSprite->z;
if (nAngle < 0) {
nAngle = pPlayerSprite->ang;
}
pGrenadeSprite->cstat &= 0x7FFF;
pGrenadeSprite->ang = nAngle;
if (push1 >= -3000)
{
int nVel = totalvel[nPlayer] << 5;
2021-10-20 21:33:08 +00:00
pActor->nTurn = ((90 - pActor->nIndex2) * (90 - pActor->nIndex2)) + nVel;
pGrenadeSprite->zvel = (-64 * push1) - 4352;
int nMov = movesprite(nGrenadeSprite, bcos(nAngle) * (pPlayerSprite->clipdist << 3), bsin(nAngle) * (pPlayerSprite->clipdist << 3), ecx, 0, 0, CLIPMASK1);
if (nMov & 0x8000)
{
nAngle = GetWallNormal(nMov & 0x3FFF);
BounceGrenade(nGrenade, nAngle);
}
}
else
{
2021-10-20 21:33:08 +00:00
pActor->nTurn = 0;
pGrenadeSprite->zvel = pPlayerSprite->zvel;
}
2021-10-20 21:33:08 +00:00
pActor->x = bcos(nAngle, -4) * pActor->nTurn;
pActor->y = bsin(nAngle, -4) * pActor->nTurn;
nPlayerGrenade[nPlayer] = -1;
return nGrenadeSprite;
}
void BuildGrenade(int nPlayer)
{
int nGrenade = GrabGrenade();
if (nGrenade < 0) return;
auto pActor = &GrenadeList[nGrenade];
int nSprite = insertsprite(nPlayerViewSect[nPlayer], 201);
assert(nSprite >= 0 && nSprite < kMaxSprites);
auto pSprite = &sprite[nSprite];
int nPlayerSprite = PlayerList[nPlayer].nSprite;
auto pPlayerSprite = &sprite[nPlayerSprite];
pSprite->x = pPlayerSprite->x;
pSprite->y = pPlayerSprite->y;
pSprite->z = pPlayerSprite->z - 3840;
pSprite->shade = -64;
pSprite->xrepeat = 20;
pSprite->yrepeat = 20;
pSprite->cstat = 0x8000;
pSprite->picnum = 1;
pSprite->pal = 0;
pSprite->clipdist = 30;
pSprite->xoffset = 0;
pSprite->yoffset = 0;
pSprite->ang = pPlayerSprite->ang;
pSprite->owner = nPlayerSprite;
pSprite->xvel = 0;
pSprite->yvel = 0;
pSprite->zvel = 0;
pSprite->hitag = 0;
pSprite->lotag = runlist_HeadRun() + 1;
pSprite->extra = -1;
pSprite->backuppos();
// GrabTimeSlot(3);
2021-10-20 21:33:08 +00:00
pActor->nIndex2 = 90;
pActor->nHealth = 0;
pActor->nCount = 16;
pActor->nTurn = -1;
pActor->nSprite = nSprite;
pActor->nIndex = 0;
pActor->nFrame = 0;
pActor->nPhase = runlist_AddRunRec(pSprite->lotag - 1, nGrenade, 0x0F0000);
pActor->nRun = runlist_AddRunRec(NewRun, nGrenade, 0x0F0000);
nGrenadePlayer[nGrenade] = nPlayer;
nPlayerGrenade[nPlayer] = nGrenade;
}
void ExplodeGrenade(short nGrenade)
{
int var_28, var_20;
auto pActor = &GrenadeList[nGrenade];
short nPlayer = nGrenadePlayer[nGrenade];
2021-10-20 21:33:08 +00:00
int nGrenadeSprite = pActor->nSprite;
auto pGrenadeSprite = &sprite[nGrenadeSprite];
short nGrenadeSect = pGrenadeSprite->sectnum;
2021-10-20 21:33:08 +00:00
pActor->nFrame = 1;
if (SectFlag[nGrenadeSect] & kSectUnderwater)
{
var_28 = 75;
var_20 = 60;
}
else
{
if (pGrenadeSprite->z < sector[nGrenadeSect].floorz)
{
var_20 = 200;
var_28 = 36;
// TODO MonoOut("GRENPOW\n");
}
else
{
var_28 = 34;
var_20 = 150;
// TODO MonoOut("GRENBOOM\n");
}
}
2021-10-20 21:33:08 +00:00
if (pActor->nTurn < 0)
{
short nPlayerSprite = PlayerList[nPlayer].nSprite;
auto pPlayerSprite = &sprite[nPlayerSprite];
short nAngle = pPlayerSprite->ang;
pGrenadeSprite->z = pPlayerSprite->z;
pGrenadeSprite->x = bcos(nAngle, -5) + pPlayerSprite->x;
pGrenadeSprite->y = bsin(nAngle, -5) + pPlayerSprite->y;
changespritesect(nGrenadeSprite, pPlayerSprite->sectnum);
if (!PlayerList[nPlayer].invincibility) {
PlayerList[nPlayer].nHealth = 1;
}
}
short nDamage = BulletInfo[kWeaponGrenade].nDamage;
if (PlayerList[nPlayer].nDouble > 0) {
nDamage *= 2;
}
runlist_RadialDamageEnemy(nGrenadeSprite, nDamage, BulletInfo[kWeaponGrenade].nRadius);
BuildAnim(nullptr, var_28, 0, pGrenadeSprite->x, pGrenadeSprite->y, pGrenadeSprite->z, pGrenadeSprite->sectnum, var_20, 4);
AddFlash(pGrenadeSprite->sectnum, pGrenadeSprite->x, pGrenadeSprite->y, pGrenadeSprite->z, 128);
nGrenadePlayer[nGrenade] = -1;
DestroyGrenade(nGrenade);
}
2021-10-15 19:20:28 +00:00
void AIGrenade::Draw(RunListEvent* ev)
{
short nGrenade = RunData[ev->nRun].nObjIndex;
auto pActor = &GrenadeList[nGrenade];
2021-10-15 19:20:28 +00:00
assert(nGrenade >= 0 && nGrenade < kMaxGrenades);
2021-10-20 21:33:08 +00:00
short nSeq = pActor->nFrame ? SeqOffsets[kSeqGrenBoom] : SeqOffsets[kSeqGrenRoll] + pActor->nIndex;
seq_PlotSequence(ev->nParam, nSeq, pActor->nHealth >> 8, 1);
2021-10-15 19:20:28 +00:00
}
void AIGrenade::Tick(RunListEvent* ev)
{
short nGrenade = RunData[ev->nRun].nObjIndex;
auto pActor = &GrenadeList[nGrenade];
assert(nGrenade >= 0 && nGrenade < kMaxGrenades);
2021-10-20 21:33:08 +00:00
short nGrenadeSprite = pActor->nSprite;
2021-10-15 19:20:28 +00:00
auto pGrenadeSprite = &sprite[nGrenadeSprite];
2021-10-20 21:33:08 +00:00
short nSeq = pActor->nFrame ? SeqOffsets[kSeqGrenBoom] : SeqOffsets[kSeqGrenRoll] + pActor->nIndex;
2021-10-20 21:33:08 +00:00
seq_MoveSequence(nGrenadeSprite, nSeq, pActor->nHealth >> 8);
pGrenadeSprite->picnum = seq_GetSeqPicnum2(nSeq, pActor->nHealth >> 8);
2021-10-20 21:33:08 +00:00
pActor->nIndex2--;
if (!pActor->nIndex2)
{
2021-10-15 19:20:28 +00:00
short nPlayer = nGrenadePlayer[nGrenade];
2021-10-20 21:33:08 +00:00
if (pActor->nTurn < 0)
{
2021-10-15 19:20:28 +00:00
PlayerList[nPlayer].field_3A = 0;
PlayerList[nPlayer].field_3FOUR = 0;
2021-10-15 19:20:28 +00:00
if (PlayerList[nPlayer].nAmmo[kWeaponGrenade])
{
2021-10-15 19:20:28 +00:00
PlayerList[nPlayer].bIsFiring = false;
}
else
{
2021-10-15 19:20:28 +00:00
SelectNewWeapon(nPlayer);
PlayerList[nPlayer].nCurrentWeapon = PlayerList[nPlayer].field_38;
PlayerList[nPlayer].field_38 = -1;
}
}
ExplodeGrenade(nGrenade);
return;
}
else
{
2021-10-20 21:33:08 +00:00
if (pActor->nTurn < 0) {
2021-10-15 19:20:28 +00:00
return;
}
2021-10-20 21:33:08 +00:00
int ebp = (pActor->nHealth + pActor->nCount) >> 8;
2021-10-20 21:33:08 +00:00
pActor->nHealth += pActor->nCount;
2021-10-15 19:20:28 +00:00
if (ebp < 0)
{
2021-10-20 21:33:08 +00:00
pActor->nHealth += SeqSize[nSeq] << 8;
2021-10-15 19:20:28 +00:00
}
else
{
if (ebp >= SeqSize[nSeq])
{
2021-10-20 21:33:08 +00:00
if (pActor->nFrame)
{
2021-10-15 19:20:28 +00:00
DestroyGrenade(nGrenade);
return;
}
else
{
2021-10-20 21:33:08 +00:00
pActor->nHealth = pActor->nFrame;
}
2021-10-15 19:20:28 +00:00
}
}
2021-10-20 21:33:08 +00:00
if (pActor->nFrame) {
2021-10-15 19:20:28 +00:00
return;
}
2021-10-15 19:20:28 +00:00
int zVel = pGrenadeSprite->zvel;
2021-10-15 19:20:28 +00:00
Gravity(nGrenadeSprite);
2021-10-20 21:33:08 +00:00
int nMov = movesprite(nGrenadeSprite, pActor->x, pActor->y, pGrenadeSprite->zvel, pGrenadeSprite->clipdist >> 1, pGrenadeSprite->clipdist >> 1, CLIPMASK1);
2021-10-15 19:20:28 +00:00
if (!nMov)
return;
2021-10-15 19:20:28 +00:00
if (nMov & 0x20000)
{
if (zVel)
{
if (SectDamage[pGrenadeSprite->sectnum] > 0)
{
2021-10-15 19:20:28 +00:00
ExplodeGrenade(nGrenade);
return;
}
2021-10-20 21:33:08 +00:00
pActor->nCount = (uint8_t)totalmoves; // limit to 8bits?
2021-10-15 19:20:28 +00:00
D3PlayFX(StaticSound[kSound3], nGrenadeSprite);
pGrenadeSprite->zvel = -(zVel >> 1);
if (pGrenadeSprite->zvel > -1280)
{
2021-10-15 19:20:28 +00:00
D3PlayFX(StaticSound[kSound5], nGrenadeSprite);
2021-10-20 21:33:08 +00:00
pActor->nCount = 0;
pActor->nHealth = 0;
2021-10-15 19:20:28 +00:00
pGrenadeSprite->zvel = 0;
2021-10-20 21:33:08 +00:00
pActor->nIndex = 1;
}
}
2021-10-20 21:33:08 +00:00
pActor->nCount = 255 - (RandomByte() * 2);
pActor->x -= (pActor->x >> 4);
pActor->y -= (pActor->y >> 4);
}
2021-10-15 19:20:28 +00:00
// loc_2CF60:
if ((nMov & 0xC000) >= 0x8000)
{
2021-10-15 19:20:28 +00:00
if ((nMov & 0xC000) <= 0x8000)
{
2021-10-15 19:20:28 +00:00
BounceGrenade(nGrenade, GetWallNormal(nMov & 0x3FFF));
}
2021-10-15 19:20:28 +00:00
else if ((nMov & 0xC000) == 0xC000)
{
BounceGrenade(nGrenade, sprite[nMov & 0x3FFF].ang);
}
}
2021-10-20 21:33:08 +00:00
pActor->nHealth = 0;
2021-10-15 19:20:28 +00:00
}
}
void AIGrenade::RadialDamage(RunListEvent* ev)
{
short nGrenade = RunData[ev->nRun].nObjIndex;
auto pActor = &GrenadeList[nGrenade];
2021-10-15 19:20:28 +00:00
assert(nGrenade >= 0 && nGrenade < kMaxGrenades);
2021-10-20 21:33:08 +00:00
short nGrenadeSprite = pActor->nSprite;
2021-10-15 19:20:28 +00:00
auto pGrenadeSprite = &sprite[nGrenadeSprite];
2021-10-20 21:33:08 +00:00
if (nGrenadeSprite != nRadialSpr && !pActor->nFrame)
2021-10-15 19:20:28 +00:00
{
if (runlist_CheckRadialDamage(nGrenadeSprite) > 280)
{
2021-10-20 21:33:08 +00:00
pActor->nIndex2 = RandomSize(4) + 1;
}
}
}
2021-10-15 19:20:28 +00:00
void FuncGrenade(int nObject, int nMessage, int nDamage, int nRun)
{
AIGrenade ai;
runlist_DispatchEvent(&ai, nObject, nMessage, nDamage, nRun);
}
END_PS_NS