diff --git a/Runtime/MP1/World/CElitePirate.cpp b/Runtime/MP1/World/CElitePirate.cpp index 477eca7e1..6a9232867 100644 --- a/Runtime/MP1/World/CElitePirate.cpp +++ b/Runtime/MP1/World/CElitePirate.cpp @@ -118,16 +118,16 @@ void CElitePirate::Think(float dt, CStateManager& mgr) { x730_collisionActorMgr2->Update(dt, mgr, CCollisionActorManager::EUpdateOptions::ObjectSpace); } x5d4_collisionActorMgr1->Update(dt, mgr, CCollisionActorManager::EUpdateOptions::ObjectSpace); - if (sub_80229208() && x5d8_data.GetX11F()) { + if (IsAttractingEnergy() && x5d8_data.GetX11F()) { x3b4_speed = 2.f * x7a0_initialSpeed; } else { x3b4_speed = x7a0_initialSpeed; } - sub_80228e50(dt); + UpdateTimers(dt); sub_80228798(); - sub_802289dc(mgr, x772_launcherId, "grenadeLauncher_LCTR"sv); + UpdateActorTransform(mgr, x772_launcherId, "grenadeLauncher_LCTR"sv); sub_80228e84(mgr); - x328_31_ = sub_80229208(); + x328_31_energyAttractor = IsAttractingEnergy(); } } @@ -265,16 +265,16 @@ void CElitePirate::PreRender(CStateManager& mgr, const zeus::CFrustum& frustum) xb4_drawFlags.x1_matSetIdx = numMaterialSets - 1 < x7cc_ ? numMaterialSets - 1 : x7cc_; } -auto CElitePirate::GetDamageVulnerability() const -> const CDamageVulnerability* { +const CDamageVulnerability* CElitePirate::GetDamageVulnerability() const { return &CDamageVulnerability::PassThroughVulnerabilty(); } -auto CElitePirate::GetDamageVulnerability(const zeus::CVector3f& pos, const zeus::CVector3f& dir, - const CDamageInfo& dInfo) const -> const CDamageVulnerability* { +const CDamageVulnerability* CElitePirate::GetDamageVulnerability(const zeus::CVector3f& pos, const zeus::CVector3f& dir, + const CDamageInfo& dInfo) const { return &CDamageVulnerability::PassThroughVulnerabilty(); } -auto CElitePirate::GetOrbitPosition(const CStateManager& mgr) const -> zeus::CVector3f { +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))) { @@ -289,7 +289,7 @@ auto CElitePirate::GetOrbitPosition(const CStateManager& mgr) const -> zeus::CVe return GetLctrTransform("lockon_target_LCTR").origin; } -auto CElitePirate::GetAimPosition(const CStateManager& mgr, float) const -> zeus::CVector3f { +zeus::CVector3f CElitePirate::GetAimPosition(const CStateManager& mgr, float) const { const std::shared_ptr& playerState = mgr.GetPlayerState(); if (x5d4_collisionActorMgr1->GetActive() && playerState->IsFiringComboBeam() && playerState->GetCurrentBeam() == CPlayerState::EBeamId::Wave) { @@ -357,7 +357,7 @@ void CElitePirate::DoUserAnimEvent(CStateManager& mgr, const CInt32POINode& node } } -auto CElitePirate::GetCollisionPrimitive() const -> const CCollisionPrimitive* { return &x738_; } +const CCollisionPrimitive* CElitePirate::GetCollisionPrimitive() const { return &x738_; } void CElitePirate::KnockBack(const zeus::CVector3f& pos, CStateManager& mgr, const CDamageInfo& info, EKnockBackType type, bool inDeferred, float magnitude) { @@ -400,19 +400,17 @@ void CElitePirate::PathFind(CStateManager& mgr, EStateMsg msg, float dt) { } if (!TooClose(mgr, 0.f) && !PathShagged(mgr, 0.f)) { CPatterned::PathFind(mgr, msg, dt); - } else { - if (PathShagged(mgr, 0.f)) { - const zeus::CVector3f& move = x8c0_.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 (PathShagged(mgr, 0.f)) { + const zeus::CVector3f& move = x8c0_.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) { @@ -661,7 +659,7 @@ void CElitePirate::Cover(CStateManager& mgr, EStateMsg msg, float dt) { x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd(zeus::skZero3f, face.normalized(), 1.f)); } } - sub_80227a90(mgr); + AttractProjectiles(mgr); sub_802277e0(mgr, dt); } else if (msg == EStateMsg::Deactivate) { x450_bodyController->SetLocomotionType(pas::ELocomotionType::Relaxed); @@ -736,7 +734,7 @@ bool CElitePirate::ShouldCallForBackup(CStateManager& mgr, float) { return ShouldCallForBackupFromLauncher(mgr, x772_launcherId); } -auto CElitePirate::GetSearchPath() -> CPathFindSearch* { return &x7d0_pathFindSearch; } +CPathFindSearch* CElitePirate::GetSearchPath() { return &x7d0_pathFindSearch; } void CElitePirate::SetupHealthInfo(CStateManager& mgr) { const CHealthInfo* const health = HealthInfo(mgr); @@ -764,7 +762,7 @@ void CElitePirate::sub_80229248() { x7d0_pathFindSearch.SetCharacterHeight(3.f * scale); } -// TODO rename +// TODO rename? void CElitePirate::SetShotAt(bool val, CStateManager& mgr) { if (!sub_802273b0() || x7b4_hp <= 0.f || !val) { x988_27_shotAt = val; @@ -956,9 +954,10 @@ bool CElitePirate::sub_80227430(const CDamageInfo& info) const { void CElitePirate::sub_80228634(CStateManager& mgr) { x8b4_ = GetTranslation(); - const zeus::CVector3f& dist = GetTranslation() - mgr.GetPlayer().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 = GetTranslation() + (x2fc_minAttackRange * dist.normalized()); + x2e0_destPos = playerPos + (x2fc_minAttackRange * dist.normalized()); x8b4_ = x2e0_destPos; } } @@ -969,8 +968,63 @@ void CElitePirate::sub_802285c4(CStateManager& mgr) { } } -void CElitePirate::sub_80227a90(CStateManager& mgr) { - // TODO +void CElitePirate::AttractProjectiles(CStateManager& mgr) { + if (!IsAlive()) { + return; + } + TCastToConstPtr actor = mgr.GetObjectById(x79c_); + if (!actor) { + return; + } + + float radius = x5d8_data.GetX1C(); + const zeus::CVector3f& actorPos = actor->GetTranslation(); + const zeus::CVector3f& pos = GetTranslation(); + rstl::reserved_vector projNearList; + zeus::CAABox aabb{pos - radius, pos + radius}; + mgr.BuildNearList(projNearList, aabb, CMaterialFilter::MakeInclude({EMaterialTypes::Projectile}), nullptr); + if (projNearList.empty()) { + return; + } + + rstl::reserved_vector charNearList; + mgr.BuildNearList(charNearList, aabb, CMaterialFilter::MakeInclude({EMaterialTypes::Character}), nullptr); + for (const TUniqueId 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)) { + 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); + + 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::sub_802277e0(CStateManager& mgr, float dt) { @@ -1000,15 +1054,15 @@ void CElitePirate::sub_802277e0(CStateManager& mgr, float dt) { x7c4_ = 0.f; } -bool CElitePirate::sub_80229208() { - if (x450_bodyController->GetLocomotionType() != pas::ELocomotionType::Crouch) { - return false; +bool CElitePirate::IsAttractingEnergy() { + if (x450_bodyController->GetLocomotionType() == pas::ELocomotionType::Crouch) { + const auto state = x450_bodyController->GetCurrentStateId(); + return state == pas::EAnimationState::Locomotion || state == pas::EAnimationState::Turn; } - const pas::EAnimationState state = x450_bodyController->GetCurrentStateId(); - return state == pas::EAnimationState::Locomotion || state == pas::EAnimationState::Turn; + return false; } -void CElitePirate::sub_80228e50(float dt) { +void CElitePirate::UpdateTimers(float dt) { if (x7b8_attackTimer > 0.f) { x7b8_attackTimer -= dt; } @@ -1025,7 +1079,7 @@ void CElitePirate::sub_80228798() { x8c0_.AddValue(pos); } -void CElitePirate::sub_802289dc(CStateManager& mgr, TUniqueId& uid, std::string_view name) { +void CElitePirate::UpdateActorTransform(CStateManager& mgr, TUniqueId& uid, std::string_view name) { if (uid == kInvalidUniqueId) { return; } @@ -1052,8 +1106,8 @@ void CElitePirate::sub_80228e84(CStateManager& mgr) { } } -void CElitePirate::ExtendTouchBounds(CStateManager& mgr, const rstl::reserved_vector& uids, - const zeus::CVector3f& vec) { +void CElitePirate::ExtendTouchBounds(const CStateManager& mgr, const rstl::reserved_vector& uids, + const zeus::CVector3f& vec) const { for (const TUniqueId uid : uids) { if (TCastToPtr actor = mgr.ObjectById(uid)) { actor->SetExtendedTouchBounds(vec); @@ -1086,13 +1140,28 @@ bool CElitePirate::ShouldFireFromLauncher(CStateManager& mgr, TUniqueId launcher return !CPatterned::IsPatternObstructed(mgr, target, target + (7.5f * rot)); } -bool CElitePirate::ShouldCallForBackupFromLauncher(CStateManager& mgr, TUniqueId uid) { +bool CElitePirate::ShouldCallForBackupFromLauncher(const CStateManager& mgr, TUniqueId uid) const { if (!x988_30_ && uid == kInvalidUniqueId && x5d8_data.GetX11E()) { return x7a8_ >= 3.f; } return false; } +bool CElitePirate::IsClosestEnergyAttractor(const CStateManager& mgr, + const rstl::reserved_vector& charNearList, + const zeus::CVector3f& projectilePos) const { + float distance = (projectilePos - GetTranslation()).magSquared(); + for (const auto id : charNearList) { + if (TCastToConstPtr actor = mgr.GetObjectById(id)) { + if (actor->GetUniqueId() != GetUniqueId() && actor->IsEnergyAttractor() && + (projectilePos - actor->GetTranslation()).magSquared() < distance) { + return false; + } + } + } + return true; +} + zeus::CVector3f CElitePirate::SUnknownStruct::GetValue(const zeus::CVector3f& v1, const zeus::CVector3f& v2) { while (!x4_.empty()) { const zeus::CVector3f v = x4_[x4_.size() - 1] - v1; diff --git a/Runtime/MP1/World/CElitePirate.hpp b/Runtime/MP1/World/CElitePirate.hpp index 811f15980..6033df77b 100644 --- a/Runtime/MP1/World/CElitePirate.hpp +++ b/Runtime/MP1/World/CElitePirate.hpp @@ -56,6 +56,7 @@ public: [[nodiscard]] float GetAttackChance() const { return x10_attackChance; } [[nodiscard]] float GetShotAtTime() const { return x14_shotAtTime; } [[nodiscard]] float GetShotAtTimeVariance() const { return x18_shotAtTimeVariance; } + [[nodiscard]] float GetX1C() const { return x1c_; } [[nodiscard]] CAssetId GetX20() const { return x20_; } [[nodiscard]] u16 GetSFXAbsorb() const { return x24_sfxAbsorb; } [[nodiscard]] const CActorParameters& GetLauncherActParams() const { return x28_launcherActParams; } @@ -213,17 +214,19 @@ private: bool sub_80227430(const CDamageInfo& info) const; void sub_80228634(CStateManager& mgr); void sub_802285c4(CStateManager& mgr); - void sub_80227a90(CStateManager& mgr); + void AttractProjectiles(CStateManager& mgr); void sub_802277e0(CStateManager& mgr, float dt); - bool sub_80229208(); - void sub_80228e50(float dt); + bool IsAttractingEnergy(); + void UpdateTimers(float dt); void sub_80228798(); - void sub_802289dc(CStateManager& mgr, TUniqueId& uid, std::string_view name); + void UpdateActorTransform(CStateManager& mgr, TUniqueId& uid, std::string_view name); void sub_80228e84(CStateManager& mgr); - void ExtendTouchBounds(CStateManager& mgr, const rstl::reserved_vector& uids, - const zeus::CVector3f& vec); + void ExtendTouchBounds(const CStateManager& mgr, const rstl::reserved_vector& uids, + const zeus::CVector3f& vec) const; bool ShouldFireFromLauncher(CStateManager& mgr, TUniqueId launcherId); - bool ShouldCallForBackupFromLauncher(CStateManager& mgr, TUniqueId uid); + bool ShouldCallForBackupFromLauncher(const CStateManager& mgr, TUniqueId uid) const; + bool IsClosestEnergyAttractor(const CStateManager& mgr, const rstl::reserved_vector& charNearList, + const zeus::CVector3f& projectilePos) const; }; } // namespace MP1 } // namespace urde diff --git a/Runtime/Weapon/CGameProjectile.cpp b/Runtime/Weapon/CGameProjectile.cpp index 62a8affdd..851054cef 100644 --- a/Runtime/Weapon/CGameProjectile.cpp +++ b/Runtime/Weapon/CGameProjectile.cpp @@ -25,7 +25,8 @@ CGameProjectile::CGameProjectile(bool active, const TToken& CMaterialFilter::MakeIncludeExclude( {EMaterialTypes::Solid, EMaterialTypes::NonSolidDamageable}, {EMaterialTypes::Projectile, EMaterialTypes::ProjectilePassthrough, excludeMat}), - CMaterialList(), dInfo, attribs | GetBeamAttribType(wType), CModelData::CModelDataNull()) + CMaterialList(EMaterialTypes::Projectile), dInfo, attribs | GetBeamAttribType(wType), + CModelData::CModelDataNull()) , x158_visorParticle(visorParticle) , x168_visorSfx(visorSfx) , x170_projectile(wDesc, xf.origin, xf.basis, scale, diff --git a/Runtime/World/CPatterned.hpp b/Runtime/World/CPatterned.hpp index 2d6ad2859..5ad14341d 100644 --- a/Runtime/World/CPatterned.hpp +++ b/Runtime/World/CPatterned.hpp @@ -133,7 +133,7 @@ protected: bool x328_28_prevOnGround : 1; bool x328_29_noPatternShagging : 1; bool x328_30_lookAtDeathDir : 1; - bool x328_31_ : 1; + bool x328_31_energyAttractor : 1; bool x329_24_ : 1; }; u32 _dummy = 0; @@ -366,6 +366,7 @@ public: } float GetDamageDuration() const { return x504_damageDur; } zeus::CVector3f GetGunEyePos() const; + bool IsEnergyAttractor() const { return x328_31_energyAttractor; } bool IsAlive() const { return x400_25_alive; } void BuildBodyController(EBodyType);