mirror of https://github.com/AxioDL/metaforce.git
176 lines
6.7 KiB
C++
176 lines
6.7 KiB
C++
#include "Runtime/MP1/World/CAtomicBeta.hpp"
|
|
|
|
#include <array>
|
|
|
|
#include "Runtime/CSimplePool.hpp"
|
|
#include "Runtime/CStateManager.hpp"
|
|
#include "Runtime/GameGlobalObjects.hpp"
|
|
#include "Runtime/Particle/CWeaponDescription.hpp"
|
|
#include "Runtime/Weapon/CElectricBeamProjectile.hpp"
|
|
#include "Runtime/Weapon/CPlayerGun.hpp"
|
|
#include "Runtime/World/CPlayer.hpp"
|
|
|
|
namespace metaforce::MP1 {
|
|
constexpr std::array skBombLocators{
|
|
"bomb2_LCTR"sv,
|
|
"bomb3_LCTR"sv,
|
|
"bomb4_LCTR"sv,
|
|
};
|
|
|
|
CAtomicBeta::CAtomicBeta(TUniqueId uid, std::string_view name, const CEntityInfo& info, const zeus::CTransform& xf,
|
|
CModelData&& mData, const CActorParameters& actParms, const CPatternedInfo& pInfo,
|
|
CAssetId electricId, CAssetId weaponId, const CDamageInfo& dInfo, CAssetId particleId,
|
|
float f1, float beamRadius, float f3, const CDamageVulnerability& dVuln, float f4, float f5,
|
|
float f6, s16 sId1, s16 sId2, s16 sId3, float f7)
|
|
: CPatterned(ECharacter::AtomicBeta, uid, name, EFlavorType::Zero, info, xf, std::move(mData), pInfo,
|
|
EMovementType::Flyer, EColliderType::One, EBodyType::RestrictedFlyer, actParms, EKnockBackVariant::Small)
|
|
, x578_minSpeed(f5)
|
|
, x57c_maxSpeed(f6)
|
|
, x580_speedStep(f7)
|
|
, x584_currentSpeed(x578_minSpeed)
|
|
, x588_frozenDamage(dVuln)
|
|
, x5f0_moveSpeed(f4)
|
|
, x5f4_(xf.basis[1])
|
|
, x600_electricWeapon(g_SimplePool->GetObj({SBIG('ELSC'), electricId}))
|
|
, x608_(g_SimplePool->GetObj({SBIG('WPSC'), weaponId}))
|
|
, x610_projectileDamage(dInfo)
|
|
, x62c_beamParticle(particleId)
|
|
, x630_(f1)
|
|
, x634_beamRadius(beamRadius)
|
|
, x638_(f3)
|
|
, x644_(CSfxManager::TranslateSFXID(sId1))
|
|
, x646_(CSfxManager::TranslateSFXID(sId2))
|
|
, x648_(CSfxManager::TranslateSFXID(sId3)) {
|
|
x460_knockBackController.SetAutoResetImpulse(false);
|
|
x460_knockBackController.SetEnableFreeze(false);
|
|
x460_knockBackController.SetX82_24(false);
|
|
}
|
|
|
|
void CAtomicBeta::CreateBeams(CStateManager& mgr) {
|
|
const SElectricBeamInfo beamInfo{x600_electricWeapon, 50.f, x634_beamRadius, 10.f, x62c_beamParticle, x630_, x638_};
|
|
|
|
for (size_t i = 0; i < kBombCount; ++i) {
|
|
const TUniqueId id = mgr.AllocateUniqueId();
|
|
x568_projectileIds.push_back(id);
|
|
mgr.AddObject(new CElectricBeamProjectile(x608_, EWeaponType::AI, beamInfo, {}, EMaterialTypes::Character,
|
|
x610_projectileDamage, id, GetAreaIdAlways(), GetUniqueId(),
|
|
EProjectileAttrib::None));
|
|
}
|
|
}
|
|
|
|
void CAtomicBeta::UpdateBeams(CStateManager& mgr, bool fireBeam) {
|
|
if (x574_beamFired == fireBeam) {
|
|
return;
|
|
}
|
|
|
|
for (size_t i = 0; i < x568_projectileIds.size(); ++i) {
|
|
// zeus::CTransform xf = GetTransform() * GetScaledLocatorTransform(skBombLocators[i]);
|
|
// zeus::CTransform newXf = zeus::lookAt(xf.origin, xf.origin + xf.basis[1], zeus::skUp);
|
|
if (auto* const proj = static_cast<CElectricBeamProjectile*>(mgr.ObjectById(x568_projectileIds[i]))) {
|
|
if (fireBeam) {
|
|
proj->Fire(GetTransform() * GetScaledLocatorTransform(skBombLocators[i]), mgr, false);
|
|
} else {
|
|
proj->ResetBeam(mgr, false);
|
|
}
|
|
}
|
|
}
|
|
x574_beamFired = fireBeam;
|
|
}
|
|
|
|
void CAtomicBeta::FreeBeams(CStateManager& mgr) {
|
|
for (TUniqueId uid : x568_projectileIds) {
|
|
mgr.FreeScriptObject(uid);
|
|
}
|
|
x568_projectileIds.clear();
|
|
}
|
|
|
|
void CAtomicBeta::UpdateOrCreateEmitter(CSfxHandle& handle, u16 id, const zeus::CVector3f& pos, float maxVol) {
|
|
if (handle)
|
|
CSfxManager::UpdateEmitter(handle, pos, {}, maxVol);
|
|
else
|
|
handle = CSfxManager::AddEmitter(id, pos, {}, maxVol, true, true, 0x7F, GetAreaIdAlways());
|
|
}
|
|
|
|
void CAtomicBeta::DestroyEmitter(CSfxHandle& handle) {
|
|
if (handle) {
|
|
CSfxManager::RemoveEmitter(handle);
|
|
handle.reset();
|
|
}
|
|
}
|
|
|
|
void CAtomicBeta::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateManager& mgr) {
|
|
if (msg == EScriptObjectMessage::Registered) {
|
|
x450_bodyController->Activate(mgr);
|
|
CreateBeams(mgr);
|
|
} else if (msg == EScriptObjectMessage::Deactivate) {
|
|
UpdateBeams(mgr, false);
|
|
DestroyEmitter(x650_);
|
|
DestroyEmitter(x654_);
|
|
DestroyEmitter(x64c_);
|
|
} else if (msg == EScriptObjectMessage::Deleted) {
|
|
FreeBeams(mgr);
|
|
}
|
|
CPatterned::AcceptScriptMsg(msg, uid, mgr);
|
|
}
|
|
|
|
void CAtomicBeta::Think(float dt, CStateManager& mgr) {
|
|
CPatterned::Think(dt, mgr);
|
|
zeus::CVector3f movementVec = x450_bodyController->GetCommandMgr().GetMoveVector();
|
|
x450_bodyController->GetCommandMgr().ClearLocomotionCmds();
|
|
if (!movementVec.isZero())
|
|
x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd(movementVec, x5f4_, 1.f));
|
|
|
|
const float mag =
|
|
x63c_ * std::max(1.f - (mgr.GetPlayer().GetTranslation() - GetTranslation()).magSquared() / (x640_ * x640_), 0.f);
|
|
if (!zeus::close_enough(mag, 0.f))
|
|
mgr.GetPlayerState()->GetStaticInterference().AddSource(GetUniqueId(), mag, 0.5f);
|
|
|
|
if (InMaxRange(mgr, dt)) {
|
|
UpdateBeams(mgr, true);
|
|
UpdateOrCreateEmitter(x650_, x646_, GetTranslation(), 96 / 127.f);
|
|
UpdateOrCreateEmitter(x654_, x648_, GetTranslation(), 96 / 127.f);
|
|
DestroyEmitter(x64c_);
|
|
} else {
|
|
UpdateBeams(mgr, false);
|
|
DestroyEmitter(x650_);
|
|
DestroyEmitter(x654_);
|
|
UpdateOrCreateEmitter(x64c_, x644_, GetTranslation(), 96 / 127.f);
|
|
}
|
|
|
|
// was hardcoded to 3 (kBombCount), but that segfaults after FreeBeams
|
|
for (size_t i = 0; i < x568_projectileIds.size(); ++i) {
|
|
if (auto* const proj = static_cast<CElectricBeamProjectile*>(mgr.ObjectById(x568_projectileIds[i]))) {
|
|
if (!proj->GetActive()) {
|
|
continue;
|
|
}
|
|
const zeus::CTransform xf = GetTransform() * GetScaledLocatorTransform(skBombLocators[i]);
|
|
proj->UpdateFx(zeus::lookAt(xf.origin, xf.origin + xf.frontVector(), zeus::skUp), dt, mgr);
|
|
}
|
|
}
|
|
|
|
float speed = x580_speedStep * (dt * (IsPlayerBeamChargedEnough(mgr) ? 1.f : -1.f)) + x584_currentSpeed;
|
|
x584_currentSpeed = zeus::clamp(x578_minSpeed, speed, x57c_maxSpeed);
|
|
x3b4_speed = x584_currentSpeed;
|
|
x450_bodyController->SetRestrictedFlyerMoveSpeed(x5f0_moveSpeed * x584_currentSpeed);
|
|
}
|
|
|
|
const CDamageVulnerability* CAtomicBeta::GetDamageVulnerability() const {
|
|
if (zeus::close_enough(x450_bodyController->GetPercentageFrozen(), 0.f))
|
|
return CPatterned::GetDamageVulnerability();
|
|
return &x588_frozenDamage;
|
|
}
|
|
|
|
void CAtomicBeta::Death(CStateManager& mgr, const zeus::CVector3f& dir, EScriptObjectState state) {
|
|
UpdateBeams(mgr, false);
|
|
DestroyEmitter(x650_);
|
|
DestroyEmitter(x654_);
|
|
DestroyEmitter(x64c_);
|
|
CPatterned::Death(mgr, dir, state);
|
|
}
|
|
|
|
bool CAtomicBeta::IsPlayerBeamChargedEnough(const CStateManager& mgr) {
|
|
const CPlayerGun* gun = mgr.GetPlayer().GetPlayerGun();
|
|
return (gun->IsCharging() ? gun->GetChargeBeamFactor() : 0.f) > 0.1f;
|
|
}
|
|
} // namespace metaforce::MP1
|