From 27643313f976197c2c7f7f187fa014d28d1f3110 Mon Sep 17 00:00:00 2001 From: Phillip Stephens Date: Fri, 22 Mar 2019 17:36:10 -0700 Subject: [PATCH 1/3] Implement CBloodFlower and more CBabygoth imps --- Runtime/Collision/CCollisionActor.hpp | 1 + Runtime/MP1/World/CBabygoth.cpp | 207 ++++++++++++++++++++++++-- Runtime/MP1/World/CBabygoth.hpp | 81 +++++++++- Runtime/MP1/World/CBloodFlower.cpp | 205 ++++++++++++++++++++++++- Runtime/MP1/World/CBloodFlower.hpp | 48 ++++++ 5 files changed, 523 insertions(+), 19 deletions(-) diff --git a/Runtime/Collision/CCollisionActor.hpp b/Runtime/Collision/CCollisionActor.hpp index cb970feef..47c6b6e95 100644 --- a/Runtime/Collision/CCollisionActor.hpp +++ b/Runtime/Collision/CCollisionActor.hpp @@ -44,6 +44,7 @@ public: const CCollisionPrimitive* GetCollisionPrimitive() const; EWeaponCollisionResponseTypes GetCollisionResponseType(const zeus::CVector3f&, const zeus::CVector3f&, const CWeaponMode&, EProjectileAttrib) const; + void SetWeaponCollisionResponseType(EWeaponCollisionResponseTypes type) { x300_responseType = type; } zeus::CTransform GetPrimitiveTransform() const; rstl::optional GetTouchBounds() const; void SetDamageVulnerability(const CDamageVulnerability& vuln); diff --git a/Runtime/MP1/World/CBabygoth.cpp b/Runtime/MP1/World/CBabygoth.cpp index 82b482988..f674c823d 100644 --- a/Runtime/MP1/World/CBabygoth.cpp +++ b/Runtime/MP1/World/CBabygoth.cpp @@ -10,6 +10,7 @@ #include "Weapon/CWeapon.hpp" #include "Weapon/CFlameThrower.hpp" #include "Weapon/CFlameInfo.hpp" +#include "World/CExplosion.hpp" #include "Particle/CWeaponDescription.hpp" #include "CStateManager.hpp" #include "CSimplePool.hpp" @@ -175,10 +176,19 @@ void CBabygoth::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateM x8e8_ = 0.f; mgr.InformListeners(GetTranslation(), EListenNoiseType::PlayerFire); } else - sub8021d478(mgr, uid); + ApplyDamage(mgr, uid); x400_24_hitByPlayerProjectile = true; break; } + case EScriptObjectMessage::InvulnDamage: { + mgr.InformListeners(GetTranslation(), EListenNoiseType::PlayerFire); + x400_24_hitByPlayerProjectile = true; + xa48_24_isAlert = true; + x8e8_ = 0.f; + if (!TCastToPtr(mgr.ObjectById(uid))) + ApplyDamage(mgr, uid); + break; + } case EScriptObjectMessage::SuspendedMove: { if (x928_colActMgr) x928_colActMgr->SetMovable(mgr, false); @@ -196,8 +206,8 @@ void CBabygoth::Think(float dt, CStateManager& mgr) { AvoidPlayerCollision(dt, mgr); if (xa49_26_) { - if (sub8023a180(x6e8_teamMgr, mgr) == 0) - sub8021d6e8(mgr); + if (!CTeamAiMgr::GetTeamAiRole(mgr, x6e8_teamMgr, GetUniqueId())) + AddToTeam(mgr); } CPatterned::Think(dt, mgr); @@ -257,6 +267,7 @@ void CBabygoth::DoUserAnimEvent(urde::CStateManager& mgr, const urde::CInt32POIN } CPatterned::DoUserAnimEvent(mgr, node, type, dt); } + const SSphereJointInfo CBabygoth::skSphereJointList[skSphereJointCount] = { {"L_knee", 1.2f}, {"R_knee", 1.2f}, {"LCTR_SHEMOUTH", 1.7f}, {"Pelvis", 1.2f}, {"butt_LCTR", 0.9f}}; @@ -345,9 +356,16 @@ void CBabygoth::RemoveFromTeam(urde::CStateManager& mgr) { } void CBabygoth::ApplySeparationBehavior(CStateManager& mgr) {} -void CBabygoth::CrackShell(CStateManager&, const TLockedToken&, const zeus::CTransform&, s16, bool) {} -void CBabygoth::sub8021d478(CStateManager&, TUniqueId) {} +void CBabygoth::ApplyDamage(CStateManager& mgr, TUniqueId uid) { + if (TCastToConstPtr weap = mgr.GetObjectById(uid)) { + if (x9f8_shellIds.empty()) + return; + + mgr.ApplyDamage(uid, x9f8_shellIds[0], weap->GetOwnerId(), weap->GetDamageInfo(), + CMaterialFilter::MakeIncludeExclude({EMaterialTypes::Solid}, {}), {}); + } +} void CBabygoth::Shock(CStateManager& mgr, float duration, float damage) { if (x9f8_shellIds.empty()) @@ -370,9 +388,64 @@ void CBabygoth::UpdateTouchBounds() { x930_aabox.Box() = bounds; } -void CBabygoth::UpdateAttackPosition(CStateManager&, zeus::CVector3f&) {} +void CBabygoth::UpdateAttackPosition(CStateManager& mgr, zeus::CVector3f& attackPos) { + attackPos = GetTranslation(); + if (x8d8_ > 0.f) + return; + attackPos = mgr.GetPlayer().GetTranslation(); + zeus::CVector3f distVec = GetTranslation() - attackPos; + if (distVec.canBeNormalized()) + attackPos += x2fc_minAttackRange * distVec.normalized(); +} -void CBabygoth::sub8021d644(urde::CStateManager&) {} +void CBabygoth::sub8021e3f4(urde::CStateManager& mgr) { + if (xa00_shellHitPoints <= 0.f) + return; + + float hp = 0.f; + if (TCastToPtr colAct = mgr.ObjectById(x9f6_)) + hp = zeus::max(hp, colAct->GetHealthInfo(mgr)->GetHP() - x570_babyData.GetShellHitPoints()); + + for (TUniqueId uid : x9f8_shellIds) { + if (TCastToPtr colAct = mgr.ObjectById(uid)) { + hp = zeus::max(hp, colAct->GetHealthInfo(mgr)->GetHP() - x570_babyData.GetShellHitPoints()); + } + } + + xa00_shellHitPoints -= hp; + if (xa00_shellHitPoints <= 0.f) { + x56c_ = 3; + sub8021d9d0(mgr); + CrackShell(mgr, xa2c_, x34_transform, x570_babyData.x15c_, false); + UpdateHealthInfo(mgr); + } else { + if (xa00_shellHitPoints < CalculateShellCrackHP(2)) { + if (x56c_ != 2) { + CrackShell(mgr, xa20_, x34_transform, x570_babyData.x15a_, false); + x56c_ = 2; + xa04_ = 2; + } + } else if (xa00_shellHitPoints < CalculateShellCrackHP(1)) { + if (x56c_ != 1) { + CrackShell(mgr, xa14_, x34_transform, x570_babyData.x158_, false); + x56c_ = 1; + xa04_ = 1; + } + } + } + + hp = (x56c_ == 3 ? x8ec_ : x570_babyData.GetShellHitPoints()); + + if (TCastToPtr colAct = mgr.ObjectById(x9f6_)) { + colAct->HealthInfo(mgr)->SetHP(hp); + } + + for (TUniqueId uid : x9f8_shellIds) { + if (TCastToPtr colAct = mgr.ObjectById(uid)) { + colAct->HealthInfo(mgr)->SetHP(hp); + } + } +} void CBabygoth::AvoidPlayerCollision(float dt, CStateManager& mgr) { if (x450_bodyController->GetLocomotionType() == pas::ELocomotionType::Crouch || @@ -397,9 +470,15 @@ void CBabygoth::AvoidPlayerCollision(float dt, CStateManager& mgr) { } } -s32 CBabygoth::sub8023a180(TUniqueId, CStateManager&) { return 0; } - -void CBabygoth::sub8021d6e8(CStateManager& mgr) {} +void CBabygoth::AddToTeam(CStateManager& mgr) { + if (x6e8_teamMgr == kInvalidUniqueId) + return; + if (TCastToPtr aiMgr = mgr.ObjectById(x6e8_teamMgr)) { + if (!aiMgr->IsPartOfTeam(GetUniqueId())) + aiMgr->AssignTeamAiRole(*this, CTeamAiRole::ETeamAiRole::Melee, CTeamAiRole::ETeamAiRole::Ranged, + CTeamAiRole::ETeamAiRole::Invalid); + } +} void CBabygoth::sub8021e2c4(float dt) { if (x8d8_ > 0.f) @@ -412,7 +491,43 @@ void CBabygoth::sub8021e2c4(float dt) { x8e8_ += dt; } -void CBabygoth::sub8021e708(CStateManager&) {} +void CBabygoth::sub8021e708(CStateManager& mgr) { + if (!x400_25_alive) + return; + + if (x56c_ == 3) { + float hp = 0.f; + if (TCastToPtr colAct = mgr.ObjectById(x9f6_)) { + hp = zeus::max(hp, colAct->GetHealthInfo(mgr)->GetHP() - x8ec_); + } + + for (TUniqueId uid : x9f8_shellIds) { + if (TCastToPtr colAct = mgr.ObjectById(uid)) { + hp = zeus::max(hp, colAct->GetHealthInfo(mgr)->GetHP() - x8ec_); + } + } + + HealthInfo(mgr)->SetHP(hp - HealthInfo(mgr)->GetHP()); + if (HealthInfo(mgr)->GetHP() <= 0.f) { + Death(mgr, {}, EScriptObjectState::DeathRattle); + xa48_26_ = true; + xa49_26_ = false; + RemoveFromTeam(mgr); + RemoveMaterial(EMaterialTypes::Orbit, EMaterialTypes::Target, mgr); + } else { + if (TCastToPtr colAct = mgr.ObjectById(x9f6_)) + colAct->HealthInfo(mgr)->SetHP(x8ec_); + + for (TUniqueId uid : x9f8_shellIds) { + if (TCastToPtr colAct = mgr.ObjectById(uid)) { + colAct->HealthInfo(mgr)->SetHP(x8ec_); + } + } + } + } else { + sub8021e3f4(mgr); + } +} void CBabygoth::UpdateParticleEffects(float dt, CStateManager& mgr) { if (CFlameThrower* flame = static_cast(mgr.ObjectById(x980_flameThrower))) { @@ -596,7 +711,7 @@ void CBabygoth::Generate(CStateManager& mgr, EStateMsg msg, float) { void CBabygoth::TargetPatrol(CStateManager& mgr, EStateMsg msg, float arg) { if (msg == EStateMsg::Activate) { xa49_29_ = false; - sub8021d644(mgr); + RemoveFromTeam(mgr); x400_24_hitByPlayerProjectile = false; x450_bodyController->SetLocomotionType(pas::ELocomotionType::Relaxed); if (HasPatrolPath(mgr, 0.f)) { @@ -733,4 +848,72 @@ bool CBabygoth::IsDestinationObstructed(CStateManager& mgr) { } return false; } + +void CBabygoth::sub8021d9d0(urde::CStateManager& mgr) { + ModelData()->AnimationData()->SubstituteModelData(xa08_noShellModel); + + for (TUniqueId uid : x9f8_shellIds) { + if (TCastToPtr colAct = mgr.ObjectById(uid)) { + colAct->SetWeaponCollisionResponseType(EWeaponCollisionResponseTypes::Unknown41); + } + } + xa04_ = 0; +} + +void CBabygoth::CrackShell(CStateManager& mgr, const TLockedToken& desc, + const zeus::CTransform& xf, u16 sfx, bool b1) { + mgr.AddObject(new CExplosion(desc, mgr.AllocateUniqueId(), true, + CEntityInfo(GetAreaIdAlways(), CEntity::NullConnectionList), "Babygoth Shell Crack Fx"sv, + xf, 0, GetModelData()->GetScale(), zeus::skWhite)); + + if (b1) + CSfxManager::SfxStart(sfx, 0x7f, 64 / 127.f, false, 0x7f, false, -1); + else + CSfxManager::AddEmitter(sfx, GetTranslation(), zeus::skUp, false, false, 0x7f, GetAreaIdAlways()); +} + +void CBabygoth::UpdateHealthInfo(urde::CStateManager& mgr) { + CHealthInfo* hInfo = HealthInfo(mgr); + if (TCastToPtr colAct = mgr.ObjectById(x9f6_)) { + (*colAct->HealthInfo(mgr)) = *hInfo; + colAct->SetDamageVulnerability(x98c_); + } + + for (TUniqueId uid : x9f8_shellIds) { + if (TCastToPtr colAct = mgr.ObjectById(uid)) { + (*colAct->HealthInfo(mgr)) = *hInfo; + colAct->SetDamageVulnerability(x98c_); + } + } +} + +float CBabygoth::CalculateShellCrackHP(u32 w1) { + if (w1 == 0) + return x570_babyData.GetShellHitPoints(); + else if (w1 == 1) + return 0.66666669f * x570_babyData.GetShellHitPoints(); + else if (w1 == 2) + return 0.33333334f * x570_babyData.GetShellHitPoints(); + + return 0.f; +} + +bool CBabygoth::ShouldTurn(urde::CStateManager& mgr, float arg) { + const float speedScale = GetModelData()->GetAnimationData()->GetSpeedScale(); + zeus::CVector3f aimPos = mgr.GetPlayer().GetAimPosition(mgr, (speedScale > 0.f ? 1.f / speedScale : 0.f)); + return zeus::CVector2f::getAngleDiff(GetTransform().basis[1].toVec2f(), (aimPos - GetTranslation()).toVec2f()) > + (arg == 0.f ? 0.78539819f : arg); +} + +bool CBabygoth::InMaxRange(CStateManager& mgr, float) { + return (GetTranslation() - mgr.GetPlayer().GetTranslation()).magSquared() < (1.5f * x300_maxAttackRange); +} + +bool CBabygoth::Listen(const zeus::CVector3f& origin, EListenNoiseType noiseType) { + if (!x400_25_alive || noiseType != EListenNoiseType::PlayerFire || (origin - GetTranslation()).magSquared() >= 1600.f) + return false; + + xa48_30_ = true; + return true; +} } // namespace urde::MP1 diff --git a/Runtime/MP1/World/CBabygoth.hpp b/Runtime/MP1/World/CBabygoth.hpp index 139601535..9d672b347 100644 --- a/Runtime/MP1/World/CBabygoth.hpp +++ b/Runtime/MP1/World/CBabygoth.hpp @@ -62,7 +62,7 @@ private: static constexpr s32 skSphereJointCount = 5; static const SSphereJointInfo skSphereJointList[skSphereJointCount]; static const std::string_view skpMouthDamageJoint; - u32 x568_ = -1; + s32 x568_ = -1; u32 x56c_ = 0; CBabygothData x570_babyData; TUniqueId x6e8_teamMgr = kInvalidUniqueId; @@ -115,14 +115,23 @@ private: }; u32 _dummy = 0; }; + void AddSphereCollisionList(const SSphereJointInfo*, s32, std::vector&); + void SetupCollisionManager(CStateManager&); + void SetupHealthInfo(CStateManager&); + void CreateFlameThrower(CStateManager&); + void ApplyContactDamage(TUniqueId, CStateManager&); + void RemoveFromTeam(CStateManager&); + void ApplySeparationBehavior(CStateManager&); + bool IsMouthCollisionActor(TUniqueId uid) { return x9f6_ == uid; } + bool IsShell(TUniqueId uid) { for (TUniqueId shellId : x9f8_shellIds) { if (shellId == uid) @@ -130,37 +139,64 @@ private: } return false; } - void CrackShell(CStateManager&, const TLockedToken&, const zeus::CTransform&, s16, bool); - void sub8021d478(CStateManager&, TUniqueId); + + void ApplyDamage(CStateManager& mgr, TUniqueId uid); + void AvoidPlayerCollision(float, CStateManager&); - s32 sub8023a180(TUniqueId, CStateManager&); - void sub8021d6e8(CStateManager&); + + void AddToTeam(CStateManager& mgr); + void sub8021e2c4(float); + void sub8021e708(CStateManager&); + void UpdateParticleEffects(float, CStateManager&); + void TryToGetUp(CStateManager& mgr); + bool CheckShouldWakeUp(CStateManager&, float); + void SetProjectilePasshtrough(CStateManager&); + void UpdateTouchBounds(); + void UpdateAttackPosition(CStateManager&, zeus::CVector3f&); - void sub8021d644(CStateManager&); + + void sub8021e3f4(CStateManager&); + bool IsDestinationObstructed(CStateManager&); + void sub8021d9d0(CStateManager&); + + void CrackShell(CStateManager&, const TLockedToken&, const zeus::CTransform&, u16, bool); + + void UpdateHealthInfo(CStateManager&); + + float CalculateShellCrackHP(u32); + public: DEFINE_PATTERNED(Babygoth) + CBabygoth(TUniqueId, std::string_view, const CEntityInfo&, const zeus::CTransform&, CModelData&&, const CPatternedInfo&, const CActorParameters&, const CBabygothData&); + void AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateManager& mgr); + void PreRender(CStateManager& mgr, const zeus::CFrustum& frustum) { CPatterned::PreRender(mgr, frustum); xb4_drawFlags.x1_matSetIdx = u8(xa04_); } void Think(float, CStateManager&); + void DoUserAnimEvent(CStateManager& mgr, const CInt32POINode& node, EUserEventType type, float dt); + float GetGravityConstant() const { return 10.f * 24.525f; } + void SetPathFindMode(EPathFindMode mode) { x8b4_pathFindMode = mode; } + const CCollisionPrimitive* GetCollisionPrimitive() const { return &x930_aabox; } + EWeaponCollisionResponseTypes GetCollisionResponseType(const zeus::CVector3f& v1, const zeus::CVector3f& v2, const CWeaponMode& wMode, EProjectileAttrib attrib) const { if (wMode.GetType() == EWeaponType::Ice) @@ -170,26 +206,50 @@ public: return CPatterned::GetCollisionResponseType(v1, v2, wMode, attrib); } + const CDamageVulnerability* GetDamageVulnerability() const { + return &CDamageVulnerability::ReflectVulnerabilty(); + } + + const CDamageVulnerability* GetDamageVulnerability(const zeus::CVector3f&, const zeus::CVector3f&, + const CDamageInfo&) const { + return &CDamageVulnerability::ReflectVulnerabilty(); + } + void TakeDamage(const zeus::CVector3f&, float) { if (x400_25_alive) x428_damageCooldownTimer = 0.33f; } + void Shock(CStateManager&, float, float); void TurnAround(CStateManager&, EStateMsg, float); + void GetUp(CStateManager&, EStateMsg, float); + void Enraged(CStateManager&, EStateMsg, float); + void FollowPattern(CStateManager&, EStateMsg, float); + void Taunt(CStateManager&, EStateMsg, float); + void Crouch(CStateManager&, EStateMsg, float); + void Deactivate(CStateManager&, EStateMsg, float); + void Generate(CStateManager&, EStateMsg, float); + void TargetPatrol(CStateManager&, EStateMsg, float); + void Patrol(CStateManager&, EStateMsg, float); + void Approach(CStateManager&, EStateMsg, float); + void PathFind(CStateManager&, EStateMsg, float); + void SpecialAttack(CStateManager&, EStateMsg, float); + void Attack(CStateManager&, EStateMsg, float); + void ProjectileAttack(CStateManager&, EStateMsg, float); bool AnimOver(CStateManager&, float) { return x568_ == 4; } @@ -199,9 +259,18 @@ public: return true; return CPatterned::SpotPlayer(mgr, arg); } + bool InPosition(CStateManager&, float) { return (x8b8_ - GetTranslation()).magSquared() < 9.f; } + bool InMaxRange(CStateManager&, float); bool ShotAt(CStateManager&, float) { return x400_24_hitByPlayerProjectile; } + + bool OffLine(CStateManager& mgr, float arg) { + SetPathFindMode(EPathFindMode::Zero); + return PathShagged(mgr, arg); + } + bool ShouldTurn(CStateManager& mgr, float arg); + bool Listen(const zeus::CVector3f&, EListenNoiseType); }; } // namespace urde::MP1 diff --git a/Runtime/MP1/World/CBloodFlower.cpp b/Runtime/MP1/World/CBloodFlower.cpp index 26fba29be..0f22587df 100644 --- a/Runtime/MP1/World/CBloodFlower.cpp +++ b/Runtime/MP1/World/CBloodFlower.cpp @@ -1,4 +1,12 @@ #include "CBloodFlower.hpp" +#include "Particle/CGenDescription.hpp" +#include "Particle/CElementGen.hpp" +#include "Weapon/CProjectileWeapon.hpp" +#include "World/CPlayer.hpp" +#include "World/CScriptTrigger.hpp" +#include "CSimplePool.hpp" +#include "CStateManager.hpp" +#include "GameGlobalObjects.hpp" namespace urde::MP1 { CBloodFlower::CBloodFlower(TUniqueId uid, std::string_view name, const CEntityInfo& info, const zeus::CTransform& xf, @@ -7,5 +15,200 @@ CBloodFlower::CBloodFlower(TUniqueId uid, std::string_view name, const CEntityIn 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) {} + EMovementType::Ground, EColliderType::One, EBodyType::Restricted, actParms, EKnockBackVariant::Medium) +, x568_podEffectDesc(g_SimplePool->GetObj({FOURCC('PART'), partId1})) +, x574_podEffect(new CElementGen(x568_podEffectDesc)) +, x578_(g_SimplePool->GetObj({FOURCC('WPSC'), wpscId1})) +, x590_projectileInfo(wpscId2, dInfo1) +, x5d4_(CSfxManager::TranslateSFXID(soundId)) +, x5dc_(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_ = 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(); + } + sub80119364(mgr); + UpdateFire(mgr); + } +} + +void CBloodFlower::sub80119364(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; +} + +static std::string_view sFireEffects[3] = { + "Fire1"sv, + "Fire2"sv, + "Fire3"sv, +}; + +void CBloodFlower::TurnEffectsOn(u32 effect, CStateManager& mgr) { + ModelData()->AnimationData()->SetParticleEffectState(sFireEffects[effect], true, mgr); +} + +void CBloodFlower::TurnEffectsOff(u32 effect, CStateManager& mgr) { + ModelData()->AnimationData()->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(GetLocatorTransform(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 f1, s32 w1) { +} + +void CBloodFlower::Render(const CStateManager& mgr) const { + CPatterned::Render(mgr); + x574_podEffect->Render(GetActorLights()); +} + +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; + sub80119364(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)); + } + } + } +} + } // namespace urde::MP1 \ No newline at end of file diff --git a/Runtime/MP1/World/CBloodFlower.hpp b/Runtime/MP1/World/CBloodFlower.hpp index 1eb1040ff..06d6ae53e 100644 --- a/Runtime/MP1/World/CBloodFlower.hpp +++ b/Runtime/MP1/World/CBloodFlower.hpp @@ -1,14 +1,62 @@ #pragma once +#include "Weapon/CProjectileInfo.hpp" #include "World/CPatterned.hpp" +namespace urde { +class CGenDescription; +class CElementGen; +class CWeaponDescription; +} + namespace urde::MP1 { class CBloodFlower : public CPatterned { + TLockedToken x568_podEffectDesc; + std::unique_ptr x574_podEffect; + TLockedToken x578_; + float x584_curAttackTime = 0.f; + float x588_projectileOffset = 0.f; + u32 x58c_projectileState = 0; + CProjectileInfo x590_projectileInfo; + float x5b8_ = 0.f; + float x5bc_projectileDelay = 0.f; + float x5c0_ = 0.f; + TLockedToken x5c4_; + s16 x5d4_; + u32 x5d8_effectState = 0; + CDamageInfo x5dc_; + CDamageInfo x5f8_podDamage; + float x614_; + CAssetId x618_; + CAssetId x61c_; + CAssetId x620_; + + void ActivateTriggers(CStateManager& mgr, bool activate); + void sub80119364(CStateManager&); + void UpdateFire(CStateManager& mgr); + void TurnEffectsOn(u32, CStateManager&); + void TurnEffectsOff(u32, CStateManager&); + void LaunchPollenProjectile(const zeus::CTransform&, CStateManager&, float, s32); public: DEFINE_PATTERNED(BloodFlower) CBloodFlower(TUniqueId, std::string_view, const CEntityInfo&, const zeus::CTransform&, CModelData&&, const CPatternedInfo&, CAssetId, CAssetId, const CActorParameters&, CAssetId, const CDamageInfo&, const CDamageInfo&, const CDamageInfo&, CAssetId, CAssetId, CAssetId, float, CAssetId, u32); + + void AcceptScriptMsg(EScriptObjectMessage, TUniqueId, CStateManager&); + void Think(float dt, CStateManager& mgr); + void DoUserAnimEvent(CStateManager& mgr, const CInt32POINode& node, EUserEventType type, float dt); + void Render(const CStateManager& mgr) const; + CProjectileInfo* GetProjectileInfo() { return &x590_projectileInfo; } + + + bool ShouldAttack(CStateManager&, float); + bool ShouldTurn(CStateManager&, float); + bool Leash(CStateManager&, float) { return x5c0_ < x3d0_playerLeashTime; } + void Active(CStateManager&, EStateMsg, float); + void InActive(CStateManager&, EStateMsg, float); + void BulbAttack(CStateManager&, EStateMsg, float); + void PodAttack(CStateManager&, EStateMsg, float); }; } // namespace urde::MP1 From d3a251589cdf5453e8e56a9e9fb9dd7c95352934 Mon Sep 17 00:00:00 2001 From: Phillip Stephens Date: Sat, 23 Mar 2019 18:54:46 -0700 Subject: [PATCH 2/3] Revert convenience macro in ScriptLoader --- Runtime/World/ScriptLoader.cpp | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/Runtime/World/ScriptLoader.cpp b/Runtime/World/ScriptLoader.cpp index 4ac8e0cab..7ad135541 100644 --- a/Runtime/World/ScriptLoader.cpp +++ b/Runtime/World/ScriptLoader.cpp @@ -118,9 +118,6 @@ namespace urde { static logvisor::Module Log("urde::ScriptLoader"); -#define UNIMPLEMENTED(name) \ - Log.report(logvisor::Warning, "Attempted to load unimplemented object '%s'", name); - static SObjectTag MorphballDoorANCS = {}; static const SObjectTag& GetMorphballDoorACS() { @@ -1922,7 +1919,6 @@ CEntity* ScriptLoader::LoadPlayerHint(CStateManager& mgr, CInputStream& in, int } CEntity* ScriptLoader::LoadRipper(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info) { - UNIMPLEMENTED("Ripper"); return nullptr; } @@ -1939,7 +1935,6 @@ CEntity* ScriptLoader::LoadPickupGenerator(CStateManager& mgr, CInputStream& in, } CEntity* ScriptLoader::LoadAIKeyframe(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info) { - UNIMPLEMENTED("AIKeyframe"); return nullptr; } @@ -1958,7 +1953,6 @@ CEntity* ScriptLoader::LoadPointOfInterest(CStateManager& mgr, CInputStream& in, } CEntity* ScriptLoader::LoadDrone(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info) { - UNIMPLEMENTED("Drone"); return nullptr; } @@ -2139,7 +2133,6 @@ CEntity* ScriptLoader::LoadEMPulse(CStateManager& mgr, CInputStream& in, int pro } CEntity* ScriptLoader::LoadIceSheegoth(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info) { - UNIMPLEMENTED("IceSheegoth"); return nullptr; } @@ -2187,7 +2180,6 @@ CEntity* ScriptLoader::LoadPlayerActor(CStateManager& mgr, CInputStream& in, int } CEntity* ScriptLoader::LoadFlaahgra(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info) { - UNIMPLEMENTED("Flaahgra"); return nullptr; } @@ -2454,7 +2446,6 @@ CEntity* ScriptLoader::LoadPlayerStateChange(CStateManager& mgr, CInputStream& i } CEntity* ScriptLoader::LoadThardus(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info) { - UNIMPLEMENTED("Thardus"); return nullptr; } @@ -2521,7 +2512,6 @@ CEntity* ScriptLoader::LoadAiJumpPoint(CStateManager& mgr, CInputStream& in, int CEntity* ScriptLoader::LoadFlaahgraTentacle(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info) { - UNIMPLEMENTED("FlaahgraTentacle"); return nullptr; } @@ -2590,7 +2580,6 @@ CEntity* ScriptLoader::LoadColorModulate(CStateManager& mgr, CInputStream& in, i CEntity* ScriptLoader::LoadThardusRockProjectile(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info) { - UNIMPLEMENTED("ThardusRockProjectile"); return nullptr; } @@ -2929,7 +2918,6 @@ CEntity* ScriptLoader::LoadActorContraption(CStateManager& mgr, CInputStream& in } CEntity* ScriptLoader::LoadOculus(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info) { - UNIMPLEMENTED("Oculus"); return nullptr; } @@ -3372,7 +3360,6 @@ CEntity* ScriptLoader::LoadWorldLightFader(CStateManager& mgr, CInputStream& in, CEntity* ScriptLoader::LoadMetroidPrimeStage2(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info) { - UNIMPLEMENTED("MetroidPrimeStage2"); return nullptr; } @@ -3425,18 +3412,15 @@ CEntity* ScriptLoader::LoadMazeNode(CStateManager& mgr, CInputStream& in, int pr } CEntity* ScriptLoader::LoadOmegaPirate(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info) { - UNIMPLEMENTED("OmegaPirate"); return nullptr; } CEntity* ScriptLoader::LoadPhazonPool(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info) { - UNIMPLEMENTED("PhazonPool"); return nullptr; } CEntity* ScriptLoader::LoadPhazonHealingNodule(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info) { - UNIMPLEMENTED("PhazonHealingNodule"); return nullptr; } @@ -3480,7 +3464,6 @@ CEntity* ScriptLoader::LoadShadowProjector(CStateManager& mgr, CInputStream& in, } CEntity* ScriptLoader::LoadEnergyBall(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info) { - UNIMPLEMENTED("EnergyBall"); return nullptr; } } // namespace urde From 799ff4a64b691568b85cbda57ed609a77f11dbc3 Mon Sep 17 00:00:00 2001 From: Phillip Stephens Date: Sat, 23 Mar 2019 22:45:47 -0700 Subject: [PATCH 3/3] Finalize CBloodFlower imps --- Runtime/MP1/World/CBloodFlower.cpp | 78 ++++++++++++++++++------ Runtime/MP1/World/CBloodFlower.hpp | 15 +++-- Runtime/Weapon/CGameProjectile.hpp | 2 + Runtime/Weapon/CProjectileWeapon.hpp | 3 +- Runtime/Weapon/CTargetableProjectile.cpp | 41 ++++++++++++- Runtime/Weapon/CTargetableProjectile.hpp | 11 +++- 6 files changed, 121 insertions(+), 29 deletions(-) diff --git a/Runtime/MP1/World/CBloodFlower.cpp b/Runtime/MP1/World/CBloodFlower.cpp index 0f22587df..12f18ef30 100644 --- a/Runtime/MP1/World/CBloodFlower.cpp +++ b/Runtime/MP1/World/CBloodFlower.cpp @@ -2,6 +2,7 @@ #include "Particle/CGenDescription.hpp" #include "Particle/CElementGen.hpp" #include "Weapon/CProjectileWeapon.hpp" +#include "Weapon/CTargetableProjectile.hpp" #include "World/CPlayer.hpp" #include "World/CScriptTrigger.hpp" #include "CSimplePool.hpp" @@ -18,10 +19,10 @@ CBloodFlower::CBloodFlower(TUniqueId uid, std::string_view name, const CEntityIn EMovementType::Ground, EColliderType::One, EBodyType::Restricted, actParms, EKnockBackVariant::Medium) , x568_podEffectDesc(g_SimplePool->GetObj({FOURCC('PART'), partId1})) , x574_podEffect(new CElementGen(x568_podEffectDesc)) -, x578_(g_SimplePool->GetObj({FOURCC('WPSC'), wpscId1})) +, x578_projectileDesc(g_SimplePool->GetObj({FOURCC('WPSC'), wpscId1})) , x590_projectileInfo(wpscId2, dInfo1) -, x5d4_(CSfxManager::TranslateSFXID(soundId)) -, x5dc_(dInfo2) +, x5d4_visorSfx(CSfxManager::TranslateSFXID(soundId)) +, x5dc_projectileDamage(dInfo2) , x5f8_podDamage(dInfo3) , x614_(f1) , x618_(partId2) @@ -35,7 +36,7 @@ CBloodFlower::CBloodFlower(TUniqueId uid, std::string_view name, const CEntityIn x590_projectileInfo.Token().Lock(); x460_knockBackController.SetAutoResetImpulse(false); if (partId5.IsValid()) { - x5c4_ = g_SimplePool->GetObj({FOURCC('PART'), partId5}); + x5c4_visorParticle = g_SimplePool->GetObj({FOURCC('PART'), partId5}); } } @@ -60,12 +61,10 @@ void CBloodFlower::sub80119364(CStateManager& mgr) { void CBloodFlower::UpdateFire(CStateManager& mgr) { if (x5d8_effectState == 0) { TurnEffectsOn(0, mgr); - } - else if (x5d8_effectState == 1) { + } else if (x5d8_effectState == 1) { TurnEffectsOff(0, mgr); TurnEffectsOn(1, mgr); - } - else if (x5d8_effectState == 2) { + } else if (x5d8_effectState == 2) { TurnEffectsOff(1, mgr); TurnEffectsOn(2, mgr); } @@ -74,9 +73,9 @@ void CBloodFlower::UpdateFire(CStateManager& mgr) { } static std::string_view sFireEffects[3] = { - "Fire1"sv, - "Fire2"sv, - "Fire3"sv, + "Fire1"sv, + "Fire2"sv, + "Fire3"sv, }; void CBloodFlower::TurnEffectsOn(u32 effect, CStateManager& mgr) { @@ -106,16 +105,40 @@ void CBloodFlower::DoUserAnimEvent(CStateManager& mgr, const CInt32POINode& node x58c_projectileState = 0; x5bc_projectileDelay = 0.5f; } - return; - } - else if (type == EUserEventType::Delete) { + 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 f1, s32 w1) { +void CBloodFlower::LaunchPollenProjectile(const zeus::CTransform& xf, CStateManager& mgr, float var_f1, s32 w1) { + static float tickPeriod = CProjectileWeapon::GetTickPeriod(); + CProjectileInfo* proj = GetProjectileInfo(); + TLockedToken projToken = proj->Token(); + + if (!projToken) + 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.9050002f) + std::sqrt(2.f * var_f1 / 4.9050002f); + float f4 = 1.f / f7; + zeus::CVector3f vel{f4 * (aimPos.x() - xf.origin.x()), f4 * (aimPos.y() - xf.origin.y()), + 2.4525001f * 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.9050002f)); + mgr.AddObject(targProj); + } } void CBloodFlower::Render(const CStateManager& mgr) const { @@ -157,7 +180,7 @@ void CBloodFlower::Active(CStateManager& mgr, EStateMsg msg, float arg) { x450_bodyController->GetCommandMgr().DeliverCmd(CBCAdditiveAimCmd()); x584_curAttackTime += arg; x450_bodyController->GetCommandMgr().DeliverAdditiveTargetVector( - GetTransform().transposeRotate(mgr.GetPlayer().GetTranslation() - GetTranslation())); + 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)); @@ -175,8 +198,8 @@ void CBloodFlower::InActive(CStateManager& mgr, EStateMsg msg, float) { void CBloodFlower::BulbAttack(CStateManager& mgr, EStateMsg msg, float) { if (msg == EStateMsg::Activate) { - x450_bodyController->GetCommandMgr().DeliverCmd(CBCProjectileAttackCmd(pas::ESeverity::Zero, - mgr.GetPlayer().GetTranslation(), true)); + x450_bodyController->GetCommandMgr().DeliverCmd( + CBCProjectileAttackCmd(pas::ESeverity::Zero, mgr.GetPlayer().GetTranslation(), true)); x58c_projectileState = 1; } } @@ -211,4 +234,23 @@ void CBloodFlower::ActivateTriggers(CStateManager& mgr, bool activate) { } } +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(); + CTargetableProjectile* 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 urde::MP1 \ No newline at end of file diff --git a/Runtime/MP1/World/CBloodFlower.hpp b/Runtime/MP1/World/CBloodFlower.hpp index 06d6ae53e..3cd208768 100644 --- a/Runtime/MP1/World/CBloodFlower.hpp +++ b/Runtime/MP1/World/CBloodFlower.hpp @@ -7,13 +7,14 @@ namespace urde { class CGenDescription; class CElementGen; class CWeaponDescription; -} +class CTargetableProjectile; +} // namespace urde namespace urde::MP1 { class CBloodFlower : public CPatterned { TLockedToken x568_podEffectDesc; std::unique_ptr x574_podEffect; - TLockedToken x578_; + TLockedToken x578_projectileDesc; float x584_curAttackTime = 0.f; float x588_projectileOffset = 0.f; u32 x58c_projectileState = 0; @@ -21,10 +22,10 @@ class CBloodFlower : public CPatterned { float x5b8_ = 0.f; float x5bc_projectileDelay = 0.f; float x5c0_ = 0.f; - TLockedToken x5c4_; - s16 x5d4_; + TLockedToken x5c4_visorParticle; + s16 x5d4_visorSfx; u32 x5d8_effectState = 0; - CDamageInfo x5dc_; + CDamageInfo x5dc_projectileDamage; CDamageInfo x5f8_podDamage; float x614_; CAssetId x618_; @@ -37,6 +38,9 @@ class CBloodFlower : public CPatterned { void TurnEffectsOn(u32, CStateManager&); void TurnEffectsOff(u32, CStateManager&); void LaunchPollenProjectile(const zeus::CTransform&, CStateManager&, float, s32); + CTargetableProjectile* CreateArcProjectile(CStateManager&, const TToken&, const zeus::CTransform&, + const CDamageInfo&, TUniqueId); + public: DEFINE_PATTERNED(BloodFlower) @@ -50,7 +54,6 @@ public: void Render(const CStateManager& mgr) const; CProjectileInfo* GetProjectileInfo() { return &x590_projectileInfo; } - bool ShouldAttack(CStateManager&, float); bool ShouldTurn(CStateManager&, float); bool Leash(CStateManager&, float) { return x5c0_ < x3d0_playerLeashTime; } diff --git a/Runtime/Weapon/CGameProjectile.hpp b/Runtime/Weapon/CGameProjectile.hpp index 46b7e43d9..bc339271f 100644 --- a/Runtime/Weapon/CGameProjectile.hpp +++ b/Runtime/Weapon/CGameProjectile.hpp @@ -82,6 +82,8 @@ public: CProjectileTouchResult CanCollideWithTrigger(CActor& act, CStateManager& mgr); zeus::CAABox GetProjectileBounds() const; rstl::optional GetTouchBounds() const; + CProjectileWeapon& ProjectileWeapon() { return x170_projectile; } + const CProjectileWeapon& GetProjectileWeapon() const { return x170_projectile; } TUniqueId GetHomingTargetId() const { return x2c0_homingTargetId; } zeus::CVector3f GetPreviousPos() const { return x298_previousPos; } }; diff --git a/Runtime/Weapon/CProjectileWeapon.hpp b/Runtime/Weapon/CProjectileWeapon.hpp index 2dea7b3df..b600609ec 100644 --- a/Runtime/Weapon/CProjectileWeapon.hpp +++ b/Runtime/Weapon/CProjectileWeapon.hpp @@ -93,9 +93,10 @@ public: void UpdateParticleFX(); virtual void Update(float dt); void SetGravity(const zeus::CVector3f& grav) { xbc_gravity = grav; } + zeus::CVector3f GetGravity() const { return xbc_gravity; } static void SetGlobalSeed(u16 seed) { g_GlobalSeed = seed; } CElementGen* GetAttachedPS1() const { return xfc_APSMGen.get(); } double GameTime() const { return xd0_curTime; } - static float GetTickPeriod() { return 0.0166667f; } + static constexpr float GetTickPeriod() { return 0.0166667f; } }; } // namespace urde diff --git a/Runtime/Weapon/CTargetableProjectile.cpp b/Runtime/Weapon/CTargetableProjectile.cpp index 5d10b6447..2d994c260 100644 --- a/Runtime/Weapon/CTargetableProjectile.cpp +++ b/Runtime/Weapon/CTargetableProjectile.cpp @@ -1,19 +1,56 @@ #include "CTargetableProjectile.hpp" +#include "CStateManager.hpp" +#include "World/CPlayer.hpp" +#include "TCastTo.hpp" namespace urde { CTargetableProjectile::CTargetableProjectile( const TToken& desc, EWeaponType type, const zeus::CTransform& xf, EMaterialTypes materials, const CDamageInfo& damage, const CDamageInfo& damage2, TUniqueId uid, TAreaId aid, TUniqueId owner, - TUniqueId homingTarget, EProjectileAttrib attribs, + const TLockedToken& weapDesc, TUniqueId homingTarget, EProjectileAttrib attribs, const rstl::optional>& visorParticle, u16 visorSfx, bool sendCollideMsg) : CEnergyProjectile(true, desc, type, xf, materials, damage, uid, aid, owner, homingTarget, attribs | EProjectileAttrib::BigProjectile | EProjectileAttrib::PartialCharge | EProjectileAttrib::PlasmaProjectile, false, zeus::skOne3f, visorParticle, visorSfx, sendCollideMsg) -, x3e0_dInfo2(damage2) { +, x3d8_weaponDesc(weapDesc) +, x3e0_damage(damage2) { x68_material.Add(EMaterialTypes::Target); x68_material.Add(EMaterialTypes::Orbit); } +void CTargetableProjectile::Accept(IVisitor& visitor) { visitor.Visit(this); } + +zeus::CVector3f CTargetableProjectile::GetAimPosition(const CStateManager& mgr, float dt) const { + static constexpr float tickRecip = 1.f / CProjectileWeapon::GetTickPeriod(); + return (dt * dt * 0.5f * tickRecip * x170_projectile.GetGravity()) + + (dt * tickRecip * x170_projectile.GetVelocity() + GetTranslation()); +} + +bool CTargetableProjectile::Explode(const zeus::CVector3f& pos, const zeus::CVector3f& normal, + EWeaponCollisionResponseTypes type, CStateManager& mgr, + const CDamageVulnerability& dVuln, TUniqueId hitActor) { + bool ret = CEnergyProjectile::Explode(pos, normal, type, mgr, dVuln, hitActor); + + if (x2e4_24_active || x2c4_ == kInvalidUniqueId || x2c4_ != mgr.GetPlayer().GetUniqueId()) + return ret; + + if (TCastToConstPtr act = mgr.GetObjectById(xec_ownerId)) { + TUniqueId uid = mgr.AllocateUniqueId(); + zeus::CTransform xf = + zeus::lookAt(x170_projectile.GetTranslation(), act->GetAimPosition(mgr, 0.f), zeus::skUp); + CEnergyProjectile* projectile = new CEnergyProjectile( + true, x3d8_weaponDesc, xf0_weaponType, xf, EMaterialTypes::Player, x3e0_damage, uid, GetAreaIdAlways(), + kInvalidUniqueId, xec_ownerId, EProjectileAttrib::None, false, zeus::skOne3f, {}, 0xFFFF, false); + mgr.AddObject(projectile); + projectile->AddMaterial(EMaterialTypes::Orbit); + mgr.GetPlayer().ResetAimTargetPrediction(uid); + mgr.GetPlayer().SetOrbitTargetId(uid, mgr); + x2c4_ = kInvalidUniqueId; + } + + return ret; +} + } // namespace urde diff --git a/Runtime/Weapon/CTargetableProjectile.hpp b/Runtime/Weapon/CTargetableProjectile.hpp index 2b549c400..5ae0f4940 100644 --- a/Runtime/Weapon/CTargetableProjectile.hpp +++ b/Runtime/Weapon/CTargetableProjectile.hpp @@ -5,14 +5,21 @@ namespace urde { class CTargetableProjectile : public CEnergyProjectile { - CDamageInfo x3e0_dInfo2; + TLockedToken x3d8_weaponDesc; + CDamageInfo x3e0_damage; public: CTargetableProjectile(const TToken& desc, EWeaponType type, const zeus::CTransform& xf, EMaterialTypes materials, const CDamageInfo& damage, const CDamageInfo& damage2, TUniqueId uid, - TAreaId aid, TUniqueId owner, TUniqueId homingTarget, EProjectileAttrib attribs, + TAreaId aid, TUniqueId owner, const TLockedToken& weapDesc, + TUniqueId homingTarget, EProjectileAttrib attribs, const rstl::optional>& visorParticle, u16 visorSfx, bool sendCollideMsg); + + void Accept(IVisitor&); + zeus::CVector3f GetAimPosition(const CStateManager&, float) const; + bool Explode(const zeus::CVector3f& pos, const zeus::CVector3f& normal, EWeaponCollisionResponseTypes type, + CStateManager& mgr, const CDamageVulnerability& dVuln, TUniqueId hitActor); }; } // namespace urde