metaforce/Runtime/MP1/World/CBloodFlower.cpp

272 lines
10 KiB
C++

#include "Runtime/MP1/World/CBloodFlower.hpp"
#include <array>
#include "Runtime/CSimplePool.hpp"
#include "Runtime/CStateManager.hpp"
#include "Runtime/GameGlobalObjects.hpp"
#include "Runtime/Particle/CElementGen.hpp"
#include "Runtime/Particle/CGenDescription.hpp"
#include "Runtime/Weapon/CProjectileWeapon.hpp"
#include "Runtime/Weapon/CTargetableProjectile.hpp"
#include "Runtime/World/CPlayer.hpp"
#include "Runtime/World/CScriptTrigger.hpp"
namespace metaforce::MP1 {
CBloodFlower::CBloodFlower(TUniqueId uid, std::string_view name, const CEntityInfo& info, const zeus::CTransform& xf,
CModelData&& mData, const CPatternedInfo& pInfo, CAssetId partId1, CAssetId wpscId1,
const CActorParameters& actParms, CAssetId wpscId2, const CDamageInfo& dInfo1,
const CDamageInfo& dInfo2, const CDamageInfo& dInfo3, CAssetId partId2, CAssetId partId3,
CAssetId partId4, float f1, CAssetId partId5, u32 soundId)
: CPatterned(ECharacter::BloodFlower, uid, name, EFlavorType::Zero, info, xf, std::move(mData), pInfo,
EMovementType::Ground, EColliderType::One, EBodyType::Restricted, actParms, EKnockBackVariant::Medium)
, x568_podEffectDesc(g_SimplePool->GetObj({FOURCC('PART'), partId1}))
, x574_podEffect(std::make_unique<CElementGen>(x568_podEffectDesc))
, x578_projectileDesc(g_SimplePool->GetObj({FOURCC('WPSC'), wpscId1}))
, x590_projectileInfo(wpscId2, dInfo1)
, x5d4_visorSfx(CSfxManager::TranslateSFXID(soundId))
, x5dc_projectileDamage(dInfo2)
, x5f8_podDamage(dInfo3)
, x614_(f1)
, x618_(partId2)
, x61c_(partId3)
, x620_(partId4) {
x588_projectileOffset = GetModelData()->GetScale().z() * GetLocatorTransform("LCTR_FLOFLOWER"sv).origin.z();
x574_podEffect->SetParticleEmission(false);
x574_podEffect->SetOrientation(xf.getRotation());
x574_podEffect->SetGlobalTranslation(xf.origin);
x574_podEffect->SetGlobalScale(GetModelData()->GetScale());
x590_projectileInfo.Token().Lock();
x460_knockBackController.SetAutoResetImpulse(false);
if (partId5.IsValid()) {
x5c4_visorParticle = g_SimplePool->GetObj({FOURCC('PART'), partId5});
}
}
void CBloodFlower::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateManager& mgr) {
CPatterned::AcceptScriptMsg(msg, uid, mgr);
if (msg == EScriptObjectMessage::Registered) {
x450_bodyController->Activate(mgr);
x5b8_ = GetHealthInfo(mgr)->GetHP();
} else if (msg == EScriptObjectMessage::Damage) {
if (x450_bodyController->IsFrozen()) {
x450_bodyController->FrozenBreakout();
}
CalculateAttackTime(mgr);
UpdateFire(mgr);
}
}
void CBloodFlower::CalculateAttackTime(CStateManager& mgr) {
x584_curAttackTime = x308_attackTimeVariation * -mgr.GetActiveRandom()->Float();
}
void CBloodFlower::UpdateFire(CStateManager& mgr) {
if (x5d8_effectState == 0) {
TurnEffectsOn(0, mgr);
} else if (x5d8_effectState == 1) {
TurnEffectsOff(0, mgr);
TurnEffectsOn(1, mgr);
} else if (x5d8_effectState == 2) {
TurnEffectsOff(1, mgr);
TurnEffectsOn(2, mgr);
}
++x5d8_effectState;
}
constexpr std::array sFireEffects{
"Fire1"sv,
"Fire2"sv,
"Fire3"sv,
};
void CBloodFlower::TurnEffectsOn(u32 effect, CStateManager& mgr) {
GetModelData()->GetAnimationData()->SetParticleEffectState(sFireEffects[effect], true, mgr);
}
void CBloodFlower::TurnEffectsOff(u32 effect, CStateManager& mgr) {
GetModelData()->GetAnimationData()->SetParticleEffectState(sFireEffects[effect], false, mgr);
}
void CBloodFlower::Think(float dt, CStateManager& mgr) {
if (!GetActive())
return;
CPatterned::Think(dt, mgr);
x574_podEffect->Update(dt);
if (x5bc_projectileDelay > 0.f)
x5bc_projectileDelay -= dt;
x5c0_ += dt;
}
void CBloodFlower::DoUserAnimEvent(CStateManager& mgr, const CInt32POINode& node, EUserEventType type, float dt) {
if (type == EUserEventType::Projectile) {
if (x58c_projectileState == 1 && x5bc_projectileDelay <= 0.f) {
LaunchPollenProjectile(GetLctrTransform(node.GetLocatorName()), mgr, x614_, 5);
x58c_projectileState = 0;
x5bc_projectileDelay = 0.5f;
}
return;
} else if (type == EUserEventType::Delete) {
if (x5d8_effectState > 0)
TurnEffectsOff((x5d8_effectState > 3 ? x5d8_effectState - 1 : 2), mgr);
}
CPatterned::DoUserAnimEvent(mgr, node, type, dt);
}
void CBloodFlower::LaunchPollenProjectile(const zeus::CTransform& xf, CStateManager& mgr, float var_f1,
s32 maxProjectiles) {
CProjectileInfo* proj = GetProjectileInfo();
TLockedToken<CWeaponDescription> projToken = proj->Token();
if (!projToken || !mgr.CanCreateProjectile(GetUniqueId(), EWeaponType::AI, maxProjectiles))
return;
zeus::CVector3f aimPos = mgr.GetPlayer().GetAimPosition(mgr, 0.f);
float zDiff = xf.origin.z() - aimPos.z();
float f2 = (zDiff > 0.f ? var_f1 : -zDiff + var_f1);
if (zDiff > 0.f)
var_f1 = zDiff + var_f1;
float f7 = std::sqrt(2.f * f2 / 4.905f) + std::sqrt(2.f * var_f1 / 4.905f);
float f4 = 1.f / f7;
zeus::CVector3f vel{f4 * (aimPos.x() - xf.origin.x()), f4 * (aimPos.y() - xf.origin.y()),
2.4525f * f7 + (-zDiff / f7)};
if (CTargetableProjectile* targProj =
CreateArcProjectile(mgr, GetProjectileInfo()->Token(), zeus::CTransform::Translate(xf.origin),
GetProjectileInfo()->GetDamage(), kInvalidUniqueId)) {
targProj->ProjectileWeapon().SetVelocity(CProjectileWeapon::GetTickPeriod() * vel);
targProj->ProjectileWeapon().SetGravity(CProjectileWeapon::GetTickPeriod() * zeus::CVector3f(0.f, 0.f, -4.905f));
mgr.AddObject(targProj);
}
}
void CBloodFlower::Render(CStateManager& mgr) {
CPatterned::Render(mgr);
x574_podEffect->Render(GetActorLights());
}
EWeaponCollisionResponseTypes CBloodFlower::GetCollisionResponseType(const zeus::CVector3f&, const zeus::CVector3f&,
const CWeaponMode& weaponMode,
EProjectileAttrib) const {
const auto* const damageVulnerability = GetDamageVulnerability();
if (damageVulnerability->WeaponHurts(weaponMode, false)) {
return EWeaponCollisionResponseTypes::Unknown28;
}
return EWeaponCollisionResponseTypes::Unknown78;
}
bool CBloodFlower::ShouldAttack(CStateManager& mgr, float arg) {
if (TooClose(mgr, 0.f))
return false;
if (x584_curAttackTime <= x304_averageAttackTime)
return false;
return (mgr.GetPlayer().GetTranslation().z() + mgr.GetPlayer().GetEyeHeight() <
x588_projectileOffset + x614_ + GetTranslation().z());
}
bool CBloodFlower::ShouldTurn(CStateManager& mgr, float) {
if (TooClose(mgr, 0.f))
return false;
zeus::CVector3f frontVec = GetTransform().basis[1];
frontVec.z() = 0.f;
frontVec.normalize();
zeus::CVector3f posDiff = mgr.GetPlayer().GetTranslation() - GetTranslation();
posDiff.z() = 0.f;
posDiff.normalize();
return posDiff.dot(frontVec) < 0.99599999f;
}
void CBloodFlower::Active(CStateManager& mgr, EStateMsg msg, float arg) {
if (msg == EStateMsg::Activate) {
x32c_animState = EAnimState::Ready;
CalculateAttackTime(mgr);
} else if (msg == EStateMsg::Update) {
TryCommand(mgr, pas::EAnimationState::LoopReaction, &CPatterned::TryLoopReaction, 0);
x450_bodyController->GetCommandMgr().DeliverCmd(CBCAdditiveAimCmd());
x584_curAttackTime += arg;
x450_bodyController->GetCommandMgr().DeliverAdditiveTargetVector(
GetTransform().transposeRotate(mgr.GetPlayer().GetTranslation() - GetTranslation()));
} else if (msg == EStateMsg::Deactivate) {
x450_bodyController->GetCommandMgr().DeliverCmd(CBodyStateCmd(EBodyStateCmd::ExitState));
x450_bodyController->GetCommandMgr().DeliverCmd(CBodyStateCmd(EBodyStateCmd::AdditiveIdle));
}
}
void CBloodFlower::InActive(CStateManager& mgr, EStateMsg msg, float) {
if (msg == EStateMsg::Activate) {
x450_bodyController->SetLocomotionType(pas::ELocomotionType::Relaxed);
x400_24_hitByPlayerProjectile = false;
} else if (msg == EStateMsg::Deactivate) {
x5c0_ = 0.f;
}
}
void CBloodFlower::BulbAttack(CStateManager& mgr, EStateMsg msg, float) {
if (msg == EStateMsg::Activate) {
x450_bodyController->GetCommandMgr().DeliverCmd(
CBCProjectileAttackCmd(pas::ESeverity::Zero, mgr.GetPlayer().GetTranslation(), true));
x58c_projectileState = 1;
}
}
void CBloodFlower::PodAttack(CStateManager& mgr, EStateMsg msg, float arg) {
if (msg == EStateMsg::Activate) {
x450_bodyController->GetCommandMgr().DeliverCmd(CBCMeleeAttackCmd(pas::ESeverity::Zero));
x574_podEffect->SetParticleEmission(true);
ActivateTriggers(mgr, true);
} else if (msg == EStateMsg::Update) {
if (TooClose(mgr, 0.f))
return;
mgr.ApplyDamage(GetUniqueId(), mgr.GetPlayer().GetUniqueId(), GetUniqueId(), x5f8_podDamage,
CMaterialFilter::MakeIncludeExclude({EMaterialTypes::Solid}, {}), {});
} else if (msg == EStateMsg::Deactivate) {
x574_podEffect->SetParticleEmission(false);
ActivateTriggers(mgr, false);
x450_bodyController->GetCommandMgr().DeliverCmd(CBCKnockBackCmd({}, pas::ESeverity::One));
}
}
void CBloodFlower::ActivateTriggers(CStateManager& mgr, bool activate) {
for (const SConnection& conn : GetConnectionList()) {
auto search = mgr.GetIdListForScript(conn.x8_objId);
for (auto it = search.first; it != search.second; ++it) {
if (TCastToPtr<CScriptTrigger> trigger = mgr.ObjectById(it->second)) {
mgr.SendScriptMsg(trigger, GetUniqueId(),
(activate ? EScriptObjectMessage::Activate : EScriptObjectMessage::Deactivate));
}
}
}
}
CTargetableProjectile* CBloodFlower::CreateArcProjectile(CStateManager& mgr, const TToken<CWeaponDescription>& desc,
const zeus::CTransform& xf, const CDamageInfo& damage,
TUniqueId uid) {
if (!x578_projectileDesc) {
return nullptr;
}
TUniqueId projId = mgr.AllocateUniqueId();
auto* targProj = new CTargetableProjectile(
desc, EWeaponType::AI, xf, EMaterialTypes::Character, damage, x5dc_projectileDamage, projId, GetAreaIdAlways(),
GetUniqueId(), x578_projectileDesc, uid, EProjectileAttrib::None, {x5c4_visorParticle}, x5d4_visorSfx, false);
if (mgr.GetPlayer().GetOrbitTargetId() == GetUniqueId()) {
mgr.GetPlayer().ResetAimTargetPrediction(projId);
mgr.GetPlayer().SetOrbitTargetId(projId, mgr);
}
return targProj;
}
} // namespace metaforce::MP1