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

880 lines
23 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 "engine.h"
#include "aistuff.h"
#include "sequence.h"
#include "exhumed.h"
#include "sound.h"
#include "player.h"
#include "names.h"
#include <string.h>
#include <assert.h>
BEGIN_PS_NS
enum { kMaxBullets = 500 };
// 32 bytes
struct Bullet
{
2021-10-16 19:21:04 +00:00
DExhumedActor* pActor;
2021-10-16 20:04:21 +00:00
DExhumedActor* pEnemy;
2021-10-16 19:21:04 +00:00
short nSeq;
short nFrame;
2019-09-25 21:16:12 +00:00
short field_6;
short field_8;
short nType;
2021-10-19 22:26:26 +00:00
short nPitch;
2019-09-25 21:16:12 +00:00
short field_E;
2019-10-01 17:59:20 +00:00
uint16_t field_10;
2019-09-25 21:16:12 +00:00
uint8_t field_12;
uint8_t nDoubleDamage;
2019-09-25 21:16:12 +00:00
int x;
int y;
int z;
};
2020-11-30 00:10:52 +00:00
FreeListArray<Bullet, kMaxBullets> BulletList;
int lasthitz, lasthitx, lasthity;
2021-10-16 22:01:06 +00:00
short lasthitsect;
short nRadialBullet = 0;
2020-11-30 00:10:52 +00:00
FSerializer& Serialize(FSerializer& arc, const char* keyname, Bullet& w, Bullet* def)
{
static Bullet nul;
if (!def)
{
def = &nul;
if (arc.isReading()) w = {};
}
if (arc.BeginObject(keyname))
{
arc("seq", w.nSeq, def->nSeq)
("frame", w.nFrame, def->nFrame)
2021-10-16 19:21:04 +00:00
("sprite", w.pActor, def->pActor)
2020-11-30 00:10:52 +00:00
("type", w.nType, def->nType)
("x", w.x, def->x)
("y", w.y, def->y)
("z", w.z, def->z)
("at6", w.field_6, def->field_6)
("at8", w.field_8, def->field_8)
2021-10-19 22:26:26 +00:00
("atc", w.nPitch, def->nPitch)
2020-11-30 00:10:52 +00:00
("ate", w.field_E, def->field_E)
("at10", w.field_10, def->field_10)
("at12", w.field_12, def->field_12)
("at13", w.nDoubleDamage, def->nDoubleDamage)
2021-10-16 20:04:21 +00:00
("enemy", w.pEnemy, def->pEnemy)
2020-11-30 00:10:52 +00:00
.EndObject();
}
return arc;
}
2020-11-30 00:10:52 +00:00
void SerializeBullet(FSerializer& arc)
{
if (arc.BeginObject("bullets"))
{
arc ("list", BulletList)
("lasthitx", lasthitx)
("lasthity", lasthity)
("lasthitz", lasthitz)
("lasthitsect", lasthitsect)
("radialbullet", nRadialBullet)
.EndObject();
}
}
bulletInfo BulletInfo[] = {
{ 25, 1, 20, -1, -1, 13, 0, 0, -1 },
{ 25, -1, 65000, -1, 31, 73, 0, 0, -1 },
{ 15, -1, 60000, -1, 31, 73, 0, 0, -1 },
{ 5, 15, 2000, -1, 14, 38, 4, 5, 3 },
{ 250, 100, 2000, -1, 33, 34, 4, 20, -1 },
{ 200, -1, 2000, -1, 20, 23, 4, 10, -1 },
{ 200, -1, 60000, 68, 68, -1, -1, 0, -1 },
{ 300, 1, 0, -1, -1, -1, 0, 50, -1 },
{ 18, -1, 2000, -1, 18, 29, 4, 0, -1 },
{ 20, -1, 2000, 37, 11, 30, 4, 0, -1 },
{ 25, -1, 3000, -1, 44, 36, 4, 15, 90 },
{ 30, -1, 1000, -1, 52, 53, 4, 20, 48 },
{ 20, -1, 3500, -1, 54, 55, 4, 30, -1 },
{ 10, -1, 5000, -1, 57, 76, 4, 0, -1 },
{ 40, -1, 1500, -1, 63, 38, 4, 10, 40 },
{ 20, -1, 2000, -1, 60, 12, 0, 0, -1 },
{ 5, -1, 60000, -1, 31, 76, 0, 0, -1 }
};
void InitBullets()
{
2020-11-30 00:10:52 +00:00
BulletList.Clear();
}
2020-11-30 00:10:52 +00:00
int GrabBullet()
{
2020-11-30 00:10:52 +00:00
int grabbed = BulletList.Get();
if (grabbed < 0) return -1;
2021-10-16 20:04:21 +00:00
BulletList[grabbed].pEnemy = nullptr;
2020-11-30 00:10:52 +00:00
return grabbed;
}
void DestroyBullet(short nBullet)
{
2021-10-16 19:21:04 +00:00
auto pActor = BulletList[nBullet].pActor;
auto pSprite = &pActor->s();
2019-09-25 21:16:12 +00:00
runlist_DoSubRunRec(BulletList[nBullet].field_6);
runlist_DoSubRunRec(pSprite->lotag - 1);
2019-09-25 21:16:12 +00:00
runlist_SubRunRec(BulletList[nBullet].field_8);
2021-10-16 19:21:04 +00:00
StopActorSound(pActor);
2021-10-16 19:21:04 +00:00
DeleteActor(pActor);
2020-11-30 00:10:52 +00:00
BulletList.Release(nBullet);
}
2021-10-16 22:01:06 +00:00
void IgniteSprite(DExhumedActor* pActor)
{
2021-10-16 22:01:06 +00:00
auto pSprite = &pActor->s();
pSprite->hitag += 2;
2021-10-24 18:21:27 +00:00
auto pAnimActor = BuildAnim(nullptr, 38, 0, pSprite->x, pSprite->y, pSprite->z, pSprite->sectnum, 40, 20);
if (pAnimActor)
{
2021-10-16 22:01:06 +00:00
pAnimActor->pTarget = pActor;
2021-10-24 18:21:27 +00:00
ChangeActorStat(pAnimActor, kStatIgnited);
2021-10-24 18:21:27 +00:00
short yRepeat = (tileHeight(pAnimActor->s().picnum) * 32) / nFlameHeight;
if (yRepeat < 1)
yRepeat = 1;
2021-10-24 18:21:27 +00:00
pAnimActor->s().yrepeat = (uint8_t)yRepeat;
}
}
void BulletHitsSprite(Bullet *pBullet, DExhumedActor* pBulletActor, DExhumedActor* pHitActor, int x, int y, int z, int nSector)
{
assert(validSectorIndex(nSector));
2019-09-25 21:16:12 +00:00
bulletInfo *pBulletInfo = &BulletInfo[pBullet->nType];
auto pHitSprite = &pHitActor->s();
2021-10-16 19:21:04 +00:00
short nStat = pHitSprite->statnum;
2019-09-25 21:16:12 +00:00
switch (pBullet->nType)
{
case 3:
{
if (nStat > 107 || nStat == kStatAnubisDrum) {
2019-09-25 21:16:12 +00:00
return;
}
2021-10-16 19:21:04 +00:00
pHitSprite->hitag++;
2019-09-25 21:16:12 +00:00
2021-10-16 19:21:04 +00:00
if (pHitSprite->hitag == 15) {
IgniteSprite(pHitActor);
2019-09-25 21:16:12 +00:00
}
if (!RandomSize(2)) {
2021-10-24 17:18:11 +00:00
BuildAnim(nullptr, pBulletInfo->field_C, 0, x, y, z, nSector, 40, pBulletInfo->nFlags);
2019-09-25 21:16:12 +00:00
}
return;
}
case 14:
{
if (nStat > 107 || nStat == kStatAnubisDrum) {
2019-09-25 21:16:12 +00:00
return;
}
// else - fall through to below cases
[[fallthrough]];
2019-09-25 21:16:12 +00:00
}
case 1:
case 2:
case 8:
case 9:
case 12:
case 13:
case 15:
case 16:
{
// loc_29E59
if (!nStat || nStat > 98) {
break;
}
2021-10-16 19:21:04 +00:00
auto pActor = pBullet->pActor;
spritetype *pSprite = &pActor->s();
2019-09-25 21:16:12 +00:00
if (nStat == kStatAnubisDrum)
{
int nAngle = (pSprite->ang + 256) - RandomSize(9);
pHitSprite->xvel = bcos(nAngle, 1);
pHitSprite->yvel = bsin(nAngle, 1);
pHitSprite->zvel = (-(RandomSize(3) + 1)) << 8;
}
else
2019-09-25 21:16:12 +00:00
{
int xVel = pHitSprite->xvel;
int yVel = pHitSprite->yvel;
pHitSprite->xvel = bcos(pSprite->ang, -2);
pHitSprite->yvel = bsin(pSprite->ang, -2);
2019-09-25 21:16:12 +00:00
MoveCreature(pHitActor);
2019-09-25 21:16:12 +00:00
pHitSprite->xvel = xVel;
pHitSprite->yvel = yVel;
}
break;
}
default:
break;
}
// BHS_switchBreak:
short nDamage = pBulletInfo->nDamage;
if (pBullet->nDoubleDamage > 1) {
2019-09-25 21:16:12 +00:00
nDamage *= 2;
}
runlist_DamageEnemy(pHitActor, pBulletActor, nDamage);
2019-09-25 21:16:12 +00:00
if (nStat <= 90 || nStat >= 199)
{
BuildAnim(nullptr, pBulletInfo->field_C, 0, x, y, z, nSector, 40, pBulletInfo->nFlags);
2019-09-25 21:16:12 +00:00
return;
}
switch (nStat)
{
case kStatDestructibleSprite:
break;
case kStatAnubisDrum:
case 102:
case kStatExplodeTrigger:
case kStatExplodeTarget:
BuildAnim(nullptr, 12, 0, x, y, z, nSector, 40, 0);
break;
default:
BuildAnim(nullptr, 39, 0, x, y, z, nSector, 40, 0);
if (pBullet->nType > 2)
{
BuildAnim(nullptr, pBulletInfo->field_C, 0, x, y, z, nSector, 40, pBulletInfo->nFlags);
}
break;
2019-09-25 21:16:12 +00:00
}
}
void BackUpBullet(int *x, int *y, int nAngle)
{
*x -= bcos(nAngle, -11);
*y -= bsin(nAngle, -11);
}
int MoveBullet(short nBullet)
{
2019-09-25 21:16:12 +00:00
short hitsect = -1;
short hitwall = -1;
2021-10-16 19:21:04 +00:00
DExhumedActor* hitactor = nullptr;
2019-09-25 21:16:12 +00:00
Bullet *pBullet = &BulletList[nBullet];
short nType = pBullet->nType;
bulletInfo *pBulletInfo = &BulletInfo[nType];
2021-10-16 19:21:04 +00:00
auto pActor = BulletList[nBullet].pActor;
spritetype *pSprite = &pActor->s();
2019-09-25 21:16:12 +00:00
int x = pSprite->x;
int y = pSprite->y;
int z = pSprite->z; // ebx
short nSectFlag = SectFlag[pSprite->sectnum];
int x2, y2, z2;
2021-10-16 19:21:04 +00:00
int nVal = 0;
Collision coll;
2019-09-25 21:16:12 +00:00
2019-10-01 17:59:20 +00:00
if (pBullet->field_10 < 30000)
2019-09-25 21:16:12 +00:00
{
2021-10-16 20:04:21 +00:00
auto pEnemyActor = BulletList[nBullet].pEnemy;
if (pEnemyActor)
2019-09-25 21:16:12 +00:00
{
2021-10-16 20:04:21 +00:00
if (!(pEnemyActor->s().cstat & 0x101))
BulletList[nBullet].pEnemy = nullptr;
2019-09-25 21:16:12 +00:00
else
{
2021-10-16 20:04:21 +00:00
coll = AngleChase(pActor, pEnemyActor, pBullet->field_10, 0, 16);
2019-09-25 21:16:12 +00:00
goto MOVEEND;
}
}
if (nType == 3)
{
if (pBullet->field_E < 8)
{
pSprite->xrepeat -= 1;
pSprite->yrepeat += 8;
pBullet->z -= 200;
if (pSprite->shade < 90) {
pSprite->shade += 35;
}
if (pBullet->field_E == 3)
{
pBullet->nSeq = 45;
pBullet->nFrame = 0;
2019-09-25 21:16:12 +00:00
pSprite->xrepeat = 40;
pSprite->yrepeat = 40;
pSprite->shade = 0;
pSprite->z += 512;
}
}
else
{
pSprite->xrepeat += 4;
pSprite->yrepeat += 4;
}
}
2021-10-16 19:21:04 +00:00
coll = movesprite(pActor, pBullet->x, pBullet->y, pBullet->z, pSprite->clipdist >> 1, pSprite->clipdist >> 1, CLIPMASK1);
2019-09-25 21:16:12 +00:00
MOVEEND:
2021-10-16 19:21:04 +00:00
if (coll.type || coll.exbits)
2019-09-25 21:16:12 +00:00
{
2021-10-16 19:21:04 +00:00
nVal = 1;
2019-09-25 21:16:12 +00:00
x2 = pSprite->x;
y2 = pSprite->y;
z2 = pSprite->z;
hitsect = pSprite->sectnum;
2021-10-16 19:21:04 +00:00
#if 0
// Original code. This was producing some beautiful undefined behavior in the first case because the index can be anything, not just a wall.
if (coll.exbits)
2019-09-25 21:16:12 +00:00
{
2021-10-16 19:21:04 +00:00
hitwall = coll & 0x3FFF;
2019-09-25 21:16:12 +00:00
goto HITWALL;
}
else
{
2021-10-16 19:21:04 +00:00
switch (coll & 0xc000)
2019-09-25 21:16:12 +00:00
{
case 0x8000:
2021-10-16 19:21:04 +00:00
hitwall = coll & 0x3FFF;
2019-09-25 21:16:12 +00:00
goto HITWALL;
case 0xc000:
2021-10-16 19:21:04 +00:00
hitsprite = coll & 0x3FFF;
2019-09-25 21:16:12 +00:00
goto HITSPRITE;
}
}
2021-10-16 19:21:04 +00:00
#else
switch (coll.type)
{
case kHitWall:
hitwall = coll.index;
goto HITWALL;
case 0xc000:
if (!coll.exbits)
{
hitactor = coll.actor;
goto HITSPRITE;
}
}
#endif
2019-09-25 21:16:12 +00:00
}
2021-10-16 19:21:04 +00:00
nVal = coll.type || coll.exbits? 1:0;
// pSprite->sectnum may have changed since we set nSectFlag ?
2019-09-25 21:16:12 +00:00
short nFlagVal = nSectFlag ^ SectFlag[pSprite->sectnum];
if (nFlagVal & kSectUnderwater)
{
DestroyBullet(nBullet);
nVal = 1;
}
if (nVal == 0 && nType != 15 && nType != 3)
{
AddFlash(pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, 0);
2019-09-25 21:16:12 +00:00
if (pSprite->pal != 5) {
pSprite->pal = 1;
2019-09-25 21:16:12 +00:00
}
}
}
else
{
nVal = 1;
2021-10-16 20:04:21 +00:00
if (BulletList[nBullet].pEnemy)
2019-09-25 21:16:12 +00:00
{
2021-10-16 20:04:21 +00:00
hitactor = BulletList[nBullet].pEnemy;
2021-10-16 19:21:04 +00:00
auto hitsprite = &hitactor->s();
x2 = hitsprite->x;
y2 = hitsprite->y;
z2 = hitsprite->z - (GetActorHeight(hitactor) >> 1);
hitsect = hitsprite->sectnum;
2019-09-25 21:16:12 +00:00
}
else
{
vec3_t startPos = { x, y, z };
hitdata_t hitData;
2019-10-27 16:36:25 +00:00
int dz;
if (bVanilla)
2021-10-19 22:26:26 +00:00
dz = -bsin(pBullet->nPitch, 3);
2019-10-27 16:36:25 +00:00
else
2021-10-19 22:26:26 +00:00
dz = -pBullet->nPitch * 512;
hitscan(&startPos, pSprite->sectnum, bcos(pSprite->ang), bsin(pSprite->ang), dz, &hitData, CLIPMASK1);
x2 = hitData.pos.x;
y2 = hitData.pos.y;
z2 = hitData.pos.z;
2021-10-16 20:04:21 +00:00
hitactor = GetActor(hitData);
hitsect = hitData.sect;
hitwall = hitData.wall;
2019-09-25 21:16:12 +00:00
}
lasthitx = x2;
lasthity = y2;
lasthitz = z2;
lasthitsect = hitsect;
2021-10-16 19:21:04 +00:00
if (hitactor)
2019-09-25 21:16:12 +00:00
{
HITSPRITE:
2021-10-16 19:21:04 +00:00
auto hitsprite = &hitactor->s();
if (pSprite->pal == 5 && hitsprite->statnum == 100)
2019-09-25 21:16:12 +00:00
{
int nPlayer = GetPlayerFromActor(hitactor);
2019-09-25 21:16:12 +00:00
if (!PlayerList[nPlayer].bIsMummified)
{
PlayerList[nPlayer].bIsMummified = true;
2019-09-25 21:16:12 +00:00
SetNewWeapon(nPlayer, kWeaponMummified);
}
}
else
{
BulletHitsSprite(pBullet, pActor->pTarget, hitactor, x2, y2, z2, hitsect);
2019-09-25 21:16:12 +00:00
}
}
else if (hitwall > -1)
{
HITWALL:
if (wall[hitwall].picnum == kEnergy1)
{
int nSector =wall[hitwall].nextsector;
2019-09-25 21:16:12 +00:00
if (nSector > -1)
{
short nDamage = BulletInfo[pBullet->nType].nDamage;
if (pBullet->nDoubleDamage > 1) {
2019-09-25 21:16:12 +00:00
nDamage *= 2;
}
2021-10-21 13:02:18 +00:00
runlist_DamageEnemy(EnergyBlocks[sector[nSector].extra], pActor, nDamage);
2019-09-25 21:16:12 +00:00
}
}
}
if (hitsect > -1) // NOTE: hitsect can be -1. this check wasn't in original code. TODO: demo compatiblity?
2019-09-25 21:16:12 +00:00
{
2021-10-16 19:21:04 +00:00
if (hitactor == nullptr && hitwall < 0)
2019-09-25 21:16:12 +00:00
{
if ((SectBelow[hitsect] >= 0 && (SectFlag[SectBelow[hitsect]] & kSectUnderwater)) || SectDepth[hitsect])
{
pSprite->x = x2;
pSprite->y = y2;
pSprite->z = z2;
2021-10-16 19:21:04 +00:00
BuildSplash(pActor, hitsect);
}
else
{
BuildAnim(nullptr, pBulletInfo->field_C, 0, x2, y2, z2, hitsect, 40, pBulletInfo->nFlags);
}
2019-09-25 21:16:12 +00:00
}
else
{
if (hitwall >= 0)
{
BackUpBullet(&x2, &y2, pSprite->ang);
2019-09-25 21:16:12 +00:00
if (nType != 3 || RandomSize(2) == 0)
{
int zOffset = RandomSize(8) << 3;
2019-09-25 21:16:12 +00:00
if (!RandomBit()) {
zOffset = -zOffset;
}
2019-09-25 21:16:12 +00:00
// draws bullet puff on walls when they're shot
BuildAnim(nullptr, pBulletInfo->field_C, 0, x2, y2, z2 + zOffset + -4096, hitsect, 40, pBulletInfo->nFlags);
}
}
else
{
pSprite->x = x2;
pSprite->y = y2;
pSprite->z = z2;
2019-09-25 21:16:12 +00:00
2021-10-16 19:21:04 +00:00
ChangeActorSect(pActor, hitsect);
}
2019-09-25 21:16:12 +00:00
if (BulletInfo[nType].nRadius)
{
nRadialBullet = nType;
2019-09-25 21:16:12 +00:00
2021-10-16 19:21:04 +00:00
runlist_RadialDamageEnemy(pActor, pBulletInfo->nDamage, pBulletInfo->nRadius);
2019-09-25 21:16:12 +00:00
nRadialBullet = -1;
2019-09-25 21:16:12 +00:00
AddFlash(pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, 128);
}
}
2019-09-25 21:16:12 +00:00
}
DestroyBullet(nBullet);
}
return nVal;
}
2021-10-16 20:04:21 +00:00
void SetBulletEnemy(short nBullet, DExhumedActor* pEnemy)
{
2019-09-25 21:16:12 +00:00
if (nBullet >= 0) {
2021-10-16 20:04:21 +00:00
BulletList[nBullet].pEnemy = pEnemy;
2019-09-25 21:16:12 +00:00
}
}
2021-10-19 22:26:26 +00:00
DExhumedActor* BuildBullet(DExhumedActor* pActor, int nType, int nZOffset, int nAngle, DExhumedActor* pTarget, int nDoubleDamage)
{
2021-10-19 22:26:26 +00:00
auto pSprite = &pActor->s();
2019-09-25 21:16:12 +00:00
Bullet sBullet;
bulletInfo *pBulletInfo = &BulletInfo[nType];
2021-10-19 22:26:26 +00:00
int nPitch = 0;
2019-09-25 21:16:12 +00:00
if (pBulletInfo->field_4 > 30000)
{
2021-10-19 22:26:26 +00:00
if (pTarget)
2019-09-25 21:16:12 +00:00
{
2021-10-19 22:26:26 +00:00
spritetype *pTargetSprite = &pTarget->s();
2019-09-25 21:16:12 +00:00
if (pTargetSprite->cstat & 0x101)
{
sBullet.nType = nType;
sBullet.nDoubleDamage = nDoubleDamage;
2019-09-25 21:16:12 +00:00
2021-10-16 19:21:04 +00:00
sBullet.pActor = insertActor(pSprite->sectnum, 200);
sBullet.pActor->s().ang = nAngle;
2019-09-25 21:16:12 +00:00
2021-10-19 22:26:26 +00:00
int nHeight = GetActorHeight(pTarget);
2019-09-25 21:16:12 +00:00
assert(validSectorIndex(pTargetSprite->sectnum));
2019-09-25 21:16:12 +00:00
BulletHitsSprite(&sBullet, pActor, pTarget, pTargetSprite->x, pTargetSprite->y, pTargetSprite->z - (nHeight >> 1), pTargetSprite->sectnum);
2021-10-16 19:21:04 +00:00
DeleteActor(sBullet.pActor);
2021-10-19 22:26:26 +00:00
return nullptr;
2019-09-25 21:16:12 +00:00
}
else
{
2021-10-19 22:26:26 +00:00
nPitch = 0;
2019-09-25 21:16:12 +00:00
}
}
}
2020-11-30 00:10:52 +00:00
int nBullet = GrabBullet();
if (nBullet < 0) {
2021-10-19 22:26:26 +00:00
return nullptr;
2019-09-25 21:16:12 +00:00
}
int nSector;
2019-09-25 21:16:12 +00:00
if (pSprite->statnum == 100)
2019-09-25 21:16:12 +00:00
{
nSector = PlayerList[GetPlayerFromActor(pActor)].nPlayerViewSect;
2019-09-25 21:16:12 +00:00
}
else
{
nSector = pSprite->sectnum;
2019-09-25 21:16:12 +00:00
}
2021-10-16 19:21:04 +00:00
auto pBulletActor = insertActor(nSector, 200);
auto pBulletSprite = &pBulletActor->s();
2021-10-19 22:26:26 +00:00
int nHeight = GetActorHeight(pActor);
2019-09-25 21:16:12 +00:00
nHeight = nHeight - (nHeight >> 2);
if (nZOffset == -1) {
nZOffset = -nHeight;
2019-09-25 21:16:12 +00:00
}
pBulletSprite->x = pSprite->x;
pBulletSprite->y = pSprite->y;
pBulletSprite->z = pSprite->z;
2019-09-25 21:16:12 +00:00
Bullet *pBullet = &BulletList[nBullet];
2021-10-16 20:04:21 +00:00
pBullet->pEnemy = nullptr;
2019-09-25 21:16:12 +00:00
pBulletSprite->cstat = 0;
pBulletSprite->shade = -64;
2019-09-25 21:16:12 +00:00
if (pBulletInfo->nFlags & 4) {
pBulletSprite->pal = 4;
2019-09-25 21:16:12 +00:00
}
else {
pBulletSprite->pal = 0;
2019-09-25 21:16:12 +00:00
}
pBulletSprite->clipdist = 25;
2019-09-25 21:16:12 +00:00
short nRepeat = pBulletInfo->xyRepeat;
if (nRepeat < 0) {
nRepeat = 30;
}
pBulletSprite->xrepeat = (uint8_t)nRepeat;
pBulletSprite->yrepeat = (uint8_t)nRepeat;
pBulletSprite->xoffset = 0;
pBulletSprite->yoffset = 0;
pBulletSprite->ang = nAngle;
pBulletSprite->xvel = 0;
pBulletSprite->yvel = 0;
pBulletSprite->zvel = 0;
pBulletSprite->lotag = runlist_HeadRun() + 1;
pBulletSprite->extra = -1;
pBulletSprite->hitag = 0;
pBulletActor->pTarget = pActor;
2021-10-19 22:26:26 +00:00
pBulletActor->nPhase = nBullet;
// GrabTimeSlot(3);
2019-09-25 21:16:12 +00:00
pBullet->field_10 = 0;
pBullet->field_E = pBulletInfo->field_2;
pBullet->nFrame = 0;
2019-09-25 21:16:12 +00:00
short nSeq;
if (pBulletInfo->field_8 != -1)
{
pBullet->field_12 = 0;
nSeq = pBulletInfo->field_8;
}
else
{
pBullet->field_12 = 1;
nSeq = pBulletInfo->nSeq;
}
pBullet->nSeq = nSeq;
pBulletSprite->picnum = seq_GetSeqPicnum(nSeq, 0, 0);
2019-09-25 21:16:12 +00:00
if (nSeq == kSeqBullet) {
pBulletSprite->cstat |= 0x8000;
2019-09-25 21:16:12 +00:00
}
2021-10-19 22:26:26 +00:00
pBullet->nPitch = nPitch;
2019-09-25 21:16:12 +00:00
pBullet->nType = nType;
2021-10-16 19:21:04 +00:00
pBullet->pActor = pBulletActor;
pBullet->field_6 = runlist_AddRunRec(pBulletSprite->lotag - 1, nBullet, 0xB0000);
pBullet->field_8 = runlist_AddRunRec(NewRun, nBullet, 0xB0000);
pBullet->nDoubleDamage = nDoubleDamage;
pBulletSprite->z += nZOffset;
pBulletSprite->backuppos();
2019-09-25 21:16:12 +00:00
int var_18 = 0;
2019-09-25 21:16:12 +00:00
nSector = pBulletSprite->sectnum;
2019-09-25 21:16:12 +00:00
while (pBulletSprite->z < sector[nSector].ceilingz)
2019-09-25 21:16:12 +00:00
{
if (SectAbove[nSector] == -1)
{
pBulletSprite->z = sector[nSector].ceilingz;
2019-09-25 21:16:12 +00:00
break;
}
nSector = SectAbove[nSector];
2021-10-16 19:21:04 +00:00
ChangeActorSect(pBulletActor, nSector);
2019-09-25 21:16:12 +00:00
}
2021-10-19 22:26:26 +00:00
if (pTarget == nullptr)
2019-09-25 21:16:12 +00:00
{
2021-10-19 22:26:26 +00:00
var_18 = (-bsin(nPitch) * pBulletInfo->field_4) >> 11;
2019-09-25 21:16:12 +00:00
}
else
{
2021-10-19 22:26:26 +00:00
auto pTargetSprite = &pTarget->s();
2019-09-25 21:16:12 +00:00
if ((unsigned int)pBulletInfo->field_4 > 30000)
{
2021-10-19 22:26:26 +00:00
BulletList[nBullet].pEnemy = pTarget;
2019-09-25 21:16:12 +00:00
}
else
{
2021-10-19 22:26:26 +00:00
nHeight = GetActorHeight(pTarget);
2019-09-25 21:16:12 +00:00
if (pTargetSprite->statnum == 100)
2019-09-25 21:16:12 +00:00
{
nHeight -= nHeight >> 2;
}
else
{
nHeight -= nHeight >> 1;
}
int var_20 = pTargetSprite->z - nHeight;
2019-09-25 21:16:12 +00:00
int x, y;
2021-10-19 22:26:26 +00:00
if (pActor != nullptr && pSprite->statnum != 100)
2019-09-25 21:16:12 +00:00
{
x = pTargetSprite->x;
y = pTargetSprite->y;
2019-09-25 21:16:12 +00:00
if (pTargetSprite->statnum != 100)
2019-09-25 21:16:12 +00:00
{
x += (pTargetSprite->xvel * 20) >> 6;
y += (pTargetSprite->yvel * 20) >> 6;
2019-09-25 21:16:12 +00:00
}
else
{
2021-10-19 22:26:26 +00:00
int nPlayer = GetPlayerFromActor(pTarget);
2019-09-25 21:16:12 +00:00
if (nPlayer > -1)
{
x += PlayerList[nPlayer].nPlayerDX * 15;
y += PlayerList[nPlayer].nPlayerDY * 15;
2019-09-25 21:16:12 +00:00
}
}
x -= pBulletSprite->x;
y -= pBulletSprite->y;
2019-09-25 21:16:12 +00:00
nAngle = GetMyAngle(x, y);
pSprite->ang = nAngle;
2019-09-25 21:16:12 +00:00
}
else
{
// loc_2ABA3:
x = pTargetSprite->x - pBulletSprite->x;
y = pTargetSprite->y - pBulletSprite->y;
2019-09-25 21:16:12 +00:00
}
int nSqrt = lsqrt(y*y + x*x);
if ((unsigned int)nSqrt > 0)
{
var_18 = ((var_20 - pBulletSprite->z) * pBulletInfo->field_4) / nSqrt;
2019-09-25 21:16:12 +00:00
}
else
{
var_18 = 0;
}
}
}
pBullet->z = 0;
pBullet->x = (pSprite->clipdist << 2) * bcos(nAngle);
pBullet->y = (pSprite->clipdist << 2) * bsin(nAngle);
2021-10-16 20:04:21 +00:00
BulletList[nBullet].pEnemy = nullptr;
2019-09-25 21:16:12 +00:00
2021-10-19 22:26:26 +00:00
2019-09-25 21:16:12 +00:00
if (MoveBullet(nBullet))
{
2021-10-16 19:21:04 +00:00
pBulletActor = nullptr;
2019-09-25 21:16:12 +00:00
}
else
{
pBullet->field_10 = pBulletInfo->field_4;
pBullet->x = bcos(nAngle, -3) * pBulletInfo->field_4;
pBullet->y = bsin(nAngle, -3) * pBulletInfo->field_4;
2019-09-25 21:16:12 +00:00
pBullet->z = var_18 >> 3;
}
2021-10-19 22:26:26 +00:00
return pBulletActor;
}
2021-10-15 19:10:16 +00:00
void AIBullet::Tick(RunListEvent* ev)
{
short nBullet = RunData[ev->nRun].nObjIndex;
2019-09-25 21:16:12 +00:00
assert(nBullet >= 0 && nBullet < kMaxBullets);
short nSeq = SeqOffsets[BulletList[nBullet].nSeq];
2021-10-16 19:21:04 +00:00
auto pActor = BulletList[nBullet].pActor;
auto pSprite = &pActor->s();
2019-09-25 21:16:12 +00:00
2021-10-15 19:10:16 +00:00
short nFlag = FrameFlag[SeqBase[nSeq] + BulletList[nBullet].nFrame];
2019-09-25 21:16:12 +00:00
2021-10-16 19:21:04 +00:00
seq_MoveSequence(pActor, nSeq, BulletList[nBullet].nFrame);
2019-09-25 21:16:12 +00:00
2021-10-15 19:10:16 +00:00
if (nFlag & 0x80)
{
2021-10-24 17:18:11 +00:00
BuildAnim(nullptr, 45, 0, pSprite->x, pSprite->y, pSprite->z, pSprite->sectnum, pSprite->xrepeat, 0);
2021-10-15 19:10:16 +00:00
}
2019-09-25 21:16:12 +00:00
2021-10-15 19:10:16 +00:00
BulletList[nBullet].nFrame++;
if (BulletList[nBullet].nFrame >= SeqSize[nSeq])
{
if (!BulletList[nBullet].field_12)
{
BulletList[nBullet].nSeq = BulletInfo[BulletList[nBullet].nType].nSeq;
BulletList[nBullet].field_12++;
}
2019-09-25 21:16:12 +00:00
2021-10-15 19:10:16 +00:00
BulletList[nBullet].nFrame = 0;
}
2019-09-25 21:16:12 +00:00
2021-10-15 19:10:16 +00:00
if (BulletList[nBullet].field_E != -1 && --BulletList[nBullet].field_E == 0)
{
DestroyBullet(nBullet);
}
else
{
MoveBullet(nBullet);
}
}
2019-09-25 21:16:12 +00:00
2021-10-15 19:10:16 +00:00
void AIBullet::Draw(RunListEvent* ev)
{
short nBullet = RunData[ev->nRun].nObjIndex;
2021-10-15 19:10:16 +00:00
assert(nBullet >= 0 && nBullet < kMaxBullets);
2019-09-25 21:16:12 +00:00
2021-10-15 19:10:16 +00:00
short nSeq = SeqOffsets[BulletList[nBullet].nSeq];
2019-09-25 21:16:12 +00:00
ev->pTSprite->statnum = 1000;
2019-09-25 21:16:12 +00:00
2021-10-15 19:10:16 +00:00
if (BulletList[nBullet].nType == 15)
{
2021-10-26 06:13:23 +00:00
seq_PlotArrowSequence(ev->nParam, nSeq, BulletList[nBullet].nFrame);
2019-09-25 21:16:12 +00:00
}
2021-10-15 19:10:16 +00:00
else
{
2021-10-26 06:13:23 +00:00
seq_PlotSequence(ev->nParam, nSeq, BulletList[nBullet].nFrame, 0);
2021-10-15 19:10:16 +00:00
ev->pTSprite->owner = -1;
}
}
END_PS_NS