From 94e25cd00156d246463c042a5b28146a64d94ccd Mon Sep 17 00:00:00 2001 From: Phillip Stephens Date: Wed, 8 Jul 2026 11:24:18 -0700 Subject: [PATCH] CScriptGunTurret matches --- config/GM8E01_00/symbols.txt | 44 +- config/GM8E01_01/symbols.txt | 2 +- .../ScriptObjects/CScriptGunTurret.hpp | 164 ++++++- .../ScriptObjects/CScriptGunTurret.cpp | 457 ++++++++++++++++++ 4 files changed, 637 insertions(+), 30 deletions(-) create mode 100644 src/MetroidPrime/ScriptObjects/CScriptGunTurret.cpp diff --git a/config/GM8E01_00/symbols.txt b/config/GM8E01_00/symbols.txt index eb2e7583..4f0d82c4 100644 --- a/config/GM8E01_00/symbols.txt +++ b/config/GM8E01_00/symbols.txt @@ -9866,21 +9866,21 @@ LaunchProjectile__16CScriptGunTurretFR13CStateManager = .text:0x80217408; // typ ShouldFire__16CScriptGunTurretCFR13CStateManager = .text:0x80217950; // type:function size:0x54 scope:global InDetectionRange__16CScriptGunTurretCFR13CStateManager = .text:0x802179A4; // type:function size:0x134 scope:global IsPlayerInFiringRange__16CScriptGunTurretCFR13CStateManager = .text:0x80217AD8; // type:function size:0x14C scope:global -IsInsignificantRotation__16CScriptGunTurretCFf = .text:0x80217C24; // type:function size:0xAC scope:global +IsStopped__16CScriptGunTurretCFf = .text:0x80217C24; // type:function size:0xAC scope:global UpdateHealthInfo__16CScriptGunTurretFR13CStateManager = .text:0x80217CD0; // type:function size:0x164 scope:global UpdateExtensionModelState__16CScriptGunTurretFf = .text:0x80217E34; // type:function size:0x128 scope:global UpdateGunOrientation__16CScriptGunTurretFfR13CStateManager = .text:0x80217F5C; // type:function size:0x8D4 scope:global UpdateTargettingMode__16CScriptGunTurretFfR13CStateManager = .text:0x80218830; // type:function size:0x198 scope:global UpdateTurretAnimation__16CScriptGunTurretFv = .text:0x802189C8; // type:function size:0x1EC scope:global -ProcessFrenzyState__16CScriptGunTurretFfR13CStateManager = .text:0x80218BB4; // type:function size:0x280 scope:global -ProcessExitTargettingState__16CScriptGunTurretFfR13CStateManager = .text:0x80218E34; // type:function size:0x11C scope:global -ProcessTargettingState__16CScriptGunTurretFfR13CStateManager = .text:0x80218F50; // type:function size:0x4DC scope:global -ProcessPanningState__16CScriptGunTurretFfR13CStateManager = .text:0x8021942C; // type:function size:0x190 scope:global -ProcessReadyState__16CScriptGunTurretFfR13CStateManager = .text:0x802195BC; // type:function size:0x108 scope:global -ProcessInactiveState__16CScriptGunTurretFfR13CStateManager = .text:0x802196C4; // type:function size:0x274 scope:global -ProcessDeactivatingState__16CScriptGunTurretFfR13CStateManager = .text:0x80219938; // type:function size:0x54 scope:global -ProcessCurrentState__16CScriptGunTurretFfR13CStateManager = .text:0x8021998C; // type:function size:0x74 scope:global -ProcessGunStateMachine__16CScriptGunTurretFfR13CStateManager = .text:0x80219A00; // type:function size:0x118 scope:global +ProcessFrenzyState__16CScriptGunTurretF9EStateMsgfR13CStateManager = .text:0x80218BB4; // type:function size:0x280 scope:global +ProcessExitTargettingState__16CScriptGunTurretF9EStateMsgfR13CStateManager = .text:0x80218E34; // type:function size:0x11C scope:global +ProcessTargettingState__16CScriptGunTurretF9EStateMsgfR13CStateManager = .text:0x80218F50; // type:function size:0x4DC scope:global +ProcessPanningState__16CScriptGunTurretF9EStateMsgfR13CStateManager = .text:0x8021942C; // type:function size:0x190 scope:global +ProcessReadyState__16CScriptGunTurretF9EStateMsgfR13CStateManager = .text:0x802195BC; // type:function size:0x108 scope:global +ProcessInactiveState__16CScriptGunTurretF9EStateMsgfR13CStateManager = .text:0x802196C4; // type:function size:0x274 scope:global +ProcessDeactivatingState__16CScriptGunTurretF9EStateMsgfR13CStateManager = .text:0x80219938; // type:function size:0x54 scope:global +ProcessCurrentState__16CScriptGunTurretF9EStateMsgfR13CStateManager = .text:0x8021998C; // type:function size:0x74 scope:global +ProcessGunStateMachine__16CScriptGunTurretF9EStateMsgfR13CStateManager = .text:0x80219A00; // type:function size:0x118 scope:global SetTurretState__16CScriptGunTurretFQ216CScriptGunTurret12ETurretState = .text:0x80219B18; // type:function size:0x80 scope:global GetOrbitPosition__16CScriptGunTurretCFRC13CStateManager = .text:0x80219B98; // type:function size:0x3C scope:global GetAimPosition__16CScriptGunTurretCFRC13CStateManagerf = .text:0x80219BD4; // type:function size:0xE4 scope:global @@ -17394,13 +17394,13 @@ skUnderwaterTexMtx = .rodata:0x803D27E0; // type:object size:0x20 scope:local da skRainDesc = .rodata:0x803D2800; // type:object size:0x18 scope:local @stringBase0 = .rodata:0x803D2818; // type:object size:0x71 scope:local data:string_table lbl_803D2890 = .rodata:0x803D2890; // type:object size:0x78 -lbl_803D2908 = .rodata:0x803D2908; // type:object size:0x34 -lbl_803D293C = .rodata:0x803D293C; // type:object size:0x108 -lbl_803D2A44 = .rodata:0x803D2A44; // type:object size:0x108 -lbl_803D2B4C = .rodata:0x803D2B4C; // type:object size:0x160 -lbl_803D2CAC = .rodata:0x803D2CAC; // type:object size:0x108 -lbl_803D2DB4 = .rodata:0x803D2DB4; // type:object size:0x108 -lbl_803D2EBC = .rodata:0x803D2EBC; // type:object size:0x134 +skStateToLocoTypeLookup = .rodata:0x803D2908; // type:object size:0x34 +skBurst2InfoTemplate = .rodata:0x803D293C; // type:object size:0x108 +skBurst3InfoTemplate = .rodata:0x803D2A44; // type:object size:0x108 +skBurst4InfoTemplate = .rodata:0x803D2B4C; // type:object size:0x160 +skOOVBurst2InfoTemplate = .rodata:0x803D2CAC; // type:object size:0x108 +skOOVBurst3InfoTemplate = .rodata:0x803D2DB4; // type:object size:0x108 +skOOVBurst4InfoTemplate = .rodata:0x803D2EBC; // type:object size:0x134 lbl_803D2FF0 = .rodata:0x803D2FF0; // type:object size:0x8 data:string lbl_803D2FF8 = .rodata:0x803D2FF8; // type:object size:0xB data:string lbl_803D3003 = .rodata:0x803D3003; // type:object size:0x35 @@ -18581,8 +18581,8 @@ lbl_803E70D0 = .data:0x803E70D0; // type:object size:0x18 __vt__9CRepulsor = .data:0x803E70E8; // type:object size:0x6C scope:global __vt__17CEnergyProjectile = .data:0x803E7158; // type:object size:0x78 scope:global lbl_803E71D0 = .data:0x803E71D0; // type:object size:0x1C -lbl_803E71EC = .data:0x803E71EC; // type:object size:0x84 -jumptable_803E7270 = .data:0x803E7270; // type:object size:0x34 scope:local +__vt__16CScriptGunTurret = .data:0x803E71EC; // type:object size:0x84 +@1043 = .data:0x803E7270; // type:object size:0x34 scope:local jumptable_803E72A4 = .data:0x803E72A4; // type:object size:0xA8 scope:local lbl_803E7350 = .data:0x803E7350; // type:object size:0x2CC jumptable_803E761C = .data:0x803E761C; // type:object size:0x60 scope:local @@ -21879,10 +21879,8 @@ lbl_805A90AC = .sbss:0x805A90AC; // type:object size:0x1 data:byte lbl_805A90B0 = .sbss:0x805A90B0; // type:object size:0x4 data:4byte lbl_805A90B4 = .sbss:0x805A90B4; // type:object size:0x1 data:byte kCheckMaterial__17CEnergyProjectile = .sbss:0x805A90B8; // type:object size:0x8 data:8byte -lbl_805A90C0 = .sbss:0x805A90C0; // type:object size:0x4 data:4byte -lbl_805A90C4 = .sbss:0x805A90C4; // type:object size:0x4 data:4byte -lbl_805A90C8 = .sbss:0x805A90C8; // type:object size:0x4 data:4byte -lbl_805A90CC = .sbss:0x805A90CC; // type:object size:0x4 data:4byte +skTurretMaterialList = .sbss:0x805A90C0; // type:object size:0x8 data:8byte +skGunMaterialList = .sbss:0x805A90C8; // type:object size:0x8 data:8byte lbl_805A90D0 = .sbss:0x805A90D0; // type:object size:0x4 align:4 data:float lbl_805A90D4 = .sbss:0x805A90D4; // type:object size:0x1 data:byte lbl_805A90D8 = .sbss:0x805A90D8; // type:object size:0x8 data:byte diff --git a/config/GM8E01_01/symbols.txt b/config/GM8E01_01/symbols.txt index 8bb71e74..d5b81ad1 100644 --- a/config/GM8E01_01/symbols.txt +++ b/config/GM8E01_01/symbols.txt @@ -9866,7 +9866,7 @@ LaunchProjectile__16CScriptGunTurretFR13CStateManager = .text:0x80217484; // typ ShouldFire__16CScriptGunTurretCFR13CStateManager = .text:0x802179CC; // type:function size:0x54 scope:global InDetectionRange__16CScriptGunTurretCFR13CStateManager = .text:0x80217A20; // type:function size:0x134 scope:global IsPlayerInFiringRange__16CScriptGunTurretCFR13CStateManager = .text:0x80217B54; // type:function size:0x14C scope:global -IsInsignificantRotation__16CScriptGunTurretCFf = .text:0x80217CA0; // type:function size:0xAC scope:global +IsStopped__16CScriptGunTurretCFf = .text:0x80217CA0; // type:function size:0xAC scope:global UpdateHealthInfo__16CScriptGunTurretFR13CStateManager = .text:0x80217D4C; // type:function size:0x164 scope:global UpdateExtensionModelState__16CScriptGunTurretFf = .text:0x80217EB0; // type:function size:0x128 scope:global UpdateGunOrientation__16CScriptGunTurretFfR13CStateManager = .text:0x80217FD8; // type:function size:0x8D4 scope:global diff --git a/include/MetroidPrime/ScriptObjects/CScriptGunTurret.hpp b/include/MetroidPrime/ScriptObjects/CScriptGunTurret.hpp index 7af6e4bb..620c8626 100644 --- a/include/MetroidPrime/ScriptObjects/CScriptGunTurret.hpp +++ b/include/MetroidPrime/ScriptObjects/CScriptGunTurret.hpp @@ -6,13 +6,57 @@ #include "MetroidPrime/CDamageInfo.hpp" #include "MetroidPrime/CDamageVulnerability.hpp" #include "MetroidPrime/CHealthInfo.hpp" +#include "MetroidPrime/Enemies/CAiFuncMap.hpp" +#include "MetroidPrime/Enemies/CBurstFire.hpp" +#include "MetroidPrime/Weapons/CProjectileInfo.hpp" +class CCollisionActorManager; +class CElementGen; class CScriptGunTurretData { public: CScriptGunTurretData(CInputStream& in, int propCount); - CScriptGunTurretData(const CScriptGunTurretData& other); static int GetMinProperties() { return skMinProperties; } + static int GetNumProperties() { return 48; } + + float GetIntoDeactivateDelay() const { return x0_intoDeactivateDelay; } + float GetIntoActivateDelay() const { return x4_intoActivateDelay; } + float GetReloadTime() const { return x8_reloadTime; } + float GetReloadTimeVariance() const { return xc_reloadTimeVariance; } + float GetPanStartTime() const { return x10_panStartTime; } + float GetPanHoldTime() const { return x14_panHoldTime; } + float GetPanTotalSearchTime() const { return x18_totalPanSearchTime; } + float GetLeftMaxAngle() const { return x1c_leftMaxAngle; } + float GetRightMaxAngle() const { return x20_rightMaxAngle; } + float GetDownMaxAngle() const { return x24_downMaxAngle; } + float GetTurnSpeed() const { return x28_turnSpeed; } + float GetDetectionRange() const { return x2c_detectionRange; } + float GetDetectionZRange() const { return x30_detectionZRange; } + float GetFreezeDuration() const { return x34_freezeDuration; } + float GetFreezeVariance() const { return x38_freezeVariance; } + bool UseFreezeTimeout() const { return x3c_freezeTimeout; } + CAssetId GetProjectileRes() const { return x40_projectileRes; } + const CDamageInfo& GetProjectileDamage() const { return x44_projectileDamage; } + CAssetId GetIdleLightRes() const { return x60_idleLightRes; } + CAssetId GetDeactivateLightRes() const { return x64_deactivateLightRes; } + CAssetId GetTargettingLightRes() const { return x68_targettingLightRes; } + CAssetId GetFrozenEffectRes() const { return x6c_frozenEffectRes; } + CAssetId GetChargingEffectRes() const { return x70_chargingEffectRes; } + CAssetId GetPanningEffectRes() const { return x74_panningEffectRes; } + CAssetId GetVisorEffectRes() const { return x78_visorEffectRes; } + ushort GetTrackingSoundId() const { return x7c_trackingSoundId; } + ushort GetLockOnSoundId() const { return x7e_lockOnSoundId; } + ushort GetUnFreezeSoundId() const { return x80_unfreezeSoundId; } + ushort GetStopClankSoundId() const { return x82_stopClankSoundId; } + ushort GetChargingSoundId() const { return x84_chargingSoundId; } + ushort GetVisorSoundId() const { return x86_visorSoundId; } + CAssetId GetExtensionModelResId() const { return x88_extensionModelResId; } + float GetExtensionDropDownDist() const { return x8c_extensionDropDownDist; } + uint GetNumInitialShots() const { return x90_numInitialShots; } + uint GetInitialShotTableIndex() const { return x94_initialShotTableIndex; } + uint GetNumSubsequentShots() const { return x98_numSubsequentShots; } + float GetFrenzyDuration() const { return x9c_frenzyDuration; } + bool IsScriptedStartOnly() const { return xa0_scriptedStartOnly; } private: static const int skMinProperties; @@ -64,18 +108,126 @@ public: kTC_Gun, }; + enum ETurretState { + kTS_Invalid = -1, + kTS_Destroyed, + kTS_Deactive, + kTS_DeactiveFromReady, + kTS_Deactivating, + kTS_DeactivatingFromReady, + kTS_Inactive, + kTS_Ready, + kTS_PanningA, + kTS_PanningB, + kTS_Targeting, + kTS_Firing, + kTS_ExitTargeting, + kTS_Frenzy + }; + CScriptGunTurret(TUniqueId uid, const rstl::string& name, ETurretComponent comp, const CEntityInfo& info, const CTransform4f& xf, const CModelData& mData, - const CAABox& aabb, const CHealthInfo& hInfo, - const CDamageVulnerability& dVuln, const CActorParameters& aParms, - const CScriptGunTurretData& turretData); + const CAABox& aabb, const CHealthInfo& hInfo, const CDamageVulnerability& dVuln, + const CActorParameters& aParms, const CScriptGunTurretData& turretData); ~CScriptGunTurret(); + void Think(float dt, CStateManager& mgr) override; + void AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateManager& mgr) override; void Accept(IVisitor& visitor) override; + void Render(const CStateManager& mgr) const override; + void AddToRenderer(const CFrustumPlanes&, const CStateManager&) const override; + rstl::optional_object< CAABox > GetTouchBounds() const override; + void Touch(CActor& actor, CStateManager& mgr) override; + + CHealthInfo* HealthInfo(CStateManager& mgr) override { return &x264_healthInfo; } + const CDamageVulnerability* GetDamageVulnerability() const override { return &x26c_damageVuln; } private: - uchar x258_pad[0x568 - 0x258]; + static const char* skGunLCTRName; + static const char* skBlastLCTRName; + static const char* skLightLCTRName; + static const char* skLockonTargetLCTRName; + void ProcessGunStateMachine(float dt, CStateManager& mgr); + void ProcessCurrentState(EStateMsg msg, float dt, CStateManager& mgr); + void ProcessDeactivatingState(EStateMsg msg, float dt, CStateManager& mgr); + void ProcessInactiveState(EStateMsg msg, float dt, CStateManager& mgr); + void ProcessReadyState(EStateMsg msg, float dt, CStateManager& mgr); + void ProcessPanningState(EStateMsg msg, float dt, CStateManager& mgr); + void ProcessTargettingState(EStateMsg msg, float dt, CStateManager& mgr); + void ProcessExitTargettingState(EStateMsg msg, float dt, CStateManager& mgr); + void ProcessFrenzyState(EStateMsg msg, float dt, CStateManager& mgr); + void UpdateTurretAnimation(); + void UpdateGunOrientation(float dt, CStateManager& mgr); + CVector3f UpdateExtensionModelState(float dt); + void UpdateTargettingSound(float dt); + void UpdateGunParticles(float dt, CStateManager& mgr); + void UpdateGunCollisionManager(float dt, CStateManager& mgr); + void UpdateFrozenState(float dt, CStateManager& mgr); + void UpdateHealthInfo(CStateManager& mgr); + void SetupCollisionManager(CStateManager& mgr); + void SetTurretState(ETurretState state, CStateManager& mgr); + void SetupExtensionModel() { + const CModelData mData( + CStaticRes(x2d4_data.GetExtensionModelResId(), GetModelData()->GetScale())); + x4a4_extensionModel = rstl::optional_object< CModelData >(mData); + } + + bool IsStopped(float dt) const; + + ETurretComponent x258_type; + TUniqueId x25c_gunId; + float x260_lastGunHP; + CHealthInfo x264_healthInfo; + CDamageVulnerability x26c_damageVuln; + CScriptGunTurretData x2d4_data; + TUniqueId x378_; + CProjectileInfo x37c_projectileInfo; + CBurstFire x3a4_burstFire; + CVector3f x404_targetPosition; + TLockedToken< CGenDescription > x410_idleLightDesc; + TLockedToken< CGenDescription > x41c_deactivateLightDesc; + TLockedToken< CGenDescription > x428_targettingLightDesc; + TLockedToken< CGenDescription > x434_frozenEffectDesc; + TLockedToken< CGenDescription > x440_chargingEffectDesc; + TLockedToken< CGenDescription > x44c_panningEffectDesc; + rstl::optional_object< TLockedToken< CGenDescription > > x458_visorEffectDesc; + rstl::auto_ptr< CElementGen > x468_idleLight; + rstl::auto_ptr< CElementGen > x470_deactivateLight; + rstl::auto_ptr< CElementGen > x478_targettingLight; + rstl::auto_ptr< CElementGen > x480_frozenEffect; + rstl::auto_ptr< CElementGen > x488_chargingEffect; + rstl::auto_ptr< CElementGen > x490_panningEffect; + TUniqueId x498_lightId; + rstl::single_ptr< CCollisionActorManager > x49c_collisionManager; + TUniqueId x4a0_collisionActor; + rstl::optional_object< CModelData > x4a4_extensionModel; + float x4f4_extensionRange; + float x4f8_extensionT; + CVector3f x4fc_extensionOffset; + CSegId x508_gunSDKSeg; + CSfxHandle x50c_targetingEmitter; + float x510_timeSinceLastTargetSfx; + CVector3f x514_lastFrontVector; + ETurretState x520_state; + float x524_curStateTime; + float x528_curInactiveTime; + float x52c_curActiveTime; + float x530_curPanTime; + float x534_fireCycleRemTime; + float x538_halfFireCycleDur; + float x53c_freezeRemTime; + int x540_turretAnim; + CVector3f x544_originalFrontVec; + CVector3f x550_originalRightVec; + int x55c_additiveChargeAnim; + bool x560_24_dead : 1; + bool x560_25_frozen : 1; + bool x560_26_firedWithSetBurst : 1; + bool x560_27_burstSet : 1; + bool x560_28_hasBeenActivated : 1; + bool x560_29_scriptedStart : 1; + bool x560_30_needsStopClankSound : 1; + bool x560_31_frenzyReverse : 1; }; -CHECK_SIZEOF(CScriptGunTurret, 0x568) #endif // _CSCRIPTGUNTURRET diff --git a/src/MetroidPrime/ScriptObjects/CScriptGunTurret.cpp b/src/MetroidPrime/ScriptObjects/CScriptGunTurret.cpp new file mode 100644 index 00000000..1707eacf --- /dev/null +++ b/src/MetroidPrime/ScriptObjects/CScriptGunTurret.cpp @@ -0,0 +1,457 @@ +#include "MetroidPrime/ScriptObjects/CScriptGunTurret.hpp" + +#include "MetaRender/CCubeRenderer.hpp" +#include "MetroidPrime/CCollisionActorManager.hpp" +#include "MetroidPrime/CGameLight.hpp" +#include "MetroidPrime/Enemies/CAiFuncMap.hpp" +#include "MetroidPrime/Player/CPlayer.hpp" +#include "MetroidPrime/Weapons/CGameProjectile.hpp" + +#include +#include +#include +#include + +static const uint skStateToLocoTypeLookup[] = { + 5, 7, 9, 0, 1, 0, 1, 2, 3, 1, 1, 1, 1, +}; + +static const SBurst skBurst2InfoTemplate[] = { + {3, {1, 2, -1, -1, 0, 0, 0, 0}, 0.150000, 0.050000}, + {3, {7, 6, -1, -1, 0, 0, 0, 0}, 0.150000, 0.050000}, + {4, {3, 5, -1, -1, 0, 0, 0, 0}, 0.150000, 0.050000}, + {60, {16, 4, -1, -1, 0, 0, 0, 0}, 0.150000, 0.050000}, + {30, {4, 4, -1, -1, 0, 0, 0, 0}, 0.150000, 0.050000}, + {0, {0, 0, 0, 0, 0, 0, 0, 0}, 0.000000, 0.000000}, +}; + +static const SBurst skBurst3InfoTemplate[] = { + {30, {4, 5, 4, -1, 0, 0, 0, 0}, 0.150000, 0.050000}, + {30, {2, 3, 4, -1, 0, 0, 0, 0}, 0.150000, 0.050000}, + {30, {3, 4, 5, -1, 0, 0, 0, 0}, 0.150000, 0.050000}, + {5, {16, 1, 2, -1, 0, 0, 0, 0}, 0.150000, 0.050000}, + {5, {8, 7, 6, -1, 0, 0, 0, 0}, 0.150000, 0.050000}, + {0, {0, 0, 0, 0, 0, 0, 0, 0}, 0.000000, 0.000000}, +}; + +static const SBurst skBurst4InfoTemplate[] = { + {5, {16, 1, 2, 3, 0, 0, 0, 0}, 0.150000, 0.050000}, + {5, {9, 8, 7, 6, 0, 0, 0, 0}, 0.150000, 0.050000}, + {15, {2, 3, 4, 5, 0, 0, 0, 0}, 0.150000, 0.050000}, + {15, {5, 4, 3, 2, 0, 0, 0, 0}, 0.150000, 0.050000}, + {15, {10, 11, 4, 13, 0, 0, 0, 0}, 0.150000, 0.050000}, + {15, {14, 13, 4, 11, 0, 0, 0, 0}, 0.150000, 0.050000}, + {30, {2, 4, 4, 6, 0, 0, 0, 0}, 0.150000, 0.050000}, + {0, {0, 0, 0, 0, 0, 0, 0, 0}, 0.000000, 0.000000}, +}; + +const SBurst skOOVBurst2InfoTemplate[] = { + {20, {16, 15, -1, -1, 0, 0, 0, 0}, 0.150000, 0.050000}, + {20, {8, 9, -1, -1, 0, 0, 0, 0}, 0.150000, 0.050000}, + {20, {13, 11, -1, -1, 0, 0, 0, 0}, 0.150000, 0.050000}, + {20, {2, 6, -1, -1, 0, 0, 0, 0}, 0.150000, 0.050000}, + {20, {3, 4, -1, -1, 0, 0, 0, 0}, 0.150000, 0.050000}, + {0, {0, 0, 0, 0, 0, 0, 0, 0}, 0.000000, 0.000000}, +}; + +const SBurst skOOVBurst3InfoTemplate[] = { + {10, {14, 4, 10, -1, 0, 0, 0, 0}, 0.150000, 0.050000}, + {10, {15, 13, 4, -1, 0, 0, 0, 0}, 0.150000, 0.050000}, + {10, {9, 11, 4, -1, 0, 0, 0, 0}, 0.150000, 0.050000}, + {35, {15, 13, 11, -1, 0, 0, 0, 0}, 0.150000, 0.050000}, + {35, {9, 11, 13, -1, 0, 0, 0, 0}, 0.150000, 0.050000}, + {0, {0, 0, 0, 0, 0, 0, 0, 0}, 0.000000, 0.000000}, +}; + +const SBurst skOOVBurst4InfoTemplate[] = { + {10, {14, 13, 4, 11, 0, 0, 0, 0}, 0.150000, 0.050000}, + {30, {1, 15, 13, 11, 0, 0, 0, 0}, 0.150000, 0.050000}, + {20, {16, 15, 14, 13, 0, 0, 0, 0}, 0.150000, 0.050000}, + {10, {8, 9, 11, 4, 0, 0, 0, 0}, 0.150000, 0.050000}, + {10, {1, 15, 13, 4, 0, 0, 0, 0}, 0.150000, 0.050000}, + {20, {8, 9, 10, 11, 0, 0, 0, 0}, 0.150000, 0.050000}, + {0, {0, 0, 0, 0, 0, 0, 0, 0}, 0.000000, 0.000000}, +}; + +const char* CScriptGunTurret::skGunLCTRName = "Gun_SDK"; +const char* CScriptGunTurret::skBlastLCTRName = "Blast_LCTR"; +const char* CScriptGunTurret::skLightLCTRName = "light_LCTR"; +const char* CScriptGunTurret::skLockonTargetLCTRName = "lockon_target_LCTR"; + +static const SBurst* skBursts[] = { + skBurst2InfoTemplate, + skBurst3InfoTemplate, + skBurst4InfoTemplate, + skOOVBurst2InfoTemplate, + skOOVBurst3InfoTemplate, + skOOVBurst4InfoTemplate, + nullptr, +}; + +#pragma inline_max_size(250) +CScriptGunTurretData::CScriptGunTurretData(CInputStream& in, const int propCount) +: x0_intoDeactivateDelay(in.Get< float >()) +, x4_intoActivateDelay(in.Get< float >()) +, x8_reloadTime(in.Get< float >()) +, xc_reloadTimeVariance(in.Get< float >()) +, x10_panStartTime(in.Get< float >()) +, x14_panHoldTime(in.Get< float >()) +, x18_totalPanSearchTime(30.f) +, x1c_leftMaxAngle(CRelAngle::FromDegrees(in.Get< float >()).AsRadians()) +, x20_rightMaxAngle(CRelAngle::FromDegrees(in.Get< float >()).AsRadians()) +, x24_downMaxAngle(CRelAngle::FromDegrees(in.Get< float >()).AsRadians()) +, x28_turnSpeed(CRelAngle::FromDegrees(in.Get< float >()).AsRadians()) +, x2c_detectionRange(in.Get< float >()) +, x30_detectionZRange(in.Get< float >()) +, x34_freezeDuration(in.Get< float >()) +, x38_freezeVariance(in.Get< float >()) +, x3c_freezeTimeout(propCount >= 48 ? in.Get< bool >() : false) +, x40_projectileRes(in.Get< CAssetId >()) +, x44_projectileDamage(in) +, x60_idleLightRes(in.Get< CAssetId >()) +, x64_deactivateLightRes(in.Get< CAssetId >()) +, x68_targettingLightRes(in.Get< CAssetId >()) +, x6c_frozenEffectRes(in.Get< CAssetId >()) +, x70_chargingEffectRes(in.Get< CAssetId >()) +, x74_panningEffectRes(in.Get< CAssetId >()) +, x78_visorEffectRes(propCount >= 44 ? in.Get< CAssetId >() : kInvalidAssetId) +, x7c_trackingSoundId(CSfxManager::TranslateSFXID(in.Get< int >())) +, x7e_lockOnSoundId(CSfxManager::TranslateSFXID(in.Get< int >())) +, x80_unfreezeSoundId(CSfxManager::TranslateSFXID(in.Get< int >())) +, x82_stopClankSoundId(CSfxManager::TranslateSFXID(in.Get< int >())) +, x84_chargingSoundId(CSfxManager::TranslateSFXID(in.Get< int >())) +, x86_visorSoundId(propCount >= 45 ? CSfxManager::TranslateSFXID(in.Get< int >()) + : CSfxManager::kInternalInvalidSfxId) +, x88_extensionModelResId(in.Get< CAssetId >()) +, x8c_extensionDropDownDist(in.Get< float >()) +, x90_numInitialShots(in.Get< int >()) +, x94_initialShotTableIndex(in.Get< int >()) +, x98_numSubsequentShots(in.Get< int >()) +, x9c_frenzyDuration(propCount >= 47 ? in.Get< float >() : 3.f) +, xa0_scriptedStartOnly(propCount >= 46 ? in.Get< bool >() : false) {} + +static const CMaterialList skTurretMaterialList(kMT_Character); +static const CMaterialList skGunMaterialList(kMT_Solid, kMT_Character, kMT_Orbit, kMT_Target); + +CScriptGunTurret::CScriptGunTurret(TUniqueId uid, const rstl::string& name, ETurretComponent comp, + const CEntityInfo& info, const CTransform4f& xf, + const CModelData& mData, const CAABox& aabb, + const CHealthInfo& hInfo, const CDamageVulnerability& dVuln, + const CActorParameters& aParms, + const CScriptGunTurretData& turretData) +: CPhysicsActor(uid, true, name, info, xf, mData, + comp == kTC_Base ? skTurretMaterialList : skGunMaterialList, aabb, + SMoverData(1000.f), aParms, 0.3f, 0.1f) +, x258_type(comp) +, x25c_gunId(kInvalidUniqueId) +, x260_lastGunHP(0.f) +, x264_healthInfo(hInfo) +, x26c_damageVuln(dVuln) +, x2d4_data(turretData) +, x378_(kInvalidUniqueId) +, x37c_projectileInfo(turretData.GetProjectileRes(), turretData.GetProjectileDamage()) +, x3a4_burstFire(skBursts, 1) +, x404_targetPosition(CVector3f::Zero()) +, x410_idleLightDesc(gpSimplePool->GetObj(SObjectTag('PART', turretData.GetIdleLightRes()))) +, x41c_deactivateLightDesc( + gpSimplePool->GetObj(SObjectTag('PART', turretData.GetDeactivateLightRes()))) +, x428_targettingLightDesc( + gpSimplePool->GetObj(SObjectTag('PART', turretData.GetTargettingLightRes()))) +, x434_frozenEffectDesc(gpSimplePool->GetObj(SObjectTag('PART', turretData.GetFrozenEffectRes()))) +, x440_chargingEffectDesc( + gpSimplePool->GetObj(SObjectTag('PART', turretData.GetChargingEffectRes()))) +, x44c_panningEffectDesc(gpSimplePool->GetObj(SObjectTag('PART', turretData.GetPanningEffectRes()))) +, x458_visorEffectDesc( + turretData.GetVisorEffectRes() != kInvalidAssetId + ? rstl::optional_object< TLockedToken< CGenDescription > >( + gpSimplePool->GetObj(SObjectTag('PART', turretData.GetVisorEffectRes()))) + : rstl::optional_object_null()) +, x468_idleLight(rs_new CElementGen(x410_idleLightDesc)) +, x470_deactivateLight(rs_new CElementGen(x41c_deactivateLightDesc)) +, x478_targettingLight(rs_new CElementGen(x428_targettingLightDesc)) +, x480_frozenEffect(rs_new CElementGen(x434_frozenEffectDesc)) +, x488_chargingEffect(rs_new CElementGen(x440_chargingEffectDesc)) +, x490_panningEffect(rs_new CElementGen(x44c_panningEffectDesc)) +, x498_lightId(kInvalidUniqueId) +, x4a0_collisionActor(kInvalidUniqueId) +, x4a4_extensionModel(rstl::optional_object< CModelData >()) +, x4f4_extensionRange(0.f) +, x4f8_extensionT(0.f) +, x4fc_extensionOffset(xf.GetTranslation()) +, x510_timeSinceLastTargetSfx(0.f) +, x514_lastFrontVector(xf.GetForward()) +, x520_state(kTS_Invalid) +, x524_curStateTime(0.f) +, x528_curInactiveTime(0.f) +, x52c_curActiveTime(0.f) +, x530_curPanTime(0.f) +, x534_fireCycleRemTime(0.f) +, x538_halfFireCycleDur(0.f) +, x53c_freezeRemTime(0.f) +, x540_turretAnim(-1) +, x544_originalFrontVec(xf.GetForward()) +, x550_originalRightVec(xf.GetRight()) +, x55c_additiveChargeAnim(-1) +, x560_24_dead(false) +, x560_25_frozen(false) +, x560_26_firedWithSetBurst(false) +, x560_27_burstSet(false) +, x560_28_hasBeenActivated(false) +, x560_29_scriptedStart(false) +, x560_30_needsStopClankSound(true) +, x560_31_frenzyReverse(false) { + + if (comp == kTC_Base && HasAnimation()) { + ModelData()->EnableLooping(true); + } + x37c_projectileInfo.Token().Lock(); +} + +CScriptGunTurret::~CScriptGunTurret() {} + +void CScriptGunTurret::Think(float dt, CStateManager& mgr) { + if (!GetActive()) { + return; + } + + switch (x258_type) { + case kTC_Base: + if (!x560_25_frozen) { + ProcessGunStateMachine(dt, mgr); + UpdateTurretAnimation(); + UpdateGunOrientation(dt, mgr); + CVector3f vec = UpdateExtensionModelState(dt); + const CAdvancementDeltas deltas = UpdateAnimation(dt, mgr, true); + SetTranslation((GetTranslation() + deltas.GetOffsetDelta()) + vec); + RotateToOR(deltas.GetOrientationDelta(), dt); + } else { + Stop(); + } + + UpdateTargettingSound(dt); + break; + case kTC_Gun: + UpdateGunParticles(dt, mgr); + const CAdvancementDeltas deltas = UpdateAnimation(dt, mgr, true); + MoveToOR(deltas.GetOffsetDelta(), dt); + RotateToOR(deltas.GetOrientationDelta(), dt); + UpdateGunCollisionManager(dt, mgr); + UpdateFrozenState(dt, mgr); + break; + } + + UpdateHealthInfo(mgr); +} + +void CScriptGunTurret::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, + CStateManager& mgr) { + CPhysicsActor::AcceptScriptMsg(msg, uid, mgr); + + switch (msg) { + case kSM_Activate: { + if (!x49c_collisionManager.null()) { + x49c_collisionManager->SetActive(mgr, true); + } + break; + } + case kSM_Deactivate: { + if (!x49c_collisionManager.null()) { + x49c_collisionManager->SetActive(mgr, false); + } + break; + } + case kSM_Registered: { + if (x258_type == kTC_Gun) { + if (x478_targettingLight->SystemHasLight()) { + x498_lightId = mgr.AllocateUniqueId(); + mgr.AddObject(rs_new CGameLight(x498_lightId, GetCurrentAreaId(), GetActive(), + rstl::string_l("ParticleLight_") + GetDebugName(), + GetTransform(), GetUniqueId(), + x478_targettingLight->GetLight(), 0, 1, 0.f)); + } + SetupCollisionManager(mgr); + } else if (x258_type == kTC_Base) { + if (x2d4_data.GetExtensionModelResId() != kInvalidAssetId) { + { + const CModelData mData( + CStaticRes(x2d4_data.GetExtensionModelResId(), GetModelData()->GetScale())); + x4a4_extensionModel = rstl::optional_object< CModelData >(mData); + } + x4f4_extensionRange = x4a4_extensionModel->GetBounds().GetDepth(); + } + + SetTurretState(kTS_Inactive, mgr); + } + break; + } + case kSM_Deleted: { + if (x258_type == kTC_Gun && x498_lightId != kInvalidUniqueId) { + mgr.DeleteObjectRequest(x498_lightId); + } + + if (x50c_targetingEmitter) { + CSfxManager::RemoveEmitter(x50c_targetingEmitter); + } + + if (!x49c_collisionManager.null()) { + x49c_collisionManager->Destroy(mgr); + } + break; + } + default: + break; + } +} + +void CScriptGunTurret::Accept(IVisitor& visitor) { visitor.Visit(*this); } +void CScriptGunTurret::Render(const CStateManager& mgr) const { + CPhysicsActor::Render(mgr); + + if (x258_type == kTC_Gun) { + if (!x560_25_frozen) { + switch (x520_state) { + case kTS_Deactive: + case kTS_DeactiveFromReady: + case kTS_Deactivating: + case kTS_DeactivatingFromReady: + x470_deactivateLight->Render(); + break; + case kTS_Inactive: + x468_idleLight->Render(); + break; + case kTS_PanningA: + case kTS_PanningB: + x490_panningEffect->Render(); + break; + case kTS_Ready: + case kTS_Targeting: + case kTS_Firing: + case kTS_ExitTargeting: + case kTS_Frenzy: + x478_targettingLight->Render(); + if (x520_state == kTS_Firing) { + x488_chargingEffect->Render(); + } + break; + default: + break; + } + } else { + x480_frozenEffect->Render(); + } + } else if (x258_type == kTC_Base && x4a4_extensionModel && x4f8_extensionT > 0.f) { + CTransform4f xf = GetTransform(); + xf.SetTranslation(x4fc_extensionOffset + x4f4_extensionRange * (CVector3f::Down() * 0.5f)); + x4a4_extensionModel->Render(mgr, xf, GetActorLights(), CModelFlags::Normal()); + } +} + +void CScriptGunTurret::AddToRenderer(const CFrustumPlanes& frustum, + const CStateManager& mgr) const { + CActor::AddToRenderer(frustum, mgr); + + if (x258_type != kTC_Gun) { + return; + } + + if (!x560_25_frozen) { + switch (x520_state) { + case kTS_Deactive: + case kTS_DeactiveFromReady: + case kTS_Deactivating: + case kTS_DeactivatingFromReady: + gpRender->AddParticleGen(*x470_deactivateLight); + break; + case kTS_Inactive: + gpRender->AddParticleGen(*x468_idleLight); + break; + case kTS_PanningA: + case kTS_PanningB: + gpRender->AddParticleGen(*x490_panningEffect); + break; + case kTS_Ready: + case kTS_Targeting: + case kTS_Firing: + case kTS_ExitTargeting: + case kTS_Frenzy: + gpRender->AddParticleGen(*x478_targettingLight); + if (x520_state == kTS_Firing || x520_state == kTS_Frenzy) { + gpRender->AddParticleGen(*x488_chargingEffect); + } + break; + default: + break; + } + } else { + gpRender->AddParticleGen(*x480_frozenEffect); + } +} + +rstl::optional_object< CAABox > CScriptGunTurret::GetTouchBounds() const { + if (GetActive() && GetMaterialList().HasMaterial(kMT_Solid)) { + return GetBoundingBox(); + } + return rstl::optional_object_null(); +} + +void CScriptGunTurret::Touch(CActor& actor, CStateManager& mgr) { + if (x258_type != kTC_Gun) { + return; + } + + if (const CGameProjectile* proj = TCastToConstPtr< CGameProjectile >(actor)) { + const CPlayer* player = mgr.GetPlayer(); + if (proj->GetOwnerId() == player->GetUniqueId()) { + const CDamageVulnerability* dVuln = GetDamageVulnerability(); + if (!x560_24_dead && x520_state != kTS_Frenzy && + (proj->GetAttribField() & CWeapon::kPA_Ice) == CWeapon::kPA_Ice && + dVuln->WeaponHits(CWeaponMode::Ice(), false)) { + x560_25_frozen = true; + SendScriptMsgs(kSS_Zero, mgr, kSM_None); + x53c_freezeRemTime = + mgr.Random()->Float() * x2d4_data.GetFreezeVariance() + x2d4_data.GetFreezeDuration(); + SetMuted(true); + } + + SendScriptMsgs(kSS_Damage, mgr, kSM_None); + } + } +} + +void CScriptGunTurret::ProcessCurrentState(EStateMsg msg, float dt, CStateManager& mgr) { + switch (x520_state) { + case kTS_Deactivating: + case kTS_DeactivatingFromReady: + ProcessDeactivatingState(msg, dt, mgr); + break; + case kTS_Inactive: + ProcessInactiveState(msg, dt, mgr); + break; + case kTS_Ready: + ProcessReadyState(msg, dt, mgr); + break; + case kTS_PanningA: + case kTS_PanningB: + ProcessPanningState(msg, dt, mgr); + break; + case kTS_Targeting: + case kTS_Firing: + ProcessTargettingState(msg, dt, mgr); + break; + case kTS_ExitTargeting: + ProcessExitTargettingState(msg, dt, mgr); + break; + case kTS_Frenzy: + ProcessFrenzyState(msg, dt, mgr); + break; + default: + break; + } +} + +bool CScriptGunTurret::IsStopped(const float dt) const { + const CVector2f thisForward(GetTransform().GetForward().ToVec2f()); + const CVector2f lastForward(x514_lastFrontVector.ToVec2f()); + + return CVector2f::GetAngleDiff(lastForward, thisForward) < dt * (2 * (M_PIF / 180)); +} \ No newline at end of file