diff --git a/Runtime/Character/CBodyStateInfo.hpp b/Runtime/Character/CBodyStateInfo.hpp index 6598d029d..b83da0227 100644 --- a/Runtime/Character/CBodyStateInfo.hpp +++ b/Runtime/Character/CBodyStateInfo.hpp @@ -31,6 +31,7 @@ public: float GetLocomotionSpeed(pas::ELocomotionAnim anim) const; float GetMaxSpeed() const; float GetMaximumPitch() const { return x30_maxPitch; } + void SetMaximumPitch(float pitch) { x30_maxPitch = pitch; } bool GetLocoAnimChangeAtEndOfAnimOnly() const { return x34_24_changeLocoAtEndOfAnimOnly; } void SetLocoAnimChangeAtEndOfAnimOnly(bool s) { x34_24_changeLocoAtEndOfAnimOnly = s; } CBodyState* GetCurrentState(); diff --git a/Runtime/MP1/World/CFlickerBat.cpp b/Runtime/MP1/World/CFlickerBat.cpp index ad848a4cb..a7d414c14 100644 --- a/Runtime/MP1/World/CFlickerBat.cpp +++ b/Runtime/MP1/World/CFlickerBat.cpp @@ -1,4 +1,10 @@ #include "CFlickerBat.hpp" +#include "CStateManager.hpp" +#include "Camera/CCameraManager.hpp" +#include "Camera/CGameCamera.hpp" +#include "Collision/CGameCollision.hpp" +#include "World/CPlayer.hpp" +#include "TCastTo.hpp" namespace urde::MP1 { @@ -8,7 +14,180 @@ CFlickerBat::CFlickerBat(TUniqueId uid, std::string_view name, CPatterned::EFlav EColliderType colType, bool b1, const CActorParameters& actParms, bool b2) : CPatterned(ECharacter::FlickerBat, uid, name, flavor, info, xf, std::move(mData), pInfo, EMovementType::Flyer, colType, EBodyType::Pitchable, actParms, EKnockBackVariant::Small) +, x580_24_(false) +, x580_25_heardShot(false) +, x580_26_(false) +, x580_27_(b2) +, x574_state(EFlickerBatState(b1)) { + SetupPlayerCollision(b1); + x3d8_xDamageThreshold = 0.f; + x402_27_noXrayModel = false; } + +void CFlickerBat::Accept(IVisitor& visitor) +{ + visitor.Visit(this); +} + +void CFlickerBat::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateManager& mgr) +{ + CPatterned::AcceptScriptMsg(msg, uid, mgr); + + if (msg == EScriptObjectMessage::Registered) + { + RemoveMaterial(EMaterialTypes::Solid, mgr); + /* TODO: Implement xf3c_ in CStateManager (skipping 801311B8 - 80131224) */ + x450_bodyController->Activate(mgr); + x450_bodyController->BodyStateInfo().SetMaximumPitch(zeus::degToRad(60.f)); + } + else if (msg == EScriptObjectMessage::Deleted) + { + /* sub80125D88(mgr.xf3c_, uid) */ + } +} + +void CFlickerBat::Think(float dt, CStateManager& mgr) +{ + if (!GetActive()) + return; + + x402_29_drawParticles = mgr.GetPlayerState()->GetActiveVisor(mgr) != CPlayerState::EPlayerVisor::XRay; + + if (GetFlickerBatState() == EFlickerBatState::Two || GetFlickerBatState() == EFlickerBatState::Three) + { + x578_ -= dt; + if (x578_ <= 0.f) + { + if (GetFlickerBatState() == EFlickerBatState::Two) + SetFlickerBatState(EFlickerBatState::Zero, mgr); + else + SetFlickerBatState(EFlickerBatState::One, mgr); + } + } + + CPatterned::Think(dt, mgr); +} + +void CFlickerBat::Render(const CStateManager& mgr) const +{ + CPatterned::Render(mgr); +} + +void CFlickerBat::Touch(CActor& act, CStateManager& mgr) +{ + CPatterned::Touch(act, mgr); +} + +void CFlickerBat::DoUserAnimEvent(CStateManager& mgr, CInt32POINode& node, EUserEventType type, float dt) +{ + CPatterned::DoUserAnimEvent(mgr, node, type, dt); +} + +void CFlickerBat::Death(CStateManager& mgr, const zeus::CVector3f& direction, EScriptObjectState state) +{ + SetFlickerBatState(EFlickerBatState::Zero, mgr); + CPatterned::Death(mgr, direction, state); +} + +bool CFlickerBat::CanBeShot(CStateManager& mgr, int) +{ + return (GetFlickerBatState() == EFlickerBatState::Zero || GetFlickerBatState() == EFlickerBatState::Two || + mgr.GetPlayerState()->GetCurrentVisor() == CPlayerState::EPlayerVisor::XRay); +} + +void CFlickerBat::Patrol(CStateManager& mgr, EStateMsg state, float dt) +{ + CPatterned::Patrol(mgr, state, dt); +} + +void CFlickerBat::Attack(CStateManager&, EStateMsg, float) +{ +} + +void CFlickerBat::Shuffle(CStateManager&, EStateMsg, float) +{ +} + +void CFlickerBat::Taunt(CStateManager&, EStateMsg, float) +{ +} + +bool CFlickerBat::InPosition(CStateManager& mgr, float arg) +{ + return GetTransform().frontVector().dot(mgr.GetPlayer().GetAimPosition(mgr, 0.f) - GetTranslation()) > 0.f; +} + +bool CFlickerBat::HearShot(CStateManager&, float) +{ + if (x580_25_heardShot) + { + x580_25_heardShot = false; + return true; + } + + return false; +} + +void CFlickerBat::SetFlickerBatState(EFlickerBatState state, CStateManager& mgr) +{ + if (state == x574_state) + return; + + FlickerBatStateChanged(state, mgr); + x574_state = state; +} + +void CFlickerBat::FlickerBatStateChanged(EFlickerBatState state, CStateManager& mgr) +{ + if (state == EFlickerBatState::Zero) + { + if (mgr.GetPlayerState()->GetCurrentVisor() != CPlayerState::EPlayerVisor::XRay) + CreateShadow(true); + + AddMaterial(EMaterialTypes::Target, mgr); + } + else if (state == EFlickerBatState::One) + { + SetMuted(true); + RemoveMaterial(EMaterialTypes::Target, mgr); + } + else if (state == EFlickerBatState::Two) + { + if (mgr.GetPlayerState()->GetCurrentVisor() != CPlayerState::EPlayerVisor::XRay) + { + CreateShadow(true); + SetMuted(false); + } + + CheckStaticIntersection(mgr); + SetupPlayerCollision(true); + } + else if (state == EFlickerBatState::Three) + { + if (mgr.GetPlayerState()->GetCurrentVisor() != CPlayerState::EPlayerVisor::XRay) + CreateShadow(true); + + CheckStaticIntersection(mgr); + SetupPlayerCollision(false); + } +} + +void CFlickerBat::CheckStaticIntersection(CStateManager& mgr) +{ + if (!x580_27_) + { + x580_26_ = false; + return; + } + + zeus::CVector3f camPos = mgr.GetCameraManager()->GetCurrentCamera(mgr)->GetTranslation(); + zeus::CVector3f diff = GetBoundingBox().center() - camPos; + float mag = diff.magnitude(); + diff *= zeus::CVector3f(1.f / mag); + x580_26_ = CGameCollision::RayStaticIntersectionBool(mgr, camPos, diff, mag, + CMaterialFilter::MakeExclude({EMaterialTypes::SeeThrough})); +} + } \ No newline at end of file diff --git a/Runtime/MP1/World/CFlickerBat.hpp b/Runtime/MP1/World/CFlickerBat.hpp index 458bda377..5731f424b 100644 --- a/Runtime/MP1/World/CFlickerBat.hpp +++ b/Runtime/MP1/World/CFlickerBat.hpp @@ -4,11 +4,52 @@ namespace urde::MP1 { -class CFlickerBat : public CPatterned +class CFlickerBat final : public CPatterned { +public: + enum class EFlickerBatState + { + Zero, + One, + Two, + Three + }; + +private: + float x568_ = 0.f; + float x56c_ = 0.f; + float x570_ = 0.f; + EFlickerBatState x574_state; + float x578_ = 1.f; + float x57c_ = 0.f; + bool x580_24_ : 1; + bool x580_25_heardShot : 1; + bool x580_26_ : 1; + bool x580_27_ : 1; public: DEFINE_PATTERNED(FlickerBat) CFlickerBat(TUniqueId, std::string_view name, EFlavorType, const CEntityInfo&, const zeus::CTransform&, CModelData&&, const CPatternedInfo&, EColliderType, bool, const CActorParameters&, bool); + + void Accept(IVisitor&); + void AcceptScriptMsg(EScriptObjectMessage, TUniqueId, CStateManager&); + void Think(float, CStateManager&); + void Render(const CStateManager&) const; + void Touch(CActor&, CStateManager&); + void DoUserAnimEvent(CStateManager&, CInt32POINode&, EUserEventType, float dt); + void Death(CStateManager& mgr, const zeus::CVector3f& direction, EScriptObjectState state); + bool CanBeShot(CStateManager&, int); + void Patrol(CStateManager&, EStateMsg, float); + void Attack(CStateManager&, EStateMsg, float); + void Shuffle(CStateManager&, EStateMsg, float); + void Taunt(CStateManager&, EStateMsg, float); + bool InPosition(CStateManager&, float); + bool HearShot(CStateManager&, float); + + EFlickerBatState GetFlickerBatState() const { return x574_state; } + void SetFlickerBatState(EFlickerBatState state, CStateManager&); + void FlickerBatStateChanged(EFlickerBatState, CStateManager&); + void CheckStaticIntersection(CStateManager&); + }; }