#include "Runtime/MP1/World/CElitePirate.hpp" #include #include #include "Runtime/Camera/CFirstPersonCamera.hpp" #include "Runtime/Collision/CCollisionActor.hpp" #include "Runtime/Collision/CCollisionActorManager.hpp" #include "Runtime/Factory/CSimplePool.hpp" #include "Runtime/CStateManager.hpp" #include "Runtime/GameGlobalObjects.hpp" #include "Runtime/MP1/World/CGrenadeLauncher.hpp" #include "Runtime/Weapon/CGameProjectile.hpp" #include "Runtime/World/CExplosion.hpp" #include "Runtime/World/CPatternedInfo.hpp" #include "Runtime/World/CPlayer.hpp" #include "Runtime/World/CWorld.hpp" #include "Runtime/World/ScriptLoader.hpp" #include "TCastTo.hpp" // Generated file, do not modify include path namespace metaforce::MP1 { namespace { constexpr std::array skLeftArmJointList{{ {"L_shoulder", "L_elbow", 1.f, 1.5f}, {"L_wrist", "L_elbow", 0.9f, 1.3f}, {"L_knee", "L_ankle", 0.9f, 1.3f}, }}; constexpr std::array skRightArmJointList{{ {"R_shoulder", "R_elbow", 1.f, 1.5f}, {"R_wrist", "R_elbow", 0.9f, 1.3f}, {"R_knee", "R_ankle", 0.9f, 1.3f}, }}; constexpr std::array skSphereJointList{{ {"Head_1", 1.2f}, {"L_Palm_LCTR", 1.5f}, {"R_Palm_LCTR", 1.5f}, {"Spine_1", 1.5f}, {"Collar", 1.2f}, {"L_Ball", 0.8f}, {"R_Ball", 0.8f}, }}; // The following used to be member functions, but are made internal as // they alter no internal state. // Used to be a member function with a pointer and size in GM8Ev0 bool IsArmClawCollider(std::string_view name, std::string_view locator, const std::array& info) { if (name == locator) { return true; } return std::any_of(info.cbegin(), info.cend(), [&name](const auto& entry) { return entry.from == name; }); } bool IsArmClawCollider(TUniqueId uid, const rstl::reserved_vector& vec) { return std::find(vec.cbegin(), vec.cend(), uid) != vec.cend(); } } // Anonymous namespace CElitePirateData::CElitePirateData(CInputStream& in, u32 propCount) : x0_tauntInterval(in.ReadFloat()) , x4_tauntVariance(in.ReadFloat()) , x8_(in.ReadFloat()) , xc_(in.ReadFloat()) , x10_attackChance(in.ReadFloat()) , x14_shotAtTime(in.ReadFloat()) , x18_shotAtTimeVariance(in.ReadFloat()) , x1c_projectileAttractionRadius(in.ReadFloat()) , x20_energyAbsorbParticleDescId(in) , x24_energyAbsorbSfxId(CSfxManager::TranslateSFXID(in.ReadLong())) , x28_launcherActParams(ScriptLoader::LoadActorParameters(in)) , x90_launcherAnimParams(ScriptLoader::LoadAnimationParameters(in)) , x9c_launcherParticleGenDescId(in) , xa0_launcherSfxId(CSfxManager::TranslateSFXID(in.ReadLong())) , xa4_grenadeModelId(in) , xa8_grenadeDamageInfo(in) , xc4_launcherHp(in.ReadFloat()) , xc8_grenadeElementGenDescId1(in) , xcc_grenadeElementGenDescId2(in) , xd0_grenadeElementGenDescId3(in) , xd4_grenadeElementGenDescId4(in) , xd8_grenadeVelocityInfo(in) , xe0_grenadeTrajectoryInfo(in) , xf0_grenadeNumBounces(in.ReadLong()) , xf4_grenadeBounceSfxId(CSfxManager::TranslateSFXID(in.ReadLong())) , xf6_grenadeExplodeSfxId(CSfxManager::TranslateSFXID(in.ReadLong())) , xf8_shockwaveParticleDescId(in) , xfc_shockwaveDamageInfo(in) , x118_shockwaveWeaponDescId(in) , x11c_shockwaveElectrocuteSfxId(CSfxManager::TranslateSFXID(in.ReadLong())) , x11e_canCallForBackup(in.ReadBool()) , x11f_fastWhenAttractingEnergy(propCount < 42 ? true : in.ReadBool()) {} CElitePirate::CElitePirate(TUniqueId uid, std::string_view name, const CEntityInfo& info, const zeus::CTransform& xf, CModelData&& mData, const CPatternedInfo& pInfo, const CActorParameters& actParms, CElitePirateData data) : CPatterned(ECharacter::ElitePirate, uid, name, EFlavorType::Zero, info, xf, std::move(mData), pInfo, EMovementType::Ground, EColliderType::One, EBodyType::BiPedal, actParms, EKnockBackVariant::Large) , x56c_vulnerability(pInfo.GetDamageVulnerability()) , x5d8_data(std::move(data)) , x6f8_boneTracking(*GetModelData()->GetAnimationData(), "Head_1", zeus::degToRad(80.f), zeus::degToRad(180.f), EBoneTrackingFlags::None) , x738_collisionAabb(GetBoundingBox(), GetMaterialList()) , x7a0_initialSpeed(x3b4_speed) , x7d0_pathFindSearch(nullptr, 1, pInfo.GetPathfindingIndex(), 1.f, 1.f) { if (x5d8_data.GetEnergyAbsorbParticleDescId().IsValid()) { x760_energyAbsorbDesc = g_SimplePool->GetObj({SBIG('PART'), x5d8_data.GetEnergyAbsorbParticleDescId()}); } x460_knockBackController.SetEnableFreeze(false); x460_knockBackController.SetAutoResetImpulse(false); x460_knockBackController.SetEnableBurn(false); x460_knockBackController.SetEnableExplodeDeath(false); x460_knockBackController.SetEnableLaggedBurnDeath(false); SetupPathFindSearch(); } void CElitePirate::Accept(IVisitor& visitor) { visitor.Visit(this); } void CElitePirate::Think(float dt, CStateManager& mgr) { if (GetActive()) { CPatterned::Think(dt, mgr); x6f8_boneTracking.Update(dt); if (HasWeakPointHead()) { x730_collisionActorMgrHead->Update(dt, mgr, CCollisionActorManager::EUpdateOptions::ObjectSpace); } x5d4_collisionActorMgr->Update(dt, mgr, CCollisionActorManager::EUpdateOptions::ObjectSpace); if (IsAttractingEnergy() && x5d8_data.IsFastWhenAttractingEnergy()) { x3b4_speed = 2.f * x7a0_initialSpeed; } else { x3b4_speed = x7a0_initialSpeed; } UpdateTimers(dt); UpdatePositionHistory(); UpdateActorTransform(mgr, x772_launcherId, "grenadeLauncher_LCTR"sv); UpdateHealthInfo(mgr); x328_31_energyAttractor = IsAttractingEnergy(); } } void CElitePirate::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateManager& mgr) { bool shouldPass = true; switch (msg) { case EScriptObjectMessage::Activate: { if (HasWeakPointHead()) { x730_collisionActorMgrHead->SetActive(mgr, true); } if (CEntity* ent = mgr.ObjectById(x772_launcherId)) { ent->SetActive(true); } break; } case EScriptObjectMessage::Deactivate: { if (HasWeakPointHead()) { x730_collisionActorMgrHead->SetActive(mgr, false); } x5d4_collisionActorMgr->SetActive(mgr, false); if (CEntity* ent = mgr.ObjectById(x772_launcherId)) { ent->SetActive(false); } break; } case EScriptObjectMessage::Alert: x988_28_alert = true; break; case EScriptObjectMessage::Touched: { if (HealthInfo(mgr)->GetHP() <= 0.f) { break; } const TCastToConstPtr actor = mgr.ObjectById(uid); if (!actor) { if (uid == x772_launcherId && x772_launcherId != kInvalidUniqueId) { SetShotAt(true, mgr); } break; } const TUniqueId touchedUid = actor->GetLastTouchedObject(); if (touchedUid != mgr.GetPlayer().GetUniqueId()) { if (TCastToConstPtr(mgr.ObjectById(touchedUid))) { SetShotAt(true, mgr); } break; } if (!x988_24_damageOn) { if (x420_curDamageRemTime <= 0.f) { CDamageInfo info = GetContactDamage(); info.SetDamage(0.5f * info.GetDamage()); mgr.ApplyDamage(GetUniqueId(), mgr.GetPlayer().GetUniqueId(), GetUniqueId(), info, CMaterialFilter::MakeInclude({EMaterialTypes::Solid}), zeus::skZero3f); x420_curDamageRemTime = x424_damageWaitTime; } break; } if ((!x988_25_attackingRightClaw || !IsArmClawCollider(uid, x774_collisionRJointIds)) && (!x988_26_attackingLeftClaw || !IsArmClawCollider(uid, x788_collisionLJointIds))) { break; } mgr.ApplyDamage(GetUniqueId(), mgr.GetPlayer().GetUniqueId(), GetUniqueId(), GetContactDamage(), CMaterialFilter::MakeInclude({EMaterialTypes::Solid}), zeus::skZero3f); x420_curDamageRemTime = x424_damageWaitTime; x988_24_damageOn = false; break; } case EScriptObjectMessage::Registered: { x450_bodyController->Activate(mgr); SetupCollisionManager(mgr); x772_launcherId = mgr.AllocateUniqueId(); CreateGrenadeLauncher(mgr, x772_launcherId); const auto& bodyStateInfo = x450_bodyController->GetBodyStateInfo(); if (bodyStateInfo.GetMaxSpeed() > 0.f) { x7a4_steeringSpeed = (0.99f * bodyStateInfo.GetLocomotionSpeed(pas::ELocomotionAnim::Walk)) / bodyStateInfo.GetMaxSpeed(); } x450_bodyController->GetCommandMgr().SetSteeringBlendMode(ESteeringBlendMode::FullSpeed); x450_bodyController->GetCommandMgr().SetSteeringSpeedRange(x7a4_steeringSpeed, x7a4_steeringSpeed); break; } case EScriptObjectMessage::Deleted: if (HasWeakPointHead()) { x730_collisionActorMgrHead->Destroy(mgr); } x5d4_collisionActorMgr->Destroy(mgr); mgr.FreeScriptObject(x772_launcherId); break; case EScriptObjectMessage::InitializedInArea: x7d0_pathFindSearch.SetArea(mgr.GetWorld()->GetAreaAlways(GetAreaIdAlways())->GetPostConstructed()->x10bc_pathArea); break; case EScriptObjectMessage::Damage: shouldPass = false; if (const TCastToConstPtr actor = mgr.ObjectById(uid)) { if (const TCastToConstPtr projectile = mgr.ObjectById(actor->GetLastTouchedObject())) { if (uid == x770_collisionHeadId) { x428_damageCooldownTimer = 0.33f; const auto& damageInfo = projectile->GetDamageInfo(); KnockBack(projectile->GetTranslation() - projectile->GetPreviousPos(), mgr, damageInfo, EKnockBackType::Direct, false, damageInfo.GetKnockBackPower()); CPatterned::AcceptScriptMsg(msg, uid, mgr); } else if (uid == x79c_energyAttractorId && x760_energyAbsorbDesc->IsLoaded()) { CreateEnergyAbsorb(mgr, projectile->GetTransform()); } SetShotAt(true, mgr); } } else if (uid == x772_launcherId && x772_launcherId != kInvalidUniqueId) { x450_bodyController->GetCommandMgr().DeliverCmd( CBCKnockBackCmd(GetTransform().frontVector(), pas::ESeverity::Eight)); } else { ApplyDamageToHead(mgr, uid); } break; case EScriptObjectMessage::InvulnDamage: { SetShotAt(true, mgr); if (!TCastToConstPtr(mgr.ObjectById(uid))) { ApplyDamageToHead(mgr, uid); } break; } default: break; } if (shouldPass) { CPatterned::AcceptScriptMsg(msg, uid, mgr); } } void CElitePirate::PreRender(CStateManager& mgr, const zeus::CFrustum& frustum) { CPatterned::PreRender(mgr, frustum); auto* modelData = GetModelData(); x6f8_boneTracking.PreRender(mgr, *modelData->GetAnimationData(), GetTransform(), modelData->GetScale(), *x450_bodyController); const auto numMaterialSets = modelData->GetNumMaterialSets(); xb4_drawFlags.x1_matSetIdx = numMaterialSets - 1 < x7cc_activeMaterialSet ? numMaterialSets - 1 : x7cc_activeMaterialSet; } const CDamageVulnerability* CElitePirate::GetDamageVulnerability() const { return &CDamageVulnerability::PassThroughVulnerabilty(); } const CDamageVulnerability* CElitePirate::GetDamageVulnerability(const zeus::CVector3f& pos, const zeus::CVector3f& dir, const CDamageInfo& dInfo) const { return &CDamageVulnerability::PassThroughVulnerabilty(); } zeus::CVector3f CElitePirate::GetOrbitPosition(const CStateManager& mgr) const { if (x772_launcherId != kInvalidUniqueId && mgr.GetPlayerState()->GetCurrentVisor() == CPlayerState::EPlayerVisor::Thermal) { if (const auto* actor = static_cast(mgr.GetObjectById(x772_launcherId))) { return GetLockOnPosition(actor); } } if (HasWeakPointHead()) { if (const TCastToConstPtr actor = mgr.GetObjectById(x770_collisionHeadId)) { return actor->GetTranslation(); } } return GetLctrTransform("lockon_target_LCTR").origin; } zeus::CVector3f CElitePirate::GetAimPosition(const CStateManager& mgr, float) const { const std::shared_ptr& playerState = mgr.GetPlayerState(); if (x5d4_collisionActorMgr->GetActive() && playerState->IsFiringComboBeam() && playerState->GetCurrentBeam() == CPlayerState::EBeamId::Wave) { if (const TCastToConstPtr actor = mgr.GetObjectById(x79c_energyAttractorId)) { return actor->GetTranslation(); } } return GetOrbitPosition(mgr); } void CElitePirate::DoUserAnimEvent(CStateManager& mgr, const CInt32POINode& node, EUserEventType type, float dt) { switch (type) { case EUserEventType::Projectile: if (x772_launcherId != kInvalidUniqueId) { CEntity* launcher = mgr.ObjectById(x772_launcherId); mgr.SendScriptMsg(launcher, GetUniqueId(), EScriptObjectMessage::Action); } return; case EUserEventType::DamageOn: x988_24_damageOn = true; return; case EUserEventType::DamageOff: x988_24_damageOn = false; return; case EUserEventType::ScreenShake: ShakeCamera(mgr); return; case EUserEventType::BeginAction: { const zeus::CVector3f origin = GetTranslation(); const zeus::CVector3f front = GetTransform().frontVector(); const float dot = (GetLctrTransform(node.GetLocatorName()).origin - origin).dot(front); const zeus::CTransform xf = zeus::CTransform::Translate({ origin.x() + dot * front.x(), origin.y() + dot * front.y(), origin.z(), }); mgr.AddObject(new CShockWave(mgr.AllocateUniqueId(), "Shock Wave", {GetAreaIdAlways(), CEntity::NullConnectionList}, xf, GetUniqueId(), GetShockWaveData(), IsElitePirate() ? 2.f : 1.3f, IsElitePirate() ? 0.4f : 0.5f)); return; } case EUserEventType::BecomeShootThrough: if (HasWeakPointHead()) { const u32 numCollisionActors = x730_collisionActorMgrHead->GetNumCollisionActors(); for (u32 i = 0; i < numCollisionActors; ++i) { const auto& description = x730_collisionActorMgrHead->GetCollisionDescFromIndex(i); if (TCastToPtr actor = mgr.ObjectById(description.GetCollisionActorId())) { actor->AddMaterial(EMaterialTypes::ProjectilePassthrough, mgr); } } } return; default: break; } CPatterned::DoUserAnimEvent(mgr, node, type, dt); } const CCollisionPrimitive* CElitePirate::GetCollisionPrimitive() const { return &x738_collisionAabb; } void CElitePirate::KnockBack(const zeus::CVector3f& pos, CStateManager& mgr, const CDamageInfo& info, EKnockBackType type, bool inDeferred, float magnitude) { if (!CanKnockBack(info)) { return; } CPatterned::KnockBack(pos, mgr, info, type, inDeferred, magnitude); if (info.GetWeaponMode().IsComboed() && info.GetWeaponMode().GetType() == EWeaponType::Ice) { Freeze(mgr, zeus::skZero3f, GetTransform().transposeRotate(pos), 1.5f); } } void CElitePirate::TakeDamage(const zeus::CVector3f& pos, float) {} void CElitePirate::Patrol(CStateManager& mgr, EStateMsg msg, float dt) { if (msg == EStateMsg::Activate) { x450_bodyController->SetLocomotionType(pas::ELocomotionType::Relaxed); x400_24_hitByPlayerProjectile = false; x989_24_onPath = false; } CPatterned::Patrol(mgr, msg, dt); } void CElitePirate::PathFind(CStateManager& mgr, EStateMsg msg, float dt) { if (msg == EStateMsg::Activate) { x989_24_onPath = true; x988_28_alert = false; x450_bodyController->SetLocomotionType(pas::ELocomotionType::Relaxed); x6f8_boneTracking.SetTarget(mgr.GetPlayer().GetUniqueId()); x6f8_boneTracking.SetActive(true); UpdateDestPos(mgr); CPatterned::PathFind(mgr, msg, dt); x7bc_tauntTimer = x5d8_data.GetTauntVariance() * mgr.GetActiveRandom()->Float() + x5d8_data.GetTauntInterval(); if (TooClose(mgr, 0.f)) { x450_bodyController->GetCommandMgr().ClearLocomotionCmds(); } } else if (msg == EStateMsg::Update) { if (x7bc_tauntTimer > 0.f) { x7bc_tauntTimer -= dt; } if (!TooClose(mgr, 0.f) && !PathShagged(mgr, 0.f)) { CPatterned::PathFind(mgr, msg, dt); } else if (PathShagged(mgr, 0.f)) { const auto move = x8c0_positionHistory.GetValue(GetTranslation(), GetTransform().frontVector()); if (move != zeus::skZero3f) { x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd(move, zeus::skZero3f, 1.f)); } } else if (ShouldTurn(mgr, 0.f)) { const zeus::CVector3f aim = mgr.GetPlayer().GetAimPosition(mgr, 0.5f * GetModelData()->GetAnimationData()->GetSpeedScale()); const zeus::CVector3f face = aim - GetTranslation(); if (face.canBeNormalized()) { x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd(zeus::skZero3f, face.normalized(), 1.f)); } } } else if (msg == EStateMsg::Deactivate) { x6f8_boneTracking.SetActive(false); } } void CElitePirate::TargetPatrol(CStateManager& mgr, EStateMsg msg, float dt) { if (msg == EStateMsg::Activate) { x450_bodyController->SetLocomotionType(pas::ELocomotionType::Relaxed); if (HasPatrolPath(mgr, 0.f)) { CPatterned::Patrol(mgr, msg, dt); UpdateDest(mgr); } else { SetDestPos(x3a0_latestLeashPosition); } x8b4_targetDestPos = x2e0_destPos; if (GetSearchPath() != nullptr) { CPatterned::PathFind(mgr, msg, dt); } } else if (msg == EStateMsg::Update) { if (PathShagged(mgr, 0.f)) { const zeus::CVector3f move = x45c_steeringBehaviors.Arrival(*this, x8b4_targetDestPos, 25.f); x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd(move, zeus::skZero3f, 1.f)); } else { CPatterned::PathFind(mgr, msg, dt); } } else if (msg == EStateMsg::Deactivate) { x988_28_alert = false; } } void CElitePirate::Halt(CStateManager& mgr, EStateMsg msg, float) { if (msg == EStateMsg::Activate) { x450_bodyController->SetLocomotionType(pas::ELocomotionType::Lurk); x989_24_onPath = false; CMaterialFilter filter = GetMaterialFilter(); filter.ExcludeList().Add( {EMaterialTypes::Wall, EMaterialTypes::Ceiling, EMaterialTypes::AIBlock, EMaterialTypes::Character}); SetMaterialFilter(filter); } else if (msg == EStateMsg::Deactivate) { CMaterialFilter filter = GetMaterialFilter(); filter.ExcludeList().Remove( {EMaterialTypes::Wall, EMaterialTypes::Ceiling, EMaterialTypes::AIBlock, EMaterialTypes::Character}); SetMaterialFilter(filter); } } void CElitePirate::Run(CStateManager& mgr, EStateMsg msg, float dt) { if (msg == EStateMsg::Activate) { x988_31_running = true; x450_bodyController->SetLocomotionType(pas::ELocomotionType::Relaxed); x450_bodyController->GetCommandMgr().SetSteeringSpeedRange(1.f, 1.f); x6f8_boneTracking.SetTarget(mgr.GetPlayer().GetUniqueId()); x6f8_boneTracking.SetActive(true); UpdateDestPos(mgr); CPatterned::PathFind(mgr, msg, dt); } else if (msg == EStateMsg::Update) { if (PathShagged(mgr, 0.f)) { const auto move = x8c0_positionHistory.GetValue(GetTranslation(), GetTransform().frontVector()); if (move != zeus::skZero3f) { x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd(move, zeus::skZero3f, 1.f)); } else if (ShouldTurn(mgr, 0.f)) { const zeus::CVector3f aim = mgr.GetPlayer().GetAimPosition(mgr, 0.5f * GetModelData()->GetAnimationData()->GetSpeedScale()); const auto face = aim - GetTranslation(); if (face.canBeNormalized()) { x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd(zeus::skZero3f, face.normalized(), 1.f)); } } } else { CPatterned::PathFind(mgr, msg, dt); } } else if (msg == EStateMsg::Deactivate) { x988_31_running = false; x6f8_boneTracking.SetActive(false); x450_bodyController->GetCommandMgr().SetSteeringSpeedRange(x7a4_steeringSpeed, x7a4_steeringSpeed); } } void CElitePirate::Generate(CStateManager& mgr, EStateMsg msg, float) { if (msg == EStateMsg::Activate) { x568_state = EState::One; } else if (msg == EStateMsg::Update) { if (x568_state == EState::Zero) { if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::Generate) { x568_state = EState::Two; } else { x450_bodyController->GetCommandMgr().DeliverCmd(CBCGenerateCmd(pas::EGenerateType::Zero)); } } else if (x568_state == EState::One) { if (ShouldTurn(mgr, 0.f)) { const auto face = mgr.GetPlayer().GetTranslation() - GetTranslation(); if (face.canBeNormalized()) { x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd(zeus::skZero3f, face.normalized(), 1.f)); } } else { x568_state = EState::Zero; } } else if (x568_state == EState::Two && x450_bodyController->GetCurrentStateId() != pas::EAnimationState::Generate) { x568_state = EState::Over; } } else if (msg == EStateMsg::Deactivate) { SetShotAt(false, mgr); SetLaunchersActive(mgr, true); } } void CElitePirate::Attack(CStateManager& mgr, EStateMsg msg, float) { if (msg == EStateMsg::Activate) { x568_state = EState::Zero; ExtendTouchBounds(mgr, x774_collisionRJointIds, zeus::CVector3f(2.f)); if (x64_modelData->GetNumMaterialSets() > 1) { x7cc_activeMaterialSet = 1; } } else if (msg == EStateMsg::Update) { if (x568_state == EState::Zero) { if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::MeleeAttack) { x568_state = EState::One; x988_25_attackingRightClaw = true; x7c8_currAnimId = x450_bodyController->GetCurrentAnimId(); } else { x450_bodyController->GetCommandMgr().DeliverCmd(CBCMeleeAttackCmd(pas::ESeverity::One)); } } else if (x568_state == EState::One) { if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::MeleeAttack) { if (x7c8_currAnimId == x450_bodyController->GetCurrentAnimId()) { x450_bodyController->GetCommandMgr().DeliverTargetVector(mgr.GetPlayer().GetTranslation() - GetTranslation()); if (ShouldAttack(mgr, 0.f)) { x450_bodyController->GetCommandMgr().DeliverCmd(CBCMeleeAttackCmd(pas::ESeverity::Two)); } } else { x568_state = EState::Two; x988_25_attackingRightClaw = false; x988_26_attackingLeftClaw = true; ExtendTouchBounds(mgr, x774_collisionRJointIds, zeus::skZero3f); ExtendTouchBounds(mgr, x788_collisionLJointIds, zeus::CVector3f(2.f)); } } else { x568_state = EState::Over; } } else if (x568_state == EState::Two) { if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::MeleeAttack) { x450_bodyController->GetCommandMgr().DeliverTargetVector(mgr.GetPlayer().GetTranslation() - GetTranslation()); } else { x568_state = EState::Over; } } } else if (msg == EStateMsg::Deactivate) { CheckAttackChance(mgr); x988_24_damageOn = false; x988_26_attackingLeftClaw = false; x988_25_attackingRightClaw = false; x7c8_currAnimId = -1; ExtendTouchBounds(mgr, x774_collisionRJointIds, zeus::skZero3f); ExtendTouchBounds(mgr, x788_collisionLJointIds, zeus::skZero3f); x7cc_activeMaterialSet = 0; } } void CElitePirate::Taunt(CStateManager& mgr, EStateMsg msg, float dt) { CAi::Taunt(mgr, msg, dt); } void CElitePirate::ProjectileAttack(CStateManager& mgr, EStateMsg msg, float) { if (msg == EStateMsg::Activate) { x568_state = EState::Zero; } else if (msg == EStateMsg::Update) { const zeus::CVector3f& playerPos = mgr.GetPlayer().GetTranslation(); if (x568_state == EState::Zero) { if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::ProjectileAttack) { x568_state = EState::Two; } else { x450_bodyController->GetCommandMgr().DeliverCmd(CBCProjectileAttackCmd(pas::ESeverity::One, playerPos, false)); } } else if (x568_state == EState::Two) { if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::ProjectileAttack) { x450_bodyController->GetCommandMgr().DeliverTargetVector(playerPos - GetTranslation()); } else { x568_state = EState::Over; } } } else if (msg == EStateMsg::Deactivate) { CheckAttackChance(mgr); } } void CElitePirate::SpecialAttack(CStateManager& mgr, EStateMsg msg, float) { if (msg == EStateMsg::Activate) { x568_state = EState::Zero; } else if (msg == EStateMsg::Update) { if (x568_state == EState::Zero) { if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::ProjectileAttack) { x568_state = EState::Two; x988_29_shockWaveAnim = true; } else { x450_bodyController->GetCommandMgr().DeliverCmd( CBCProjectileAttackCmd(pas::ESeverity::Two, mgr.GetPlayer().GetTranslation(), false)); } } else if (x568_state == EState::Two) { if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::ProjectileAttack) { x450_bodyController->GetCommandMgr().DeliverTargetVector(mgr.GetPlayer().GetTranslation() - GetTranslation()); } else { x568_state = EState::Over; } } } else if (msg == EStateMsg::Deactivate) { CheckAttackChance(mgr); x988_29_shockWaveAnim = false; } } void CElitePirate::CallForBackup(CStateManager& mgr, EStateMsg msg, float) { if (msg == EStateMsg::Activate) { x568_state = EState::Zero; x988_30_calledForBackup = true; SetShotAt(false, mgr); } else if (msg == EStateMsg::Update) { if (x568_state == EState::Zero) { if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::Generate) { x568_state = EState::Two; } else { x450_bodyController->GetCommandMgr().DeliverCmd(CBCGenerateCmd(pas::EGenerateType::Five, zeus::skZero3f)); } } else if (x568_state == EState::Two) { if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::Generate) { x568_state = EState::Over; } } } else if (msg == EStateMsg::Deactivate) { SendScriptMsgs(EScriptObjectState::Zero, mgr, EScriptObjectMessage::None); } } void CElitePirate::Cover(CStateManager& mgr, EStateMsg msg, float dt) { if (msg == EStateMsg::Activate) { x450_bodyController->SetLocomotionType(pas::ELocomotionType::Crouch); if (HasWeakPointHead()) { if (TCastToPtr actor = mgr.ObjectById(x770_collisionHeadId)) { actor->SetDamageVulnerability(CDamageVulnerability::ImmuneVulnerabilty()); } } x5d4_collisionActorMgr->SetActive(mgr, true); x6f8_boneTracking.SetTarget(mgr.GetPlayer().GetUniqueId()); x6f8_boneTracking.SetActive(true); UpdateDestPos(mgr); CPatterned::PathFind(mgr, msg, dt); if (TooClose(mgr, 0.f)) { x450_bodyController->GetCommandMgr().ClearLocomotionCmds(); } } else if (msg == EStateMsg::Update) { if (x988_27_shotAt) { x7c0_shotAtTimer -= dt; if (x7c0_shotAtTimer <= 0.f) { x988_27_shotAt = false; } } x7a8_pathShaggedTime = PathShagged(mgr, 0.f) ? x7a8_pathShaggedTime + dt : 0.f; if (!TooClose(mgr, 0.f) && !PathShagged(mgr, 0.f)) { CPatterned::PathFind(mgr, msg, dt); } else if (PathShagged(mgr, 0.f)) { const auto move = x8c0_positionHistory.GetValue(GetTranslation(), GetTransform().frontVector()); if (move != zeus::skZero3f) { x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd(move, zeus::skZero3f, 1.f)); } } else if (ShouldTurn(mgr, 0.f)) { const zeus::CVector3f aim = mgr.GetPlayer().GetAimPosition(mgr, 0.5f * GetModelData()->GetAnimationData()->GetSpeedScale()); const zeus::CVector3f face = aim - GetTranslation(); if (face.canBeNormalized()) { x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd(zeus::skZero3f, face.normalized(), 1.f)); } } AttractProjectiles(mgr); UpdateAbsorbBodyState(mgr, dt); } else if (msg == EStateMsg::Deactivate) { x450_bodyController->SetLocomotionType(pas::ELocomotionType::Relaxed); x6f8_boneTracking.SetActive(false); if (HasWeakPointHead()) { if (TCastToPtr actor = mgr.ObjectById(x770_collisionHeadId)) { actor->SetDamageVulnerability(x56c_vulnerability); } } x5d4_collisionActorMgr->SetActive(mgr, false); } } bool CElitePirate::TooClose(CStateManager& mgr, float) { return x2fc_minAttackRange * x2fc_minAttackRange > (GetTranslation() - mgr.GetPlayer().GetTranslation()).magSquared(); } bool CElitePirate::InDetectionRange(CStateManager& mgr, float arg) { return x988_28_alert ? true : CPatterned::InDetectionRange(mgr, arg); } bool CElitePirate::SpotPlayer(CStateManager& mgr, float arg) { return x988_28_alert ? true : CPatterned::SpotPlayer(mgr, arg); } bool CElitePirate::AnimOver(CStateManager& mgr, float) { return x568_state == EState::Over; } bool CElitePirate::ShouldAttack(CStateManager& mgr, float) { if ((mgr.GetPlayer().GetTranslation() - GetTranslation()).magSquared() > x2fc_minAttackRange * x2fc_minAttackRange) { return false; } return !ShouldTurn(mgr, 0.f); } bool CElitePirate::InPosition(CStateManager& mgr, float) { return (x8b4_targetDestPos - GetTranslation()).magSquared() < 25.f; } bool CElitePirate::ShouldTurn(CStateManager& mgr, float) { return zeus::CVector2f::getAngleDiff((mgr.GetPlayer().GetTranslation() - GetTranslation()).toVec2f(), GetTransform().frontVector().toVec2f()) > zeus::degToRad(15.f); } bool CElitePirate::AggressionCheck(CStateManager& mgr, float arg) { if (x772_launcherId == kInvalidUniqueId && !PathShagged(mgr, arg)) { if (x988_31_running) { return true; } return 4.f * x300_maxAttackRange * x300_maxAttackRange < (mgr.GetPlayer().GetTranslation() - GetTranslation()).magSquared(); } return false; } bool CElitePirate::ShouldTaunt(CStateManager& mgr, float) { return x7bc_tauntTimer <= 0.f; } bool CElitePirate::ShouldFire(CStateManager& mgr, float) { return ShouldFireFromLauncher(mgr, x772_launcherId); } bool CElitePirate::ShotAt(CStateManager& mgr, float) { return x988_27_shotAt; } bool CElitePirate::ShouldSpecialAttack(CStateManager& mgr, float) { if (x7b8_attackTimer <= 0.f && GetAreaIdAlways() == mgr.GetPlayer().GetAreaIdAlways()) { const zeus::CVector3f dist = mgr.GetPlayer().GetAimPosition(mgr, 0.f) - GetTranslation(); const float magSquared = dist.magSquared(); if (x2fc_minAttackRange * x2fc_minAttackRange <= magSquared && magSquared <= x300_maxAttackRange * x300_maxAttackRange) { return std::abs(dist.z()) < 3.f; } } return false; } bool CElitePirate::ShouldCallForBackup(CStateManager& mgr, float) { return ShouldCallForBackupFromLauncher(mgr, x772_launcherId); } CPathFindSearch* CElitePirate::GetSearchPath() { return &x7d0_pathFindSearch; } void CElitePirate::SetupHealthInfo(CStateManager& mgr) { const CHealthInfo* const health = HealthInfo(mgr); x7b4_hp = health->GetHP(); if (HasWeakPointHead()) { if (TCastToPtr actor = mgr.ObjectById(x770_collisionHeadId)) { auto* actHealth = actor->HealthInfo(mgr); actHealth->SetHP(health->GetHP()); actHealth->SetKnockbackResistance(health->GetKnockbackResistance()); actor->SetDamageVulnerability(x56c_vulnerability); } } SetupLauncherHealthInfo(mgr, x772_launcherId); } void CElitePirate::SetLaunchersActive(CStateManager& mgr, bool val) { SetLauncherActive(mgr, val, x772_launcherId); } void CElitePirate::SetupPathFindSearch() { const float scale = 1.5f * GetModelData()->GetScale().y(); const float fVar1 = IsElitePirate() ? 5.f : 1.f; const zeus::CAABox box{{-scale, -scale, 0.f}, {scale, scale, fVar1 * scale}}; SetBoundingBox(box); x738_collisionAabb.SetBox(box); x7d0_pathFindSearch.SetCharacterRadius(scale); x7d0_pathFindSearch.SetCharacterHeight(3.f * scale); } void CElitePirate::SetShotAt(bool val, CStateManager& mgr) { if (!IsElitePirate() || x7b4_hp <= 0.f || !val) { x988_27_shotAt = val; } else if (HealthInfo(mgr)->GetHP() / x7b4_hp <= x7b0_) { x7b0_ -= 0.2f; x988_27_shotAt = true; } if (x988_27_shotAt) { x7c0_shotAtTimer = mgr.GetActiveRandom()->Float() * x5d8_data.GetShotAtTimeVariance() + x5d8_data.GetShotAtTime(); } else { x7c0_shotAtTimer = 0.f; } } void CElitePirate::AddCollisionList(const SJointInfo* joints, size_t count, std::vector& outJoints) const { const CAnimData* animData = GetModelData()->GetAnimationData(); for (size_t i = 0; i < count; ++i) { const auto& joint = joints[i]; const CSegId from = animData->GetLocatorSegId(joint.from); const CSegId to = animData->GetLocatorSegId(joint.to); if (to.IsInvalid() || from.IsInvalid()) { continue; } outJoints.emplace_back(CJointCollisionDescription::SphereSubdivideCollision( to, from, joint.radius, joint.separation, CJointCollisionDescription::EOrientationType::One, joint.from, 10.f)); } } void CElitePirate::AddSphereCollisionList(const SSphereJointInfo* joints, size_t count, std::vector& outJoints) const { const CAnimData* animData = GetModelData()->GetAnimationData(); for (size_t i = 0; i < count; ++i) { const auto& joint = joints[i]; const CSegId seg = animData->GetLocatorSegId(joint.name); if (seg.IsInvalid()) { continue; } outJoints.emplace_back(CJointCollisionDescription::SphereCollision(seg, joint.radius, joint.name, 10.f)); } } void CElitePirate::SetupCollisionManager(CStateManager& mgr) { constexpr size_t jointInfoCount = skLeftArmJointList.size() + skRightArmJointList.size() + skSphereJointList.size(); std::vector joints; joints.reserve(jointInfoCount); AddCollisionList(skLeftArmJointList.data(), skLeftArmJointList.size(), joints); AddCollisionList(skRightArmJointList.data(), skLeftArmJointList.size(), joints); AddSphereCollisionList(skSphereJointList.data(), skSphereJointList.size(), joints); if (HasWeakPointHead()) { x730_collisionActorMgrHead = std::make_unique(mgr, GetUniqueId(), GetAreaIdAlways(), joints, true); x730_collisionActorMgrHead->SetActive(mgr, GetActive()); } x774_collisionRJointIds.clear(); x788_collisionLJointIds.clear(); const CAnimData* animData = GetModelData()->GetAnimationData(); constexpr zeus::CVector3f bounds{4.f, 4.f, 2.f}; joints.emplace_back(CJointCollisionDescription::OBBCollision(animData->GetLocatorSegId("L_Palm_LCTR"sv), bounds, zeus::skZero3f, "Shield"sv, 10.f)); x5d4_collisionActorMgr = std::make_unique(mgr, GetUniqueId(), GetAreaIdAlways(), joints, false); SetupCollisionActorInfo(mgr); SetupHealthInfo(mgr); SetMaterialFilter(CMaterialFilter::MakeIncludeExclude( {EMaterialTypes::Solid}, {EMaterialTypes::CollisionActor, EMaterialTypes::AIPassthrough, EMaterialTypes::Player})); AddMaterial(EMaterialTypes::ProjectilePassthrough, mgr); } void CElitePirate::SetupCollisionActorInfo(CStateManager& mgr) { if (HasWeakPointHead()) { for (size_t i = 0; i < x730_collisionActorMgrHead->GetNumCollisionActors(); ++i) { const auto& colDesc = x730_collisionActorMgrHead->GetCollisionDescFromIndex(i); const TUniqueId uid = colDesc.GetCollisionActorId(); if (TCastToPtr act = mgr.ObjectById(uid)) { if (colDesc.GetName() == "Head_1"sv) { x770_collisionHeadId = uid; } else if (IsArmClawCollider(colDesc.GetName(), "R_Palm_LCTR"sv, skRightArmJointList)) { x774_collisionRJointIds.push_back(uid); } else if (IsArmClawCollider(colDesc.GetName(), "L_Palm_LCTR"sv, skLeftArmJointList)) { x788_collisionLJointIds.push_back(uid); } if (uid != x770_collisionHeadId) { act->SetDamageVulnerability(CDamageVulnerability::ReflectVulnerabilty()); } } } x730_collisionActorMgrHead->AddMaterial( mgr, {EMaterialTypes::AIJoint, EMaterialTypes::CameraPassthrough, EMaterialTypes::Immovable}); } const CJointCollisionDescription& description = x5d4_collisionActorMgr->GetCollisionDescFromIndex(0); x79c_energyAttractorId = description.GetCollisionActorId(); if (TCastToPtr act = mgr.ObjectById(x79c_energyAttractorId)) { act->SetWeaponCollisionResponseType(EWeaponCollisionResponseTypes::None); } x5d4_collisionActorMgr->AddMaterial(mgr, {EMaterialTypes::AIJoint, EMaterialTypes::CameraPassthrough}); } void CElitePirate::CreateGrenadeLauncher(CStateManager& mgr, TUniqueId uid) { const CAnimationParameters& params = x5d8_data.GetLauncherAnimParams(); if (!params.GetACSFile().IsValid()) { return; } CModelData mData(CAnimRes(params.GetACSFile(), params.GetCharacter(), GetModelData()->GetScale(), params.GetInitialAnimation(), true)); const zeus::CAABox bounds = mData.GetBounds(GetTransform().getRotation()); mgr.AddObject( new CGrenadeLauncher(uid, "Grenade Launcher", {GetAreaIdAlways(), CEntity::NullConnectionList}, GetTransform(), std::move(mData), bounds, CHealthInfo(x5d8_data.GetLauncherHP(), 10.f), x56c_vulnerability, x5d8_data.GetLauncherActParams(), GetUniqueId(), x5d8_data.GetGrenadeLauncherData(), 0.f)); } void CElitePirate::ApplyDamageToHead(CStateManager& mgr, TUniqueId uid) { if (!HasWeakPointHead()) { return; } if (const TCastToConstPtr weapon = mgr.ObjectById(uid)) { CDamageInfo damageInfo = weapon->GetDamageInfo(); damageInfo.SetRadius(0.f); mgr.ApplyDamage(uid, x770_collisionHeadId, weapon->GetOwnerId(), damageInfo, CMaterialFilter::MakeInclude({EMaterialTypes::Solid}), zeus::skZero3f); } } void CElitePirate::CreateEnergyAbsorb(CStateManager& mgr, const zeus::CTransform& xf) { if (x7ac_energyAbsorbCooldown > 0.f) { return; } mgr.AddObject(new CExplosion(*x760_energyAbsorbDesc, mgr.AllocateUniqueId(), true, {GetAreaIdAlways(), CEntity::NullConnectionList}, "Absorb energy Fx", xf, 0, GetModelData()->GetScale(), zeus::skWhite)); CSfxManager::AddEmitter(x5d8_data.GetEnergyAbsorbSfxId(), GetTranslation(), zeus::skUp, false, false, 0x7f, GetAreaIdAlways()); x7ac_energyAbsorbCooldown = 0.25f; } void CElitePirate::SetupLauncherHealthInfo(CStateManager& mgr, TUniqueId uid) { const CHealthInfo* const health = HealthInfo(mgr); if (uid == kInvalidUniqueId) { return; } if (const TCastToPtr actor = mgr.ObjectById(uid)) { auto* actHealth = actor->HealthInfo(mgr); actHealth->SetHP(x5d8_data.GetLauncherHP()); actHealth->SetKnockbackResistance(health->GetKnockbackResistance()); actor->SetDamageVulnerability(x56c_vulnerability); } } void CElitePirate::SetLauncherActive(CStateManager& mgr, bool val, TUniqueId uid) { if (uid == kInvalidUniqueId) { return; } if (auto* entity = mgr.ObjectById(uid)) { mgr.SendScriptMsg(entity, GetUniqueId(), val ? EScriptObjectMessage::Start : EScriptObjectMessage::Stop); } } zeus::CVector3f CElitePirate::GetLockOnPosition(const CActor* actor) const { const zeus::CTransform targetTransform = actor->GetLocatorTransform("lockon_target_LCTR"sv); return actor->GetTranslation() + actor->GetTransform().rotate(targetTransform.origin); } bool CElitePirate::CanKnockBack(const CDamageInfo& info) const { return !x400_25_alive || info.GetWeaponMode().IsComboed() || info.GetWeaponMode().GetType() != EWeaponType::Plasma; } void CElitePirate::UpdateDestPos(CStateManager& mgr) { x8b4_targetDestPos = GetTranslation(); const zeus::CVector3f playerPos = mgr.GetPlayer().GetTranslation(); const zeus::CVector3f dist = GetTranslation() - playerPos; if (dist.canBeNormalized() && dist.magSquared() > x2fc_minAttackRange * x2fc_minAttackRange) { x2e0_destPos = playerPos + (x2fc_minAttackRange * dist.normalized()); x8b4_targetDestPos = x2e0_destPos; } } void CElitePirate::CheckAttackChance(CStateManager& mgr) { if (mgr.GetActiveRandom()->Float() > x5d8_data.GetAttackChance()) { x7b8_attackTimer = x308_attackTimeVariation * mgr.GetActiveRandom()->Float() + x304_averageAttackTime; } } void CElitePirate::AttractProjectiles(CStateManager& mgr) { if (!IsAlive()) { return; } const TCastToConstPtr actor = mgr.GetObjectById(x79c_energyAttractorId); if (!actor) { return; } float radius = x5d8_data.GetProjectileAttractionRadius(); const zeus::CVector3f actorPos = actor->GetTranslation(); const zeus::CVector3f pos = GetTranslation(); EntityList projNearList; const zeus::CAABox aabb{pos - radius, pos + radius}; mgr.BuildNearList(projNearList, aabb, CMaterialFilter::MakeInclude({EMaterialTypes::Projectile}), nullptr); if (projNearList.empty()) { return; } EntityList charNearList; mgr.BuildNearList(charNearList, aabb, CMaterialFilter::MakeInclude({EMaterialTypes::Character}), nullptr); for (const auto& projId : projNearList) { TCastToPtr projectile = mgr.ObjectById(projId); if (!projectile || projectile->GetType() == EWeaponType::Missile || projectile->GetOwnerId() != mgr.GetPlayer().GetUniqueId() || projectile->GetAreaIdAlways() != GetAreaIdAlways()) { continue; } const zeus::CVector3f projectilePos = projectile->GetTranslation(); const zeus::CVector3f actorProjDist = actorPos - projectilePos; if (GetTransform().frontVector().dot(actorProjDist) < 0.f) { const zeus::CVector3f projectileDir = projectilePos - projectile->GetPreviousPos(); if (projectileDir.canBeNormalized() && IsClosestEnergyAttractor(mgr, charNearList, projectilePos)) { const float actorProjMag = actorProjDist.magnitude(); const zeus::CVector3f b = projectilePos + ((0.5f * actorProjMag) * projectileDir.normalized()); const zeus::CVector3f c = actorPos + zeus::CVector3f{0.f, 0.f, 0.4f * 0.4f * actorProjMag}; const zeus::CVector3f p1 = zeus::getBezierPoint(projectilePos, b, c, actorPos, 0.333f); const zeus::CVector3f p2 = zeus::getBezierPoint(projectilePos, b, c, actorPos, 0.666f); const float magAdd = (p2 - p1).magnitude() + (p1 - projectilePos).magnitude() + (actorPos - p2).magnitude(); const zeus::CVector3f p3 = zeus::getBezierPoint(projectilePos, b, c, actorPos, projectileDir.magnitude() / magAdd); const zeus::CVector3f look = p3 - projectilePos; if (look.canBeNormalized()) { zeus::CTransform xf = zeus::lookAt(zeus::skZero3f, look); xf.orthonormalize(); CProjectileWeapon& weapon = projectile->ProjectileWeapon(); weapon.SetWorldSpaceOrientation(xf); const zeus::CVector3f scaledVelocity = 0.8f * weapon.GetVelocity().normalized(); weapon.SetVelocity(weapon.GetVelocity() * 0.39999998f + (scaledVelocity * 0.6f)); } } } SetShotAt(true, mgr); } } void CElitePirate::UpdateAbsorbBodyState(CStateManager& mgr, float dt) { if (!x988_27_shotAt || x450_bodyController->IsFrozen()) { return; } x7c4_absorbUpdateTimer += dt; if (x7c4_absorbUpdateTimer < 3.f) { return; } if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::Turn && x450_bodyController->GetBodyStateInfo().GetCurrentState()->IsMoving()) { x450_bodyController->GetCommandMgr().DeliverCmd( CBCAdditiveReactionCmd(pas::EAdditiveReactionType::Six, 1.f, false)); } else { bool b = false; if (HasWeakPointHead()) { if (const TCastToConstPtr actor = mgr.GetObjectById(x770_collisionHeadId)) { const float z = actor->GetTranslation().z(); b = z - 0.5f * (z - GetTranslation().z()) <= mgr.GetPlayer().GetTranslation().z(); } } b = b || TooClose(mgr, 0.f); x450_bodyController->GetCommandMgr().DeliverCmd( CBCAdditiveReactionCmd(b ? pas::EAdditiveReactionType::Seven : pas::EAdditiveReactionType::Five, 1.f, false)); } x7c4_absorbUpdateTimer = 0.f; } bool CElitePirate::IsAttractingEnergy() const { if (x450_bodyController->GetLocomotionType() == pas::ELocomotionType::Crouch) { const auto state = x450_bodyController->GetCurrentStateId(); return state == pas::EAnimationState::Locomotion || state == pas::EAnimationState::Turn; } return false; } void CElitePirate::UpdateTimers(float dt) { if (x7b8_attackTimer > 0.f) { x7b8_attackTimer -= dt; } if (x7ac_energyAbsorbCooldown > 0.f) { x7ac_energyAbsorbCooldown -= dt; } } void CElitePirate::UpdatePositionHistory() { const zeus::CVector3f pos = GetTranslation(); if (x7d0_pathFindSearch.OnPath(pos) == CPathFindSearch::EResult::Success) { x8c0_positionHistory.Clear(); } x8c0_positionHistory.AddValue(pos); } void CElitePirate::UpdateActorTransform(CStateManager& mgr, TUniqueId& uid, std::string_view name) { if (uid == kInvalidUniqueId) { return; } auto* actor = static_cast(mgr.ObjectById(uid)); if (actor == nullptr) { uid = kInvalidUniqueId; return; } actor->SetTransform(GetLctrTransform(name)); } void CElitePirate::UpdateHealthInfo(CStateManager& mgr) { const float hp = HealthInfo(mgr)->GetHP(); if (HasWeakPointHead()) { if (TCastToPtr actor = mgr.ObjectById(x770_collisionHeadId)) { const float headHp = actor->HealthInfo(mgr)->GetHP(); HealthInfo(mgr)->SetHP(hp - (hp - headHp)); *actor->HealthInfo(mgr) = *HealthInfo(mgr); } } if (HealthInfo(mgr)->GetHP() <= 0.f) { Death(mgr, zeus::skZero3f, EScriptObjectState::DeathRattle); RemoveMaterial(EMaterialTypes::Orbit, EMaterialTypes::Target, mgr); } } void CElitePirate::ExtendTouchBounds(const CStateManager& mgr, const rstl::reserved_vector& uids, const zeus::CVector3f& vec) const { for (const auto& uid : uids) { if (TCastToPtr actor = mgr.ObjectById(uid)) { actor->SetExtendedTouchBounds(vec); } } } bool CElitePirate::ShouldFireFromLauncher(CStateManager& mgr, TUniqueId launcherId) { if (x7b8_attackTimer > 0.f || launcherId == kInvalidUniqueId) { return false; } const auto* launcher = static_cast(mgr.GetObjectById(launcherId)); if (launcher == nullptr) { return false; } const zeus::CVector3f aim = mgr.GetPlayer().GetAimPosition(mgr, 0.f); if (x300_maxAttackRange * x300_maxAttackRange > (aim - GetTranslation()).magSquared() || ShouldTurn(mgr, 0.f)) { return false; } const zeus::CVector3f origin = GetLockOnPosition(launcher); if (IsPatternObstructed(mgr, origin, aim)) { return false; } const zeus::CVector3f target = CGrenadeLauncher::GrenadeTarget(mgr); float angleOut = x5d8_data.GetGrenadeTrajectoryInfo().GetAngleMin(); float velocityOut = x5d8_data.GetGrenadeTrajectoryInfo().GetVelocityMin(); CGrenadeLauncher::CalculateGrenadeTrajectory(target, origin, x5d8_data.GetGrenadeTrajectoryInfo(), angleOut, velocityOut); const zeus::CVector3f rot = GetTransform().rotate({0.f, std::cos(angleOut), std::sin(angleOut)}); return !CPatterned::IsPatternObstructed(mgr, target, target + (7.5f * rot)); } bool CElitePirate::ShouldCallForBackupFromLauncher(const CStateManager& mgr, TUniqueId uid) const { if (x988_30_calledForBackup || uid != kInvalidUniqueId || !x5d8_data.CanCallForBackup()) { return false; } return x7a8_pathShaggedTime >= 3.f; } bool CElitePirate::IsClosestEnergyAttractor(const CStateManager& mgr, const EntityList& charNearList, const zeus::CVector3f& projectilePos) const { const float distance = (projectilePos - GetTranslation()).magSquared(); for (const auto& id : charNearList) { if (const TCastToConstPtr actor = mgr.GetObjectById(id)) { if (actor->GetUniqueId() != GetUniqueId() && actor->IsEnergyAttractor() && (projectilePos - actor->GetTranslation()).magSquared() < distance) { return false; } } } return true; } void CElitePirate::ShakeCamera(CStateManager& mgr) { CPlayer& player = mgr.GetPlayer(); const float distance = (GetTranslation() - player.GetTranslation()).magnitude(); const float scale = x988_29_shockWaveAnim ? 1.f : 0.25f; const float magnitude = (scale * GetModelData()->GetScale().magnitude()) - (0.005f * distance); if (magnitude <= 0.f || player.GetSurfaceRestraint() == CPlayer::ESurfaceRestraints::Air || player.IsInWaterMovement()) { return; } if (player.GetMorphballTransitionState() == CPlayer::EPlayerMorphBallState::Morphed) { const float intensity = x988_29_shockWaveAnim ? 20.f : 10.f; player.ApplyImpulseWR(player.GetMass() * intensity * zeus::skUp, zeus::CAxisAngle{}); player.SetMoveState(CPlayer::EPlayerMovementState::ApplyJump, mgr); } else if (mgr.GetCameraManager()->GetCurrentCameraId() == mgr.GetCameraManager()->GetFirstPersonCamera()->GetUniqueId()) { mgr.GetCameraManager()->AddCameraShaker(CCameraShakeData{0.5f, magnitude}, true); } } zeus::CVector3f CElitePirate::SPositionHistory::GetValue(const zeus::CVector3f& pos, const zeus::CVector3f& face) { while (!x4_values.empty()) { const zeus::CVector3f v = x4_values.back() - pos; if (v.dot(face) > 0.f && v.isMagnitudeSafe()) { return v.normalized(); } x4_values.pop_back(); } return zeus::skZero3f; } void CElitePirate::SPositionHistory::AddValue(const zeus::CVector3f& pos) { if (x4_values.size() > 15) { return; } if (x4_values.empty()) { x4_values.emplace_back(pos); return; } if (x4_values.back().magSquared() > x0_magSquared) { x4_values.emplace_back(pos); } } } // namespace metaforce::MP1