metaforce/Runtime/Weapon/CFlameThrower.cpp

235 lines
8.9 KiB
C++

#include "Weapon/CFlameThrower.hpp"
#include "Weapon/CFlameInfo.hpp"
#include "World/CGameLight.hpp"
#include "World/CPlayer.hpp"
#include "Particle/CElementGen.hpp"
#include "Graphics/CBooRenderer.hpp"
#include "Collision/CInternalRayCastStructure.hpp"
#include "CStateManager.hpp"
#include "GameGlobalObjects.hpp"
#include "CSimplePool.hpp"
#include "TCastTo.hpp"
#include "CFlameThrower.hpp"
namespace urde {
const zeus::CVector3f CFlameThrower::kLightOffset(0, 3.f, 2.f);
CFlameThrower::CFlameThrower(const TToken<CWeaponDescription>& wDesc, std::string_view name, EWeaponType wType,
const CFlameInfo& flameInfo, const zeus::CTransform& xf, EMaterialTypes matType,
const CDamageInfo& dInfo, TUniqueId uid, TAreaId aId, TUniqueId owner,
EProjectileAttrib attribs, CAssetId assetId1, s16 sId, CAssetId assetId2)
: CGameProjectile(false, wDesc, name, wType, xf, matType, dInfo, uid, aId, owner, kInvalidUniqueId, attribs, false,
zeus::CVector3f(1.f), {}, -1, false)
, x2e8_flameXf(xf)
, x338_(flameInfo.x10_)
, x33c_flameDesc(g_SimplePool->GetObj({FOURCC('PART'), flameInfo.GetFlameFxId()}))
, x348_flameGen(new CElementGen(x33c_flameDesc))
, x34c_flameWarp(176.f - float(flameInfo.GetLength()), xf.origin, bool(flameInfo.GetAttributes() & 0x4))
, x3f4_playerSteamTxtr(assetId1)
, x3f8_playerHitSfx(sId)
, x3fc_playerIceTxtr(assetId2)
, x400_24_active(false)
, x400_25_particlesActive(false)
, x400_26_(!(flameInfo.GetAttributes() & 1))
, x400_27_coneCollision((flameInfo.GetAttributes() & 0x2) != 0) {
}
void CFlameThrower::Accept(IVisitor& visitor) { visitor.Visit(this); }
void CFlameThrower::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateManager& mgr) {
if (msg == EScriptObjectMessage::Registered) {
xe6_27_thermalVisorFlags |= 2;
mgr.AddWeaponId(xec_ownerId, xf0_weaponType);
} else if (msg == EScriptObjectMessage::Deleted) {
mgr.RemoveWeaponId(xec_ownerId, xf0_weaponType);
DeleteProjectileLight(mgr);
}
CGameProjectile::AcceptScriptMsg(msg, uid, mgr);
}
void CFlameThrower::SetTransform(const zeus::CTransform& xf, float) { x2e8_flameXf = xf; }
void CFlameThrower::Reset(CStateManager& mgr, bool resetWarp) {
SetFlameLightActive(mgr, false);
if (resetWarp) {
SetActive(false);
x400_25_particlesActive = false;
x3f0_flameState = EFlameState::Default;
x330_particleWaitDelayTimer = 0.f;
x334_fireStopTimer = 0.f;
x318_flameBounds = zeus::skNullBox;
x348_flameGen->SetParticleEmission(false);
x34c_flameWarp.ResetPosition(x2e8_flameXf.origin);
} else {
x348_flameGen->SetParticleEmission(false);
x400_25_particlesActive = false;
x3f0_flameState = EFlameState::FireStopTimer;
}
}
void CFlameThrower::Fire(const zeus::CTransform&, CStateManager& mgr, bool) {
SetActive(true);
x400_25_particlesActive = true;
x400_24_active = true;
x3f0_flameState = EFlameState::FireStart;
CreateFlameParticles(mgr);
}
void CFlameThrower::CreateFlameParticles(CStateManager& mgr) {
DeleteProjectileLight(mgr);
x348_flameGen.reset(new CElementGen(x33c_flameDesc));
x348_flameGen->SetParticleEmission(true);
x348_flameGen->SetZTest(x400_27_coneCollision);
x348_flameGen->AddModifier(&x34c_flameWarp);
if (x348_flameGen->SystemHasLight() && x2c8_projectileLight == kInvalidUniqueId)
CreateProjectileLight("FlameThrower_Light"sv, x348_flameGen->GetLight(), mgr);
}
void CFlameThrower::AddToRenderer(const zeus::CFrustum&, const CStateManager& mgr) const {
g_Renderer->AddParticleGen(*x348_flameGen);
EnsureRendered(mgr, x2e8_flameXf.origin, GetRenderBounds());
}
void CFlameThrower::SetFlameLightActive(CStateManager& mgr, bool active) {
if (x2c8_projectileLight == kInvalidUniqueId)
return;
if (TCastToPtr<CGameLight> light = mgr.ObjectById(x2c8_projectileLight))
light->SetActive(active);
}
void CFlameThrower::UpdateFlameState(float dt, CStateManager& mgr) {
switch(x3f0_flameState) {
case EFlameState::FireStart:
x3f0_flameState = EFlameState::FireActive;
break;
case EFlameState::FireStopTimer:
x334_fireStopTimer += 4.f * dt;
if (x334_fireStopTimer > 1.f) {
x334_fireStopTimer = 1.f;
x3f0_flameState = EFlameState::FireWaitForParticlesDone;
x400_24_active = false;
}
break;
case EFlameState::FireWaitForParticlesDone:
x330_particleWaitDelayTimer += dt;
if (x330_particleWaitDelayTimer > 0.1f && x348_flameGen && x348_flameGen->GetParticleCountAll() == 0) {
x3f0_flameState = EFlameState::Default;
Reset(mgr, true);
}
break;
default:
break;
}
}
CRayCastResult CFlameThrower::DoCollisionCheck(TUniqueId& idOut, const zeus::CAABox& aabb, CStateManager& mgr) {
CRayCastResult ret;
rstl::reserved_vector<TUniqueId, 1024> nearList;
mgr.BuildNearList(nearList, aabb, CMaterialFilter::skPassEverything, this);
const auto& colPoints = x34c_flameWarp.GetCollisionPoints();
if (x400_27_coneCollision && colPoints.size() > 0) {
float radiusPitch = (x34c_flameWarp.GetMaxSize() - x34c_flameWarp.GetMinSize()) /
float(colPoints.size()) * 0.5f;
float curRadius = radiusPitch;
for (int i = 1; i < colPoints.size(); ++i) {
zeus::CVector3f delta = colPoints[i] - colPoints[i - 1];
zeus::CTransform lookXf = zeus::lookAt(colPoints[i - 1], colPoints[i]);
lookXf.origin = delta * 0.5f + colPoints[i - 1];
zeus::COBBox obb(lookXf, {curRadius, delta.magnitude() * 0.5f, curRadius});
for (TUniqueId id : nearList) {
if (CActor* act = static_cast<CActor*>(mgr.ObjectById(id))) {
CProjectileTouchResult tres = CanCollideWith(*act, mgr);
if (tres.GetActorId() == kInvalidUniqueId)
continue;
auto tb = act->GetTouchBounds();
if (!tb)
continue;
if (obb.AABoxIntersectsBox(*tb)) {
CCollidableAABox caabb(*tb, act->GetMaterialList());
zeus::CVector3f flameToAct = act->GetAimPosition(mgr, 0.f) - x2e8_flameXf.origin;
float flameToActDist = flameToAct.magnitude();
CInternalRayCastStructure rc(x2e8_flameXf.origin, flameToAct.normalized(), flameToActDist,
{}, CMaterialFilter::skPassEverything);
CRayCastResult cres = caabb.CastRayInternal(rc);
if (cres.IsInvalid())
continue;
return cres;
}
}
}
curRadius += radiusPitch;
}
} else {
for (int i = 0; i < colPoints.size() - 1; ++i) {
zeus::CVector3f delta = colPoints[i + 1] - colPoints[i];
float deltaMag = delta.magnitude();
if (deltaMag <= 0.f)
break;
CRayCastResult cres = RayCollisionCheckWithWorld(idOut, colPoints[i], colPoints[i + 1], deltaMag, nearList, mgr);
if (cres.IsValid())
return cres;
}
}
return ret;
}
void CFlameThrower::ApplyDamageToActor(CStateManager& mgr, TUniqueId id, float dt) {
if (mgr.GetPlayer().GetUniqueId() == id && x3f4_playerSteamTxtr.IsValid() && x3fc_playerIceTxtr.IsValid())
mgr.GetPlayer().Freeze(mgr, x3f4_playerSteamTxtr, x3f8_playerHitSfx, x3fc_playerIceTxtr);
CDamageInfo useDInfo = CDamageInfo(x12c_curDamageInfo, dt);
ApplyDamageToActors(mgr, useDInfo);
}
void CFlameThrower::Think(float dt, CStateManager& mgr) {
CWeapon::Think(dt, mgr);
if (!GetActive())
return;
UpdateFlameState(dt, mgr);
zeus::CVector3f flamePoint = x2e8_flameXf.origin;
bool r28 = x3f0_flameState == EFlameState::FireActive || x3f0_flameState == EFlameState::FireStopTimer;
if (r28) {
x34c_flameWarp.Activate(true);
x34c_flameWarp.SetWarpPoint(flamePoint);
x34c_flameWarp.SetStateManager(mgr);
x348_flameGen->SetTranslation(flamePoint);
x348_flameGen->SetOrientation(x2e8_flameXf.getRotation());
} else {
x34c_flameWarp.Activate(false);
}
x348_flameGen->Update(dt);
x34c_flameWarp.SetMaxDistSq(0.f);
x34c_flameWarp.SetFloatingPoint(flamePoint);
if (r28 && x34c_flameWarp.IsProcessed()) {
x318_flameBounds = x34c_flameWarp.CalculateBounds();
TUniqueId id = kInvalidUniqueId;
CRayCastResult res = DoCollisionCheck(id, x318_flameBounds, mgr);
if (TCastToPtr<CActor> act = mgr.ObjectById(id)) {
ApplyDamageToActor(mgr, id, dt);
} else if (res.IsValid()) {
CMaterialFilter useFilter = xf8_filter;
CDamageInfo useDInfo = CDamageInfo(x12c_curDamageInfo, dt);
mgr.ApplyDamageToWorld(xec_ownerId, *this, res.GetPoint(), useDInfo, useFilter);
}
}
CActor::SetTransform(x2e8_flameXf.getRotation());
CActor::SetTranslation(x2e8_flameXf.origin);
if (x2c8_projectileLight != kInvalidUniqueId) {
if (TCastToPtr<CGameLight> light = mgr.ObjectById(x2c8_projectileLight)) {
light->SetTransform(GetTransform());
light->SetTranslation(x34c_flameWarp.GetFloatingPoint());
if (x348_flameGen && x348_flameGen->SystemHasLight())
light->SetLight(x348_flameGen->GetLight());
}
}
}
} // namespace urde