From bb86f3dc01b044cefd51becc8fe9e50e75d60efb Mon Sep 17 00:00:00 2001 From: Henrique Gemignani Passos Lima Date: Sat, 1 Oct 2022 17:08:15 +0300 Subject: [PATCH] Add CPlayerState (#5) * Start CPlayerState * CPlayerState::CalculateHealth symbol rename * Add inline constructor for CHealthInfo * Add skeleton for the rest of CPlayerState * Match CPlayerState::DecrPickUp * Match more methods in CPlayerState * Run formatting * Fix function order * Improving the constants Former-commit-id: 1e30daa096f8ab2c1df6431d50a385671a913bf0 --- asm/MetroidPrime/Player/CGameState.s | 2 +- asm/MetroidPrime/Player/CPlayerState.s | 51 +-- .../ScriptObjects/CScriptPickupGenerator.s | 2 +- include/MetroidPrime/CHealthInfo.hpp | 5 +- include/MetroidPrime/Player/CPlayerState.hpp | 137 +++++- .../Player/CStaticInterference.hpp | 23 + src/MetroidPrime/Player/CPlayerState.cpp | 419 ++++++++++++++++++ 7 files changed, 603 insertions(+), 36 deletions(-) create mode 100644 include/MetroidPrime/Player/CStaticInterference.hpp create mode 100644 src/MetroidPrime/Player/CPlayerState.cpp diff --git a/asm/MetroidPrime/Player/CGameState.s b/asm/MetroidPrime/Player/CGameState.s index 75a22a0b..4774d5c0 100644 --- a/asm/MetroidPrime/Player/CGameState.s +++ b/asm/MetroidPrime/Player/CGameState.s @@ -742,7 +742,7 @@ lbl_801D40C0: /* 801D4168 001D10C8 80 7E 00 98 */ lwz r3, 0x98(r30) /* 801D416C 001D10CC 7F E4 FB 78 */ mr r4, r31 /* 801D4170 001D10D0 80 63 00 00 */ lwz r3, 0(r3) -/* 801D4174 001D10D4 4B EB DD 49 */ bl PutTo__12CPlayerStateFR13COutPutStream +/* 801D4174 001D10D4 4B EB DD 49 */ bl PutTo__12CPlayerStateFR13COutputStream /* 801D4178 001D10D8 7F E4 FB 78 */ mr r4, r31 /* 801D417C 001D10DC 38 7E 01 7C */ addi r3, r30, 0x17c /* 801D4180 001D10E0 48 03 B1 15 */ bl PutTo__12CGameOptionsFR16CMemoryStreamOut diff --git a/asm/MetroidPrime/Player/CPlayerState.s b/asm/MetroidPrime/Player/CPlayerState.s index 0db636b0..78d8b7a8 100644 --- a/asm/MetroidPrime/Player/CPlayerState.s +++ b/asm/MetroidPrime/Player/CPlayerState.s @@ -122,8 +122,8 @@ GetMissileComboChargeFactor__12CPlayerStateFv: .global GetComboFireAmmoPeriod__12CPlayerStateCFv GetComboFireAmmoPeriod__12CPlayerStateCFv: /* 80091204 0008E164 80 03 00 08 */ lwz r0, 8(r3) -/* 80091208 0008E168 3C 60 80 3D */ lis r3, lbl_803CD778@ha -/* 8009120C 0008E16C 38 63 D7 78 */ addi r3, r3, lbl_803CD778@l +/* 80091208 0008E168 3C 60 80 3D */ lis r3, kComboAmmoPeriods@ha +/* 8009120C 0008E16C 38 63 D7 78 */ addi r3, r3, kComboAmmoPeriods@l /* 80091210 0008E170 54 00 10 3A */ slwi r0, r0, 2 /* 80091214 0008E174 7C 23 04 2E */ lfsx f1, r3, r0 /* 80091218 0008E178 4E 80 00 20 */ blr @@ -131,8 +131,8 @@ GetComboFireAmmoPeriod__12CPlayerStateCFv: .global GetMissileCostForAltAttack__12CPlayerStateCFv GetMissileCostForAltAttack__12CPlayerStateCFv: /* 8009121C 0008E17C 80 03 00 08 */ lwz r0, 8(r3) -/* 80091220 0008E180 3C 60 80 3D */ lis r3, lbl_803CD764@ha -/* 80091224 0008E184 38 63 D7 64 */ addi r3, r3, lbl_803CD764@l +/* 80091220 0008E180 3C 60 80 3D */ lis r3, kMissileCosts@ha +/* 80091224 0008E184 38 63 D7 64 */ addi r3, r3, kMissileCosts@l /* 80091228 0008E188 54 00 10 3A */ slwi r0, r0, 2 /* 8009122C 0008E18C 7C 63 00 2E */ lwzx r3, r3, r0 /* 80091230 0008E190 4E 80 00 20 */ blr @@ -860,18 +860,12 @@ DecrPickUp__12CPlayerStateFQ212CPlayerState9EItemTypei: /* 80091B98 0008EAF8 4D 80 00 20 */ bltlr /* 80091B9C 0008EAFC 2C 04 00 28 */ cmpwi r4, 0x28 /* 80091BA0 0008EB00 40 81 00 08 */ ble lbl_80091BA8 - -.global sub_80091ba4 -sub_80091ba4: /* 80091BA4 0008EB04 4E 80 00 20 */ blr lbl_80091BA8: /* 80091BA8 0008EB08 2C 04 00 07 */ cmpwi r4, 7 /* 80091BAC 0008EB0C 40 80 00 10 */ bge lbl_80091BBC /* 80091BB0 0008EB10 2C 04 00 04 */ cmpwi r4, 4 /* 80091BB4 0008EB14 41 82 00 10 */ beq lbl_80091BC4 - -.global sub_80091bb8 -sub_80091bb8: /* 80091BB8 0008EB18 4E 80 00 20 */ blr lbl_80091BBC: /* 80091BBC 0008EB1C 2C 04 00 09 */ cmpwi r4, 9 @@ -917,7 +911,6 @@ lbl_80091C28: /* 80091C48 0008EBA8 7C 04 00 2E */ lwzx r0, r4, r0 /* 80091C4C 0008EBAC 7C 09 03 A6 */ mtctr r0 /* 80091C50 0008EBB0 4E 80 04 20 */ bctr -.global lbl_80091C54 lbl_80091C54: /* 80091C54 0008EBB4 57 C0 18 38 */ slwi r0, r30, 3 /* 80091C58 0008EBB8 7C 9D 02 14 */ add r4, r29, r0 @@ -930,7 +923,6 @@ lbl_80091C54: /* 80091C74 0008EBD4 40 81 00 50 */ ble lbl_80091CC4 /* 80091C78 0008EBD8 90 64 00 28 */ stw r3, 0x28(r4) /* 80091C7C 0008EBDC 48 00 00 48 */ b lbl_80091CC4 -.global lbl_80091C80 lbl_80091C80: /* 80091C80 0008EBE0 37 FD 00 0C */ addic. r31, r29, 0xc /* 80091C84 0008EBE4 41 82 00 40 */ beq lbl_80091CC4 @@ -943,14 +935,13 @@ lbl_80091C80: /* 80091CA0 0008EC00 C8 21 00 08 */ lfd f1, 8(r1) /* 80091CA4 0008EC04 EC 21 10 28 */ fsubs f1, f1, f2 /* 80091CA8 0008EC08 EF E1 00 2A */ fadds f31, f1, f0 -/* 80091CAC 0008EC0C 48 00 00 85 */ bl CalculateHealth__12CPlayerStateFUi +/* 80091CAC 0008EC0C 48 00 00 85 */ bl CalculateHealth__12CPlayerStateFv /* 80091CB0 0008EC10 FC 1F 08 40 */ fcmpo cr0, f31, f1 /* 80091CB4 0008EC14 40 81 00 0C */ ble lbl_80091CC0 /* 80091CB8 0008EC18 D0 3F 00 00 */ stfs f1, 0(r31) /* 80091CBC 0008EC1C 48 00 00 08 */ b lbl_80091CC4 lbl_80091CC0: /* 80091CC0 0008EC20 D3 FF 00 00 */ stfs f31, 0(r31) -.global lbl_80091CC4 lbl_80091CC4: /* 80091CC4 0008EC24 2C 1E 00 18 */ cmpwi r30, 0x18 /* 80091CC8 0008EC28 40 82 00 14 */ bne lbl_80091CDC @@ -984,8 +975,8 @@ ResetAndIncrPickUp__12CPlayerStateFQ212CPlayerState9EItemTypei: /* 80091D28 0008EC88 38 21 00 10 */ addi r1, r1, 0x10 /* 80091D2C 0008EC8C 4E 80 00 20 */ blr -.global CalculateHealth__12CPlayerStateFUi -CalculateHealth__12CPlayerStateFUi: +.global CalculateHealth__12CPlayerStateFv +CalculateHealth__12CPlayerStateFv: /* 80091D30 0008EC90 94 21 FF F0 */ stwu r1, -0x10(r1) /* 80091D34 0008EC94 3C 00 43 30 */ lis r0, 0x4330 /* 80091D38 0008EC98 C8 42 8B 20 */ lfd f2, lbl_805AA840@sda21(r2) @@ -1017,11 +1008,11 @@ InitializePowerUp__12CPlayerStateFQ212CPlayerState9EItemTypei: /* 80091D94 0008ECF4 48 00 00 DC */ b lbl_80091E70 lbl_80091D98: /* 80091D98 0008ECF8 57 A0 18 38 */ slwi r0, r29, 3 -/* 80091D9C 0008ECFC 3C 60 80 3D */ lis r3, lbl_803CD6C0@ha +/* 80091D9C 0008ECFC 3C 60 80 3D */ lis r3, kPowerUpMaxValues@ha /* 80091DA0 0008ED00 7F DF 02 14 */ add r30, r31, r0 /* 80091DA4 0008ED04 57 A6 10 3A */ slwi r6, r29, 2 /* 80091DA8 0008ED08 80 FE 00 2C */ lwz r7, 0x2c(r30) -/* 80091DAC 0008ED0C 38 03 D6 C0 */ addi r0, r3, lbl_803CD6C0@l +/* 80091DAC 0008ED0C 38 03 D6 C0 */ addi r0, r3, kPowerUpMaxValues@l /* 80091DB0 0008ED10 38 81 00 08 */ addi r4, r1, 8 /* 80091DB4 0008ED14 3B DE 00 28 */ addi r30, r30, 0x28 /* 80091DB8 0008ED18 7C 65 3A 14 */ add r3, r5, r7 @@ -1099,8 +1090,8 @@ ReInitializePowerUp__12CPlayerStateFQ212CPlayerState9EItemTypei: /* 80091EB4 0008EE14 38 21 00 10 */ addi r1, r1, 0x10 /* 80091EB8 0008EE18 4E 80 00 20 */ blr -.global PutTo__12CPlayerStateFR13COutPutStream -PutTo__12CPlayerStateFR13COutPutStream: +.global PutTo__12CPlayerStateFR13COutputStream +PutTo__12CPlayerStateFR13COutputStream: /* 80091EBC 0008EE1C 94 21 FF C0 */ stwu r1, -0x40(r1) /* 80091EC0 0008EE20 7C 08 02 A6 */ mflr r0 /* 80091EC4 0008EE24 90 01 00 44 */ stw r0, 0x44(r1) @@ -1131,9 +1122,9 @@ PutTo__12CPlayerStateFR13COutPutStream: /* 80091F28 0008EE88 7C 65 1B 78 */ mr r5, r3 /* 80091F2C 0008EE8C 7F E3 FB 78 */ mr r3, r31 /* 80091F30 0008EE90 48 2A D4 8D */ bl WriteBits__13COutputStreamFii -/* 80091F34 0008EE94 3C 60 80 3D */ lis r3, lbl_803CD6C0@ha +/* 80091F34 0008EE94 3C 60 80 3D */ lis r3, kPowerUpMaxValues@ha /* 80091F38 0008EE98 3B 9E 00 28 */ addi r28, r30, 0x28 -/* 80091F3C 0008EE9C 3B A3 D6 C0 */ addi r29, r3, lbl_803CD6C0@l +/* 80091F3C 0008EE9C 3B A3 D6 C0 */ addi r29, r3, kPowerUpMaxValues@l /* 80091F40 0008EEA0 3B 60 00 00 */ li r27, 0 lbl_80091F44: /* 80091F44 0008EEA4 80 7D 00 00 */ lwz r3, 0(r29) @@ -1264,9 +1255,9 @@ __ct__12CPlayerStateFR12CInputStream: /* 80092118 0008F078 7C 64 1B 78 */ mr r4, r3 /* 8009211C 0008F07C 7F E3 FB 78 */ mr r3, r31 /* 80092120 0008F080 48 2A CC 19 */ bl ReadBits__12CInputStreamFUi -/* 80092124 0008F084 3C 80 80 3D */ lis r4, lbl_803CD6C0@ha +/* 80092124 0008F084 3C 80 80 3D */ lis r4, kPowerUpMaxValues@ha /* 80092128 0008F088 90 7E 00 20 */ stw r3, 0x20(r30) -/* 8009212C 0008F08C 3B 64 D6 C0 */ addi r27, r4, lbl_803CD6C0@l +/* 8009212C 0008F08C 3B 64 D6 C0 */ addi r27, r4, kPowerUpMaxValues@l /* 80092130 0008F090 3B 9E 00 24 */ addi r28, r30, 0x24 /* 80092134 0008F094 3B A0 00 00 */ li r29, 0 lbl_80092138: @@ -1749,8 +1740,8 @@ sub_800926c4: .section .rodata .balign 8 -.global lbl_803CD6C0 -lbl_803CD6C0: +.global kPowerUpMaxValues +kPowerUpMaxValues: # ROM: 0x3CA6C0 .4byte 0x00000001 .4byte 0x00000001 @@ -1794,8 +1785,8 @@ lbl_803CD6C0: .4byte 0x00000001 .4byte 0x00000001 -.global lbl_803CD764 -lbl_803CD764: +.global kMissileCosts +kMissileCosts: # ROM: 0x3CA764 .4byte 0x00000005 .4byte 0x0000000A @@ -1803,8 +1794,8 @@ lbl_803CD764: .4byte 0x0000000A .4byte 0x00000001 -.global lbl_803CD778 -lbl_803CD778: +.global kComboAmmoPeriods +kComboAmmoPeriods: # ROM: 0x3CA778 .float 0.2 .float 0.1 diff --git a/asm/MetroidPrime/ScriptObjects/CScriptPickupGenerator.s b/asm/MetroidPrime/ScriptObjects/CScriptPickupGenerator.s index 1e40109b..40497873 100644 --- a/asm/MetroidPrime/ScriptObjects/CScriptPickupGenerator.s +++ b/asm/MetroidPrime/ScriptObjects/CScriptPickupGenerator.s @@ -569,7 +569,7 @@ lbl_8015E630: /* 8015E634 0015B594 41 82 00 20 */ beq lbl_8015E654 /* 8015E638 0015B598 C3 C3 00 00 */ lfs f30, 0(r3) /* 8015E63C 0015B59C 7F E3 FB 78 */ mr r3, r31 -/* 8015E640 0015B5A0 4B F3 36 F1 */ bl CalculateHealth__12CPlayerStateFUi +/* 8015E640 0015B5A0 4B F3 36 F1 */ bl CalculateHealth__12CPlayerStateFv /* 8015E644 0015B5A4 FC 1E 08 40 */ fcmpo cr0, f30, f1 /* 8015E648 0015B5A8 40 80 00 0C */ bge lbl_8015E654 /* 8015E64C 0015B5AC 3B 20 00 01 */ li r25, 1 diff --git a/include/MetroidPrime/CHealthInfo.hpp b/include/MetroidPrime/CHealthInfo.hpp index 7287dabd..b531a54e 100644 --- a/include/MetroidPrime/CHealthInfo.hpp +++ b/include/MetroidPrime/CHealthInfo.hpp @@ -5,8 +5,9 @@ class CHealthInfo { public: - CHealthInfo(f32 hp, f32 resist); - // TODO + CHealthInfo(f32 hp, f32 resist) : x0_health(hp), x4_knockbackResistance(resist) {} + void SetHP(float hp) { x0_health = hp; } + float GetHP() const { return x0_health; } private: f32 x0_health; diff --git a/include/MetroidPrime/Player/CPlayerState.hpp b/include/MetroidPrime/Player/CPlayerState.hpp index 808f6aca..a2e459b6 100644 --- a/include/MetroidPrime/Player/CPlayerState.hpp +++ b/include/MetroidPrime/Player/CPlayerState.hpp @@ -1,12 +1,66 @@ #ifndef _CPLAYERSTATE_HPP #define _CPLAYERSTATE_HPP +#include "Kyoto/IObjectStore.hpp" +#include "MetroidPrime/CHealthInfo.hpp" +#include "MetroidPrime/Player/CStaticInterference.hpp" +#include "rstl/pair.hpp" +#include "rstl/reserved_vector.hpp" +#include "rstl/vector.hpp" #include "types.h" class CStateManager; class CPlayerState { public: + enum EItemType { + kIT_Invalid = -1, + kIT_PowerBeam = 0, + kIT_IceBeam = 1, + kIT_WaveBeam = 2, + kIT_PlasmaBeam = 3, + kIT_Missiles = 4, + kIT_ScanVisor = 5, + kIT_MorphBallBombs = 6, + kIT_PowerBombs = 7, + kIT_Flamethrower = 8, + kIT_ThermalVisor = 9, + kIT_ChargeBeam = 10, + kIT_SuperMissile = 11, + kIT_GrappleBeam = 12, + kIT_XRayVisor = 13, + kIT_IceSpreader = 14, + kIT_SpaceJumpBoots = 15, + kIT_MorphBall = 16, + kIT_CombatVisor = 17, + kIT_BoostBall = 18, + kIT_SpiderBall = 19, + kIT_PowerSuit = 20, + kIT_GravitySuit = 21, + kIT_VariaSuit = 22, + kIT_PhazonSuit = 23, + kIT_EnergyTanks = 24, + kIT_UnknownItem1 = 25, + kIT_HealthRefill = 26, + kIT_UnknownItem2 = 27, + kIT_Wavebuster = 28, + kIT_Truth = 29, + kIT_Strength = 30, + kIT_Elder = 31, + kIT_Wild = 32, + kIT_Lifegiver = 33, + kIT_Warrior = 34, + kIT_Chozo = 35, + kIT_Nature = 36, + kIT_Sun = 37, + kIT_World = 38, + kIT_Spirit = 39, + kIT_Newborn = 40, + + /* This must remain at the end of the list */ + kIT_Max + }; + enum EPlayerVisor { kPV_Combat, kPV_XRay, @@ -34,12 +88,91 @@ public: kBI_Phazon2 = 27, }; - EPlayerVisor GetActiveVisor(const CStateManager& mgr) const; + CPlayerState(); + explicit CPlayerState(CInputStream& stream); + void PutTo(COutputStream& stream); + + u32 GetMissileCostForAltAttack() const; + float GetComboFireAmmoPeriod() const; + static float GetMissileComboChargeFactor(); + u32 CalculateItemCollectionRate(); + u32 GetTotalPickupCount(); void SetIsFusionEnabled(bool v); + bool GetIsFusionEnabled(); + + EPlayerSuit GetCurrentSuit(); + EPlayerSuit GetCurrentSuitRaw() const { return x20_currentSuit; } + EBeamId GetCurrentBeam() const { return x8_currentBeam; } + void SetCurrentBeam(EBeamId beam) { x8_currentBeam = beam; } + bool CanVisorSeeFog(const CStateManager& stateMgr) const; + EPlayerVisor GetCurrentVisor() const { return x14_currentVisor; } + EPlayerVisor GetTransitioningVisor() const { return x18_transitioningVisor; } + EPlayerVisor GetActiveVisor(const CStateManager& mgr) const; + + void UpdateStaticInterference(CStateManager& stateMgr, const float& dt); + void IncreaseScanTime(u32 time, float val); + void SetScanTime(CAssetId res, float time); + float GetScanTime(CAssetId time) const; + bool GetIsVisorTransitioning() const; + float GetVisorTransitionFactor() const; + void UpdateVisorTransition(float dt); + void StartTransitionToVisor(EPlayerVisor visor); + void ResetVisor(); + + bool ItemEnabled(EItemType type) const; + void DisableItem(EItemType type); + void EnableItem(EItemType type); + bool HasPowerUp(EItemType type) const; + u32 GetPowerUp(EItemType type); + u32 GetItemCapacity(EItemType type) const; + u32 GetItemAmount(EItemType type) const; + void DecrPickUp(EItemType type, int amount); + void IncrPickUp(EItemType type, int amount); + void ResetAndIncrPickUp(EItemType type, int amount); + static float GetEnergyTankCapacity(); + static float GetBaseHealthCapacity(); + float CalculateHealth(); + void InitializePowerUp(CPlayerState::EItemType type, int capacity); + void ReInitializePowerUp(CPlayerState::EItemType type, int capacity); + + void InitializeScanTimes(); + + static uint GetBitCount(uint); + + CStaticInterference& GetStaticInterference() { return x188_staticIntf; } + + const rstl::vector< rstl::pair< CAssetId, float > >& GetScanTimes() const { + return x170_scanTimes; + } + + CHealthInfo& GetHealthInfo() { return xc_health; } + + const CHealthInfo& GetHealthInfo() const { return xc_health; } + private: - u8 pad[0x198]; + struct CPowerUp { + int x0_amount; + int x4_capacity; + CPowerUp() : x0_amount(0), x4_capacity(0) {} + CPowerUp(int amount, int capacity); + }; + + bool x0_24_alive : 1; + bool x0_25_firingComboBeam : 1; + bool x0_26_fusion : 1; + u32 x4_enabledItems; + EBeamId x8_currentBeam; + CHealthInfo xc_health; + EPlayerVisor x14_currentVisor; + EPlayerVisor x18_transitioningVisor; + float x1c_visorTransitionFactor; + EPlayerSuit x20_currentSuit; + rstl::reserved_vector< CPowerUp, 41 > x24_powerups; + rstl::vector< rstl::pair< CAssetId, float > > x170_scanTimes; + rstl::pair< u32, u32 > x180_scanCompletionRate; + CStaticInterference x188_staticIntf; }; #endif diff --git a/include/MetroidPrime/Player/CStaticInterference.hpp b/include/MetroidPrime/Player/CStaticInterference.hpp new file mode 100644 index 00000000..c215ec0e --- /dev/null +++ b/include/MetroidPrime/Player/CStaticInterference.hpp @@ -0,0 +1,23 @@ +#ifndef _CSTATICINTERFERENCE_HPP +#define _CSTATICINTERFERENCE_HPP + +#include "types.h" + +#include "MetroidPrime/TGameTypes.hpp" +#include "rstl/vector.hpp" + +struct CStaticInterferenceSource { + TUniqueId x0_id; + float x4_magnitude; + float x8_timeLeft; +}; + +class CStaticInterference { +public: + CStaticInterference(int); + +private: + rstl::vector< CStaticInterferenceSource > sources; +}; + +#endif diff --git a/src/MetroidPrime/Player/CPlayerState.cpp b/src/MetroidPrime/Player/CPlayerState.cpp new file mode 100644 index 00000000..a59c2077 --- /dev/null +++ b/src/MetroidPrime/Player/CPlayerState.cpp @@ -0,0 +1,419 @@ +#include "MetroidPrime/Player/CPlayerState.hpp" + +#include "Kyoto/Math/CMath.hpp" +#include "rstl/math.hpp" + +#include + +static const int kPowerUpMaxValues[] = { + 1, 1, 1, 1, 250, 1, 1, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 14, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +}; + +static const int kMissileCosts[] = { + 5, 10, 10, 10, 1, +}; + +static const float kComboAmmoPeriods[] = { + 0.2f, 0.1f, 0.2f, 0.2f, 1.f, +}; + +static const float kEnergyTankCapacity = 100.f; +static const float kBaseHealthCapacity = 99.f; + + +uint CPlayerState::GetBitCount(uint val) { + int bits = 0; + for (; val != 0; val >>= 1) { + bits += 1; + } + return bits; +} + +CPlayerState::CPowerUp::CPowerUp(int amount, int capacity) +: x0_amount(amount), x4_capacity(capacity) {} + + +CPlayerState::CPlayerState() +: x0_24_alive(true) +, x0_25_firingComboBeam(false) +, x0_26_fusion(false) +, x4_enabledItems(0) +, x8_currentBeam(kBI_Power) +, xc_health(99.f, 50.f) +, x14_currentVisor(kPV_Combat) +, x18_transitioningVisor(x14_currentVisor) +, x1c_visorTransitionFactor(0.2f) +, x20_currentSuit(kPS_Power) +, x24_powerups(CPowerUp(0, 0)) +, x170_scanTimes() +, x180_scanCompletionRate(0, 0) +, x188_staticIntf(5) {} + +CPlayerState::CPlayerState(CInputStream& stream) +: x0_24_alive(true) +, x0_25_firingComboBeam(false) +, x0_26_fusion(false) +, x4_enabledItems(0) +, x8_currentBeam(kBI_Power) +, xc_health(99.f, 50.f) +, x14_currentVisor(kPV_Combat) +, x18_transitioningVisor(x14_currentVisor) +, x1c_visorTransitionFactor(0.2f) +, x20_currentSuit(kPS_Power) +, x24_powerups() +, x170_scanTimes() +, x180_scanCompletionRate(0, 0) +, x188_staticIntf(5) { + // TODO +} + +void CPlayerState::PutTo(COutputStream& stream) { + /* + stream.WriteBits(x4_enabledItems, 32); + + const float realHP = xc_health.GetHP(); + u32 integralHP; + std::memcpy(&integralHP, &realHP, sizeof(u32)); + + stream.WriteBits(integralHP, 32); + stream.WriteBits(u32(x8_currentBeam), COutputStream::GetBitCount(5)); + stream.WriteBits(u32(x20_currentSuit), COutputStream::GetBitCount(4)); + for (size_t i = 0; i < x24_powerups.size(); ++i) { + const CPowerUp& pup = x24_powerups[i]; + stream.WriteBits(pup.x0_amount, COutputStream::GetBitCount(PowerUpMaxValues[i])); + stream.WriteBits(pup.x4_capacity, COutputStream::GetBitCount(PowerUpMaxValues[i])); + } + + for (const auto& scanTime : x170_scanTimes) { + if (scanTime.second >= 1.f) + stream.WriteBits(true, 1); + else + stream.WriteBits(false, 1); + } + + stream.WriteBits(x180_scanCompletionRate.first, COutputStream::GetBitCount(0x100)); + stream.WriteBits(x180_scanCompletionRate.second, COutputStream::GetBitCount(0x100)); + */ +} + +void CPlayerState::ReInitializePowerUp(CPlayerState::EItemType type, int capacity) { + x24_powerups[u32(type)].x4_capacity = 0; + InitializePowerUp(type, capacity); +} + +void CPlayerState::InitializePowerUp(CPlayerState::EItemType type, int capacity) { + if (type >= kIT_Max) + return; + + CPowerUp& pup = x24_powerups[u32(type)]; + pup.x4_capacity = CMath::Clamp(0, pup.x4_capacity + capacity, kPowerUpMaxValues[u32(type)]); + pup.x0_amount = rstl::min_val(pup.x0_amount, pup.x4_capacity); + if (type >= kIT_PowerSuit && type <= kIT_PhazonSuit) { + if (HasPowerUp(kIT_PhazonSuit)) + x20_currentSuit = kPS_Phazon; + else if (HasPowerUp(kIT_GravitySuit)) + x20_currentSuit = kPS_Gravity; + else if (HasPowerUp(kIT_VariaSuit)) + x20_currentSuit = kPS_Varia; + else + x20_currentSuit = kPS_Power; + } +} + +float CPlayerState::CalculateHealth() { + return (kEnergyTankCapacity * x24_powerups[kIT_EnergyTanks].x0_amount) + kBaseHealthCapacity; +} + +void CPlayerState::ResetAndIncrPickUp(CPlayerState::EItemType type, int amount) { + x24_powerups[u32(type)].x0_amount = 0; + IncrPickUp(type, amount); +} + +void CPlayerState::IncrPickUp(EItemType type, int amount) { + if (type < 0 || kIT_Max - 1 < type) { + return; + } + + if (0 <= amount) { + switch (type) { + case kIT_Missiles: + case kIT_PowerBombs: + case kIT_ChargeBeam: + case kIT_SpaceJumpBoots: + case kIT_EnergyTanks: + case kIT_Truth: + case kIT_Strength: + case kIT_Elder: + case kIT_Wild: + case kIT_Lifegiver: + case kIT_Warrior: + case kIT_Chozo: + case kIT_Nature: + case kIT_Sun: + case kIT_World: + case kIT_Spirit: + case kIT_Newborn: { + int oldCapacity = x24_powerups[type].x4_capacity; + x24_powerups[type].x0_amount += amount; + if (oldCapacity < x24_powerups[type].x0_amount) { + x24_powerups[type].x0_amount = oldCapacity; + } + break; + } + case kIT_HealthRefill: { + CHealthInfo* info = &xc_health; + if (info != NULL) { + float newHealth = float(amount) + info->GetHP(); + float maxHealth = CalculateHealth(); + if (newHealth <= newHealth) { + info->SetHP(newHealth); + } else { + info->SetHP(maxHealth); + } + } + } + } + if (type == kIT_EnergyTanks) + IncrPickUp(kIT_HealthRefill, 9999); + } +} + +void CPlayerState::DecrPickUp(CPlayerState::EItemType type, int amount) { + if (type < 0 || kIT_Max - 1 < type) { + return; + } + + switch (type) { + case kIT_Missiles: + case kIT_PowerBombs: + case kIT_Flamethrower: + x24_powerups[type].x0_amount -= amount; + if (x24_powerups[type].x0_amount < 0) { + x24_powerups[type].x0_amount = 0; + } + default: + return; + } +} + +u32 CPlayerState::GetItemAmount(CPlayerState::EItemType type) const { + if (type < 0 || kIT_Max - 1 < type) { + return 0; + } + + switch (type) { + case kIT_SpaceJumpBoots: + case kIT_PowerBombs: + case kIT_Flamethrower: + case kIT_EnergyTanks: + case kIT_Missiles: + case kIT_Truth: + case kIT_Strength: + case kIT_Elder: + case kIT_Wild: + case kIT_Lifegiver: + case kIT_Warrior: + case kIT_Chozo: + case kIT_Nature: + case kIT_Sun: + case kIT_World: + case kIT_Spirit: + case kIT_Newborn: + return x24_powerups[u32(type)].x0_amount; + } + + return 0; +} + +u32 CPlayerState::GetItemCapacity(CPlayerState::EItemType type) const { + if (type < 0 || kIT_Max - 1 < type) { + return 0; + } + return x24_powerups[u32(type)].x4_capacity; +} + +bool CPlayerState::HasPowerUp(CPlayerState::EItemType type) const { + if (type < 0 || kIT_Max - 1 < type) { + return false; + } + return x24_powerups[u32(type)].x4_capacity != 0; +} + +u32 CPlayerState::GetPowerUp(CPlayerState::EItemType type) { + if (type < 0 || kIT_Max - 1 < type) { + return 0; + } + return x24_powerups[u32(type)].x4_capacity; +} + +void CPlayerState::EnableItem(CPlayerState::EItemType type) { + if (HasPowerUp(type)) + x4_enabledItems |= (1 << u32(type)); +} + +void CPlayerState::DisableItem(CPlayerState::EItemType type) { + if (HasPowerUp(type)) + x4_enabledItems &= ~(1 << u32(type)); +} + +bool CPlayerState::ItemEnabled(CPlayerState::EItemType type) const { + if (HasPowerUp(type)) + return (x4_enabledItems & (1 << u32(type))); + return false; +} + +void CPlayerState::ResetVisor() { + x18_transitioningVisor = x14_currentVisor = kPV_Combat; + x1c_visorTransitionFactor = 0.0f; +} + +void CPlayerState::StartTransitionToVisor(CPlayerState::EPlayerVisor visor) { + if (x18_transitioningVisor == visor) + return; + x18_transitioningVisor = visor; +} + +void CPlayerState::UpdateVisorTransition(float dt) { + if (!GetIsVisorTransitioning()) + return; + + if (x14_currentVisor == x18_transitioningVisor) { + x1c_visorTransitionFactor += dt; + if (x1c_visorTransitionFactor > 0.2f) + x1c_visorTransitionFactor = 0.2f; + } else { + x1c_visorTransitionFactor -= dt; + if (x1c_visorTransitionFactor < 0.f) { + x14_currentVisor = x18_transitioningVisor; + x1c_visorTransitionFactor = fabs(x1c_visorTransitionFactor); + if (x1c_visorTransitionFactor > 0.19999f) + x1c_visorTransitionFactor = 0.19999f; + } + } +} + +float CPlayerState::GetVisorTransitionFactor() const { return x1c_visorTransitionFactor / 0.2f; } + +bool CPlayerState::GetIsVisorTransitioning() const { + return x14_currentVisor != x18_transitioningVisor || x1c_visorTransitionFactor < 0.2f; +} + +float CPlayerState::GetBaseHealthCapacity() { return kBaseHealthCapacity; } + +float CPlayerState::GetEnergyTankCapacity() { return kEnergyTankCapacity; } + +void CPlayerState::InitializeScanTimes() { + if (x170_scanTimes.size()) + return; + + // TODO + // const auto& scanStates = g_MemoryCardSys->GetScanStates(); + // x170_scanTimes.reserve(scanStates.size()); + // for (const auto& state : scanStates) + // x170_scanTimes.emplace_back(state.first, 0.f); +} + +float CPlayerState::GetScanTime(CAssetId res) const { + // TODO + rstl::vector< rstl::pair< CAssetId, float > >::const_iterator it = x170_scanTimes.end(); + // const auto it = std::find_if(x170_scanTimes.cbegin(), x170_scanTimes.cend(), + // [&](const auto& test) -> bool { return test.first == res; }); + + if (it == x170_scanTimes.end()) + return 0.f; + + return it->second; +} + +void CPlayerState::SetScanTime(CAssetId res, float time) { + // TODO + rstl::vector< rstl::pair< CAssetId, float > >::iterator it = x170_scanTimes.end(); + // auto it = std::find_if(x170_scanTimes.begin(), x170_scanTimes.end(), + // [&](const auto& test) -> bool { return test.first == res; }); + + if (it != x170_scanTimes.end()) + it->second = time; +} + +void CPlayerState::UpdateStaticInterference(CStateManager& stateMgr, const float& dt) { + // TODO + // x188_staticIntf.Update(stateMgr, dt); +} + +CPlayerState::EPlayerVisor CPlayerState::GetActiveVisor(const CStateManager& stateMgr) const { + void* cam = NULL; + // const CFirstPersonCamera* cam = + // TCastToConstPtr(stateMgr.GetCameraManager()->GetCurrentCamera(stateMgr)).GetPtr(); + return (cam ? x14_currentVisor : kPV_Combat); +} + +bool CPlayerState::CanVisorSeeFog(const CStateManager& stateMgr) const { + EPlayerVisor activeVisor = GetActiveVisor(stateMgr); + if (activeVisor == kPV_Combat || activeVisor == kPV_Scan) + return true; + return true; +} + +CPlayerState::EPlayerSuit CPlayerState::GetCurrentSuit() { + if (GetIsFusionEnabled()) + return kPS_FusionPower; + + return x20_currentSuit; +} + +bool CPlayerState::GetIsFusionEnabled() { return x0_26_fusion; } + +void CPlayerState::SetIsFusionEnabled(bool val) { x0_26_fusion = val; } + +u32 CPlayerState::GetTotalPickupCount() { return 99; } + +u32 CPlayerState::CalculateItemCollectionRate() { + u32 total = GetItemCapacity(kIT_PowerBombs); + + if (total >= 4) + total -= 3; + total += GetItemCapacity(kIT_WaveBeam); + total += GetItemCapacity(kIT_IceBeam); + total += GetItemCapacity(kIT_PlasmaBeam); + total += GetItemCapacity(kIT_Missiles) / 5; + total += GetItemCapacity(kIT_MorphBallBombs); + total += GetItemCapacity(kIT_Flamethrower); + total += GetItemCapacity(kIT_ThermalVisor); + total += GetItemCapacity(kIT_ChargeBeam); + total += GetItemCapacity(kIT_SuperMissile); + total += GetItemCapacity(kIT_GrappleBeam); + total += GetItemCapacity(kIT_XRayVisor); + total += GetItemCapacity(kIT_IceSpreader); + total += GetItemCapacity(kIT_SpaceJumpBoots); + total += GetItemCapacity(kIT_MorphBall); + total += GetItemCapacity(kIT_BoostBall); + total += GetItemCapacity(kIT_SpiderBall); + total += GetItemCapacity(kIT_GravitySuit); + total += GetItemCapacity(kIT_VariaSuit); + total += GetItemCapacity(kIT_EnergyTanks); + total += GetItemCapacity(kIT_Truth); + total += GetItemCapacity(kIT_Strength); + total += GetItemCapacity(kIT_Elder); + total += GetItemCapacity(kIT_Wild); + total += GetItemCapacity(kIT_Lifegiver); + total += GetItemCapacity(kIT_Warrior); + total += GetItemCapacity(kIT_Chozo); + total += GetItemCapacity(kIT_Nature); + total += GetItemCapacity(kIT_Sun); + total += GetItemCapacity(kIT_World); + total += GetItemCapacity(kIT_Spirit); + total += GetItemCapacity(kIT_Newborn); + return total + GetItemCapacity(kIT_Wavebuster); +} + +u32 CPlayerState::GetMissileCostForAltAttack() const { + return kMissileCosts[size_t(x8_currentBeam)]; +} + +float CPlayerState::GetComboFireAmmoPeriod() const { + return kComboAmmoPeriods[size_t(x8_currentBeam)]; +} + +float CPlayerState::GetMissileComboChargeFactor() { return 1.8f; } +