diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 39f4633a..2df29749 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,7 +12,7 @@ jobs: strategy: fail-fast: false matrix: - version: [GM8E01_00] # GM8E01_01, GM8E01_48 + version: [GM8E01_00, GM8E01_01] #, GM8E01_48 steps: # Checkout the repository diff --git a/config/GM8E01_00/symbols.txt b/config/GM8E01_00/symbols.txt index 2cc2ca15..eeb97a9f 100644 --- a/config/GM8E01_00/symbols.txt +++ b/config/GM8E01_00/symbols.txt @@ -11761,7 +11761,7 @@ fn_802AE990 = .text:0x802AE990; // type:function size:0x2A0 fn_802AEC30 = .text:0x802AEC30; // type:function size:0x18 fn_802AEC48 = .text:0x802AEC48; // type:function size:0xC0 GetBounds__17CProjectileWeaponCFv = .text:0x802AED08; // type:function size:0x400 scope:global -SetGlobalSeed__17CProjectileWeaponFUs = .text:0x802AF108; // type:function size:0x8 scope:global +SetGlobalSeed__17CProjectileWeaponFUi = .text:0x802AF108; // type:function size:0x8 scope:global GetGravity__17CProjectileWeaponCFv = .text:0x802AF110; // type:function size:0x8 scope:global SetGravity__17CProjectileWeaponFRC9CVector3f = .text:0x802AF118; // type:function size:0x1C scope:global GetVelocity__17CProjectileWeaponCFv = .text:0x802AF134; // type:function size:0x8 scope:global @@ -18623,8 +18623,8 @@ lbl_803EC448 = .data:0x803EC448; // type:object size:0x28 __vt__23CCollidableOBBTreeGroup = .data:0x803EC470; // type:object size:0x28 scope:global __vt__60TObjOwnerDerivedFromIObj<32CCollidableOBBTreeGroupContainer> = .data:0x803EC498; // type:object size:0x10 jumptable_803EC4A8 = .data:0x803EC4A8; // type:object size:0x20 scope:local -lbl_803EC4C8 = .data:0x803EC4C8; // type:object size:0x20 -lbl_803EC4E8 = .data:0x803EC4E8; // type:object size:0x20 +__vt__17CProjectileWeapon = .data:0x803EC4C8; // type:object size:0x20 +__vt__17IWeaponProjectile = .data:0x803EC4E8; // type:object size:0x20 scope:weak lbl_803EC508 = .data:0x803EC508; // type:object size:0x10 lbl_803EC518 = .data:0x803EC518; // type:object size:0x10 __vt__22CDefaultWeaponRenderer = .data:0x803EC528; // type:object size:0x10 scope:weak diff --git a/config/GM8E01_01/symbols.txt b/config/GM8E01_01/symbols.txt index 0beeb8a7..d3f49355 100644 --- a/config/GM8E01_01/symbols.txt +++ b/config/GM8E01_01/symbols.txt @@ -11761,7 +11761,7 @@ fn_802AE990 = .text:0x802AEA3C; // type:function size:0x2A0 scope:global fn_802AEC30 = .text:0x802AECDC; // type:function size:0x18 scope:global fn_802AEC48 = .text:0x802AECF4; // type:function size:0xC0 scope:global GetBounds__17CProjectileWeaponCFv = .text:0x802AEDB4; // type:function size:0x400 scope:global -SetGlobalSeed__17CProjectileWeaponFUs = .text:0x802AF1B4; // type:function size:0x8 scope:global +SetGlobalSeed__17CProjectileWeaponFUi = .text:0x802AF1B4; // type:function size:0x8 scope:global GetGravity__17CProjectileWeaponCFv = .text:0x802AF1BC; // type:function size:0x8 scope:global SetGravity__17CProjectileWeaponFRC9CVector3f = .text:0x802AF1C4; // type:function size:0x1C scope:global GetVelocity__17CProjectileWeaponCFv = .text:0x802AF1E0; // type:function size:0x8 scope:global @@ -17741,7 +17741,7 @@ lbl_803D62D0 = .rodata:0x803D62D0; // type:object size:0x20 lbl_803D62F0 = .rodata:0x803D62F0; // type:object size:0x8 lbl_803D62F8 = .rodata:0x803D62F8; // type:object size:0x20 lbl_803D6318 = .rodata:0x803D6318; // type:object size:0x50 -lbl_803D6368 = .rodata:0x803D6368; // type:object size:0x8 +@stringBase0 = .rodata:0x803D6368; // type:object size:0x7 scope:local data:string_table lbl_803D6370 = .rodata:0x803D6370; // type:object size:0x8 lbl_803D6378 = .rodata:0x803D6378; // type:object size:0x80 lbl_803D63F8 = .rodata:0x803D63F8; // type:object size:0x178 data:4byte @@ -18657,8 +18657,8 @@ lbl_803EC628 = .data:0x803EC628; // type:object size:0x28 lbl_803EC650 = .data:0x803EC650; // type:object size:0x28 lbl_803EC678 = .data:0x803EC678; // type:object size:0x10 jumptable_803EC688 = .data:0x803EC688; // type:object size:0x20 scope:local -lbl_803EC6A8 = .data:0x803EC6A8; // type:object size:0x20 -lbl_803EC6C8 = .data:0x803EC6C8; // type:object size:0x20 +__vt__17CProjectileWeapon = .data:0x803EC6A8; // type:object size:0x20 +__vt__17IWeaponProjectile = .data:0x803EC6C8; // type:object size:0x20 scope:weak lbl_803EC6E8 = .data:0x803EC6E8; // type:object size:0x10 lbl_803EC6F8 = .data:0x803EC6F8; // type:object size:0x10 __vt__22CDefaultWeaponRenderer = .data:0x803EC708; // type:object size:0x10 @@ -21051,7 +21051,7 @@ lbl_805A880C = .sdata:0x805A880C; // type:object size:0x1 data:byte lbl_805A8810 = .sdata:0x805A8810; // type:object size:0x8 data:4byte lbl_805A8818 = .sdata:0x805A8818; // type:object size:0x8 data:4byte lbl_805A8820 = .sdata:0x805A8820; // type:object size:0x8 -lbl_805A8828 = .sdata:0x805A8828; // type:object size:0x8 data:4byte +skGlobalSeed__17CProjectileWeapon = .sdata:0x805A8828; // type:object size:0x4 data:4byte sWeaponRenderer__15IWeaponRenderer = .sdata:0x805A8830; // type:object size:0x8 data:4byte lbl_805A8838 = .sdata:0x805A8838; // type:object size:0x4 data:float lbl_805A883C = .sdata:0x805A883C; // type:object size:0x4 data:float diff --git a/include/Kyoto/Particles/CElementGen.hpp b/include/Kyoto/Particles/CElementGen.hpp index d2c3d779..24b76b87 100644 --- a/include/Kyoto/Particles/CElementGen.hpp +++ b/include/Kyoto/Particles/CElementGen.hpp @@ -77,6 +77,7 @@ public: int GetMaxParticles() const { return x90_MAXP; } int GetEmitterTime() const; int GetSystemCount(); + void EndLifetime(); int GetCumulativeParticleCount() const { return x260_cumulativeParticles; } bool IsIndirectTextured() diff --git a/include/Kyoto/Particles/CParticleSwoosh.hpp b/include/Kyoto/Particles/CParticleSwoosh.hpp new file mode 100644 index 00000000..b404eee9 --- /dev/null +++ b/include/Kyoto/Particles/CParticleSwoosh.hpp @@ -0,0 +1,97 @@ +#ifndef _CPARTICLESWOOSH_HPP +#define _CPARTICLESWOOSH_HPP + +#include "Kyoto/CRandom16.hpp" +#include "Kyoto/Graphics/CColor.hpp" +#include "Kyoto/Math/CTransform4f.hpp" +#include "Kyoto/Particles/CParticleGen.hpp" +#include "Kyoto/Particles/IElement.hpp" +#include "Kyoto/TToken.hpp" +#include "MetroidPrime/CParticleDatabase.hpp" + +#include "dolphin/gx/GXEnum.h" + +class CParticleSwoosh : public CParticleGen { +public: + struct SSwooshData {}; + CParticleSwoosh(TToken< CSwooshDescription > desc, int i); + ~CParticleSwoosh(); + + void Update(double); + void Render() const; + void SetOrientation(const CTransform4f& orientation); + void SetTranslation(const CVector3f& translation); + void SetGlobalOrientation(const CTransform4f& orientation); + void SetGlobalTranslation(const CVector3f& translation); + void SetGlobalScale(const CVector3f& scale); + void SetLocalScale(const CVector3f& scale); + void SetParticleEmission(bool emission); + void SetModulationColor(const CColor& col); + void SetGeneratorRate(float rate) {} + const CTransform4f& GetOrientation() const; + const CVector3f& GetTranslation() const; + CTransform4f GetGlobalOrientation() const; + CVector3f GetGlobalTranslation() const; + CVector3f GetGlobalScale() const; + bool GetParticleEmission() const; + CColor GetModulationColor() const; + bool IsSystemDeletable() const; + rstl::optional_object< CAABox > GetBounds() const; + int GetParticleCount() const; + bool SystemHasLight() const; + CLight GetLight(); + void DestroyParticles(); + void AddModifier(CWarp*); + uint Get4CharId() const; + + void SetWarmUp() { x1d0_26_forceOneUpdate = true; } + +private: + TLockedToken< CSwooshDescription > x1c_desc; + uint x28_curFrame; + int x2c_PSLT; + double x30_curTime; + CVector3f x38_translation; + CTransform4f x44_orientation; + CTransform4f x74_invOrientation; + CVector3f xa4_globalTranslation; + CTransform4f xa4_globalOrientation; + CVector3f xe0_globalScale; + CTransform4f xec_scaleXf; + CTransform4f x11c_invScaleXf; + CVector3f x14c_localScale; + uint x158_curParticle; + rstl::vector< SSwooshData > x15c_swooshes; + rstl::vector< CVector3f > x16c_p0; + rstl::vector< CVector3f > x17c_p1; + rstl::vector< CVector3f > x18c_p2; + rstl::vector< CVector3f > x19c_p3; + uint x1ac_particleCount; + int x1b0_SPLN; + int x1b4_LENG; + int x1b8_SIDE; + GXPrimitive x1bc_prim; + CRandom16 x1c0_rand; + float x1c4_; + float x1c8_; + float x1cc_TSPN; + bool x1d0_24_emitting : 1; + bool x1d0_25_AALP : 1; + bool x1d0_26_forceOneUpdate : 1; + bool x1d0_27_renderGaps : 1; + bool x1d0_28_LLRD : 1; + bool x1d0_29_VLS1 : 1; + bool x1d0_30_VLS2 : 1; + bool x1d0_31_constantTex : 1; + bool x1d1_24_constantUv : 1; + SUVElementSet x1d4_uvs; + CTexture* x1e4_tex; + float x1e8_uvSpan; + int x1ec_TSPN; + CVector3f x1f0_aabbMin; + CVector3f x1fc_aabbMax; + float x208_maxRadius; + CColor x20c_moduColor; +}; + +#endif // _CPARTICLESWOOSH_HPP diff --git a/include/MetaRender/CCubeRenderer.hpp b/include/MetaRender/CCubeRenderer.hpp index 4cd5a189..6e0b2837 100644 --- a/include/MetaRender/CCubeRenderer.hpp +++ b/include/MetaRender/CCubeRenderer.hpp @@ -35,7 +35,7 @@ public: void PostRenderFogs() override; void SetModelMatrix(const CTransform4f& xf) override; void AddParticleGen(const CParticleGen& gen) override; - void AddParticleGen2() override; + void AddParticleGen(const CParticleGen& gen, const CVector3f&, const CAABox&) override; void AddPlaneObject() override; void AddDrawable(const void* obj, const CVector3f& pos, const CAABox& bounds, int mode, IRenderer::EDrawableSorting sorting) override; diff --git a/include/MetaRender/IRenderer.hpp b/include/MetaRender/IRenderer.hpp index 94a8275a..a2b8fd00 100644 --- a/include/MetaRender/IRenderer.hpp +++ b/include/MetaRender/IRenderer.hpp @@ -2,6 +2,7 @@ #define _IRENDERER #include "types.h" +#include #include "rstl/pair.hpp" #include "rstl/vector.hpp" @@ -46,7 +47,7 @@ public: virtual void PostRenderFogs(); virtual void SetModelMatrix(const CTransform4f& xf); virtual void AddParticleGen(const CParticleGen& gen); - virtual void AddParticleGen2(); + virtual void AddParticleGen(const CParticleGen& gen, const CVector3f&, const CAABox&); virtual void AddPlaneObject(); virtual void AddDrawable(const void* obj, const CVector3f& pos, const CAABox& bounds, int mode, IRenderer::EDrawableSorting sorting); diff --git a/include/Weapons/CCollisionResponseData.hpp b/include/Weapons/CCollisionResponseData.hpp new file mode 100644 index 00000000..bbdfadfb --- /dev/null +++ b/include/Weapons/CCollisionResponseData.hpp @@ -0,0 +1,18 @@ +#ifndef _COLLISIONRESPONSEDATA_HPP +#define _COLLISIONRESPONSEDATA_HPP + +#include "MetroidPrime/ActorCommon.hpp" +#include "Weapons/CDecalDescription.hpp" +#include + +class CCollisionResponseData { +public: + rstl::optional_object< TLockedToken< CDecalDescription > > + GetDecalDescription(EWeaponCollisionResponseTypes type) const; + rstl::optional_object< TLockedToken< CGenDescription > > + GetParticleDescription(EWeaponCollisionResponseTypes type) const; + uint GetSoundEffectId(EWeaponCollisionResponseTypes type) const; + float GetAudibleRange() const; + float GetAudibleFallOff() const; +}; +#endif diff --git a/include/Weapons/CProjectileWeapon.hpp b/include/Weapons/CProjectileWeapon.hpp new file mode 100644 index 00000000..1000f98a --- /dev/null +++ b/include/Weapons/CProjectileWeapon.hpp @@ -0,0 +1,104 @@ +#ifndef _CPROJECTILEWEAPON_HPP +#define _CPROJECTILEWEAPON_HPP + +#include +#include +#include +#include +#include + +#include + +#include + +#include "Kyoto/Math/CMatrix4f.hpp" +#include "Kyoto/Particles/CGenDescription.hpp" +#include "MetroidPrime/ActorCommon.hpp" +#include "MetroidPrime/Weapons/WeaponTypes.hpp" +#include "Weapons/CDecalDescription.hpp" +#include "Weapons/CWeaponDescription.hpp" +#include "Weapons/IWeaponProjectile.hpp" +#include "rstl/optional_object.hpp" + +class CProjectileWeapon : public IWeaponProjectile { + static uint skGlobalSeed; + CProjectileWeapon(const TToken< CWeaponDescription >& description, const CVector3f& worldOffset, + const CTransform4f& localToWorld, const CVector3f& scale, int flags); + ~CProjectileWeapon(); + + static float GetTickPeriod(); + + bool Update(float dt); + void UpdateParticleFX(); + const CTransform4f GetTransform() const; + CTransform4f GetTransform(); + const CVector3f GetTranslation() const; + void SetRelativeOrientation(const CTransform4f& orient); + void SetWorldSpaceOrientation(const CTransform4f& orient); + void UpdatePSTranslationAndOrientation(); + void UpdateChildParticleSystems(float dt); + const bool IsSystemDeletable() const; + void Render() const; + void AddToRenderer() const; + void RenderParticles() const; + rstl::optional_object< TLockedToken< CGenDescription > > + CollisionOccured(const EWeaponCollisionResponseTypes colType, const bool deflected, + const bool useTarget, const CVector3f& pos, const CVector3f& normal, + const CVector3f& target); + + uint GetSoundIdForCollision(EWeaponCollisionResponseTypes type) const; + + rstl::optional_object< TLockedToken< CDecalDescription > > + GetDecalForCollision(EWeaponCollisionResponseTypes type) const; + + float GetAudibleRange() const; + float GetAudibleFallOff() const; + float GetMaxTurnRate() const; + void SetVelocity(const CVector3f& velocity); + const CVector3f& GetVelocity() const; + void SetGravity(const CVector3f& gravity); + const CVector3f& GetGravity() const; + static void SetGlobalSeed(uint seed); + + rstl::optional_object< CAABox > GetBounds() const; + +private: + TLockedToken< CWeaponDescription > x4_weaponDesc; + CRandom16 x10_random; + CTransform4f x14_localToWorldXf; + CTransform4f x44_localXf; + CVector3f x74_worldOffset; + CVector3f x80_localOffset; + CVector3f x8c_projOffset; + CVector3f x98_scale; + CVector3f xa4_localOffset2; + CVector3f xb0_velocity; + CVector3f xbc_gravity; + CColor xc8_ambientLightColor; + double xd0_curTime; + double xd8_remainderTime; + float xe0_maxTurnRate; + int xe4_flags; + int xe8_lifetime; + int xec_childSystemUpdateRate; + int xf0_; + int xf4_curFrame; + int xf8_lastParticleFrame; + CElementGen* xfc_APSMGen; + CElementGen* x100_APS2Gen; + CElementGen* x104_; + rstl::optional_object< TLockedToken< CModel > > x108_model; + CParticleSwoosh* x118_swoosh1; + CParticleSwoosh* x11c_swoosh2; + CParticleSwoosh* x120_swoosh3; + bool x124_24_active : 1; + bool x124_25_APSO : 1; + bool x124_26_AP11 : 1; + bool x124_27_AP21 : 1; + bool x124_28_AS11 : 1; + bool x124_29_AS12 : 1; + bool x124_30_AS13 : 1; + bool x124_31_VMD2 : 1; +}; + +#endif // _CPROJECTILEWEAPON_HPP diff --git a/include/Weapons/CWeaponDescription.hpp b/include/Weapons/CWeaponDescription.hpp index a3b6b79b..f006edf0 100644 --- a/include/Weapons/CWeaponDescription.hpp +++ b/include/Weapons/CWeaponDescription.hpp @@ -15,11 +15,13 @@ class CCollisionResponseData; class CWeaponDescription { public: - typedef rstl::optional_object< TCachedToken< CModel > > TParticleModel; + typedef rstl::optional_object< TLockedToken< CModel > > TParticleModel; typedef rstl::optional_object< TCachedToken< CGenDescription > > TChildGeneratorDesc; typedef rstl::optional_object< TCachedToken< CSwooshDescription > > TSwooshGeneratorDesc; typedef rstl::optional_object< TCachedToken< CCollisionResponseData > > TCollisionResponseDesc; + const TCollisionResponseDesc& GetCollisionResponse() const { return x94_COLR; } + const TParticleModel& GetOHEF() const { return x84_OHEF; } CWeaponDescription(); ~CWeaponDescription(); CVectorElement* x0_IORN; diff --git a/include/Weapons/IWeaponProjectile.hpp b/include/Weapons/IWeaponProjectile.hpp new file mode 100644 index 00000000..b72ebc03 --- /dev/null +++ b/include/Weapons/IWeaponProjectile.hpp @@ -0,0 +1,17 @@ +#ifndef _IWEAPONPROJECTILE_HPP +#define _IWEAPONPROJECTILE_HPP + +class CVector3f; +class CTransform4f; +class IWeaponProjectile { +public: + virtual ~IWeaponProjectile() {}; + + virtual bool Update(float dt) = 0; + virtual void AddToRenderer() const = 0; + virtual void Render() const = 0; + virtual const CVector3f GetTranslation() const = 0; + virtual const CTransform4f GetTransform() const = 0; +}; + +#endif // _IWEAPONPROJECTILE_HPP diff --git a/include/Weapons/IWeaponRenderer.hpp b/include/Weapons/IWeaponRenderer.hpp index d63ae882..ad7d3775 100644 --- a/include/Weapons/IWeaponRenderer.hpp +++ b/include/Weapons/IWeaponRenderer.hpp @@ -10,6 +10,7 @@ public: virtual ~IWeaponRenderer() = 0; virtual void AddParticleGen(const CParticleGen& gen) = 0; static void SetRenderer(IWeaponRenderer* renderer) { sWeaponRenderer = renderer; } + static IWeaponRenderer* GetRenderer() { return sWeaponRenderer; } private: static IWeaponRenderer* sWeaponRenderer; diff --git a/src/MetroidPrime/Player/CPlayerGun.cpp b/src/MetroidPrime/Player/CPlayerGun.cpp index d6a2faf6..0f94cc66 100644 --- a/src/MetroidPrime/Player/CPlayerGun.cpp +++ b/src/MetroidPrime/Player/CPlayerGun.cpp @@ -36,6 +36,7 @@ #include "MetaRender/CCubeRenderer.hpp" +#include "Weapons/IWeaponRenderer.hpp" #include "math.h" static float kVerticalAngleTable[3] = {-30.f, 0.f, 30.f}; @@ -364,7 +365,7 @@ void CPlayerGun::PreRender(CStateManager& mgr, const CFrustumPlanes& frustum, } if (x833_28_phazonBeamActive) { - gpRender->AllocatePhazonSuitMaskTexture(); + IWeaponRenderer::SetRenderer(IWeaponRenderer *renderer)->AllocatePhazonSuitMaskTexture(); } } diff --git a/src/Weapons/CProjectileWeapon.cpp b/src/Weapons/CProjectileWeapon.cpp new file mode 100644 index 00000000..b405911e --- /dev/null +++ b/src/Weapons/CProjectileWeapon.cpp @@ -0,0 +1,585 @@ +#include "Weapons/CProjectileWeapon.hpp" +#include "Kyoto/Graphics/CLight.hpp" +#include "Kyoto/Math/CVector3f.hpp" +#include "Weapons/IWeaponRenderer.hpp" +#include "rstl/optional_object.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +uint CProjectileWeapon::skGlobalSeed = 99; + +float CProjectileWeapon::GetTickPeriod() { return 1 / 60.f; } + +CProjectileWeapon::CProjectileWeapon(const TToken< CWeaponDescription >& description, + const CVector3f& worldOffset, const CTransform4f& localToWorld, + const CVector3f& scale, int flags) +: x4_weaponDesc(description) +, x10_random(skGlobalSeed) +, x14_localToWorldXf(localToWorld) +, x44_localXf(CTransform4f::Identity()) +, x74_worldOffset(worldOffset) +, x80_localOffset(CVector3f::Zero()) +, x8c_projOffset(CVector3f::Zero()) +, x98_scale(CVector3f(1.f, 1.f, 1.f)) +, xa4_localOffset2(CVector3f::Zero()) +, xb0_velocity(CVector3f::Zero()) +, xbc_gravity(CVector3f::Zero()) +, xc8_ambientLightColor(CColor::White()) +, xd0_curTime(0.0) +, xd8_remainderTime(0.0) +, xe0_maxTurnRate(0.f) +, xe4_flags(flags) +, xe8_lifetime(0) +, xec_childSystemUpdateRate(0) +, xf0_(0) +, xf4_curFrame(0) +, xf8_lastParticleFrame(-1) +, xfc_APSMGen(nullptr) +, x100_APS2Gen(nullptr) +, x104_(nullptr) +, x118_swoosh1(nullptr) +, x11c_swoosh2(nullptr) +, x120_swoosh3(nullptr) +, x124_24_active(true) +, x124_25_APSO(false) +, x124_26_AP11(false) +, x124_27_AP21(false) +, x124_28_AS11(false) +, x124_29_AS12(false) +, x124_30_AS13(false) +, x124_31_VMD2(false) { + CGlobalRandom __(x10_random); + x124_31_VMD2 = x4_weaponDesc->x10_VMD2; + x124_25_APSO = x4_weaponDesc->x28_APSO; + uint unk = xe4_flags & 1; + if (x4_weaponDesc->x34_APSM) { + xfc_APSMGen = rs_new CElementGen(*x4_weaponDesc->x34_APSM, CElementGen::kMOT_Normal, + unk ? CElementGen::kOSF_Two : CElementGen::kOSF_One); + xfc_APSMGen->SetGlobalScale(scale); + } + + if (x4_weaponDesc->x44_APS2) { + x100_APS2Gen = rs_new CElementGen(*x4_weaponDesc->x44_APS2, CElementGen::kMOT_Normal, + unk ? CElementGen::kOSF_Two : CElementGen::kOSF_One); + x100_APS2Gen->SetGlobalScale(scale); + } + if (x4_weaponDesc->x54_ASW1) { + x118_swoosh1 = rs_new CParticleSwoosh(*x4_weaponDesc->x54_ASW1, 0); + x118_swoosh1->SetGlobalScale(scale); + } + if (x4_weaponDesc->x64_ASW2) { + x11c_swoosh2 = rs_new CParticleSwoosh(*x4_weaponDesc->x64_ASW2, 0); + x11c_swoosh2->SetGlobalScale(scale); + } + if (x4_weaponDesc->x74_ASW3) { + x120_swoosh3 = rs_new CParticleSwoosh(*x4_weaponDesc->x74_ASW3, 0); + x120_swoosh3->SetGlobalScale(scale); + } + + if (x4_weaponDesc->x14_PSLT) { + x4_weaponDesc->x14_PSLT->GetValue(0, xe8_lifetime); + } else { + xe8_lifetime = 0x7FFFFF; + } + + if (x4_weaponDesc->x4_IVEC) { + x4_weaponDesc->x4_IVEC->GetValue(0, xb0_velocity); + } + + if (x4_weaponDesc->x0_IORN) { + CTransform4f orient(CTransform4f::Identity()); + CVector3f angle(0.f, 0.f, 0.f); + x4_weaponDesc->x0_IORN->GetValue(0, angle); + CRelAngle relAngleX = CRelAngle::FromDegrees(angle.GetX()); + orient.RotateLocalX(relAngleX); + CRelAngle relAngleY = CRelAngle::FromDegrees(angle.GetY()); + orient.RotateLocalY(relAngleY); + CRelAngle relAngleZ = CRelAngle::FromDegrees(angle.GetZ()); + orient.RotateLocalZ(relAngleZ); + SetRelativeOrientation(orient); + } else { + SetRelativeOrientation(CTransform4f::Identity()); + } + if (x4_weaponDesc->GetOHEF()) { + x108_model = *x4_weaponDesc->GetOHEF(); + } + + x124_26_AP11 = x4_weaponDesc->x2a_AP11; + x124_27_AP21 = x4_weaponDesc->x2b_AP21; + x124_28_AS11 = x4_weaponDesc->x2c_AS11; + x124_29_AS12 = x4_weaponDesc->x2d_AS12; + x124_30_AS13 = x4_weaponDesc->x2e_AS13; + UpdateChildParticleSystems(1.f / 60.f); +} + +CProjectileWeapon::~CProjectileWeapon() { + delete xfc_APSMGen; + delete x100_APS2Gen; + delete x104_; + delete x118_swoosh1; + delete x11c_swoosh2; + delete x120_swoosh3; +} + +inline f32 val() { return 1.0f; } + +bool CProjectileWeapon::Update(float dt) { + CGlobalRandom __(x10_random); + + double actualTime = xf4_curFrame * (1.0 / 60.0); + xec_childSystemUpdateRate = 0; + double useDt = close_enough(dt, 1.f / 60.f) ? 1.0 / 60.0 : dt; + useDt *= val(); + if (useDt < 0.f) { + useDt = 0.f; + } + xd0_curTime += useDt; + while (actualTime < xd0_curTime && !close_enough(actualTime, xd0_curTime)) { + if (xf4_curFrame < xe8_lifetime) { + CParticleGlobals::SetEmitterTime(xf4_curFrame); + CParticleGlobals::SetParticleLifetime(xe8_lifetime); + CParticleGlobals::UpdateParticleLifetimeTweenValues(xf4_curFrame); + UpdatePSTranslationAndOrientation(); + } + + actualTime += 1.f / 60.f; + ++xf4_curFrame; + ++xec_childSystemUpdateRate; + } + + if (close_enough(actualTime, xd0_curTime)) { + xd0_curTime = actualTime; + } + + xd8_remainderTime = (float)((actualTime - xd0_curTime) / 60.0); + + if (xf4_curFrame < xe8_lifetime) { + xe0_maxTurnRate = 0.f; + if (CRealElement* trat = x4_weaponDesc->x30_TRAT) { + trat->GetValue(0, xe0_maxTurnRate); + } + } + + return false; +} + +void CProjectileWeapon::UpdateParticleFX() { + for (int i = 0; i < xec_childSystemUpdateRate; ++i) { + UpdateChildParticleSystems(1.f / 60.f); + } +} + +const CTransform4f CProjectileWeapon::GetTransform() const { + return x14_localToWorldXf * x44_localXf; +} + +CTransform4f CProjectileWeapon::GetTransform() { return x14_localToWorldXf * x44_localXf; } +const CVector3f CProjectileWeapon::GetTranslation() const { + return x14_localToWorldXf * (x80_localOffset + x44_localXf * x8c_projOffset) + x74_worldOffset; +} + +void CProjectileWeapon::SetRelativeOrientation(const CTransform4f& orient) { x44_localXf = orient; } + +void CProjectileWeapon::SetWorldSpaceOrientation(const CTransform4f& orient) { + x44_localXf = x14_localToWorldXf.GetInverse() * orient; +} + +void CProjectileWeapon::UpdatePSTranslationAndOrientation() { + if (xe8_lifetime >= xf4_curFrame && x124_24_active) { + + if (CModVectorElement* psvm = x4_weaponDesc->xc_PSVM) { + psvm->GetValue(xf4_curFrame, xb0_velocity, x80_localOffset); + } + + if (x124_31_VMD2) { + x80_localOffset += x44_localXf * xb0_velocity; + } else { + x80_localOffset += xb0_velocity; + } + + xb0_velocity += xbc_gravity / 60.f; + + if (CVectorElement* psov = x4_weaponDesc->x8_PSOV) { + CVector3f orient(0.f, 0.f, 0.f); + psov->GetValue(xf4_curFrame, orient); + + CTransform4f xf = x44_localXf; + xf.RotateLocalX(CRelAngle::FromDegrees(orient.GetX())); + xf.RotateLocalY(CRelAngle::FromDegrees(orient.GetY())); + xf.RotateLocalZ(CRelAngle::FromDegrees(orient.GetZ())); + SetRelativeOrientation(xf); + } + + if (CVectorElement* pscl = x4_weaponDesc->x18_PSCL) { + pscl->GetValue(xf4_curFrame, x98_scale); + } + + if (CColorElement* pcol = x4_weaponDesc->x1c_PCOL) { + pcol->GetValue(xf4_curFrame, xc8_ambientLightColor); + } + + if (CVectorElement* pofs = x4_weaponDesc->x20_POFS) { + pofs->GetValue(xf4_curFrame, xa4_localOffset2); + } + + if (CVectorElement* ofst = x4_weaponDesc->x24_OFST) { + ofst->GetValue(xf4_curFrame, x8c_projOffset); + } + } +} + +void CProjectileWeapon::UpdateChildParticleSystems(float dt) { + double useDt = (close_enough(dt, 1.f / 60.f)) ? 1.0 / 60.0 : dt; + + if (xfc_APSMGen) { + if (xf8_lastParticleFrame != xf4_curFrame) { + if (xf4_curFrame > xe8_lifetime) { + xfc_APSMGen->SetParticleEmission(false); + xfc_APSMGen->EndLifetime(); + } else { + if (x124_26_AP11) { + xfc_APSMGen->SetGlobalTranslation(GetTranslation()); + } else { + xfc_APSMGen->SetTranslation(GetTranslation()); + } + + if (x124_25_APSO) { + xfc_APSMGen->SetOrientation(GetTransform()); + } + } + } + xfc_APSMGen->Update(useDt); + if (xfc_APSMGen->IsSystemDeletable() == TRUE) { + delete xfc_APSMGen; + xfc_APSMGen = nullptr; + } + } + + if (x100_APS2Gen) { + if (xf8_lastParticleFrame != xf4_curFrame) { + if (xf4_curFrame > xe8_lifetime) { + x100_APS2Gen->SetParticleEmission(false); + x100_APS2Gen->EndLifetime(); + } else { + if (x124_27_AP21) { + x100_APS2Gen->SetGlobalTranslation(GetTranslation()); + } else { + x100_APS2Gen->SetTranslation(GetTranslation()); + } + + if (x124_25_APSO) { + x100_APS2Gen->SetOrientation(GetTransform()); + } + } + } + + x100_APS2Gen->Update(useDt); + if (x100_APS2Gen->IsSystemDeletable() == TRUE) { + delete x100_APS2Gen; + x100_APS2Gen = nullptr; + } + } + + if (x118_swoosh1) { + if (xf8_lastParticleFrame != xf4_curFrame) { + if (xf4_curFrame > xe8_lifetime) { + x118_swoosh1->SetParticleEmission(false); + } else { + if (x124_28_AS11) { + x118_swoosh1->SetGlobalTranslation(GetTranslation()); + } else { + x118_swoosh1->SetTranslation(GetTranslation()); + } + x118_swoosh1->SetOrientation(GetTransform()); + } + } + x118_swoosh1->SetWarmUp(); + x118_swoosh1->Update(0.0); + if (x118_swoosh1->IsSystemDeletable() == TRUE) { + delete x118_swoosh1; + x118_swoosh1 = nullptr; + } + } + + if (x11c_swoosh2) { + if (xf8_lastParticleFrame != xf4_curFrame) { + if (xf4_curFrame > xe8_lifetime) { + x11c_swoosh2->SetParticleEmission(false); + } else { + if (x124_29_AS12) { + x11c_swoosh2->SetGlobalTranslation(GetTranslation()); + } else { + x11c_swoosh2->SetTranslation(GetTranslation()); + } + x11c_swoosh2->SetOrientation(GetTransform()); + } + } + x11c_swoosh2->SetWarmUp(); + x11c_swoosh2->Update(0.0); + if (x11c_swoosh2->IsSystemDeletable() == TRUE) { + delete x11c_swoosh2; + x11c_swoosh2 = nullptr; + } + } + + if (x120_swoosh3) { + if (xf8_lastParticleFrame != xf4_curFrame) { + if (xf4_curFrame > xe8_lifetime) { + x120_swoosh3->SetParticleEmission(false); + } else { + if (x124_30_AS13) { + x120_swoosh3->SetGlobalTranslation(GetTranslation()); + } else { + x120_swoosh3->SetTranslation(GetTranslation()); + } + x120_swoosh3->SetOrientation(GetTransform()); + } + } + x120_swoosh3->SetWarmUp(); + x120_swoosh3->Update(0.0); + if (x120_swoosh3->IsSystemDeletable() == TRUE) { + delete x120_swoosh3; + x120_swoosh3 = nullptr; + } + } + + if (x104_) { + x104_->Update(useDt); + if (x104_->IsSystemDeletable() == TRUE) { + delete x104_; + x104_ = nullptr; + } + } + + xf8_lastParticleFrame = xf4_curFrame; +} + +const bool CProjectileWeapon::IsSystemDeletable() const { + bool ret = true; + if (xfc_APSMGen && !xfc_APSMGen->IsSystemDeletable()) { + ret = false; + } else if (x100_APS2Gen && !x100_APS2Gen->IsSystemDeletable()) { + ret = false; + } else if (x118_swoosh1 && !x118_swoosh1->IsSystemDeletable()) { + ret = false; + } else if (x11c_swoosh2 && !x11c_swoosh2->IsSystemDeletable()) { + ret = false; + } else if (x120_swoosh3 && !x120_swoosh3->IsSystemDeletable()) { + ret = false; + } else if (x104_ && !x104_->IsSystemDeletable()) { + ret = false; + } else if (x124_24_active) { + ret = xf4_curFrame >= xe8_lifetime; + } + return ret; +} + +void CProjectileWeapon::Render() const { + if (xf4_curFrame <= xe8_lifetime && x124_24_active && x108_model) { + CTransform4f localXf = CTransform4f::Translate( + x80_localOffset + (x44_localXf * x8c_projOffset) + xa4_localOffset2); + CTransform4f worldXf = CTransform4f::Translate(x74_worldOffset); + CTransform4f scaleXf = + CTransform4f::Scale(x98_scale.GetX(), x98_scale.GetY(), x98_scale.GetZ()); + CGraphics::SetModelMatrix(worldXf * x14_localToWorldXf * localXf * scaleXf * x44_localXf); + CLight light = CLight::BuildLocalAmbient(CVector3f::Zero(), xc8_ambientLightColor); + (*x108_model)->Draw(CModelFlags(CModelFlags::kT_Opaque, 1.f)); + } +} + +void CProjectileWeapon::AddToRenderer() const { + if (xfc_APSMGen) + IWeaponRenderer::GetRenderer()->AddParticleGen(*xfc_APSMGen); + if (x100_APS2Gen) + IWeaponRenderer::GetRenderer()->AddParticleGen(*x100_APS2Gen); + if (x118_swoosh1) + IWeaponRenderer::GetRenderer()->AddParticleGen(*x118_swoosh1); + if (x11c_swoosh2) + IWeaponRenderer::GetRenderer()->AddParticleGen(*x11c_swoosh2); + if (x120_swoosh3) + IWeaponRenderer::GetRenderer()->AddParticleGen(*x120_swoosh3); + if (x104_) + IWeaponRenderer::GetRenderer()->AddParticleGen(*x104_); +} + +void CProjectileWeapon::RenderParticles() const { + if (xfc_APSMGen) { + xfc_APSMGen->Render(); + } + if (x100_APS2Gen) { + x100_APS2Gen->Render(); + } + if (x118_swoosh1) { + x118_swoosh1->Render(); + } + if (x11c_swoosh2) { + x11c_swoosh2->Render(); + } + if (x120_swoosh3) { + x120_swoosh3->Render(); + } + if (x104_) { + x104_->Render(); + } +} + +rstl::optional_object< TLockedToken< CGenDescription > > CProjectileWeapon::CollisionOccured( + const EWeaponCollisionResponseTypes colType, const bool deflected, const bool useTarget, + const CVector3f& pos, const CVector3f& normal, const CVector3f& target) { + x80_localOffset = x14_localToWorldXf.TransposeRotate(pos - x74_worldOffset) - x8c_projOffset; + + if (deflected) { + CVector3f posToTarget = target - GetTranslation(); + if (useTarget && posToTarget.CanBeNormalized()) { + SetWorldSpaceOrientation(CTransform4f::LookAt(CVector3f::Zero(), posToTarget.AsNormalized())); + } else { + CVector3f col = GetTransform().GetColumn(kDY); + CVector3f lookPos = (CVector3f::Dot(normal, col) * 2.f) * normal; + CVector3f lookPos2 = col - lookPos; + CTransform4f lookXf = CTransform4f::LookAt(CVector3f::Zero(), lookPos2, normal); + SetWorldSpaceOrientation(lookXf); + } + return rstl::optional_object_null(); + } + + x124_24_active = false; + if (xfc_APSMGen) { + xfc_APSMGen->SetParticleEmission(false); + } + if (x100_APS2Gen) { + x100_APS2Gen->SetParticleEmission(false); + } + if (x118_swoosh1) { + x118_swoosh1->SetParticleEmission(false); + } + if (x11c_swoosh2) { + x11c_swoosh2->SetParticleEmission(false); + } + if (x120_swoosh3) { + x120_swoosh3->SetParticleEmission(false); + } + if (!x4_weaponDesc->x94_COLR) { + return rstl::optional_object_null(); + } + TToken< CCollisionResponseData > tok = (*x4_weaponDesc->GetCollisionResponse()); + return tok->GetParticleDescription(colType); +} + +uint CProjectileWeapon::GetSoundIdForCollision(EWeaponCollisionResponseTypes type) const { + if (!x4_weaponDesc->GetCollisionResponse()) { + return -1; + } + TToken< CCollisionResponseData > tok = (*x4_weaponDesc->GetCollisionResponse()); + return tok->GetSoundEffectId(type); +} + +rstl::optional_object< TLockedToken< CDecalDescription > > +CProjectileWeapon::GetDecalForCollision(EWeaponCollisionResponseTypes type) const { + if (!x4_weaponDesc->GetCollisionResponse()) { + return rstl::optional_object_null(); + } + TToken< CCollisionResponseData > tok = (*x4_weaponDesc->GetCollisionResponse()); + return tok->GetDecalDescription(type); +} + +float CProjectileWeapon::GetAudibleRange() const { + if (!x4_weaponDesc->GetCollisionResponse()) { + return 0.f; + } + TToken< CCollisionResponseData > tok = (*x4_weaponDesc->GetCollisionResponse()); + return tok->GetAudibleRange(); +} + +float CProjectileWeapon::GetAudibleFallOff() const { + if (!x4_weaponDesc->GetCollisionResponse()) { + return 0.f; + } + TToken< CCollisionResponseData > tok = (*x4_weaponDesc->GetCollisionResponse()); + return tok->GetAudibleFallOff(); +} + +float CProjectileWeapon::GetMaxTurnRate() const { return xe0_maxTurnRate; } + +void CProjectileWeapon::SetVelocity(const CVector3f& velocity) { xb0_velocity = velocity; } + +const CVector3f& CProjectileWeapon::GetVelocity() const { return xb0_velocity; } + +void CProjectileWeapon::SetGravity(const CVector3f& gravity) { xbc_gravity = gravity; } + +const CVector3f& CProjectileWeapon::GetGravity() const { return xbc_gravity; } + +void CProjectileWeapon::SetGlobalSeed(uint seed) { skGlobalSeed = seed; } + +rstl::optional_object< CAABox > CProjectileWeapon::GetBounds() const { + CAABox ret = CAABox::MakeMaxInvertedBox(); + bool hasBox = false; + + if (xfc_APSMGen) { + rstl::optional_object< CAABox > bounds = xfc_APSMGen->GetBounds(); + if (bounds) { + CAABox& b = *bounds; + ret.AccumulateBounds(b.GetMinPoint()); + ret.AccumulateBounds(b.GetMaxPoint()); + hasBox = true; + } + } + + if (x100_APS2Gen) { + rstl::optional_object< CAABox > bounds = x100_APS2Gen->GetBounds(); + if (bounds) { + CAABox& b = *bounds; + ret.AccumulateBounds(b.GetMinPoint()); + ret.AccumulateBounds(b.GetMaxPoint()); + hasBox = true; + } + } + + if (x118_swoosh1) { + rstl::optional_object< CAABox > bounds = x118_swoosh1->GetBounds(); + if (bounds) { + CAABox& b = *bounds; + ret.AccumulateBounds(b.GetMinPoint()); + ret.AccumulateBounds(b.GetMaxPoint()); + hasBox = true; + } + } + + if (x11c_swoosh2) { + rstl::optional_object< CAABox > bounds = x11c_swoosh2->GetBounds(); + if (bounds) { + CAABox& b = *bounds; + ret.AccumulateBounds(b.GetMinPoint()); + ret.AccumulateBounds(b.GetMaxPoint()); + hasBox = true; + } + } + + if (x120_swoosh3) { + rstl::optional_object< CAABox > bounds = x120_swoosh3->GetBounds(); + if (bounds) { + CAABox& b = *bounds; + ret.AccumulateBounds(b.GetMinPoint()); + ret.AccumulateBounds(b.GetMaxPoint()); + hasBox = true; + } + } + + if (hasBox) { + return ret; + } + + return rstl::optional_object_null(); +}