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