From dd705a3db9d1e30b05e47d9f2283ab0fdfd21353 Mon Sep 17 00:00:00 2001 From: Phillip Stephens Date: Wed, 5 Dec 2018 16:46:35 -0800 Subject: [PATCH] Implement CSeedling --- Runtime/Character/CBodyStateCmdMgr.hpp | 4 +- Runtime/Character/CharacterCommon.hpp | 30 +-- Runtime/MP1/World/CSeedling.cpp | 243 ++++++++++++++++++++++++- Runtime/MP1/World/CSeedling.hpp | 28 +++ Runtime/World/CPatterned.cpp | 10 + Runtime/World/CPatterned.hpp | 8 + 6 files changed, 304 insertions(+), 19 deletions(-) diff --git a/Runtime/Character/CBodyStateCmdMgr.hpp b/Runtime/Character/CBodyStateCmdMgr.hpp index b7c455a6f..5feeffa2b 100644 --- a/Runtime/Character/CBodyStateCmdMgr.hpp +++ b/Runtime/Character/CBodyStateCmdMgr.hpp @@ -91,9 +91,9 @@ public: CBCGenerateCmd(pas::EGenerateType type, s32 animId = -1) : CBodyStateCmd(EBodyStateCmd::Generate), x8_type(type), x18_animId(animId) { x1c_24_targetTransform = false; x1c_25_overrideAnim = false; } - CBCGenerateCmd(pas::EGenerateType type, const zeus::CVector3f& vec) + CBCGenerateCmd(pas::EGenerateType type, const zeus::CVector3f& vec, bool targetTransform = false, bool overrideAnim = false) : CBodyStateCmd(EBodyStateCmd::Generate), x8_type(type), xc_targetPos(vec) - { x1c_24_targetTransform = false; x1c_25_overrideAnim = false; } + { x1c_24_targetTransform = targetTransform; x1c_25_overrideAnim = overrideAnim; } pas::EGenerateType GetGenerateType() const { return x8_type; } const zeus::CVector3f& GetExitTargetPos() const { return xc_targetPos; } bool HasExitTargetPos() const { return x1c_24_targetTransform; } diff --git a/Runtime/Character/CharacterCommon.hpp b/Runtime/Character/CharacterCommon.hpp index 52922ed40..6c720d509 100644 --- a/Runtime/Character/CharacterCommon.hpp +++ b/Runtime/Character/CharacterCommon.hpp @@ -7,21 +7,21 @@ namespace pas enum class ELocomotionType { Invalid = -1, - Crouch, - Relaxed, - Lurk, - Combat, - Internal4, - Internal5, - Internal6, - Internal7, - Internal8, - Internal9, - Internal10, - Internal11, - Internal12, - Internal13, - Internal14 + Crouch = 0, + Relaxed = 1, + Lurk = 2, + Combat = 3, + Internal4 = 4, + Internal5 = 5, + Internal6 = 6, + Internal7 = 7, + Internal8 = 8, + Internal9 = 9, + Internal10 = 10, + Internal11 = 11, + Internal12 = 12, + Internal13 = 13, + Internal14 = 14 }; enum class ELocomotionAnim diff --git a/Runtime/MP1/World/CSeedling.cpp b/Runtime/MP1/World/CSeedling.cpp index 1bb16002c..c985ac712 100644 --- a/Runtime/MP1/World/CSeedling.cpp +++ b/Runtime/MP1/World/CSeedling.cpp @@ -1,5 +1,12 @@ #include "MP1/World/CSeedling.hpp" +#include "World/CPatternedInfo.hpp" +#include "World/CWorld.hpp" +#include "World/CGameArea.hpp" +#include "World/CPlayer.hpp" +#include "CStateManager.hpp" #include "TCastTo.hpp" +#include "CSeedling.hpp" + namespace urde { @@ -8,12 +15,21 @@ namespace MP1 CSeedling::CSeedling(TUniqueId uid, std::string_view name, const CEntityInfo& info, const zeus::CTransform& xf, CModelData&& mData, const CPatternedInfo& pInfo, const CActorParameters& actParms, - CAssetId, CAssetId, const CDamageInfo&, const CDamageInfo&, float f1, float f2, float f3, float f4) + CAssetId needleId, CAssetId weaponId, const CDamageInfo& dInfo1, const CDamageInfo& dInfo2, + float f1, float f2, float f3, float f4) : CWallWalker(ECharacter::Seedling, uid, name, EFlavorType::Zero, info, xf, std::move(mData), pInfo, EMovementType::Ground, EColliderType::Zero, EBodyType::WallWalker, actParms, f1, f2, EKnockBackVariant::Small, f3, EWalkerType::Seedling, f4, false) +, x5d8_searchPath(nullptr, 1, pInfo.GetPathfindingIndex(), 1.f, 1.f) +, x6bc_spikeData(new CModelData(CStaticRes(needleId, GetModelData()->GetScale()))) +, x6c0_projectileInfo(weaponId, dInfo1) +, x6e8_deathDamage(dInfo2) +, x722_24_renderOnlyClusterA(true) +, x722_25_curNeedleCluster(false) { - + const_cast*>(&x6c0_projectileInfo.Token())->Lock(); + CreateShadow(false); + SetKeepInThermalVisor(); } void CSeedling::Accept(IVisitor& visitor) @@ -21,5 +37,228 @@ void CSeedling::Accept(IVisitor& visitor) visitor.Visit(this); } +const std::string CSeedling::skNeedleLocators[2][6] = + { + { + "A_spike1_LCTR_SDK", + "A_spike2_LCTR_SDK", + "A_spike3_LCTR_SDK", + "A_spike4_LCTR_SDK", + "A_spike5_LCTR_SDK", + "A_spike6_LCTR_SDK", + }, + { + "B_spike1_LCTR_SDK", + "B_spike2_LCTR_SDK", + "B_spike3_LCTR_SDK", + "B_spike4_LCTR_SDK", + "B_spike5_LCTR_SDK", + "B_spike6_LCTR_SDK", + } + }; + +void CSeedling::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateManager& mgr) +{ + CPatterned::AcceptScriptMsg(msg, uid, mgr); + + if (msg == EScriptObjectMessage::Activate) + { + x5d6_27_disableMove = false; + TUniqueId id = GetWaypointForState(mgr, EScriptObjectState::Patrol, EScriptObjectMessage::Follow); + if (id != kInvalidUniqueId) + x2dc_destObj = id; + } + else if (msg == EScriptObjectMessage::Registered) + { + x450_bodyController->Activate(mgr); + x704_modelBounds = GetModelData()->GetBounds(); + } + else if (msg == EScriptObjectMessage::InitializedInArea) + { + x5d8_searchPath.SetArea(mgr.GetWorld()->GetAreaAlways(GetAreaIdAlways())->GetPostConstructed()->x10bc_pathArea); + } +} + +void CSeedling::Think(float dt, CStateManager& mgr) +{ + if (!GetActive()) + return; + + ++x5d4_thinkCounter; + x5d6_26_playerObstructed = false; + const CGameArea* area = mgr.GetWorld()->GetAreaAlways(GetAreaIdAlways()); + CGameArea::EOcclusionState occlusionState = CGameArea::EOcclusionState::Occluded; + if (area && area->IsPostConstructed()) + occlusionState = area->GetPostConstructed()->x10dc_occlusionState; + + if (occlusionState == CGameArea::EOcclusionState::Occluded) + x5d6_26_playerObstructed = true; + + if (!x5d6_26_playerObstructed) + { + zeus::CVector3f playerPos = mgr.GetPlayer().GetTranslation(); + float distance = (playerPos - GetTranslation()).magnitude(); + if (distance > x5c4_playerObstructionMinDist) + { + zeus::CVector3f direction = (playerPos - GetTranslation()).normalized(); + CRayCastResult result = mgr.RayStaticIntersection(playerPos, direction, distance, + CMaterialFilter::skPassEverything); + if (result.IsValid()) + x5d6_26_playerObstructed = true; + } + } + + if (x5d6_26_playerObstructed) + xf8_24_movable = false; + + xf8_24_movable = !x5d6_24_alignToFloor; + CWallWalker::Think(dt, mgr); + + if (!x5d6_25_hasAlignSurface && x450_bodyController->GetPercentageFrozen() < 0.00001f && x5d6_24_alignToFloor) + AlignToFloor(mgr, x590_colSphere.GetSphere().radius, GetTranslation() + (2.f * (dt * GetVelocity())), dt); + + if (x71c_attackCoolOff > 0.f) + x71c_attackCoolOff -= dt; +} + +void CSeedling::Render(const CStateManager& mgr) const +{ + if (x400_25_alive && x6bc_spikeData) + { + u32 index = x722_24_renderOnlyClusterA ? 0 : u32(x722_25_curNeedleCluster); + CModelFlags flags; + flags.x2_flags = 3; + flags.x4_color = zeus::CColor::skWhite; + + for (const std::string& sv : skNeedleLocators[index]) + x6bc_spikeData->Render(mgr, GetLctrTransform(sv), x90_actorLights.get(), flags); + } + + CWallWalker::Render(mgr); +} + +void CSeedling::DoUserAnimEvent(CStateManager& mgr, const CInt32POINode& node, EUserEventType type, float dt) +{ + if (type == EUserEventType::Projectile) + LaunchNeedles(mgr); + else if (type == EUserEventType::BeginAction) + x722_24_renderOnlyClusterA = true; + else + CPatterned::DoUserAnimEvent(mgr, node, type, dt); +} + +std::experimental::optional CSeedling::GetTouchBounds() const +{ + return x704_modelBounds.getTransformedAABox(GetTransform()); +} + +void CSeedling::Touch(CActor& act, CStateManager& mgr) +{ + if (x400_25_alive) + { + if (TCastToPtr pl = act) + MassiveDeath(mgr); + } + + CPatterned::Touch(act, mgr); +} + +void CSeedling::Patrol(CStateManager& mgr, EStateMsg msg, float) +{ + if (msg == EStateMsg::Activate) + { + x450_bodyController->SetLocomotionType(pas::ELocomotionType::Relaxed); + x5d6_24_alignToFloor = true; + x150_momentum.zeroOut(); + x5d6_25_hasAlignSurface = false; + xf8_24_movable = false; + + TUniqueId id = (x720_prevObj != kInvalidUniqueId ? x720_prevObj : + GetWaypointForState(mgr, EScriptObjectState::Patrol, + EScriptObjectMessage::Follow)); + + if (id != kInvalidUniqueId) + x2dc_destObj = id; + } + else if (msg == EStateMsg::Update) + { + UpdateWPDestination(mgr); + zeus::CVector3f upVec = GetTransform().upVector(); + x450_bodyController->GetCommandMgr().DeliverCmd( + CBCLocomotionCmd(ProjectVectorToPlane((x2e0_destPos - GetTranslation()).normalized(), upVec), {}, 0.f)); + x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd( + ProjectVectorToPlane(ProjectVectorToPlane(x45c_steeringBehaviors.Seek(*this, x2e0_destPos), upVec), upVec), + {}, 1.f)); + x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd(1.f * GetTransform().frontVector(), {}, 0.f)); + } + else if (msg == EStateMsg::Deactivate) + { + x720_prevObj = x2dc_destObj; + } +} + +void CSeedling::Active(CStateManager& mgr, EStateMsg msg, float arg) +{ + if (msg == EStateMsg::Activate) + x450_bodyController->SetLocomotionType(pas::ELocomotionType::Lurk); + CPatterned::Patrol(mgr, msg, arg); +} + +void CSeedling::Enraged(CStateManager&, EStateMsg msg, float) +{ + if (msg == EStateMsg::Activate) + x450_bodyController->SetLocomotionType(pas::ELocomotionType::Internal8); +} + +void CSeedling::ProjectileAttack(CStateManager& mgr, EStateMsg msg, float) +{ + if (msg == EStateMsg::Activate) + x32c_animState = EAnimState::One; + else if (msg == EStateMsg::Update) + TryCommand(mgr, pas::EAnimationState::ProjectileAttack, &CPatterned::TryProjectileAttack, 0); + else if (msg == EStateMsg::Deactivate) + { + x32c_animState = EAnimState::Zero; + x71c_attackCoolOff = (x300_maxAttackRange * mgr.GetActiveRandom()->Float()) + x304_averageAttackTime; + } +} + +void CSeedling::Generate(CStateManager& mgr, EStateMsg msg, float) +{ + if (msg == EStateMsg::Activate) + x32c_animState = EAnimState::One; + else if (msg == EStateMsg::Update) + TryCommand(mgr, pas::EAnimationState::Generate, &CPatterned::TryGenerate, 0); +} + +bool CSeedling::ShouldAttack(CStateManager& mgr, float) +{ + if (x71c_attackCoolOff > 0.f) + return false; + + return mgr.CanCreateProjectile(GetUniqueId(), EWeaponType::AI, 6); +} + +void CSeedling::LaunchNeedles(CStateManager& mgr) +{ + for (const std::string& needle : skNeedleLocators[u32(x722_25_curNeedleCluster)]) + LaunchProjectile(GetLctrTransform(needle), mgr, 6, EProjectileAttrib::None, true, {}, 0xFFFF, false, + GetModelData()->GetScale()); + + x722_25_curNeedleCluster = !x722_25_curNeedleCluster; + x722_24_renderOnlyClusterA = false; +} + +void CSeedling::MassiveDeath(CStateManager& mgr) +{ + if (x400_25_alive) + { + mgr.ApplyDamageToWorld(GetUniqueId(), *this, GetTranslation(), x6e8_deathDamage, + CMaterialFilter::MakeIncludeExclude({EMaterialTypes::Solid}, {})); + LaunchNeedles(mgr); + } + CPatterned::MassiveDeath(mgr); +} + } } diff --git a/Runtime/MP1/World/CSeedling.hpp b/Runtime/MP1/World/CSeedling.hpp index f703ccd91..c95f9d1c7 100644 --- a/Runtime/MP1/World/CSeedling.hpp +++ b/Runtime/MP1/World/CSeedling.hpp @@ -1,6 +1,8 @@ #pragma once #include "World/CWallWalker.hpp" +#include "World/CPathFindSearch.hpp" +#include "Weapon/CProjectileInfo.hpp" namespace urde { @@ -8,6 +10,17 @@ namespace MP1 { class CSeedling : public CWallWalker { + static const std::string skNeedleLocators[2][6]; + CPathFindSearch x5d8_searchPath; + std::unique_ptr x6bc_spikeData; + CProjectileInfo x6c0_projectileInfo; + CDamageInfo x6e8_deathDamage; + zeus::CAABox x704_modelBounds = zeus::CAABox::skNullBox; + float x71c_attackCoolOff = 0.f; + TUniqueId x720_prevObj = kInvalidUniqueId; + bool x722_24_renderOnlyClusterA : 1; + bool x722_25_curNeedleCluster : 1; + void LaunchNeedles(CStateManager&); public: DEFINE_PATTERNED(Seedling) CSeedling(TUniqueId, std::string_view, const CEntityInfo&, const zeus::CTransform&, @@ -16,6 +29,21 @@ public: float, float, float, float); void Accept(IVisitor&); + void AcceptScriptMsg(EScriptObjectMessage, TUniqueId, CStateManager&); + void Think(float, CStateManager&); + void Render(const CStateManager&) const; + void DoUserAnimEvent(CStateManager& mgr, const CInt32POINode& node, EUserEventType type, float dt); + CProjectileInfo* GetProjectileInfo() { return &x6c0_projectileInfo; } + std::experimental::optional GetTouchBounds() const; + void Touch(CActor&, CStateManager&); + CPathFindSearch* GetSearchPath() { return &x5d8_searchPath; } + void Patrol(CStateManager&, EStateMsg, float); + void Active(CStateManager&, EStateMsg, float); + void Enraged(CStateManager&, EStateMsg, float); + void ProjectileAttack(CStateManager&, EStateMsg, float); + void Generate(CStateManager&, EStateMsg, float); + bool ShouldAttack(CStateManager&, float); + void MassiveDeath(CStateManager&); }; } } diff --git a/Runtime/World/CPatterned.cpp b/Runtime/World/CPatterned.cpp index 6650e29c2..ffc4cde11 100644 --- a/Runtime/World/CPatterned.cpp +++ b/Runtime/World/CPatterned.cpp @@ -1026,6 +1026,16 @@ void CPatterned::TryLoopReaction(CStateManager& mgr, int arg) x450_bodyController->GetCommandMgr().DeliverCmd(CBCLoopReactionCmd(pas::EReactionType(arg))); } +void CPatterned::TryProjectileAttack(CStateManager&, int arg) +{ + x450_bodyController->GetCommandMgr().DeliverCmd(CBCProjectileAttackCmd(pas::ESeverity(arg), x2e0_destPos, false)); +} + +void CPatterned::TryGenerate(CStateManager& mgr, int arg) +{ + x450_bodyController->GetCommandMgr().DeliverCmd(CBCGenerateCmd(pas::EGenerateType(arg), x2e0_destPos, true)); +} + void CPatterned::BuildBodyController(EBodyType bodyType) { if (x450_bodyController) diff --git a/Runtime/World/CPatterned.hpp b/Runtime/World/CPatterned.hpp index a7d1a7cc9..4a8502cb1 100644 --- a/Runtime/World/CPatterned.hpp +++ b/Runtime/World/CPatterned.hpp @@ -381,6 +381,8 @@ public: void TryCommand(CStateManager& mgr, pas::EAnimationState state, CPatternedTryFunc func, int arg); void TryLoopReaction(CStateManager& mgr, int arg); + void TryProjectileAttack(CStateManager& mgr, int arg); + void TryGenerate(CStateManager& mgr, int arg); virtual bool KnockbackWhenFrozen() const { return true; } virtual void MassiveDeath(CStateManager& mgr); @@ -422,6 +424,12 @@ public: bool CanLongJump() const { return x328_26_longJump; } bool IsMakingBigStrike() const { return x402_28_isMakingBigStrike; } + void SetKeepInThermalVisor() + { + x403_24_keepThermalVisorState = true; + xe6_27_thermalVisorFlags = 1 | 2; + } + //region Casting Functions template