From 543a24ea9e16a33e7dbc99bfbe0df00b1b283caa Mon Sep 17 00:00:00 2001 From: Phillip Stephens Date: Sat, 19 Sep 2020 15:08:07 -0700 Subject: [PATCH] Initial CMetroidPrimeEssence behavior --- Runtime/CStateManager.cpp | 2 +- Runtime/MP1/World/CMakeLists.txt | 1 + Runtime/MP1/World/CMetroidPrimeEssence.cpp | 607 +++++++++++++++++++++ Runtime/MP1/World/CMetroidPrimeEssence.hpp | 111 ++++ Runtime/World/CScriptPlayerActor.cpp | 4 +- Runtime/World/ScriptLoader.cpp | 31 +- Runtime/World/ScriptLoader.hpp | 2 +- 7 files changed, 750 insertions(+), 8 deletions(-) create mode 100644 Runtime/MP1/World/CMetroidPrimeEssence.cpp create mode 100644 Runtime/MP1/World/CMetroidPrimeEssence.hpp diff --git a/Runtime/CStateManager.cpp b/Runtime/CStateManager.cpp index 129aca1b5..ed769b797 100644 --- a/Runtime/CStateManager.cpp +++ b/Runtime/CStateManager.cpp @@ -192,7 +192,7 @@ CStateManager::CStateManager(const std::weak_ptr& relayTracker, x90c_loaderFuncs[size_t(EScriptObjectType::Burrower)] = ScriptLoader::LoadBurrower; x90c_loaderFuncs[size_t(EScriptObjectType::ScriptBeam)] = ScriptLoader::LoadBeam; x90c_loaderFuncs[size_t(EScriptObjectType::WorldLightFader)] = ScriptLoader::LoadWorldLightFader; - x90c_loaderFuncs[size_t(EScriptObjectType::MetroidPrimeStage2)] = ScriptLoader::LoadMetroidPrimeStage2; + x90c_loaderFuncs[size_t(EScriptObjectType::MetroidPrimeStage2)] = ScriptLoader::LoadMetroidPrimeEssence; x90c_loaderFuncs[size_t(EScriptObjectType::MetroidPrimeStage1)] = ScriptLoader::LoadMetroidPrimeStage1; x90c_loaderFuncs[size_t(EScriptObjectType::MazeNode)] = ScriptLoader::LoadMazeNode; x90c_loaderFuncs[size_t(EScriptObjectType::OmegaPirate)] = ScriptLoader::LoadOmegaPirate; diff --git a/Runtime/MP1/World/CMakeLists.txt b/Runtime/MP1/World/CMakeLists.txt index 15f231e82..0417d8109 100644 --- a/Runtime/MP1/World/CMakeLists.txt +++ b/Runtime/MP1/World/CMakeLists.txt @@ -32,6 +32,7 @@ set(MP1_WORLD_SOURCES CMetroidPrimeExo.hpp CMetroidPrimeExo.cpp CMetroidPrimeProjectile.hpp CMetroidPrimeProjectile.cpp CMetroidPrimeRelay.hpp CMetroidPrimeRelay.cpp + CMetroidPrimeEssence.hpp CMetroidPrimeEssence.cpp CNewIntroBoss.hpp CNewIntroBoss.cpp COmegaPirate.hpp COmegaPirate.cpp CParasite.hpp CParasite.cpp diff --git a/Runtime/MP1/World/CMetroidPrimeEssence.cpp b/Runtime/MP1/World/CMetroidPrimeEssence.cpp new file mode 100644 index 000000000..071e25fa7 --- /dev/null +++ b/Runtime/MP1/World/CMetroidPrimeEssence.cpp @@ -0,0 +1,607 @@ +#include "Runtime/MP1/World/CMetroidPrimeEssence.hpp" + +#include "Runtime/CSimplePool.hpp" +#include "Runtime/CStateManager.hpp" +#include "Runtime/Collision/CCollisionActor.hpp" +#include "Runtime/Collision/CCollisionActorManager.hpp" +#include "Runtime/GameGlobalObjects.hpp" +#include "Runtime/Weapon/CGameProjectile.hpp" +#include "Runtime/World/CGameArea.hpp" +#include "Runtime/World/CPatternedInfo.hpp" +#include "Runtime/World/CPlayer.hpp" +#include "Runtime/World/CWorld.hpp" +#include "Runtime/Graphics/CBooRenderer.hpp" + +#include "DataSpec/DNAMP1/SFX/MetroidPrime.h" + +#include "TCastTo.hpp" // Generated file, do not modify include path + +namespace urde::MP1 { +namespace { +std::array skJointInfo{{ + {"lockon_target_LCTR", 1.5f}, +}}; + +std::array skUnkInts1{{0, 1, 0, 2}}; +std::array skUnkInts2{{1, 2, 3}}; + +} // namespace +CMetroidPrimeEssence::CMetroidPrimeEssence(urde::TUniqueId uid, std::string_view name, const urde::CEntityInfo& info, + const zeus::CTransform& xf, urde::CModelData&& mData, + const urde::CPatternedInfo& pInfo, const urde::CActorParameters& actParms, + urde::CAssetId particle1, const urde::CDamageInfo& dInfo, float f1, + urde::CAssetId electric, u32 w1, urde::CAssetId particle2) +: CPatterned(ECharacter::MetroidPrimeEssence, uid, name, EFlavorType::Zero, info, xf, std::move(mData), pInfo, + EMovementType::Flyer, EColliderType::One, EBodyType::Flyer, actParms, EKnockBackVariant::Medium) +, x568_(g_SimplePool->GetObj({FOURCC('PART'), particle2})) +, x574_searchPath(nullptr, 3, pInfo.GetPathfindingIndex(), 1.f, 1.f) +, x660_(particle1) +, x664_(electric) +, x698_(dInfo) +, x6b4_(xf.origin) +, x70c_(CSfxManager::TranslateSFXID(w1)) {} + +void CMetroidPrimeEssence::Think(float dt, CStateManager& mgr) { + if (!GetActive()) { + return; + } + + CPatterned::Think(dt, mgr); + if (IsAlive()) { + UpdatePhase(dt, mgr); + } + + x450_bodyController->FaceDirection((mgr.GetPlayer().GetTranslation() - GetTranslation()).normalized(), dt); + x658_collisionManager->Update(dt, mgr, CCollisionActorManager::EUpdateOptions::ObjectSpace); + UpdateHealth(mgr); + CountListeningAi(mgr); + if (x70e_30_) { + x6d4_ = 2.f * dt + x6d4_; + if (x6d4_ >= 1.f) { + x6d4_ = 0.f; + } + + sub8027ce5c(-4.f * x6d4_ * (x6d4_ - 1.f)); + } +} + +void CMetroidPrimeEssence::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId other, CStateManager& mgr) { + CPatterned::AcceptScriptMsg(msg, other, mgr); + + switch (msg) { + case EScriptObjectMessage::Activate: + x658_collisionManager->SetActive(mgr, true); + break; + case EScriptObjectMessage::Deactivate: + x658_collisionManager->SetActive(mgr, false); + break; + case EScriptObjectMessage::Start: + x70e_25_ = true; + break; + case EScriptObjectMessage::Stop: + x70e_25_ = false; + break; + case EScriptObjectMessage::Touched: { + if (TCastToPtr colAct = mgr.ObjectById(other)) { + if (colAct->GetLastTouchedObject() == mgr.GetPlayer().GetUniqueId()) { + mgr.ApplyDamage(GetUniqueId(), mgr.GetPlayer().GetUniqueId(), GetUniqueId(), GetContactDamage(), + CMaterialFilter::MakeIncludeExclude({EMaterialTypes::Solid}, {}), zeus::skZero3f); + x420_curDamageRemTime = x424_damageWaitTime; + } + } + break; + } + case EScriptObjectMessage::Registered: { + SetupCollisionActorManager(mgr); + x658_collisionManager->SetActive(mgr, true); + x6cc_ = GetModelData()->GetScale().x(); + x6d0_ = 0.9f * x6cc_ + x6cc_; + x55c_moveScale.splat(1.f / (0.625f * x6cc_)); + const float hp = GetHealthInfo(mgr)->GetHP(); + x6c0_ = 0.3f * hp; + if (hp > 0.f) { + x6c4_ = 1.f / hp; + } + x450_bodyController->Activate(mgr); + break; + } + case EScriptObjectMessage::Deleted: { + x658_collisionManager->Destroy(mgr); + mgr.SetBossParams(kInvalidUniqueId, 0.f, 0); + break; + } + case EScriptObjectMessage::InitializedInArea: { + x574_searchPath.SetArea(mgr.GetWorld()->GetArea(GetAreaIdAlways())->GetPostConstructed()->x10bc_pathArea); + x704_ = GetWaypointForState(mgr, EScriptObjectState::Play, EScriptObjectMessage::Activate); + break; + } + case EScriptObjectMessage::Damage: { + if (TCastToPtr colAct = mgr.ObjectById(other)) { + if (TCastToConstPtr proj = mgr.GetObjectById(colAct->GetLastTouchedObject())) { + if (proj->GetOwnerId() == mgr.GetPlayer().GetUniqueId()) { + if (colAct->GetDamageVulnerability()->WeaponHits(proj->GetDamageInfo().GetWeaponMode(), false) && + proj->GetDamageInfo().GetWeaponMode().GetType() == EWeaponType::Phazon) { + sub8027cee0(mgr); + TakeDamage(zeus::skForward, 1.f); + if (!x70e_24_ && !x70e_26_) { + GetBodyController()->GetCommandMgr().DeliverCmd( + CBCKnockBackCmd{GetTransform().frontVector(), pas::ESeverity::One}); + sub8027cce0(mgr); + } + } + } + } + } else if (TCastToConstPtr proj = mgr.GetObjectById(other)) { + mgr.ApplyDamage(other, x706_lockOnTargetCollider, proj->GetOwnerId(), proj->GetDamageInfo(), + CMaterialFilter::MakeIncludeExclude({EMaterialTypes::Solid}, {}), zeus::skZero3f); + } + break; + } + default: + break; + } +} + +void CMetroidPrimeEssence::PreRender(CStateManager& mgr, const zeus::CFrustum& frustum) { + CPatterned::PreRender(mgr, frustum); +} + +void CMetroidPrimeEssence::AddToRenderer(const zeus::CFrustum& frustum, CStateManager& mgr) { + + if (GetActive() && x65c_) { + g_Renderer->AddParticleGen(*x65c_); + } + CPatterned::AddToRenderer(frustum, mgr); +} + +void CMetroidPrimeEssence::Render(CStateManager& mgr) { + if (x70e_27_) { + mgr.DrawSpaceWarp(x6b4_, 1.f); + } + CPatterned::Render(mgr); +} + +zeus::CVector3f CMetroidPrimeEssence::GetAimPosition(const CStateManager& mgr, float dt) const { + if (TCastToConstPtr colAct = mgr.GetObjectById(x706_lockOnTargetCollider)) { + return colAct->GetTranslation(); + } + return CPatterned::GetAimPosition(mgr, dt); +} + +void CMetroidPrimeEssence::DoUserAnimEvent(CStateManager& mgr, const CInt32POINode& node, EUserEventType type, + float dt) { + CPatterned::DoUserAnimEvent(mgr, node, type, dt); +} + +void CMetroidPrimeEssence::Death(CStateManager& mgr, const zeus::CVector3f& direction, EScriptObjectState state) { + if (!IsAlive()) { + return; + } + + sub8027ee88(mgr); + sub8027d790(mgr, false); + if (TCastToPtr colAct = mgr.ObjectById(x706_lockOnTargetCollider)) { + colAct->AddMaterial(EMaterialTypes::ProjectilePassthrough, mgr); + } + CPatterned::Death(mgr, direction, state); +} + +void CMetroidPrimeEssence::Dead(CStateManager& mgr, EStateMsg msg, float dt) { + if (msg != EStateMsg::Update || GetModelData()->GetAnimationData()->IsAnimTimeRemaining(dt, "Whole Body"sv)) { + return; + } + + DeathDelete(mgr); +} + +void CMetroidPrimeEssence::PathFind(CStateManager& mgr, EStateMsg msg, float dt) { + CPatterned::PathFind(mgr, msg, dt); + if (msg == EStateMsg::Update) { + sub8027cb40(GetTranslation()); + } +} + +void CMetroidPrimeEssence::Halt(CStateManager& mgr, EStateMsg msg, float dt) { + // Intentionally empty +} + +void CMetroidPrimeEssence::Generate(CStateManager& mgr, EStateMsg msg, float dt) { + if (msg == EStateMsg::Activate) { + zeus::CTransform xf = zeus::lookAt(GetTranslation(), mgr.GetPlayer().GetTranslation()); + xf.origin = GetTranslation(); + SetTransform(xf); + } else if (msg == EStateMsg::Deactivate) { + mgr.SetBossParams(GetUniqueId(), GetHealthInfo(mgr)->GetHP(), 91); + sub8027d790(mgr, true); + } +} + +void CMetroidPrimeEssence::JumpBack(CStateManager& mgr, EStateMsg msg, float dt) { + if (msg == EStateMsg::Activate) { + x32c_animState = EAnimState::Ready; + x700_ = sub8027cfd4(mgr, 1); + } else if (msg == EStateMsg::Update) { + TryCommand(mgr, pas::EAnimationState::Step, &CPatterned::TryStep, x700_); + } else if (msg == EStateMsg::Deactivate) { + x32c_animState = EAnimState::NotReady; + } +} + +void CMetroidPrimeEssence::Skid(CStateManager& mgr, EStateMsg msg, float dt) { + if (msg == EStateMsg::Activate) { + x32c_animState = EAnimState::Ready; + } else if (msg == EStateMsg::Update) { + TryCommand(mgr, pas::EAnimationState::Step, &CPatterned::TryStep, 5); + } else if (msg == EStateMsg::Deactivate) { + x32c_animState = EAnimState::NotReady; + } +} + +void CMetroidPrimeEssence::FadeIn(CStateManager& mgr, EStateMsg msg, float dt) { + if (msg == EStateMsg::Activate) { + x6f8_ = sub8027d428(); + x32c_animState = EAnimState::Ready; + x70e_24_ = true; + } else if (msg == EStateMsg::Update) { + TryCommand(mgr, pas::EAnimationState::ProjectileAttack, &CPatterned::TryProjectileAttack, 0); + } else if (msg == EStateMsg::Deactivate) { + x70e_24_ = false; + x70e_27_ = false; + x70e_29_ = false; + x70e_30_ = false; + x32c_animState = EAnimState::NotReady; + } +} + +void CMetroidPrimeEssence::FadeOut(CStateManager& mgr, EStateMsg msg, float dt) { + if (msg != EStateMsg::Activate) { + return; + } + + DoPhaseTransition(mgr); +} + +void CMetroidPrimeEssence::Taunt(CStateManager& mgr, EStateMsg msg, float dt) { + if (msg == EStateMsg::Activate) { + x32c_animState = EAnimState::Ready; + } else if (msg == EStateMsg::Update) { + TryCommand(mgr, pas::EAnimationState::Taunt, &CPatterned::TryTaunt, 2); + } else if (msg == EStateMsg::Deactivate) { + x32c_animState = EAnimState::NotReady; + } +} + +void CMetroidPrimeEssence::TelegraphAttack(CStateManager& mgr, EStateMsg msg, float dt) { + if (msg == EStateMsg::Activate) { + x32c_animState = EAnimState::Ready; + x70e_30_ = true; + } else if (msg == EStateMsg::Update) { + if (!x70e_30_) { + TryCommand(mgr, pas::EAnimationState::MeleeAttack, &CPatterned::TryMeleeAttack, 2); + } else { + TryCommand(mgr, pas::EAnimationState::ProjectileAttack, &CPatterned::TryProjectileAttack, 5); + } + } else if (msg == EStateMsg::Deactivate) { + x32c_animState = EAnimState::NotReady; + x70e_30_ = false; + sub8027ce5c(dt); + } +} + +void CMetroidPrimeEssence::Dodge(CStateManager& mgr, EStateMsg msg, float dt) { + if (msg == EStateMsg::Activate) { + x32c_animState = EAnimState::Ready; + x700_ = sub8027cfd4(mgr, 0); + } else if (msg == EStateMsg::Update) { + TryCommand(mgr, pas::EAnimationState::Step, &CPatterned::TryStep, x700_); + } else if (msg == EStateMsg::Deactivate) { + x32c_animState = EAnimState::NotReady; + } +} + +void CMetroidPrimeEssence::PathFindEx(CStateManager& mgr, EStateMsg msg, float dt) { + CPatterned::PathFind(mgr, msg, dt); + if (msg == EStateMsg::Activate) { + x70e_24_ = true; + } else if (msg == EStateMsg::Update) { + sub8027cb40(x2e0_destPos); + } else if (msg == EStateMsg::Deactivate) { + x70e_24_ = false; + } +} + +bool CMetroidPrimeEssence::HasPatrolPath(CStateManager& mgr, float dt) { + return !x70e_30_ && CPatterned::HasPatrolPath(mgr, dt); +} + +bool CMetroidPrimeEssence::ShouldAttack(CStateManager& mgr, float dt) { return x70e_30_ && x70e_25_; } + +bool CMetroidPrimeEssence::InPosition(CStateManager& mgr, float dt) { + return (GetTranslation().z() - mgr.GetPlayer().GetTranslation().z()) > 0.25f; +} + +bool CMetroidPrimeEssence::CoverFind(CStateManager& mgr, float dt) { + return (x2e0_destPos - GetTranslation()).magSquared() < 90.f; +} + +bool CMetroidPrimeEssence::ShouldTaunt(CStateManager& mgr, float dt) { + const CHealthInfo* info = GetHealthInfo(mgr); + if (!info || info->GetHP() <= x6c0_) { + return false; + } + + return mgr.GetActiveRandom()->Next() % 100 < 50; +} + +bool CMetroidPrimeEssence::ShouldCrouch(CStateManager& mgr, float dt) { + if (x6f0_ < x6f4_) { + ++x6f0_; + return false; + } + + x6f4_ = std::min(static_cast(3.f * (1.f - x6c4_ * GetHealthInfo(mgr)->GetHP())), x6ec_); + x6f0_ = 0; + return true; +} + +bool CMetroidPrimeEssence::ShouldMove(CStateManager& mgr, float dt) { return x70e_30_; } + +CPathFindSearch* CMetroidPrimeEssence::GetSearchPath() { return &x574_searchPath; } + +void CMetroidPrimeEssence::sub8027cb40(const zeus::CVector3f& vec) { + pas::EStepDirection stepDir = GetStepDirection(GetBodyController()->GetCommandMgr().GetMoveVector()); + if (stepDir == pas::EStepDirection::Forward && + (x2e0_destPos - GetTranslation()).normalized().dot(GetTransform().frontVector().normalized()) < + zeus::degToRad(-15.f)) { + stepDir = pas::EStepDirection::Backward; + } + + GetBodyController()->GetCommandMgr().DeliverCmd(CBCStepCmd(stepDir, pas::EStepType::Normal)); + GetBodyController()->GetCommandMgr().DeliverTargetVector(vec - GetTranslation()); +} + +void CMetroidPrimeEssence::sub8027cce0(CStateManager& mgr) { + if (CSfxManager::IsPlaying(x708_)) { + return; + } + CAudioSys::C3DEmitterParmData emitterData{zeus::skZero3f, zeus::skZero3f, 1000.f, 0.1f, 1, SFXsfx0B67, 1.f, + 0.16f, false, 127}; + emitterData.x0_pos = GetTargetTransform(mgr).origin; + x708_ = CSfxManager::AddEmitter(emitterData, true, 127, false, GetAreaIdAlways()); +} + +zeus::CTransform CMetroidPrimeEssence::GetTargetTransform(CStateManager& mgr) { + if (TCastToPtr colAct = mgr.ObjectById(x706_lockOnTargetCollider)) { + return colAct->GetTransform(); + } + + return GetTransform(); +} + +void CMetroidPrimeEssence::sub8027ce5c(float dt) { + const auto matCount = static_cast(GetModelData()->GetNumMaterialSets() - 2); + u32 iVar1 = matCount - (matCount * dt); + if (x6fc_ != iVar1) { + x6fc_ = iVar1; + } +} + +void CMetroidPrimeEssence::sub8027cee0(CStateManager& mgr) { + const float hp = x6c4_ * GetHealthInfo(mgr)->GetHP(); + if (hp < 0.f) { + return; + } + + bool sendMsg = false; + if (x6d8_ == 0 && hp < 0.75f) { + x6d8_ = 1; + } else if (x6d8_ == 1 && hp < 0.5f) { + sendMsg = true; + x6d8_ = 2; + } else if (x6d8_ == 2 && hp < 0.25f) { + sendMsg = true; + x6d8_ = 3; + } + + if (sendMsg) { + SendScriptMsgs(EScriptObjectState::DeactivateState, mgr, EScriptObjectMessage::None); + } +} + +u32 CMetroidPrimeEssence::sub8027cfd4(CStateManager& mgr, u32 w1) { + zeus::CTransform xf = GetTargetTransform(mgr); + u32 uVar1 = zeus::countLeadingZeros(w1); + uVar1 >>= 5; + std::array vec; + const zeus::CVector3f* dir = &vec[uVar1]; + vec[0] = -xf.frontVector(); + vec[1] = -xf.rightVector(); + u32 uVar5 = 1 << uVar1; + for (size_t i = uVar1; i < 3; ++i) { + CRayCastResult res = mgr.RayStaticIntersection(xf.origin, *dir, 20.f, CMaterialFilter::skPassEverything); + if (res.IsInvalid()) { + uVar5 |= 1 << i; + } + ++dir; + } + + u32 uVar3 = 0; + if (uVar5 < 8) { + u32 numBits = zeus::PopCount(uVar5); + if (numBits == 2) { + u32 uVar1_ = mgr.GetActiveRandom()->Next(); + if ((uVar1_ & 1) == false) { + uVar3 = (uVar5 & 1) ^ 1; + } else { + uVar3 = ((uVar5 >> 2) & 1) + 1; + } + } else if (numBits < 2) { + uVar3 = uVar5 >> 1; + } else if (numBits == 3) { + uVar3 = mgr.GetActiveRandom()->Range(uVar1, 2); + } + } + + return skUnkInts2[uVar3]; +} + +void CMetroidPrimeEssence::DoPhaseTransition(CStateManager& mgr) { + x330_stateMachineState.SetDelay(2.f); + x70e_26_ = true; + x70e_27_ = true; + x6c8_ = 1.f; + x70e_29_ = false; + + bool uVar3 = x6dc_ == skUnkInts1[size_t(mgr.GetPlayerState()->GetCurrentVisor())]; + if (skUnkInts1[size_t(mgr.GetPlayerState()->GetCurrentVisor())] == x6dc_) { + x65c_ = std::make_unique(x568_, CElementGen::EModelOrientationType::Normal, + CElementGen::EOptionalSystemFlags::One); + + if (x65c_) { + zeus::CTransform xf = GetTargetTransform(mgr); + x65c_->SetGlobalScale(GetModelData()->GetScale()); + x65c_->SetGlobalOrientation(xf.getRotation()); + x65c_->SetGlobalTranslation(xf.origin); + } + } + + CSfxManager::AddEmitter(SFXsfx0B7D + uVar3, GetTranslation(), zeus::skZero3f, true, false, 127, kInvalidAreaId); + x6e0_ = x6dc_; + ++x6dc_; + if (x6dc_ > 2) { + x6dc_ = 0; + } +} + +void CMetroidPrimeEssence::ShakeCamera(CStateManager& mgr, float f1) { + float mag = 0.5f - (0.01f * (GetTranslation() - mgr.GetPlayer().GetTranslation()).magnitude()); + if (mag < 0.f || mgr.GetPlayer().GetSurfaceRestraint() == CPlayer::ESurfaceRestraints::Air) { + return; + } + + mgr.GetCameraManager()->AddCameraShaker(CCameraShakeData(0.5f, mag), true); +} + +void CMetroidPrimeEssence::sub8027d52c(CStateManager& mgr, const SShockWaveData& shockWaveData) {} + +CRayCastResult CMetroidPrimeEssence::sub8027d704(CStateManager& mgr) { return CRayCastResult(); } + +void CMetroidPrimeEssence::sub8027d790(CStateManager& mgr, bool active) {} + +void CMetroidPrimeEssence::sub8027d824(CStateManager& mgr) {} + +bool CMetroidPrimeEssence::sub8027e870(const zeus::CTransform& xf, CStateManager& mgr) { return false; } + +void CMetroidPrimeEssence::sub8027ee88(CStateManager& mgr) {} + +void CMetroidPrimeEssence::CountListeningAi(CStateManager& mgr) { + x6e0_ = 0; + for (auto* ent : mgr.GetListeningAiObjectList()) { + if (TCastToPtr ai = ent) { + if (ai != this && ai->GetActive() && ai->GetAreaIdAlways() == GetAreaIdAlways()) { + ++x6e4_; + } + } + } +} + +void CMetroidPrimeEssence::UpdatePhase(float dt, CStateManager& mgr) { + if (skUnkInts1[size_t(mgr.GetPlayerState()->GetCurrentVisor())] == x6dc_) { + x42c_color.a() = 1.f - x6c8_; + GetModelData()->SetScale(zeus::CVector3f((x6cc_ - x6d0_) + x6d0_)); + if (!x70e_28_) { + AddMaterial(EMaterialTypes::Orbit, EMaterialTypes::Target, mgr); + sub8027d790(mgr, true); + x70e_28_ = true; + } + } else { + x42c_color.a() = skUnkInts1[size_t(mgr.GetPlayerState()->GetCurrentVisor())] == x6e0_ ? x6c8_ : 0.f; + GetModelData()->SetScale(zeus::CVector3f((x6cc_ - x6d0_) + x6d0_)); + if (x70e_28_) { + RemoveMaterial(EMaterialTypes::Orbit, EMaterialTypes::Target, mgr); + sub8027d790(mgr, false); + x70e_28_ = false; + } + } + + zeus::CTransform xf = GetTargetTransform(mgr); + if (x70e_26_) { + x6c8_ -= 0.5f * dt; + x6b4_ = xf.origin; + if (x6c8_ < 0.f) { + x6c8_ = 0.f; + x70e_26_ = false; + x70e_27_ = false; + } + } + + if (!x65c_) { + return; + } + + if (!x65c_->IsSystemDeletable()) { + x65c_->SetGlobalOrientation(xf.getRotation()); + x65c_->SetGlobalTranslation(xf.origin); + x65c_->Update(dt); + } else { + x65c_.reset(); + } +} + +void CMetroidPrimeEssence::UpdateHealth(CStateManager& mgr) { + if (!IsAlive()) { + return; + } + + if (TCastToPtr colAct = mgr.ObjectById(x706_lockOnTargetCollider)) { + colAct->SetDamageVulnerability(*GetDamageVulnerability()); + HealthInfo(mgr)->SetHP(colAct->GetHealthInfo(mgr)->GetHP()); + } + + if (GetHealthInfo(mgr)->GetHP() <= 0.f) { + Death(mgr, zeus::skZero3f, EScriptObjectState::DeathRattle); + RemoveMaterial(EMaterialTypes::Orbit, EMaterialTypes::Target, mgr); + } +} + +void CMetroidPrimeEssence::SetLockOnTargetHealthAndDamageVulns(CStateManager& mgr) { + if (TCastToPtr colAct = mgr.ObjectById(x706_lockOnTargetCollider)) { + *colAct->HealthInfo(mgr) = *HealthInfo(mgr); + colAct->SetDamageVulnerability(*GetDamageVulnerability()); + } +} + +void CMetroidPrimeEssence::AddSphereCollisions(SSphereJointInfo* info, size_t count, + std::vector& vecOut) { + const CAnimData* animData = GetModelData()->GetAnimationData(); + for (size_t i = 0; i < count; ++i) { + CSegId segId = animData->GetLocatorSegId(info[i].name); + if (segId.IsInvalid()) { + continue; + } + vecOut.push_back(CJointCollisionDescription::SphereCollision(segId, info[i].radius, info[i].name, 1000.f)); + } +} + +void CMetroidPrimeEssence::SetupCollisionActorManager(CStateManager& mgr) { + std::vector joints; + AddSphereCollisions(skJointInfo.data(), skJointInfo.size(), joints); + x658_collisionManager = + std::make_unique(mgr, GetUniqueId(), GetAreaIdAlways(), joints, false); + + for (size_t i = 0; i < x658_collisionManager->GetNumCollisionActors(); ++i) { + const auto& info = x658_collisionManager->GetCollisionDescFromIndex(i); + if (TCastToPtr colAct = mgr.ObjectById(info.GetCollisionActorId())) { + if (info.GetName() == "lockon_target_LCTR"sv) { + x706_lockOnTargetCollider = info.GetCollisionActorId(); + } + } + } + + SetLockOnTargetHealthAndDamageVulns(mgr); + SetMaterialFilter(CMaterialFilter::MakeIncludeExclude( + {EMaterialTypes::Solid}, {EMaterialTypes::CollisionActor, EMaterialTypes::Player, EMaterialTypes::Character})); + AddMaterial(EMaterialTypes::ProjectilePassthrough, mgr); +} + +} // namespace urde::MP1 \ No newline at end of file diff --git a/Runtime/MP1/World/CMetroidPrimeEssence.hpp b/Runtime/MP1/World/CMetroidPrimeEssence.hpp new file mode 100644 index 000000000..91ea70c7d --- /dev/null +++ b/Runtime/MP1/World/CMetroidPrimeEssence.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include "Runtime/Collision/CJointCollisionDescription.hpp" +#include "Runtime/MP1/World/CShockWave.hpp" +#include "Runtime/World/CPathFindSearch.hpp" +#include "Runtime/World/CPatterned.hpp" + +namespace urde { +class CCollisionActorManager; +namespace MP1 { +class CMetroidPrimeEssence : public CPatterned { + TCachedToken x568_; + CPathFindSearch x574_searchPath; + std::unique_ptr x658_collisionManager; + std::unique_ptr x65c_; + CAssetId x660_; + CAssetId x664_; + zeus::CTransform x668_; + CDamageInfo x698_; + zeus::CVector3f x6b4_; + float x6c0_ = 0.f; + float x6c4_ = 0.f; + float x6c8_ = 0.f; + float x6cc_ = 4.f; + float x6d0_ = 0.9f * x6cc_ + x6cc_; + float x6d4_ = 0.f; + u32 x6d8_ = 0; + u32 x6dc_ = 0; + u32 x6e0_ = x6dc_; + u32 x6e4_ = 0; + u32 x6e8_ = 2; + u32 x6ec_ = 4; + u32 x6f0_ = 0; + u32 x6f4_ = x6e8_ - 1; + u32 x6f8_ = 2; + u32 x6fc_ = 0; + u32 x700_ = 1; + TUniqueId x704_ = kInvalidUniqueId; + TUniqueId x706_lockOnTargetCollider = kInvalidUniqueId; + CSfxHandle x708_; + s16 x70c_; + bool x70e_24_ : 1 = false; + bool x70e_25_ : 1 = true; + bool x70e_26_ : 1 = false; + bool x70e_27_ : 1 = false; + bool x70e_28_ : 1 = true; + bool x70e_29_ : 1 = false; + bool x70e_30_ : 1 = false; + bool x70e_31_ : 1 = false; + + void sub8027cb40(const zeus::CVector3f& vec); + void sub8027cce0(CStateManager& mgr); + zeus::CTransform GetTargetTransform(CStateManager& mgr); + void sub8027ce5c(float f1); + void sub8027cee0(CStateManager& mgr); + u32 sub8027cfd4(CStateManager& mgr, u32 w1); + void DoPhaseTransition(CStateManager& mgr); + u32 sub8027d428() { return 2; } + void ShakeCamera(CStateManager& mgr, float f1); + void sub8027d52c(CStateManager& mgr, const SShockWaveData& shockWaveData); + CRayCastResult sub8027d704(CStateManager& mgr); + void sub8027d790(CStateManager& mgr, bool active); + void sub8027d824(CStateManager& mgr); + bool sub8027e870(const zeus::CTransform& xf, CStateManager& mgr); + void sub8027ee88(CStateManager& mgr); + void CountListeningAi(CStateManager& mgr); + void UpdatePhase(float dt, CStateManager& mgr); + void UpdateHealth(CStateManager& mgr); + void SetLockOnTargetHealthAndDamageVulns(CStateManager& mgr); + void AddSphereCollisions(SSphereJointInfo* info, size_t count, std::vector& vecOut); + void SetupCollisionActorManager(CStateManager& mgr); + +public: + DEFINE_PATTERNED(MetroidPrimeEssence); + + CMetroidPrimeEssence(TUniqueId uid, std::string_view name, const CEntityInfo& info, const zeus::CTransform& xf, + CModelData&& mData, const CPatternedInfo& pInfo, const CActorParameters& actParms, + CAssetId particle1, const CDamageInfo& dInfo, float f1, CAssetId electric, u32 w1, + CAssetId particle2); + + void Think(float dt, CStateManager& mgr) override; + void AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId other, CStateManager& mgr) override; + void PreRender(CStateManager& mgr, const zeus::CFrustum& frustum) override; + void AddToRenderer(const zeus::CFrustum& frustum, CStateManager& mgr) override; + void Render(CStateManager& mgr) override; + zeus::CVector3f GetAimPosition(const CStateManager& mgr, float dt) const override; + void DoUserAnimEvent(CStateManager& mgr, const CInt32POINode& node, EUserEventType type, float dt) override; + void Death(CStateManager& mgr, const zeus::CVector3f& direction, EScriptObjectState state) override; + void Dead(CStateManager& mgr, EStateMsg msg, float dt) override; + void PathFind(CStateManager& mgr, EStateMsg msg, float dt) override; + void Halt(CStateManager& mgr, EStateMsg msg, float dt) override; + void Generate(CStateManager& mgr, EStateMsg msg, float dt) override; + void JumpBack(CStateManager& mgr, EStateMsg msg, float dt) override; + void Skid(CStateManager& mgr, EStateMsg msg, float dt) override; + void FadeIn(CStateManager& mgr, EStateMsg msg, float dt) override; + void FadeOut(CStateManager& mgr, EStateMsg msg, float dt) override; + void Taunt(CStateManager& mgr, EStateMsg msg, float dt) override; + void TelegraphAttack(CStateManager& mgr, EStateMsg msg, float dt) override; + void Dodge(CStateManager& mgr, EStateMsg msg, float dt) override; + void PathFindEx(CStateManager& mgr, EStateMsg msg, float dt) override; + bool HasPatrolPath(CStateManager& mgr, float dt) override; + bool ShouldAttack(CStateManager& mgr, float dt) override; + bool InPosition(CStateManager& mgr, float dt) override; + bool CoverFind(CStateManager& mgr, float dt) override; + bool ShouldTaunt(CStateManager& mgr, float dt) override; + bool ShouldCrouch(CStateManager& mgr, float dt) override; + bool ShouldMove(CStateManager& mgr, float dt) override; + CPathFindSearch* GetSearchPath() override; +}; +} // namespace MP1 +} // namespace urde \ No newline at end of file diff --git a/Runtime/World/CScriptPlayerActor.cpp b/Runtime/World/CScriptPlayerActor.cpp index b087277c5..f37ffa92e 100644 --- a/Runtime/World/CScriptPlayerActor.cpp +++ b/Runtime/World/CScriptPlayerActor.cpp @@ -428,8 +428,8 @@ void CScriptPlayerActor::Render(CStateManager& mgr) { const float radius = zeus::clamp(0.25f, (6.f - vecFromCam.magnitude()) / 6.f, 2.f); const float offsetX = std::sin(x34c_phazonOffsetAngle); const float offsetY = std::sin(x34c_phazonOffsetAngle) * 0.5f; - g_Renderer->DrawPhazonSuitIndirectEffect(zeus::CColor(0.1f, 1.f), x338_phazonIndirectTexture, zeus::skWhite, - radius, 0.05f, offsetX, offsetY); + //g_Renderer->DrawPhazonSuitIndirectEffect(zeus::CColor(0.1f, 1.f), x338_phazonIndirectTexture, zeus::skWhite, + // radius, 0.05f, offsetX, offsetY); } } diff --git a/Runtime/World/ScriptLoader.cpp b/Runtime/World/ScriptLoader.cpp index 5638b3266..0388b7334 100644 --- a/Runtime/World/ScriptLoader.cpp +++ b/Runtime/World/ScriptLoader.cpp @@ -31,6 +31,7 @@ #include "Runtime/MP1/World/CDrone.hpp" #include "Runtime/MP1/World/CMetroid.hpp" #include "Runtime/MP1/World/CMetroidBeta.hpp" +#include "Runtime/MP1/World/CMetroidPrimeEssence.hpp" #include "Runtime/MP1/World/CMetroidPrimeRelay.hpp" #include "Runtime/MP1/World/CNewIntroBoss.hpp" #include "Runtime/MP1/World/COmegaPirate.hpp" @@ -3693,10 +3694,32 @@ CEntity* ScriptLoader::LoadWorldLightFader(CStateManager& mgr, CInputStream& in, 0.f, zeus::skZero2f, false, active, 0.f, 0.f, f1, f2); } -CEntity* ScriptLoader::LoadMetroidPrimeStage2(CStateManager& mgr, CInputStream& in, int propCount, - const CEntityInfo& info) { - return nullptr; -} +CEntity* ScriptLoader::LoadMetroidPrimeEssence(CStateManager& mgr, CInputStream& in, int propCount, + const CEntityInfo& info) { + if (!EnsurePropertyCount(propCount, 11, "MetroidPrimeEssence")) { + return nullptr; + } + + SScaledActorHead aHead = LoadScaledActorHead(in, mgr); + auto [valid, pInfoPropCount] = CPatternedInfo::HasCorrectParameterCount(in); + if (!valid) { + return nullptr; + } + CPatternedInfo pInfo{in, pInfoPropCount}; + CActorParameters actParms = LoadActorParameters(in); + CAssetId particle1{in}; + CDamageInfo dInfo{in}; + CAssetId electric{in}; + u32 w3 = in.readUint32Big(); + CAssetId particle2{in}; + + const CAnimationParameters& animParms = pInfo.GetAnimationParameters(); + CModelData mData{CAnimRes(animParms.GetACSFile(), animParms.GetCharacter(), aHead.x40_scale, + animParms.GetInitialAnimation(), true)}; + return new MP1::CMetroidPrimeEssence(mgr.AllocateUniqueId(), aHead.x0_name, info, aHead.x10_transform, + std::move(mData), pInfo, actParms, particle1, dInfo, aHead.x40_scale.y(), + electric, w3, particle2); +}; CEntity* ScriptLoader::LoadMetroidPrimeStage1(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info) { diff --git a/Runtime/World/ScriptLoader.hpp b/Runtime/World/ScriptLoader.hpp index 86d6cea48..528cb8c70 100644 --- a/Runtime/World/ScriptLoader.hpp +++ b/Runtime/World/ScriptLoader.hpp @@ -154,7 +154,7 @@ public: static CEntity* LoadBurrower(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info); static CEntity* LoadBeam(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info); static CEntity* LoadWorldLightFader(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info); - static CEntity* LoadMetroidPrimeStage2(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info); + static CEntity* LoadMetroidPrimeEssence(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info); static CEntity* LoadMetroidPrimeStage1(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info); static CEntity* LoadMazeNode(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info); static CEntity* LoadOmegaPirate(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info);