#include "Runtime/MP1/World/CBloodFlower.hpp" #include #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(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 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 trigger = mgr.ObjectById(it->second)) { mgr.SendScriptMsg(trigger, GetUniqueId(), (activate ? EScriptObjectMessage::Activate : EScriptObjectMessage::Deactivate)); } } } } CTargetableProjectile* CBloodFlower::CreateArcProjectile(CStateManager& mgr, const TToken& 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