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