From 13ac16e1b84f3cd52629f578afb231a5b1c181bb Mon Sep 17 00:00:00 2001 From: Luke Street Date: Tue, 24 Feb 2026 23:38:31 -0700 Subject: [PATCH 1/3] Lots of CScriptWater and some of ScriptLoader --- config/GM8E01_00/symbols.txt | 50 +- config/GM8E01_01/symbols.txt | 10 +- include/Collision/CRayCastResult.hpp | 1 + include/Kyoto/Graphics/CGraphics.hpp | 2 +- include/Kyoto/Math/CMatrix3f.hpp | 15 + include/Kyoto/Math/CVector2f.hpp | 2 +- include/MetroidPrime/CActorLights.hpp | 11 + include/MetroidPrime/CActorParameters.hpp | 8 +- include/MetroidPrime/CAnimData.hpp | 2 + include/MetroidPrime/CFluidPlane.hpp | 1 + include/MetroidPrime/CFluidPlaneCPU.hpp | 98 ++ include/MetroidPrime/CFluidPlaneDoor.hpp | 11 +- include/MetroidPrime/CFluidPlaneManager.hpp | 14 +- include/MetroidPrime/CModelData.hpp | 4 +- include/MetroidPrime/CRippleManager.hpp | 2 + include/MetroidPrime/CStateManager.hpp | 3 + include/MetroidPrime/Enemies/CAtomicAlpha.hpp | 82 ++ include/MetroidPrime/Enemies/CAtomicBeta.hpp | 79 ++ include/MetroidPrime/Enemies/CWarWasp.hpp | 125 ++ include/MetroidPrime/ScriptLoader.hpp | 5 +- .../ScriptObjects/CScriptActorRotate.hpp | 2 +- .../ScriptObjects/CScriptBallTrigger.hpp | 2 +- .../ScriptObjects/CScriptDistanceFog.hpp | 6 +- .../ScriptObjects/CScriptDockAreaChange.hpp | 2 +- .../ScriptObjects/CScriptMemoryRelay.hpp | 2 +- .../ScriptObjects/CScriptPickup.hpp | 2 +- .../ScriptObjects/CScriptSpecialFunction.hpp | 4 +- .../ScriptObjects/CScriptTrigger.hpp | 3 + .../ScriptObjects/CScriptWater.hpp | 43 +- src/MetroidPrime/CFluidPlaneCPU.cpp | 104 ++ src/MetroidPrime/CFluidPlaneDoor.cpp | 8 +- src/MetroidPrime/CFluidPlaneManager.cpp | 6 + src/MetroidPrime/CRippleManager.cpp | 7 + src/MetroidPrime/CStateManager.cpp | 5 +- src/MetroidPrime/Cameras/CCameraManager.cpp | 10 +- src/MetroidPrime/Enemies/CAtomicAlpha.cpp | 37 + src/MetroidPrime/Enemies/CAtomicBeta.cpp | 62 + src/MetroidPrime/Enemies/CWarWasp.cpp | 62 + src/MetroidPrime/ScriptLoader.cpp | 1009 ++++++++++++++++- .../ScriptObjects/CScriptBallTrigger.cpp | 2 +- .../ScriptObjects/CScriptDistanceFog.cpp | 4 +- .../ScriptObjects/CScriptWater.cpp | 747 ++++++++++++ 42 files changed, 2522 insertions(+), 132 deletions(-) create mode 100644 include/MetroidPrime/Enemies/CAtomicAlpha.hpp create mode 100644 include/MetroidPrime/Enemies/CAtomicBeta.hpp create mode 100644 src/MetroidPrime/CFluidPlaneCPU.cpp create mode 100644 src/MetroidPrime/Enemies/CAtomicAlpha.cpp create mode 100644 src/MetroidPrime/Enemies/CAtomicBeta.cpp create mode 100644 src/MetroidPrime/Enemies/CWarWasp.cpp create mode 100644 src/MetroidPrime/ScriptObjects/CScriptWater.cpp diff --git a/config/GM8E01_00/symbols.txt b/config/GM8E01_00/symbols.txt index a30b7b820..988b3c66e 100644 --- a/config/GM8E01_00/symbols.txt +++ b/config/GM8E01_00/symbols.txt @@ -6339,19 +6339,19 @@ fn_800FFBE8 = .text:0x800FFBE8; // type:function size:0xA8 fn_800FFC90 = .text:0x800FFC90; // type:function size:0xA8 PreRender__12CScriptWaterFR13CStateManagerRC14CFrustumPlanes = .text:0x800FFD38; // type:function size:0x208 scope:global AddToRenderer__12CScriptWaterCFRC14CFrustumPlanesRC13CStateManager = .text:0x800FFF40; // type:function size:0xE8 scope:global -GetRenderBounds__12CScriptWaterCFRC12CTransform4f = .text:0x80100028; // type:function size:0x68 scope:global +GetSortingBounds__12CScriptWaterCFRC13CStateManager = .text:0x80100028; // type:function size:0x68 scope:global CalculateRenderBounds__12CScriptWaterFv = .text:0x80100090; // type:function size:0xDC scope:global Accept__12CScriptWaterFR8IVisitor = .text:0x8010016C; // type:function size:0x38 scope:global Think__12CScriptWaterFfR13CStateManager = .text:0x801001A4; // type:function size:0x428 scope:global UpdateSplashInhabitants__12CScriptWaterFR13CStateManager = .text:0x801005CC; // type:function size:0x1EC scope:global Touch__12CScriptWaterFR6CActorR13CStateManager = .text:0x801007B8; // type:function size:0x1B4 scope:global -GetNextConnectedWater__12CScriptWaterFR13CStateManager = .text:0x8010096C; // type:function size:0xF4 scope:global +GetNextConnectedWater__12CScriptWaterCFRC13CStateManager = .text:0x8010096C; // type:function size:0xF4 scope:global AcceptScriptMsg__12CScriptWaterF20EScriptObjectMessage9TUniqueIdR13CStateManager = .text:0x80100A60; // type:function size:0x2B0 scope:global __dt__12CScriptWaterFv = .text:0x80100D10; // type:function size:0x1FC scope:global __dt__14CFluidPlaneCPUFv = .text:0x80100F0C; // type:function size:0x140 scope:global -clear__Q24rstl78reserved_vector>,3>Fv = .text:0x8010104C; // type:function size:0xA0 scope:global -__ct__12CScriptWaterFR13CStateManager9TUniqueIdRCQ24rstl66basic_string,Q24rstl17rmemory_allocator>RC11CEntityInfoRC9CVector3fRC6CAABoxRC11CDamageInfoRC9CVector3fUibbUiUiUiUiUiUiUiRC9CVector3fffbQ211CFluidPlane10EFluidTypebfRC14CFluidUVMotionffffffffRC6CColorRC6CColorUiUiUiUiUiiiiifUiffffffffRC6CColorUifffUiUibiiPCUi = .text:0x801010EC; // type:function size:0xD20 scope:global -fn_80101E0C = .text:0x80101E0C; // type:function size:0x7C +__dt__Q24rstl78reserved_vector>,3>Fv = .text:0x8010104C; // type:function size:0xA0 scope:global +__ct__12CScriptWaterFR13CStateManager9TUniqueIdRCQ24rstl66basic_string,Q24rstl17rmemory_allocator>RC11CEntityInfoRC9CVector3fRC6CAABoxRC11CDamageInfoRC9CVector3fUibbUiUiUiUiUiUiUiRC9CVector3ffffbQ211CFluidPlane10EFluidTypebfRC14CFluidUVMotionffffffffRC6CColorRC6CColorUiUiUiUiUiiiiiifUiffffffffRC6CColorUifffUiUibiiPCUi = .text:0x801010EC; // type:function size:0xD20 scope:global +do_erase__Q24rstl60list,Q24rstl17rmemory_allocator>FPQ34rstl60list,Q24rstl17rmemory_allocator>4node = .text:0x80101E0C; // type:function size:0x7C __dt__Q24rstl60list,Q24rstl17rmemory_allocator>Fv = .text:0x80101E88; // type:function size:0x78 scope:weak GetCollisionResponseType__7CWeaponCFRC9CVector3fRC9CVector3fRC11CWeaponModei = .text:0x80101F00; // type:function size:0x8 scope:global Render__7CWeaponCFRC13CStateManager = .text:0x80101F08; // type:function size:0x4 scope:global @@ -7442,7 +7442,7 @@ __sinit_CPuddleToadGamma_cpp = .text:0x8014CFDC; // type:function size:0x20 scop AcceptScriptMsg__18CScriptDistanceFogF20EScriptObjectMessage9TUniqueIdR13CStateManager = .text:0x8014CFFC; // type:function size:0x1C4 scope:global Accept__18CScriptDistanceFogFR8IVisitor = .text:0x8014D1C0; // type:function size:0x38 scope:global __dt__18CScriptDistanceFogFv = .text:0x8014D1F8; // type:function size:0x60 scope:global -__ct__18CScriptDistanceFogF9TUniqueIdRCQ24rstl66basic_string,Q24rstl17rmemory_allocator>RC11CEntityInfo11ERglFogModeRC6CColorRC9CVector2ffRC9CVector2fbbffff = .text:0x8014D258; // type:function size:0x16C scope:global +__ct__18CScriptDistanceFogF9TUniqueIdRCQ24rstl66basic_string,Q24rstl17rmemory_allocator>RC11CEntityInfo11ERglFogModeRC6CColorRC9CVector2ff9CVector2fbbffff = .text:0x8014D258; // type:function size:0x16C scope:global __dt__19CBSProjectileAttackFv = .text:0x8014D3C4; // type:function size:0x5C scope:global CanShoot__19CBSProjectileAttackCFv = .text:0x8014D420; // type:function size:0x8 scope:global GetBodyStateTransition__19CBSProjectileAttackFfR15CBodyController = .text:0x8014D428; // type:function size:0xEC scope:global @@ -7944,7 +7944,7 @@ Think__18CScriptBallTriggerFfR13CStateManager = .text:0x801765B0; // type:functi InhabitantExited__18CScriptBallTriggerFR6CActorR13CStateManager = .text:0x80176838; // type:function size:0x58 scope:global InhabitantAdded__18CScriptBallTriggerFR6CActorR13CStateManager = .text:0x80176890; // type:function size:0x3C scope:global __dt__18CScriptBallTriggerFv = .text:0x801768CC; // type:function size:0x60 scope:global -__ct__18CScriptBallTriggerF9TUniqueIdRCQ24rstl66basic_string,Q24rstl17rmemory_allocator>RC11CEntityInfoRC9CVector3fRC9CVector3fbfffRC9CVector3fb = .text:0x8017692C; // type:function size:0x1AC scope:global +__ct__18CScriptBallTriggerF9TUniqueIdRCQ24rstl66basic_string,Q24rstl17rmemory_allocator>RC11CEntityInfoRC9CVector3fRC9CVector3fbfff9CVector3fb = .text:0x8017692C; // type:function size:0x1AC scope:global calculate_ball_aabox__Fv = .text:0x80176AD8; // type:function size:0x54 scope:local __dt__17CPlasmaProjectileFv = .text:0x80176B2C; // type:function size:0x1EC scope:global __dt__15CBeamProjectileFv = .text:0x80176D18; // type:function size:0x11C scope:global @@ -8340,9 +8340,9 @@ UpdatePatch__FfRQ220CFluidPlaneCPURender10SPatchInfoRC14CFluidPlaneCPURC9CVector ApplyTurbulence__FfRA45_A45_Q220CFluidPlaneCPURender13SHFieldSamplePCUcRA256_CfRCQ220CFluidPlaneCPURender10SPatchInfoRC14CFluidPlaneCPURC9CVector3f = .text:0x8019C03C; // type:function size:0x164 scope:global UpdatePatchNoNormals__FPCUcPCUcQ220CFluidPlaneCPURender10SPatchInfo = .text:0x8019C1A0; // type:function size:0x328 scope:global UpdatePatchWithNormals__FPCUcPCUcQ220CFluidPlaneCPURender10SPatchInfo = .text:0x8019C4C8; // type:function size:0x66C scope:global -__dt__11CTurbulenceFv = .text:0x8019CB34; // type:function size:0x54 scope:global +__dt__Q214CFluidPlaneCPU11CTurbulenceFv = .text:0x8019CB34; // type:function size:0x54 scope:global __ct__Q214CFluidPlaneCPU11CTurbulenceFffffffff = .text:0x8019CB88; // type:function size:0x238 scope:global -__ct__14CFluidPlaneCPUFUiUiUiUiUiUifUiQ211CFluidPlane10EFluidTypefRC9CVector3ffRC14CFluidUVMotionfffffffffffff = .text:0x8019CDC0; // type:function size:0x5E0 scope:global +__ct__14CFluidPlaneCPUFUiUiUiUiUiUifUiUiQ211CFluidPlane10EFluidTypefRC9CVector3ffRC14CFluidUVMotionffffffffffffff = .text:0x8019CDC0; // type:function size:0x5E0 scope:global AddRipple__15CFluidPlaneDoorFRC7CRippleRC12CScriptWaterR13CStateManager = .text:0x8019D3A0; // type:function size:0x4 AddRipple__15CFluidPlaneDoorFf9TUniqueIdRC9CVector3fRC9CVector3fRC12CScriptWaterR13CStateManagerRC9CVector3f = .text:0x8019D3A4; // type:function size:0x4 AddRipple__15CFluidPlaneDoorFf9TUniqueIdRC9CVector3fRC12CScriptWaterR13CStateManager = .text:0x8019D3A8; // type:function size:0x4 @@ -10435,7 +10435,7 @@ Draw__15CQuitGameScreenFv = .text:0x802449BC; // type:function size:0xE4 scope:g ProcessUserInput__15CQuitGameScreenFRC11CFinalInput = .text:0x80244AA0; // type:function size:0x70 scope:global __ct__15CQuitGameScreenF9EQuitType = .text:0x80244B10; // type:function size:0x98 scope:global __dt__12CAtomicAlphaFv = .text:0x80244BA8; // type:function size:0x14C scope:global -fn_80244CF4 = .text:0x80244CF4; // type:function size:0x8 +ProjectileInfo__12CAtomicAlphaFv = .text:0x80244CF4; // type:function size:0x8 scope:global GetSearchPath__12CAtomicAlphaFv = .text:0x80244CFC; // type:function size:0x8 scope:global GetCollisionResponseType__12CAtomicAlphaCFRC9CVector3fRC9CVector3fRC11CWeaponModei = .text:0x80244D04; // type:function size:0x54 scope:global Attack__12CAtomicAlphaFR13CStateManager9EStateMsgf = .text:0x80244D58; // type:function size:0xF0 scope:global @@ -10449,7 +10449,7 @@ CollidedWith__12CAtomicAlphaFRC9TUniqueIdRC18CCollisionInfoListR13CStateManager Think__12CAtomicAlphaFfR13CStateManager = .text:0x80245514; // type:function size:0x84 scope:global AcceptScriptMsg__12CAtomicAlphaF20EScriptObjectMessage9TUniqueIdR13CStateManager = .text:0x80245598; // type:function size:0xD0 scope:global Accept__12CAtomicAlphaFR8IVisitor = .text:0x80245668; // type:function size:0x38 scope:global -__ct__12CAtomicAlpha = .text:0x802456A0; // type:function size:0x390 scope:global +__ct__12CAtomicAlphaF9TUniqueIdRCQ24rstl66basic_string,Q24rstl17rmemory_allocator>RC11CEntityInfoRC12CTransform4fRC10CModelDataRC16CActorParametersRC14CPatternedInfoUiRC11CDamageInfofffUibb = .text:0x802456A0; // type:function size:0x390 scope:global IsScanComplete__14CLogBookScreenF13EScanCategoryUiRC12CPlayerState = .text:0x80245A30; // type:function size:0x64 scope:global IsScanCategoryReady__14CLogBookScreenF13EScanCategory = .text:0x80245A94; // type:function size:0xA0 scope:global InputDisabled__14CLogBookScreenCFv = .text:0x80245B34; // type:function size:0x14 scope:global @@ -10663,7 +10663,7 @@ GetCollisionResponseType__11CAtomicBetaCFRC9CVector3fRC9CVector3fRC11CWeaponMode Touch__11CAtomicBetaFR6CActorR13CStateManager = .text:0x80250B5C; // type:function size:0xE0 scope:global GetDamageVulnerability__11CAtomicBetaCFv = .text:0x80250C3C; // type:function size:0x58 scope:global fn_80250C94 = .text:0x80250C94; // type:function size:0x3C -fn_80250CD0 = .text:0x80250CD0; // type:function size:0x4C +DestroyEmitter__11CAtomicBetaFR10CSfxHandle = .text:0x80250CD0; // type:function size:0x4C fn_80250D1C = .text:0x80250D1C; // type:function size:0x90 Think__11CAtomicBetaFfR13CStateManager = .text:0x80250DAC; // type:function size:0x464 scope:global fn_80251210 = .text:0x80251210; // type:function size:0xC8 @@ -10672,7 +10672,7 @@ fn_80251494 = .text:0x80251494; // type:function size:0x174 AcceptScriptMsg__11CAtomicBetaF20EScriptObjectMessage9TUniqueIdR13CStateManager = .text:0x80251608; // type:function size:0xE4 scope:global Accept__11CAtomicBetaFR8IVisitor = .text:0x802516EC; // type:function size:0x38 scope:global __dt__11CAtomicBetaFv = .text:0x80251724; // type:function size:0x108 scope:global -__ct__11CAtomicBeta = .text:0x8025182C; // type:function size:0x384 scope:global +__ct__11CAtomicBetaF9TUniqueIdRCQ24rstl66basic_string,Q24rstl17rmemory_allocator>RC11CEntityInfoRC12CTransform4f10CModelDataRC16CActorParametersRC14CPatternedInfoUiUiRC11CDamageInfoUifffRC20CDamageVulnerabilityfffUsUsUsf = .text:0x8025182C; // type:function size:0x384 scope:global __dt__23CElectricBeamProjectileFv = .text:0x80251BB0; // type:function size:0xD0 scope:global AcceptScriptMsg__23CElectricBeamProjectileF20EScriptObjectMessage9TUniqueIdR13CStateManager = .text:0x80251C80; // type:function size:0xB8 scope:global ResetBeam__23CElectricBeamProjectileFR13CStateManagerb = .text:0x80251D38; // type:function size:0x9C scope:global @@ -16965,7 +16965,7 @@ lbl_803CE008 = .rodata:0x803CE008; // type:object size:0x10 lbl_803CE018 = .rodata:0x803CE018; // type:object size:0x7 data:string @stringBase0 = .rodata:0x803CE020; // type:object size:0x4F scope:local data:string_table lbl_803CE070 = .rodata:0x803CE070; // type:object size:0xC data:4byte -lbl_803CE07C = .rodata:0x803CE07C; // type:object size:0x25C +@stringBase0 = .rodata:0x803CE07C; // type:object size:0x25C scope:local data:string_table lbl_803CE2D8 = .rodata:0x803CE2D8; // type:object size:0x20 lbl_803CE2F8 = .rodata:0x803CE2F8; // type:object size:0x40 lbl_803CE338 = .rodata:0x803CE338; // type:object size:0x74 @@ -17044,8 +17044,8 @@ lbl_803CECA2 = .rodata:0x803CECA2; // type:object size:0x19 data:string lbl_803CECBB = .rodata:0x803CECBB; // type:object size:0x13D lbl_803CEDF8 = .rodata:0x803CEDF8; // type:object size:0x7 data:string @stringBase0 = .rodata:0x803CEE00; // type:object size:0x1C scope:local data:string_table -lbl_803CEE20 = .rodata:0x803CEE20; // type:object size:0x18 -lbl_803CEE38 = .rodata:0x803CEE38; // type:object size:0x7 data:string +kSplashScales = .rodata:0x803CEE20; // type:object size:0x18 scope:local +@stringBase0 = .rodata:0x803CEE38; // type:object size:0x7 scope:local data:string_table lbl_803CEE40 = .rodata:0x803CEE40; // type:object size:0x10 data:4byte @stringBase0 = .rodata:0x803CEE50; // type:object size:0x7 scope:local data:string_table lbl_803CEE58 = .rodata:0x803CEE58; // type:object size:0x38 data:4byte @@ -17477,7 +17477,7 @@ lbl_803D447C = .rodata:0x803D447C; // type:object size:0x78 lbl_803D44F4 = .rodata:0x803D44F4; // type:object size:0x60 lbl_803D4554 = .rodata:0x803D4554; // type:object size:0x60 lbl_803D45B4 = .rodata:0x803D45B4; // type:object size:0x17C -lbl_803D4730 = .rodata:0x803D4730; // type:object size:0x30 +@stringBase0 = .rodata:0x803D4730; // type:object size:0x30 scope:local data:string_table lbl_803D4760 = .rodata:0x803D4760; // type:object size:0x7 data:string @stringBase0 = .rodata:0x803D4768; // type:object size:0x16 scope:local data:string_table lbl_803D4780 = .rodata:0x803D4780; // type:object size:0x18 @@ -17490,9 +17490,9 @@ lbl_803D4AA0 = .rodata:0x803D4AA0; // type:object size:0xF data:string lbl_803D4AAF = .rodata:0x803D4AAF; // type:object size:0xF data:string lbl_803D4ABE = .rodata:0x803D4ABE; // type:object size:0x10 data:string lbl_803D4ACE = .rodata:0x803D4ACE; // type:object size:0x82 -lbl_803D4B50 = .rodata:0x803D4B50; // type:object size:0xB data:string -lbl_803D4B5B = .rodata:0x803D4B5B; // type:object size:0xB data:string -lbl_803D4B66 = .rodata:0x803D4B66; // type:object size:0x12 +@stringBase0 = .rodata:0x803D4B50; // type:object size:0xB scope:local data:string_table +@stringBase1 = .rodata:0x803D4B5B; // type:object size:0xB scope:local data:string_table +@stringBase2 = .rodata:0x803D4B66; // type:object size:0x12 scope:local data:string_table lbl_803D4B78 = .rodata:0x803D4B78; // type:object size:0x20 lbl_803D4B98 = .rodata:0x803D4B98; // type:object size:0x30 data:4byte lbl_803D4BC8 = .rodata:0x803D4BC8; // type:object size:0x50 data:4byte @@ -18367,11 +18367,11 @@ __vt__19CScriptCameraShaker = .data:0x803DFE00; // type:object size:0x20 scope:g __vt__20CScriptActorKeyframe = .data:0x803DFE20; // type:object size:0x20 scope:global __vt__14CBCScriptedCmd = .data:0x803DFE40; // type:object size:0xC scope:weak __vt__20CConsoleOutputWindow = .data:0x803DFE50; // type:object size:0x1C -lbl_803DFE70 = .data:0x803DFE70; // type:object size:0x80 +__vt__12CScriptWater = .data:0x803DFE70; // type:object size:0x80 scope:global __vt__7CWeapon = .data:0x803DFEF0; // type:object size:0x6C scope:global @468 = .data:0x803DFF5C; // type:object size:0x24 scope:local __vt__16CLightParameters = .data:0x803DFF80; // type:object size:0xC scope:global -lbl_803DFF90 = .data:0x803DFF90; // type:object size:0x2CC +__vt__8CWarWasp = .data:0x803DFF90; // type:object size:0x2CC scope:global jumptable_803E025C = .data:0x803E025C; // type:object size:0x48 scope:local __vt__14CAudioStateWin = .data:0x803E02A8; // type:object size:0x1C scope:global lbl_803E02C8 = .data:0x803E02C8; // type:object size:0x70 @@ -18511,7 +18511,7 @@ lbl_803E4550 = .data:0x803E4550; // type:object size:0x44 lbl_803E4594 = .data:0x803E4594; // type:object size:0x44 lbl_803E45D8 = .data:0x803E45D8; // type:object size:0x70 __vt__15CBeamProjectile = .data:0x803E4648; // type:object size:0x80 scope:global -lbl_803E46C8 = .data:0x803E46C8; // type:object size:0x20 +__vt__14CFluidPlaneCPU = .data:0x803E46C8; // type:object size:0x20 scope:global lbl_803E46E8 = .data:0x803E46E8; // type:object size:0x20 __vt__20CScriptRoomAcoustics = .data:0x803E4708; // type:object size:0x20 scope:global lbl_803E4728 = .data:0x803E4728; // type:object size:0x2CC @@ -18642,7 +18642,7 @@ lbl_803E87D8 = .data:0x803E87D8; // type:object size:0x54 jumptable_803E882C = .data:0x803E882C; // type:object size:0x38 scope:local jumptable_803E8864 = .data:0x803E8864; // type:object size:0x38 scope:local lbl_803E889C = .data:0x803E889C; // type:object size:0x8 scope:local -lbl_803E88A8 = .data:0x803E88A8; // type:object size:0x2D0 +__vt__12CAtomicAlpha = .data:0x803E88A8; // type:object size:0x2D0 scope:global lbl_803E8B78 = .data:0x803E8B78; // type:object size:0x54 lbl_803E8BCC = .data:0x803E8BCC; // type:object size:0x4C @322 = .data:0x803E8C18; // type:object size:0x24 scope:local @@ -18655,7 +18655,7 @@ lbl_803E8DAC = .data:0x803E8DAC; // type:object size:0xC data:4byte jumptable_803E8DB8 = .data:0x803E8DB8; // type:object size:0x44 scope:local jumptable_803E8DFC = .data:0x803E8DFC; // type:object size:0x44 scope:local lbl_803E8E40 = .data:0x803E8E40; // type:object size:0xC data:4byte -lbl_803E8E4C = .data:0x803E8E4C; // type:object size:0x2CC +__vt__11CAtomicBeta = .data:0x803E8E4C; // type:object size:0x2CC scope:global __vt__23CElectricBeamProjectile = .data:0x803E9118; // type:object size:0x80 scope:global lbl_803E9198 = .data:0x803E9198; // type:object size:0xC data:4byte lbl_803E91A4 = .data:0x803E91A4; // type:object size:0xC data:4byte diff --git a/config/GM8E01_01/symbols.txt b/config/GM8E01_01/symbols.txt index 7d5f41727..cda0ca092 100644 --- a/config/GM8E01_01/symbols.txt +++ b/config/GM8E01_01/symbols.txt @@ -6349,9 +6349,9 @@ GetNextConnectedWater__12CScriptWaterFR13CStateManager = .text:0x801009E8; // ty AcceptScriptMsg__12CScriptWaterF20EScriptObjectMessage9TUniqueIdR13CStateManager = .text:0x80100ADC; // type:function size:0x2B0 scope:global __dt__12CScriptWaterFv = .text:0x80100D8C; // type:function size:0x1FC scope:global __dt__14CFluidPlaneCPUFv = .text:0x80100F88; // type:function size:0x140 scope:global -clear__Q24rstl78reserved_vector>,3>Fv = .text:0x801010C8; // type:function size:0xA0 scope:global +__dt__Q24rstl78reserved_vector>,3>Fv = .text:0x801010C8; // type:function size:0xA0 scope:global __ct__12CScriptWaterFR13CStateManager9TUniqueIdRCQ24rstl66basic_string,Q24rstl17rmemory_allocator>RC11CEntityInfoRC9CVector3fRC6CAABoxRC11CDamageInfoRC9CVector3fUibbUiUiUiUiUiUiUiRC9CVector3fffbQ211CFluidPlane10EFluidTypebfRC14CFluidUVMotionffffffffRC6CColorRC6CColorUiUiUiUiUiiiiifUiffffffffRC6CColorUifffUiUibiiPCUi = .text:0x80101168; // type:function size:0xD20 scope:global -fn_80101E0C = .text:0x80101E88; // type:function size:0x7C scope:global +do_erase__Q24rstl60list,Q24rstl17rmemory_allocator>FPQ34rstl60list,Q24rstl17rmemory_allocator>4node = .text:0x80101E88; // type:function size:0x7C scope:global fn_80101E88 = .text:0x80101F04; // type:function size:0x78 scope:global GetCollisionResponseType__7CWeaponCFRC9CVector3fRC9CVector3fRC11CWeaponModei = .text:0x80101F7C; // type:function size:0x8 scope:global Render__7CWeaponCFRC13CStateManager = .text:0x80101F84; // type:function size:0x4 scope:global @@ -7442,7 +7442,7 @@ __sinit_CPuddleToadGamma_cpp = .text:0x8014D058; // type:function size:0x20 scop AcceptScriptMsg__18CScriptDistanceFogF20EScriptObjectMessage9TUniqueIdR13CStateManager = .text:0x8014D078; // type:function size:0x1C4 scope:global Accept__18CScriptDistanceFogFR8IVisitor = .text:0x8014D23C; // type:function size:0x38 scope:global __dt__18CScriptDistanceFogFv = .text:0x8014D274; // type:function size:0x60 scope:global -__ct__18CScriptDistanceFogF9TUniqueIdRCQ24rstl66basic_string,Q24rstl17rmemory_allocator>RC11CEntityInfo11ERglFogModeRC6CColorRC9CVector2ffRC9CVector2fbbffff = .text:0x8014D2D4; // type:function size:0x16C scope:global +__ct__18CScriptDistanceFogF9TUniqueIdRCQ24rstl66basic_string,Q24rstl17rmemory_allocator>RC11CEntityInfo11ERglFogModeRC6CColorRC9CVector2ff9CVector2fbbffff = .text:0x8014D2D4; // type:function size:0x16C scope:global __dt__19CBSProjectileAttackFv = .text:0x8014D440; // type:function size:0x5C scope:global CanShoot__19CBSProjectileAttackCFv = .text:0x8014D49C; // type:function size:0x8 scope:global GetBodyStateTransition__19CBSProjectileAttackFfR15CBodyController = .text:0x8014D4A4; // type:function size:0xEC scope:global @@ -7944,7 +7944,7 @@ Think__18CScriptBallTriggerFfR13CStateManager = .text:0x8017662C; // type:functi InhabitantExited__18CScriptBallTriggerFR6CActorR13CStateManager = .text:0x801768B4; // type:function size:0x58 scope:global InhabitantAdded__18CScriptBallTriggerFR6CActorR13CStateManager = .text:0x8017690C; // type:function size:0x3C scope:global __dt__18CScriptBallTriggerFv = .text:0x80176948; // type:function size:0x60 scope:global -__ct__18CScriptBallTriggerF9TUniqueIdRCQ24rstl66basic_string,Q24rstl17rmemory_allocator>RC11CEntityInfoRC9CVector3fRC9CVector3fbfffRC9CVector3fb = .text:0x801769A8; // type:function size:0x1AC scope:global +__ct__18CScriptBallTriggerF9TUniqueIdRCQ24rstl66basic_string,Q24rstl17rmemory_allocator>RC11CEntityInfoRC9CVector3fRC9CVector3fbfff9CVector3fb = .text:0x801769A8; // type:function size:0x1AC scope:global calculate_ball_aabox__Fv = .text:0x80176B54; // type:function size:0x54 scope:local __dt__17CPlasmaProjectileFv = .text:0x80176BA8; // type:function size:0x1EC scope:global __dt__15CBeamProjectileFv = .text:0x80176D94; // type:function size:0x11C scope:global @@ -10663,7 +10663,7 @@ GetCollisionResponseType__11CAtomicBetaCFRC9CVector3fRC9CVector3fRC11CWeaponMode Touch__11CAtomicBetaFR6CActorR13CStateManager = .text:0x80250BD8; // type:function size:0xE0 scope:global GetDamageVulnerability__11CAtomicBetaCFv = .text:0x80250CB8; // type:function size:0x58 scope:global fn_80250C94 = .text:0x80250D10; // type:function size:0x3C scope:global -fn_80250CD0 = .text:0x80250D4C; // type:function size:0x4C scope:global +DestroyEmitter__11CAtomicBetaFR10CSfxHandle = .text:0x80250D4C; // type:function size:0x4C scope:global fn_80250D1C = .text:0x80250D98; // type:function size:0x90 scope:global Think__11CAtomicBetaFfR13CStateManager = .text:0x80250E28; // type:function size:0x464 scope:global fn_80251210 = .text:0x8025128C; // type:function size:0xC8 scope:global diff --git a/include/Collision/CRayCastResult.hpp b/include/Collision/CRayCastResult.hpp index eb6007ede..d47b3c848 100644 --- a/include/Collision/CRayCastResult.hpp +++ b/include/Collision/CRayCastResult.hpp @@ -28,6 +28,7 @@ class CRayCastResult { const CVector3f& GetPoint() const { return x4_point; } const CPlane& GetPlane() const { return x10_plane; } bool IsValid() const { return x20_valid != kI_Invalid; } + bool GetValid() const { return x20_valid; } // TODO: figure out what's going on here bool IsInvalid() const { return x20_valid == kI_Invalid; } const CMaterialList& GetMaterial() const { return x28_material; } diff --git a/include/Kyoto/Graphics/CGraphics.hpp b/include/Kyoto/Graphics/CGraphics.hpp index 1e7205747..05318692e 100644 --- a/include/Kyoto/Graphics/CGraphics.hpp +++ b/include/Kyoto/Graphics/CGraphics.hpp @@ -21,7 +21,7 @@ enum ERglFogMode { kRFM_PerspLin = GX_FOG_PERSP_LIN, kRFM_PerspExp = GX_FOG_PERSP_EXP, - kRFM_PerspExp2 = GX_FOG_ORTHO_EXP2, + kRFM_PerspExp2 = GX_FOG_PERSP_EXP2, kRFM_PerspRevExp = GX_FOG_PERSP_REVEXP, kRFM_PerspRevExp2 = GX_FOG_PERSP_REVEXP2, diff --git a/include/Kyoto/Math/CMatrix3f.hpp b/include/Kyoto/Math/CMatrix3f.hpp index 96e8c4ee4..36b3fc7a2 100644 --- a/include/Kyoto/Math/CMatrix3f.hpp +++ b/include/Kyoto/Math/CMatrix3f.hpp @@ -66,7 +66,22 @@ class CMatrix3f { float Get21() const { return m21; } float Get22() const { return m22; } + inline CVector3f GetColumn(EDimX dim) const { return CVector3f(m00, m10, m20); } inline CVector3f GetColumn(EDimY dim) const { return CVector3f(m01, m11, m21); } + inline CVector3f GetColumn(EDimZ dim) const { return CVector3f(m02, m12, m22); } + + inline CVector3f GetColumn(int idx) const { + switch (idx) { + case 0: + return CVector3f(m00, m10, m20); + case 1: + return CVector3f(m01, m11, m21); + case 2: + return CVector3f(m02, m12, m22); + default: + return CVector3f::Zero(); + } + } static CMatrix3f FromTransform(const CTransform4f& xf); diff --git a/include/Kyoto/Math/CVector2f.hpp b/include/Kyoto/Math/CVector2f.hpp index 48293ebf3..f1c7e37e3 100644 --- a/include/Kyoto/Math/CVector2f.hpp +++ b/include/Kyoto/Math/CVector2f.hpp @@ -10,7 +10,7 @@ class CVector2f { public: CVector2f(float x, float y); - CVector2f(CInputStream& in) : mX(in.ReadFloat()), mY(in.ReadFloat()) {} + CVector2f(CInputStream& in) : mX(in.Get< float >()), mY(in.Get< float >()) {} float GetX() const { return mX; } float GetY() const { return mY; } diff --git a/include/MetroidPrime/CActorLights.hpp b/include/MetroidPrime/CActorLights.hpp index 188365a1e..bf6a36be6 100644 --- a/include/MetroidPrime/CActorLights.hpp +++ b/include/MetroidPrime/CActorLights.hpp @@ -46,6 +46,17 @@ class CActorLights { void SetFindShadowLight(bool v) { x298_27_findShadowLight = v; } void SetShadowDynamicRangeThreshold(float t) { x2d0_shadowDynamicRangeThreshold = t; } + void SetMaxAreaLights(int n) { + x2b8_maxAreaLights = n; + x298_26_hasAreaLights = x2b8_maxAreaLights > 0; + } + void SetInArea(bool v) { x298_28_inArea = v; } + void SetMaxDynamicLights(int n) { x2bc_maxDynamicLights = n; } + void SetAmbienceGenerated(bool v) { x298_29_ambienceGenerated = v; } + void SetFindNearestDynamicLights(bool v) { x29a_findNearestDynamicLights = v; } + bool GetIsDirty() const { return x298_24_dirty; } + int GetMaxAreaLights() const { return x2b8_maxAreaLights; } + private: rstl::reserved_vector< CLight, 4 > x0_areaLights; rstl::reserved_vector< CLight, 4 > x144_dynamicLights; diff --git a/include/MetroidPrime/CActorParameters.hpp b/include/MetroidPrime/CActorParameters.hpp index 9c134f418..5785c2ecc 100644 --- a/include/MetroidPrime/CActorParameters.hpp +++ b/include/MetroidPrime/CActorParameters.hpp @@ -34,13 +34,13 @@ class CLightParameters { }; CLightParameters(); - CLightParameters(bool castShadow, float shadowScale, + CLightParameters(const bool castShadow, float shadowScale, CLightParameters::EShadowTessellation shadowTess, float shadowAlpha, - float maxShadowHeight, const CColor& ambientColor, bool makeLights, + float maxShadowHeight, const CColor& ambientColor, const bool makeLights, CLightParameters::EWorldLightingOptions useWorldLighting, CLightParameters::ELightRecalculationOptions lightRecalcOpts, const CVector3f& lightingPositionOffset, int maxDynamicLights, int maxAreaLights, - bool ambChannelOverflow, int useLightSet); + const bool ambChannelOverflow, int useLightSet); virtual ~CLightParameters(); const CColor& GetAmbientColor() const { return x18_ambientColor; } @@ -50,7 +50,7 @@ class CLightParameters { int GetMaxDynamicLights() const { return x38_maxDynamicLights; } int GetMaxAreaLights() const { return x3c_maxAreaLights; } - static CLightParameters None(); + static CLightParameters None() { return CLightParameters(); } static uint GetFramesBetweenRecalculation(ELightRecalculationOptions opts); rstl::auto_ptr< CActorLights > MakeActorLights() const; diff --git a/include/MetroidPrime/CAnimData.hpp b/include/MetroidPrime/CAnimData.hpp index bc4eb828f..013c02609 100644 --- a/include/MetroidPrime/CAnimData.hpp +++ b/include/MetroidPrime/CAnimData.hpp @@ -42,6 +42,8 @@ class CAnimData { kAD_Backward, }; + ~CAnimData(); + void PreRender(); void EnableLooping(bool v) { x220_25_loop = v; diff --git a/include/MetroidPrime/CFluidPlane.hpp b/include/MetroidPrime/CFluidPlane.hpp index 317bb05c9..0f15a5203 100644 --- a/include/MetroidPrime/CFluidPlane.hpp +++ b/include/MetroidPrime/CFluidPlane.hpp @@ -1,5 +1,6 @@ #ifndef _CFLUIDPLANE #define _CFLUIDPLANE +#include "MetroidPrime/CFluidPlaneManager.hpp" #endif // _CFLUIDPLANE diff --git a/include/MetroidPrime/CFluidPlaneCPU.hpp b/include/MetroidPrime/CFluidPlaneCPU.hpp index 3b32e4a49..65fd837f8 100644 --- a/include/MetroidPrime/CFluidPlaneCPU.hpp +++ b/include/MetroidPrime/CFluidPlaneCPU.hpp @@ -3,9 +3,107 @@ #include "MetroidPrime/CFluidPlaneManager.hpp" +class CFluidUVMotion; + class CFluidPlaneCPU : public CFluidPlane { public: + class CTurbulence { + public: + CTurbulence(float speed, float distance, float freqMax, float freqMin, float phaseMax, + float phaseMin, float amplitudeMax, float amplitudeMin); + ~CTurbulence(); + + float GetHeight(float sel) const { + return x20_table[(x24_tableCount - 1) & int(sel * x28_heightSelPitch)]; + } + float GetOODistance() const { return x30_ooTurbDistance; } + float GetOOSpeed() const { return x2c_ooTurbSpeed; } + bool HasTurbulence() const { return x34_hasTurbulence; } + + private: + float x0_speed; + float x4_distance; + float x8_freqMax; + float xc_freqMin; + float x10_phaseMax; + float x14_phaseMin; + float x18_amplitudeMax; + float x1c_amplitudeMin; + float* x20_table; + int x24_tableCount; + float x28_heightSelPitch; + float x2c_ooTurbSpeed; + float x30_ooTurbDistance; + bool x34_hasTurbulence; + }; + +public: + CFluidPlaneCPU(uint patternMap1, uint patternMap2, uint colorMap, uint bumpMap, uint envMap, + uint envBumpMap, float unitsPerLightmapTexel, uint lightmap, + uint tileSubdivisions, EFluidType fluidType, float tileSize, + const CVector3f& bumpLightDir, float alpha, const CFluidUVMotion& uvMotion, + float bumpScale, float turbSpeed, float turbDistance, float turbFreqMax, + float turbFreqMin, float turbPhaseMax, float turbPhaseMin, + float turbAmplitudeMax, float turbAmplitudeMin, float specularMin, + float specularMax, float reflectionBlend, float reflectionSize, + float rippleIntensity); + ~CFluidPlaneCPU(); + + void Render(const CStateManager& mgr, float alpha, const CAABox& aabb, const CTransform4f& xf, + const CTransform4f& areaXf, bool noNormals, const CFrustumPlanes& frustum, + const rstl::optional_object< CRippleManager >& rippleManager, TUniqueId waterId, + const bool* gridFlags, int gridDimX, int gridDimY, + const CVector3f& areaCenter) const override; + + void RenderSetup(const CStateManager& mgr, float alpha, const CTransform4f& xf, + const CTransform4f& areaXf, const CAABox& aabb, CScriptWater* water) const; + void RenderCleanup() const; + void CalculateLightmapMtx(const CTransform4f& areaXf, const CTransform4f& xf, + const CAABox& aabb, int idx); + + float GetReflectionBlend() const { return x114_reflectionBlend; } + float GetSpecularMax() const { return x110_specularMax; } + float GetSpecularMin() const { return x10c_specularMin; } + float GetReflectionSize() const { return x118_reflectionSize; } + float GetBumpScale() const { return xfc_bumpScale; } + bool HasBumpMap() const { return xb0_bumpMap; } + const TLockedToken< CTexture >& GetBumpMap() const { return *xb0_bumpMap; } + bool HasEnvMap() const { return xc0_envMap; } + const TLockedToken< CTexture >& GetEnvMap() const { return *xc0_envMap; } + bool HasEnvBumpMap() const { return xd0_envBumpMap; } + const TLockedToken< CTexture >& GetEnvBumpMap() const { return *xd0_envBumpMap; } + bool HasLightMap() const { return xe0_lightmap; } + const TLockedToken< CTexture >& GetLightMap() const { return *xe0_lightmap; } + const CVector3f& GetBumpLightDir() const { return xf0_bumpLightDir; } + float GetTileSize() const { return x100_tileSize; } + int GetTileSubdivisions() const { return x104_tileSubdivisions; } + float GetRippleResolution() const { return x108_rippleResolution; } + float GetTurbulenceHeight(float sel) const { return x120_turbulence.GetHeight(sel); } + float GetOOTurbulenceDistance() const { return x120_turbulence.GetOODistance(); } + float GetOOTurbulenceSpeed() const { return x120_turbulence.GetOOSpeed(); } + bool HasTurbulence() const { return x120_turbulence.HasTurbulence(); } +private: + CAssetId xa0_texIdBumpMap; + CAssetId xa4_texIdEnvMap; + CAssetId xa8_texIdEnvBumpMap; + CAssetId xac_texIdLightmap; + rstl::optional_object< TLockedToken< CTexture > > xb0_bumpMap; + rstl::optional_object< TLockedToken< CTexture > > xc0_envMap; + rstl::optional_object< TLockedToken< CTexture > > xd0_envBumpMap; + rstl::optional_object< TLockedToken< CTexture > > xe0_lightmap; + CVector3f xf0_bumpLightDir; + float xfc_bumpScale; + float x100_tileSize; + int x104_tileSubdivisions; + float x108_rippleResolution; + float x10c_specularMin; + float x110_specularMax; + float x114_reflectionBlend; + float x118_reflectionSize; + float x11c_unitsPerLightmapTexel; + CTurbulence x120_turbulence; }; +CHECK_SIZEOF(CFluidPlaneCPU, 0x158) #endif // _CFLUIDPLANECPU diff --git a/include/MetroidPrime/CFluidPlaneDoor.hpp b/include/MetroidPrime/CFluidPlaneDoor.hpp index 519ab4174..6418c83f6 100644 --- a/include/MetroidPrime/CFluidPlaneDoor.hpp +++ b/include/MetroidPrime/CFluidPlaneDoor.hpp @@ -18,9 +18,14 @@ class CFluidPlaneDoor : public CFluidPlane { const CVector3f& velocity, const CScriptWater& water, CStateManager& mgr, const CVector3f& upVec) override {} void AddRipple(const CRipple& ripple, const CScriptWater& water, CStateManager& mgr) override {} - void Update() override; - void Render(const CStateManager& mgr, const CAABox&, const CFrustumPlanes&, const CRippleManager&, - const CVector3f&) override; + void Render(const CStateManager& mgr, float alpha, const CAABox& aabb, + const CTransform4f& xf, const CTransform4f& areaXf, bool noNormals, + const CFrustumPlanes& frustum, + const rstl::optional_object< CRippleManager >& rippleManager, + TUniqueId waterId, const bool* gridFlags, int gridDimX, int gridDimY, + const CVector3f& areaCenter) const override; + + void Update(); void RenderCleanup() const; diff --git a/include/MetroidPrime/CFluidPlaneManager.hpp b/include/MetroidPrime/CFluidPlaneManager.hpp index b6da70fe8..8e5a60b74 100644 --- a/include/MetroidPrime/CFluidPlaneManager.hpp +++ b/include/MetroidPrime/CFluidPlaneManager.hpp @@ -29,6 +29,8 @@ class CScriptWater; class CStateManager; class CRipple; class CTexture; +class CTransform4f; +class CRippleManager; class CFluidPlaneCPURender { @@ -62,9 +64,16 @@ class CFluidPlane { const CVector3f& velocity, const CScriptWater& water, CStateManager& mgr, const CVector3f& upVec); virtual void AddRipple(const CRipple& ripple, const CScriptWater& water, CStateManager& mgr); - virtual void Update(); virtual void Render(const CStateManager& mgr, const CAABox&, const CFrustumPlanes&, const CRippleManager&, const CVector3f&); + virtual void Render(const CStateManager& mgr, float alpha, const CAABox& aabb, + const CTransform4f& xf, const CTransform4f& areaXf, bool noNormals, + const CFrustumPlanes& frustum, + const rstl::optional_object< CRippleManager >& rippleManager, + TUniqueId waterId, const bool* gridFlags, int gridDimX, int gridDimY, + const CVector3f& areaCenter) const {} + + void Update(); float CalculateRippleIntensity(const float base) const; float GetRippleScaleFromKineticEnergy(float baseI, float velDot); @@ -98,6 +107,8 @@ class CFluidPlane { }; CHECK_SIZEOF(CFluidPlane, 0xA0); +extern const bool sRenderFog; + class CFluidPlaneManager { public: CFluidPlaneManager(); @@ -107,6 +118,7 @@ class CFluidPlaneManager { const CVector3f& pos, float factor, bool sfx); CRippleManager& RippleManager() { return x0_rippleManager; } + const CRippleManager& GetRippleManager() const { return x0_rippleManager; } float GetLastSplashDeltaTime(TUniqueId uid) const; float GetLastRippleDeltaTime(TUniqueId uid) const; diff --git a/include/MetroidPrime/CModelData.hpp b/include/MetroidPrime/CModelData.hpp index e0388e16b..ac35bcc27 100644 --- a/include/MetroidPrime/CModelData.hpp +++ b/include/MetroidPrime/CModelData.hpp @@ -4,6 +4,7 @@ #include "types.h" #include "MetroidPrime/TGameTypes.hpp" +#include "MetroidPrime/CAnimData.hpp" #include "Kyoto/Animation/IAnimReader.hpp" #include "Kyoto/Graphics/CColor.hpp" @@ -18,7 +19,6 @@ class CAABox; class CActorLights; -class CAnimData; class CAnimRes; class CFrustumPlanes; class CModel; @@ -70,7 +70,7 @@ class CModelData { CModelData(); CModelData(const CAnimRes&); CModelData(const CStaticRes&); - //CModelData(const CModelData&); + CModelData(const CModelData&); ~CModelData(); CAdvancementDeltas AdvanceAnimation(float dt, CStateManager& mgr, TAreaId aid, bool advTree); diff --git a/include/MetroidPrime/CRippleManager.hpp b/include/MetroidPrime/CRippleManager.hpp index 001072fef..f508415d8 100644 --- a/include/MetroidPrime/CRippleManager.hpp +++ b/include/MetroidPrime/CRippleManager.hpp @@ -15,6 +15,8 @@ class CRippleManager { public: CRippleManager(int maxRipples, float alpha); + CRippleManager(const CRippleManager& other); + ~CRippleManager(); void Init(int maxRipples); void Update(float dt); diff --git a/include/MetroidPrime/CStateManager.hpp b/include/MetroidPrime/CStateManager.hpp index 3a70ee909..1adbf408b 100644 --- a/include/MetroidPrime/CStateManager.hpp +++ b/include/MetroidPrime/CStateManager.hpp @@ -33,6 +33,7 @@ class CAABox; class CActor; +class CPlane; class CActorModelParticles; class CCameraManager; class CEnvFxManager; @@ -127,6 +128,8 @@ class CStateManager : public TOneStatic< CStateManager > { void DeliverScriptMsg(CEntity* ent, TUniqueId target, EScriptObjectMessage msg); void SendScriptMsgAlways(TUniqueId uid, TUniqueId src, EScriptObjectMessage msg); bool AddDrawableActor(const CActor& actor, const CVector3f& pos, const CAABox& bounds) const; + void AddDrawableActorPlane(const CActor& actor, const CPlane& plane, + const CAABox& bounds) const; void SetupParticleHook(const CActor& actor) const; void DeleteObjectRequest(TUniqueId uid); rstl::pair< TEditorId, TUniqueId > GenerateObject(const TEditorId& eid); diff --git a/include/MetroidPrime/Enemies/CAtomicAlpha.hpp b/include/MetroidPrime/Enemies/CAtomicAlpha.hpp new file mode 100644 index 000000000..770a9e192 --- /dev/null +++ b/include/MetroidPrime/Enemies/CAtomicAlpha.hpp @@ -0,0 +1,82 @@ +#ifndef _CATOMICALPHA +#define _CATOMICALPHA + +#include "types.h" + +#include "MetroidPrime/CModelData.hpp" +#include "MetroidPrime/CSteeringBehaviors.hpp" +#include "MetroidPrime/Enemies/CPatterned.hpp" +#include "MetroidPrime/PathFinding/CPathFindSearch.hpp" +#include "MetroidPrime/Weapons/CProjectileInfo.hpp" + +#include "Kyoto/Animation/CharacterCommon.hpp" + +#include "rstl/reserved_vector.hpp" +#include "rstl/string.hpp" + +class CAtomicAlpha : public CPatterned { +public: + CAtomicAlpha(TUniqueId uid, const rstl::string& name, const CEntityInfo& info, + const CTransform4f& xf, const CModelData& mData, + const CActorParameters& actParms, const CPatternedInfo& pInfo, + CAssetId bombWeapon, const CDamageInfo& bombDamage, float bombDropDelay, + float bombReappearDelay, float bombRappearTime, CAssetId cmdl, + bool invisible, bool applyBeamAttraction); + + // CEntity + ~CAtomicAlpha() override {} + void Accept(IVisitor& visitor) override; + void Think(float dt, CStateManager& mgr) override; + void AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateManager& mgr) override; + + // CActor + void AddToRenderer(const CFrustumPlanes& frustum, const CStateManager& mgr) const override; + void Render(const CStateManager& mgr) const override; + EWeaponCollisionResponseTypes GetCollisionResponseType(const CVector3f&, const CVector3f&, + const CWeaponMode&, int) const override; + void DoUserAnimEvent(CStateManager& mgr, const CInt32POINode& node, EUserEventType type, + float dt) override; + + // CPhysicsActor + void CollidedWith(const TUniqueId& id, const CCollisionInfoList& list, + CStateManager& mgr) override; + + // CAi + void Patrol(CStateManager& mgr, EStateMsg msg, float arg) override; + void Attack(CStateManager& mgr, EStateMsg msg, float arg) override; + bool Leash(CStateManager& mgr, float arg) override; + bool AggressionCheck(CStateManager& mgr, float arg) override; + + // CPatterned + CProjectileInfo* ProjectileInfo() override { return &x668_bombProjectile; } + CPathFindSearch* GetSearchPath() override { return &x580_pathFind; } + +private: + enum { kBombCount = 4 }; + + struct SBomb { + rstl::string x0_locatorName; + pas::ELocomotionType x10_locomotionType; + float x14_scaleTime; + + SBomb(const rstl::string& name, pas::ELocomotionType loco, float scale) + : x0_locatorName(name), x10_locomotionType(loco), x14_scaleTime(scale) {} + }; + + bool x568_24_inRange : 1; + bool x568_25_invisible : 1; + bool x568_26_applyBeamAttraction : 1; + float x56c_bombDropDelay; + float x570_bombReappearDelay; + float x574_bombRappearTime; + float x578_bombTime; + uint x57c_curBomb; + CPathFindSearch x580_pathFind; + CSteeringBehaviors x664_steeringBehaviors; + CProjectileInfo x668_bombProjectile; + CModelData x690_bombModel; + rstl::reserved_vector< SBomb, kBombCount > x6dc_bombLocators; +}; +CHECK_SIZEOF(CAtomicAlpha, 0x740) + +#endif // _CATOMICALPHA diff --git a/include/MetroidPrime/Enemies/CAtomicBeta.hpp b/include/MetroidPrime/Enemies/CAtomicBeta.hpp new file mode 100644 index 000000000..1a7ed149d --- /dev/null +++ b/include/MetroidPrime/Enemies/CAtomicBeta.hpp @@ -0,0 +1,79 @@ +#ifndef _CATOMICBETA +#define _CATOMICBETA + +#include "types.h" + +#include "MetroidPrime/CDamageInfo.hpp" +#include "MetroidPrime/CDamageVulnerability.hpp" +#include "MetroidPrime/Enemies/CPatterned.hpp" + +#include "Kyoto/Audio/CSfxHandle.hpp" + +#include "rstl/reserved_vector.hpp" +#include "rstl/string.hpp" + +class CElectricDescription; +class CWeaponDescription; + +class CAtomicBeta : public CPatterned { +public: + CAtomicBeta(TUniqueId uid, const rstl::string& name, const CEntityInfo& info, + const CTransform4f& xf, CModelData mData, const CActorParameters& actParms, + const CPatternedInfo& pInfo, CAssetId electricId, CAssetId weaponId, + const CDamageInfo& dInfo, CAssetId particleId, float beamFadeSpeed, + float beamRadius, float beamDamageInterval, + const CDamageVulnerability& frozenDVuln, float moveSpeed, float minSpeed, + float maxSpeed, ushort sId1, ushort sId2, ushort sId3, float speedStep); + + // CEntity + ~CAtomicBeta() override; + void Accept(IVisitor& visitor) override; + void Think(float dt, CStateManager& mgr) override; + void AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateManager& mgr) override; + + // CActor + const CDamageVulnerability* GetDamageVulnerability() const override; + void Touch(CActor& other, CStateManager& mgr) override; + EWeaponCollisionResponseTypes GetCollisionResponseType(const CVector3f&, const CVector3f&, + const CWeaponMode&, int) const override; + + // CAi + const CDamageVulnerability* GetDamageVulnerability() override; + +private: + void CreateBeams(CStateManager& mgr); + void UpdateBeams(CStateManager& mgr, bool fire); + void FreeBeams(CStateManager& mgr); + void UpdateOrCreateEmitter(CSfxHandle& handle, ushort sfxId, const CVector3f& pos, float vol); + void DestroyEmitter(CSfxHandle& handle); + + static bool IsPlayerBeamChargedEnough(const CStateManager& mgr); + + rstl::reserved_vector< TUniqueId, 3 > x568_projectileIds; + bool x574_beamFired; + float x578_minSpeed; + float x57c_maxSpeed; + float x580_speedStep; + float x584_currentSpeed; + CDamageVulnerability x588_frozenDamage; + float x5f0_moveSpeed; + CVector3f x5f4_direction; + TToken< CElectricDescription > x600_electricWeapon; + TToken< CWeaponDescription > x608_weaponDesc; + CDamageInfo x610_projectileDamage; + CAssetId x62c_beamParticle; + float x630_beamFadeSpeed; + float x634_beamRadius; + float x638_beamDamageInterval; + float x63c_; + float x640_; + ushort x644_sfxId1; + ushort x646_sfxId2; + ushort x648_sfxId3; + CSfxHandle x64c_sfxHandle1; + CSfxHandle x650_sfxHandle2; + CSfxHandle x654_sfxHandle3; +}; +CHECK_SIZEOF(CAtomicBeta, 0x658) + +#endif // _CATOMICBETA diff --git a/include/MetroidPrime/Enemies/CWarWasp.hpp b/include/MetroidPrime/Enemies/CWarWasp.hpp index d840a4c2e..cadb6a0f8 100644 --- a/include/MetroidPrime/Enemies/CWarWasp.hpp +++ b/include/MetroidPrime/Enemies/CWarWasp.hpp @@ -1,10 +1,135 @@ #ifndef _CWARWASP #define _CWARWASP +#include "types.h" + #include "MetroidPrime/Enemies/CPatterned.hpp" +#include "MetroidPrime/PathFinding/CPathFindSearch.hpp" +#include "MetroidPrime/Weapons/CProjectileInfo.hpp" + +#include "Collision/CCollidableSphere.hpp" + +#include "Kyoto/Animation/CharacterCommon.hpp" +#include "Kyoto/Math/CQuaternion.hpp" + +#include "rstl/optional_object.hpp" class CWarWasp : public CPatterned { +public: + CWarWasp(TUniqueId uid, const rstl::string& name, const CEntityInfo& info, + const CTransform4f& xf, const CModelData& mData, const CPatternedInfo& pInfo, + CPatterned::EFlavorType flavor, CPatterned::EColliderType collider, + const CDamageInfo& dInfo, const CActorParameters& actParms, + CAssetId projectileWeapon, const CDamageInfo& projectileDamage, + CAssetId projectileVisorParticle, uint projectileVisorSfx); + + // CEntity + ~CWarWasp() override; + void Accept(IVisitor& visitor) override; + void Think(float dt, CStateManager& mgr) override; + void AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateManager& mgr) override; + + // CActor + rstl::optional_object< CAABox > GetTouchBounds() const override; + void DoUserAnimEvent(CStateManager& mgr, const CInt32POINode& node, + EUserEventType type) override; + const CCollisionPrimitive* GetCollisionPrimitive() const override { return &x570_cSphere; } + + // CAi + void Death(const CVector3f& dir, CStateManager& mgr) override; + bool IsListening() const override { return true; } + bool Listen(const CVector3f& pos, EListenNoiseType type) override; + CVector3f GetOrigin() const override; + + // CAi - state actions + void Patrol(CStateManager& mgr, EStateMsg msg, float arg) override; + void PathFind(CStateManager& mgr, EStateMsg msg, float arg) override; + void TargetPatrol(CStateManager& mgr, EStateMsg msg, float arg) override; + void Generate(CStateManager& mgr, EStateMsg msg, float arg) override; + void Deactivate(CStateManager& mgr, EStateMsg msg, float arg) override; + void Attack(CStateManager& mgr, EStateMsg msg, float arg) override; + void JumpBack(CStateManager& mgr, EStateMsg msg, float arg) override; + void Shuffle(CStateManager& mgr, EStateMsg msg, float arg) override; + void ProjectileAttack(CStateManager& mgr, EStateMsg msg, float arg) override; + void TelegraphAttack(CStateManager& mgr, EStateMsg msg, float arg) override; + void Dodge(CStateManager& mgr, EStateMsg msg, float arg) override; + void Retreat(CStateManager& mgr, EStateMsg msg, float arg) override; + void SpecialAttack(CStateManager& mgr, EStateMsg msg, float arg) override; + + // CAi - state transitions + bool InAttackPosition(CStateManager& mgr, float arg) override; + bool Leash(CStateManager& mgr, float arg) override; + bool PathShagged(CStateManager& mgr, float arg) override; + bool AnimOver(CStateManager& mgr, float arg) override; + bool ShouldAttack(CStateManager& mgr, float arg) override; + bool InPosition(CStateManager& mgr, float arg) override; + bool ShouldTurn(CStateManager& mgr, float arg) override; + bool HearShot(CStateManager& mgr, float arg) override; + bool ShouldFire(CStateManager& mgr, float arg) override; + bool ShouldDodge(CStateManager& mgr, float arg) override; + bool ShouldSpecialAttack(CStateManager& mgr, float arg) override; + + // CPatterned + CPathFindSearch* GetSearchPath() override { return &x590_pfSearch; } + CProjectileInfo* ProjectileInfo() override { return &x6d4_projectileInfo; } + +private: + void SwarmAdd(CStateManager& mgr); + void SwarmRemove(CStateManager& mgr); + void ApplyDamage(CStateManager& mgr); + void SetUpCircleBurstWaypoint(CStateManager& mgr); + CVector3f GetProjectileAimPos(CStateManager& mgr, float zBias); + CVector3f GetCloseInPos(CStateManager& mgr, const CVector3f& aimPos) const; + float GetCloseInZBasis(CStateManager& mgr) const; + void SetUpPathFindBehavior(CStateManager& mgr); + int GetAttackTeamSize(CStateManager& mgr, int team); + float CalcTimeToNextAttack(CStateManager& mgr); + float CalcOffTotemAngle(CStateManager& mgr); + void JoinCircleAttackTeam(int unit, CStateManager& mgr); + void SetUpCircleTelegraphTeam(CStateManager& mgr); + TUniqueId GetAttackTeamLeader(CStateManager& mgr, int team); + void TryCircleTeamMerge(CStateManager& mgr); + float GetTeamZStratum(int team); + float CalcSeekMagnitude(CStateManager& mgr); + void UpdateTelegraphMoveSpeed(CStateManager& mgr); + bool CheckCircleAttackSpread(CStateManager& mgr, int team); + void ApplyNormalSteering(CStateManager& mgr); + void ApplySeparationBehavior(CStateManager& mgr, float sep); + bool PathToHiveIsClear(CStateManager& mgr); + bool SteerToDeactivatePos(CStateManager& mgr, EStateMsg msg, float dt); + CVector3f CalcShuffleDest(CStateManager& mgr); + void UpdateTouchBounds(); + int x568_stateProg; + CCollidableSphere x570_cSphere; + CPathFindSearch x590_pfSearch; + TUniqueId x674_aiMgr; + CVector3f x678_targetPos; + CDamageInfo x684_contactDamage; + CQuaternion x6a0_initialRot; + CVector3f x6b0_circleBurstPos; + CVector3f x6bc_circleBurstDir; + CVector3f x6c8_circleBurstRight; + CProjectileInfo x6d4_projectileInfo; + float x6fc_initialSpeed; + float x700_attackRemTime; + pas::EStepDirection x704_dodgeDir; + int x708_circleAttackTeam; + int x70c_initialCircleAttackTeam; + int x710_initialCircleAttackTeamUnit; + float x714_circleTelegraphSeekHeight; + float x718_circleBurstOffTotemAngle; + rstl::optional_object< TLockedToken< CGenDescription > > x71c_projectileVisorParticle; + u16 x72c_projectileVisorSfx; + bool x72e_24_jumpBackRepeat : 1; + bool x72e_25_canApplyDamage : 1; + bool x72e_26_initiallyInactive : 1; + bool x72e_27_teamMatesMelee : 1; + bool x72e_28_inProjectileAttack : 1; + bool x72e_29_pathObstructed : 1; + bool x72e_30_isRetreating : 1; + bool x72e_31_heardNoise : 1; }; +CHECK_SIZEOF(CWarWasp, 0x730) #endif // _CWARWASP diff --git a/include/MetroidPrime/ScriptLoader.hpp b/include/MetroidPrime/ScriptLoader.hpp index 20c6fcdad..1c5f781bc 100644 --- a/include/MetroidPrime/ScriptLoader.hpp +++ b/include/MetroidPrime/ScriptLoader.hpp @@ -107,14 +107,15 @@ class ScriptLoader { static CEntity* LoadColorModulate(CStateManager&, CInputStream&, int, const CEntityInfo&); static CEntity* LoadThardusRockProjectile(CStateManager&, CInputStream&, int, const CEntityInfo&); static CEntity* LoadMidi(CStateManager&, CInputStream&, int, const CEntityInfo&); - static CEntity* LoadStreamedAudio(CStateManager&, CInputStream&, int, const CEntityInfo&); + static CEntity* LoadStreamedMusic(CStateManager&, CInputStream&, int, const CEntityInfo&); static CEntity* LoadRepulsor(CStateManager&, CInputStream&, int, const CEntityInfo&); static CEntity* LoadGunTurret(CStateManager&, CInputStream&, int, const CEntityInfo&); static CEntity* LoadFogVolume(CStateManager&, CInputStream&, int, const CEntityInfo&); static CEntity* LoadBabygoth(CStateManager&, CInputStream&, int, const CEntityInfo&); static CEntity* LoadEyeball(CStateManager&, CInputStream&, int, const CEntityInfo&); static CEntity* LoadRadialDamage(CStateManager&, CInputStream&, int, const CEntityInfo&); - static CEntity* LoadCameraPitchVolume(CStateManager&, CInputStream&, int, const CEntityInfo&); + static CEntity* LoadScriptCameraPitchVolume(CStateManager&, CInputStream&, int, + const CEntityInfo&); static CEntity* LoadEnvFxDensityController(CStateManager&, CInputStream&, int, const CEntityInfo&); static CEntity* LoadMagdolite(CStateManager&, CInputStream&, int, const CEntityInfo&); diff --git a/include/MetroidPrime/ScriptObjects/CScriptActorRotate.hpp b/include/MetroidPrime/ScriptObjects/CScriptActorRotate.hpp index af9aae2e2..6d1d0025b 100644 --- a/include/MetroidPrime/ScriptObjects/CScriptActorRotate.hpp +++ b/include/MetroidPrime/ScriptObjects/CScriptActorRotate.hpp @@ -11,7 +11,7 @@ class CScriptPlatform; class CScriptActorRotate : public CEntity { public: CScriptActorRotate(TUniqueId, const rstl::string&, const CEntityInfo&, const CVector3f&, - const float, const bool, const bool, const bool); + float, bool, bool, bool); ~CScriptActorRotate(); void Accept(IVisitor& visitor) override; diff --git a/include/MetroidPrime/ScriptObjects/CScriptBallTrigger.hpp b/include/MetroidPrime/ScriptObjects/CScriptBallTrigger.hpp index 9714889b6..34ad7c409 100644 --- a/include/MetroidPrime/ScriptObjects/CScriptBallTrigger.hpp +++ b/include/MetroidPrime/ScriptObjects/CScriptBallTrigger.hpp @@ -6,7 +6,7 @@ class CScriptBallTrigger : public CScriptTrigger { public: CScriptBallTrigger(TUniqueId, const rstl::string&, const CEntityInfo&, const CVector3f&, - const CVector3f&, bool, float, float, float, const CVector3f&, bool); + const CVector3f&, bool, float, float, float, CVector3f, bool); ~CScriptBallTrigger(); void Accept(IVisitor&) override; diff --git a/include/MetroidPrime/ScriptObjects/CScriptDistanceFog.hpp b/include/MetroidPrime/ScriptObjects/CScriptDistanceFog.hpp index b0ec93d82..4c2f4b109 100644 --- a/include/MetroidPrime/ScriptObjects/CScriptDistanceFog.hpp +++ b/include/MetroidPrime/ScriptObjects/CScriptDistanceFog.hpp @@ -3,6 +3,8 @@ #include "MetroidPrime/CEntity.hpp" +#include "Kyoto/Math/CVector2f.hpp" + #include "MetroidPrime/CAreaFog.hpp" #include "MetroidPrime/CGameArea.hpp" @@ -21,8 +23,8 @@ class CScriptDistanceFog : public CEntity { public: CScriptDistanceFog(TUniqueId, const rstl::string&, const CEntityInfo&, ERglFogMode, const CColor&, - const CVector2f&, float, const CVector2f&, bool, bool, float, float, float, - float); + const CVector2f&, float, CVector2f, const bool, const bool, float, float, + float, float); ~CScriptDistanceFog(); void Accept(IVisitor& visitor) override; diff --git a/include/MetroidPrime/ScriptObjects/CScriptDockAreaChange.hpp b/include/MetroidPrime/ScriptObjects/CScriptDockAreaChange.hpp index 4ecbea767..e8c17d246 100644 --- a/include/MetroidPrime/ScriptObjects/CScriptDockAreaChange.hpp +++ b/include/MetroidPrime/ScriptObjects/CScriptDockAreaChange.hpp @@ -7,7 +7,7 @@ class CScriptDockAreaChange : public CEntity { int x34_dockReference; public: - CScriptDockAreaChange(TUniqueId, const rstl::string&, const CEntityInfo&, int, bool); + CScriptDockAreaChange(TUniqueId, const rstl::string&, const CEntityInfo&, int, const bool); void AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId objId, CStateManager& stateMgr) override; void Accept(IVisitor& visitor) override; diff --git a/include/MetroidPrime/ScriptObjects/CScriptMemoryRelay.hpp b/include/MetroidPrime/ScriptObjects/CScriptMemoryRelay.hpp index 869084467..4587fef20 100644 --- a/include/MetroidPrime/ScriptObjects/CScriptMemoryRelay.hpp +++ b/include/MetroidPrime/ScriptObjects/CScriptMemoryRelay.hpp @@ -8,7 +8,7 @@ class CScriptMemoryRelay : public CEntity { bool x34_25_skipSendActive : 1; bool x34_26_ignoreMessages : 1; public: - CScriptMemoryRelay(TUniqueId, const rstl::string&, const CEntityInfo&, bool, bool, bool); + CScriptMemoryRelay(TUniqueId, const rstl::string&, const CEntityInfo&, bool, bool, const bool); ~CScriptMemoryRelay(); void AcceptScriptMsg(EScriptObjectMessage, TUniqueId, CStateManager&); diff --git a/include/MetroidPrime/ScriptObjects/CScriptPickup.hpp b/include/MetroidPrime/ScriptObjects/CScriptPickup.hpp index d0be67a7f..035bf5985 100644 --- a/include/MetroidPrime/ScriptObjects/CScriptPickup.hpp +++ b/include/MetroidPrime/ScriptObjects/CScriptPickup.hpp @@ -28,7 +28,7 @@ class CScriptPickup : public CPhysicsActor { const CTransform4f& xf, const CModelData& mData, const CActorParameters& aParams, const CAABox& aabb, CPlayerState::EItemType itemType, int amount, int capacity, CAssetId pickupEffect, float possibility, float lifeTime, float fadeInTime, - float startDelay, bool active); + float startDelay, const bool active); ~CScriptPickup(); void Think(float, CStateManager&) override; diff --git a/include/MetroidPrime/ScriptObjects/CScriptSpecialFunction.hpp b/include/MetroidPrime/ScriptObjects/CScriptSpecialFunction.hpp index 979109f1b..51a4bad6e 100644 --- a/include/MetroidPrime/ScriptObjects/CScriptSpecialFunction.hpp +++ b/include/MetroidPrime/ScriptObjects/CScriptSpecialFunction.hpp @@ -120,8 +120,8 @@ class CScriptSpecialFunction : public CActor { public: CScriptSpecialFunction(TUniqueId, const rstl::string&, const CEntityInfo&, const CTransform4f&, ESpecialFunction, const rstl::string&, float, float, float, float, - const CVector3f&, const CColor&, bool, const CDamageInfo&, int, int, - CPlayerState::EItemType, ushort, ushort, ushort); + const CVector3f&, const CColor&, const bool, const CDamageInfo&, int, int, + CPlayerState::EItemType, const ushort, const ushort, const ushort); void Accept(IVisitor& visitor) override; void Think(float, CStateManager&) override; diff --git a/include/MetroidPrime/ScriptObjects/CScriptTrigger.hpp b/include/MetroidPrime/ScriptObjects/CScriptTrigger.hpp index b6daecb6d..3381ae2d6 100644 --- a/include/MetroidPrime/ScriptObjects/CScriptTrigger.hpp +++ b/include/MetroidPrime/ScriptObjects/CScriptTrigger.hpp @@ -63,6 +63,9 @@ class CScriptTrigger : public CActor { void Touch(CActor&, CStateManager&) override; CAABox GetTriggerBoundsWR() const; + const CAABox& GetTriggerBounds() const { return x130_bounds; } + void SetTriggerBounds(const CAABox& bounds) { x130_bounds = bounds; } + bool CameraInside() const { return x148_25_camSubmerged; } rstl::optional_object< CAABox > GetTouchBounds() const override; void AcceptScriptMsg(EScriptObjectMessage, TUniqueId, CStateManager&) override; void Think(float, CStateManager&) override; diff --git a/include/MetroidPrime/ScriptObjects/CScriptWater.hpp b/include/MetroidPrime/ScriptObjects/CScriptWater.hpp index c7c3c96e7..7ce8c0c38 100644 --- a/include/MetroidPrime/ScriptObjects/CScriptWater.hpp +++ b/include/MetroidPrime/ScriptObjects/CScriptWater.hpp @@ -29,11 +29,12 @@ class CScriptWater : public CScriptTrigger { public: CScriptWater(CStateManager&, TUniqueId, const rstl::string&, const CEntityInfo&, const CVector3f&, const CAABox&, const CDamageInfo&, const CVector3f&, uint, bool, bool, uint, uint, - uint, uint, uint, uint, const CVector3f&, float, float, float, bool, + uint, uint, uint, uint, uint, const CVector3f&, float, float, float, bool, CFluidPlane::EFluidType, bool, float, const CFluidUVMotion&, float, float, float, float, float, float, float, float, const CColor&, const CColor&, uint, uint, uint, - uint, int, int, int, float, uint, float, float, float, float, float, float, float, - float, const CColor&, uint, uint, bool, int, int, const uint*); + uint, uint, int, int, int, int, int, float, uint, float, float, float, float, + float, float, float, float, const CColor&, uint, float, float, float, uint, uint, + bool, int, int, const uint*); // CEntity ~CScriptWater() override; @@ -53,11 +54,20 @@ class CScriptWater : public CScriptTrigger { CAABox GetSortingBounds(const CStateManager&) const override; bool CanRippleAtPoint(const CVector3f&) const; - // UpdateSplashInhabitants__12CScriptWaterFR13CStateManager + void UpdateSplashInhabitants(CStateManager&); + void SetupGrid(bool recomputeClipping); + void SetupGridClipping(CStateManager&, int computeVerts); + int GetPatchRenderFlags(int x, int y) const; + void SetMorphing(const bool m); + const CScriptWater* GetNextConnectedWater(const CStateManager&) const; // RenderSurface__12CScriptWaterFv - CFluidPlaneCPU& FluidPlane() { return *x1b4_fluidPlane; } - const CFluidPlaneCPU& GetFluidPlane() const { return *x1b4_fluidPlane; } + CFluidPlaneCPU& FluidPlane() { + return reinterpret_cast< CFluidPlaneCPU& >(*x1b4_fluidPlane); + } + const CFluidPlaneCPU& GetFluidPlane() const { + return reinterpret_cast< const CFluidPlaneCPU& >(*x1b4_fluidPlane); + } // GetWRSurfacePlane__12CScriptWaterCFv float GetSurfaceZ() const { return GetTriggerBoundsWR().GetMaxPoint().GetZ(); } const CColor& GetUnderwaterFogColor() const { return x2a8_insideFogColor; } @@ -71,20 +81,19 @@ class CScriptWater : public CScriptTrigger { } ushort GetUnmorphVisorRunoffSfx() const { return x262_unmorphVisorRunoffSfx; } // GetFluidType__12CScriptWaterCFv - // IsMorphing__12CScriptWaterCFv - // SetMorphing__12CScriptWaterFb + bool IsMorphing() const { return x2e8_26_morphing; } // GetFrustumPlanes__12CScriptWaterCFv - // GetSplashIndex__12CScriptWaterCFf - // GetSplashEffect__12CScriptWaterCFf - // GetSplashSound__12CScriptWaterCFf - // GetSplashEffectScale__12CScriptWaterCFf + int GetSplashIndex(float scale) const; + const rstl::optional_object< TLockedToken< CGenDescription > >& GetSplashEffect(float scale) const; + int GetSplashSound(float scale) const; + float GetSplashEffectScale(float scale) const; // GetSplashColor__12CScriptWaterCFv - // kSplashScales__12CScriptWater + static const float kSplashScales[6]; private: CFrustumPlanes x150_frustum; - rstl::single_ptr< CFluidPlaneCPU > x1b4_fluidPlane; + rstl::single_ptr< CFluidPlane > x1b4_fluidPlane; CVector3f x1b8_positionMorphed; CVector3f x1c4_extentMorphed; float x1d0_morphInTime; @@ -126,10 +135,10 @@ class CScriptWater : public CScriptTrigger { int x2cc_gridCellCount; int x2d0_patchDimX; int x2d4_patchDimY; - rstl::single_ptr< bool[] > x2d8_tileIntersects; - rstl::single_ptr< bool[] > x2dc_vertIntersects; + rstl::single_ptr< bool > x2d8_tileIntersects; + rstl::single_ptr< bool > x2dc_vertIntersects; // 0: all clear, 1: all intersect, 2: partial intersect - rstl::single_ptr< uchar[] > x2e0_patchIntersects; + rstl::single_ptr< char > x2e0_patchIntersects; int x2e4_computedGridCellCount; bool x2e8_24_b4 : 1; bool x2e8_25_morphIn : 1; diff --git a/src/MetroidPrime/CFluidPlaneCPU.cpp b/src/MetroidPrime/CFluidPlaneCPU.cpp new file mode 100644 index 000000000..d833b2d7d --- /dev/null +++ b/src/MetroidPrime/CFluidPlaneCPU.cpp @@ -0,0 +1,104 @@ +#include "MetroidPrime/CFluidPlaneCPU.hpp" + +#include "Kyoto/CResFactory.hpp" +#include "Kyoto/CSimplePool.hpp" +#include "Kyoto/Math/CMath.hpp" + +#include "Kyoto/Alloc/CMemory.hpp" + +static int kTableSize = 2048; + +CFluidPlaneCPU::CTurbulence::CTurbulence(float speed, float distance, float freqMax, float freqMin, + float phaseMax, float phaseMin, float amplitudeMax, + float amplitudeMin) +: x0_speed(speed) +, x4_distance(distance) +, x8_freqMax(freqMax) +, xc_freqMin(freqMin) +, x10_phaseMax(phaseMax) +, x14_phaseMin(phaseMin) +, x18_amplitudeMax(amplitudeMax) +, x1c_amplitudeMin(amplitudeMin) +, x20_table(NULL) +, x24_tableCount(0) +, x28_heightSelPitch(0.f) +, x2c_ooTurbSpeed(1.f / x0_speed) +, x30_ooTurbDistance(1.f / x4_distance) +, x34_hasTurbulence(false) { + if (x18_amplitudeMax == 0.f && x1c_amplitudeMin == 0.f) { + return; + } + + x24_tableCount = kTableSize; + x28_heightSelPitch = (float)x24_tableCount; + x20_table = rs_new float[x24_tableCount]; + + float anglePitch = M_2PIF / x28_heightSelPitch; + float freqConstant = 0.5f * (x8_freqMax + xc_freqMin); + float freqLinear = 0.5f * (x8_freqMax - xc_freqMin); + float phaseConstant = 0.5f * (x10_phaseMax + x14_phaseMin); + float phaseLinear = 0.5f * (x10_phaseMax - x14_phaseMin); + float amplitudeConstant = 0.5f * (x18_amplitudeMax + x1c_amplitudeMin); + float amplitudeLinear = 0.5f * (x18_amplitudeMax - x1c_amplitudeMin); + + float curAng = 0.f; + for (int i = 0; i < x24_tableCount; ++i, curAng += anglePitch) { + float angCos = CMath::FastCosR(curAng); + x20_table[i] = + (amplitudeLinear * angCos + amplitudeConstant) * + CMath::FastSinR((freqLinear * angCos + freqConstant) * curAng + + (phaseLinear * angCos + phaseConstant)); + } + x34_hasTurbulence = true; +} + +CFluidPlaneCPU::CTurbulence::~CTurbulence() { delete[] x20_table; } + +CFluidPlaneCPU::CFluidPlaneCPU(uint patternMap1, uint patternMap2, uint colorMap, uint bumpMap, + uint envMap, uint envBumpMap, float unitsPerLightmapTexel, + uint lightmap, uint tileSubdivisions, EFluidType fluidType, + float tileSize, const CVector3f& bumpLightDir, float alpha, + const CFluidUVMotion& uvMotion, float bumpScale, float turbSpeed, + float turbDistance, float turbFreqMax, float turbFreqMin, + float turbPhaseMax, float turbPhaseMin, float turbAmplitudeMax, + float turbAmplitudeMin, float specularMin, float specularMax, + float reflectionBlend, float reflectionSize, float rippleIntensity) +: CFluidPlane(patternMap1, patternMap2, colorMap, alpha, fluidType, rippleIntensity, uvMotion) +, xa0_texIdBumpMap(bumpMap) +, xa4_texIdEnvMap(envMap) +, xa8_texIdEnvBumpMap(envBumpMap) +, xac_texIdLightmap(lightmap) +, xb0_bumpMap() +, xc0_envMap() +, xd0_envBumpMap() +, xe0_lightmap() +, xf0_bumpLightDir(bumpLightDir.AsNormalized()) +, xfc_bumpScale(bumpScale) +, x100_tileSize(tileSize) +, x104_tileSubdivisions(tileSubdivisions & ~1u) +, x108_rippleResolution(tileSize / (float)x104_tileSubdivisions) +, x10c_specularMin(specularMin) +, x110_specularMax(specularMax) +, x114_reflectionBlend(reflectionBlend) +, x118_reflectionSize(reflectionSize) +, x11c_unitsPerLightmapTexel(unitsPerLightmapTexel) +, x120_turbulence(turbSpeed, turbDistance, turbFreqMax, turbFreqMin, turbPhaseMax, turbPhaseMin, + turbAmplitudeMax, turbAmplitudeMin) { + if (gpResourceFactory->GetResourceTypeById(xa0_texIdBumpMap) == 'TXTR' && + (fluidType == kFT_Lava || fluidType == kFT_ThickLava)) { + xb0_bumpMap = gpSimplePool->GetObj(SObjectTag('TXTR', xa0_texIdBumpMap)); + } + if (gpResourceFactory->GetResourceTypeById(xa4_texIdEnvMap) == 'TXTR' && + fluidType == kFT_NormalWater) { + xc0_envMap = gpSimplePool->GetObj(SObjectTag('TXTR', xa4_texIdEnvMap)); + } + if (gpResourceFactory->GetResourceTypeById(xa8_texIdEnvBumpMap) == 'TXTR') { + xd0_envBumpMap = gpSimplePool->GetObj(SObjectTag('TXTR', xa8_texIdEnvBumpMap)); + } + if (gpResourceFactory->GetResourceTypeById(xac_texIdLightmap) == 'TXTR' && + fluidType != kFT_Lava && fluidType != kFT_ThickLava) { + xe0_lightmap = gpSimplePool->GetObj(SObjectTag('TXTR', xac_texIdLightmap)); + } +} + +CFluidPlaneCPU::~CFluidPlaneCPU() {} diff --git a/src/MetroidPrime/CFluidPlaneDoor.cpp b/src/MetroidPrime/CFluidPlaneDoor.cpp index ed796d3a8..4737fa012 100644 --- a/src/MetroidPrime/CFluidPlaneDoor.cpp +++ b/src/MetroidPrime/CFluidPlaneDoor.cpp @@ -65,7 +65,11 @@ void CFluidPlaneDoor::RenderSetup(const CStateManager& mgr, float alpha, const C CGX::SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_POS, GX_TEXMTX5, false, GX_PTIDENTITY); } -void CFluidPlaneDoor::Render(const CStateManager& mgr, const CAABox&, const CFrustumPlanes&, - const CRippleManager&, const CVector3f&) {} +void CFluidPlaneDoor::Render(const CStateManager& mgr, float alpha, const CAABox& aabb, + const CTransform4f& xf, const CTransform4f& areaXf, bool noNormals, + const CFrustumPlanes& frustum, + const rstl::optional_object< CRippleManager >& rippleManager, + TUniqueId waterId, const bool* gridFlags, int gridDimX, int gridDimY, + const CVector3f& areaCenter) const {} void CFluidPlaneDoor::RenderCleanup() const {} diff --git a/src/MetroidPrime/CFluidPlaneManager.cpp b/src/MetroidPrime/CFluidPlaneManager.cpp index 6b24f9cb3..8f9962bbf 100644 --- a/src/MetroidPrime/CFluidPlaneManager.cpp +++ b/src/MetroidPrime/CFluidPlaneManager.cpp @@ -14,6 +14,7 @@ #include "rstl/math.hpp" const float CFluidPlane::kRippleIntensityRange = 1.f; +const bool sRenderFog = true; uint fn_8012F098() { return 0; @@ -109,3 +110,8 @@ void CFluidPlane::AddRipple(const CRipple& ripple, const CScriptWater& water, CS void CFluidPlane::Render(const CStateManager& mgr, const CAABox&, const CFrustumPlanes&, const CRippleManager&, const CVector3f&) {} + +// void CFluidPlane::Render(const CStateManager&, float, const CAABox&, const CTransform4f&, +// const CTransform4f&, bool, const CFrustumPlanes&, +// const rstl::optional_object< CRippleManager >&, TUniqueId, +// const bool*, int, int, const CVector3f&) const {} diff --git a/src/MetroidPrime/CRippleManager.cpp b/src/MetroidPrime/CRippleManager.cpp index db560d90b..53421e97c 100644 --- a/src/MetroidPrime/CRippleManager.cpp +++ b/src/MetroidPrime/CRippleManager.cpp @@ -9,6 +9,13 @@ CRippleManager::CRippleManager(int maxRipples, float alpha) Init(maxRipples); } +CRippleManager::CRippleManager(const CRippleManager& other) +: x0_maxTimeFalloff(other.x0_maxTimeFalloff) +, x4_ripples(other.x4_ripples) +, x14_alpha(other.x14_alpha) {} + +CRippleManager::~CRippleManager() {} + void CRippleManager::Init(int maxRipples) { x4_ripples.resize(maxRipples); for (AUTO(it, x4_ripples.begin()); it != x4_ripples.end(); ++it) { diff --git a/src/MetroidPrime/CStateManager.cpp b/src/MetroidPrime/CStateManager.cpp index e7de8bd48..456ab25f2 100644 --- a/src/MetroidPrime/CStateManager.cpp +++ b/src/MetroidPrime/CStateManager.cpp @@ -265,7 +265,7 @@ CStateManager::CStateManager(const rstl::ncrc_ptr< CScriptMailbox >& mailbox, x90c_loaderFuncs[kST_ColorModulate] = ScriptLoader::LoadColorModulate; x90c_loaderFuncs[kST_ThardusRockProjectile] = ScriptLoader::LoadThardusRockProjectile; x90c_loaderFuncs[kST_Midi] = ScriptLoader::LoadMidi; - x90c_loaderFuncs[kST_StreamedAudio] = ScriptLoader::LoadStreamedAudio; // Or music? + x90c_loaderFuncs[kST_StreamedAudio] = ScriptLoader::LoadStreamedMusic; x90c_loaderFuncs[kST_WorldTeleporterToo] = ScriptLoader::LoadWorldTeleporter; x90c_loaderFuncs[kST_Repulsor] = ScriptLoader::LoadRepulsor; x90c_loaderFuncs[kST_GunTurret] = ScriptLoader::LoadGunTurret; @@ -273,8 +273,7 @@ CStateManager::CStateManager(const rstl::ncrc_ptr< CScriptMailbox >& mailbox, x90c_loaderFuncs[kST_Babygoth] = ScriptLoader::LoadBabygoth; x90c_loaderFuncs[kST_Eyeball] = ScriptLoader::LoadEyeball; x90c_loaderFuncs[kST_RadialDamage] = ScriptLoader::LoadRadialDamage; - x90c_loaderFuncs[kST_CameraPitchVolume] = - ScriptLoader::LoadCameraPitchVolume; // LoadScriptCameraPitchVolume + x90c_loaderFuncs[kST_CameraPitchVolume] = ScriptLoader::LoadScriptCameraPitchVolume; x90c_loaderFuncs[kST_ElitePirate] = ScriptLoader::LoadElitePirate; x90c_loaderFuncs[kST_MetroidBeta] = ScriptLoader::LoadMetroidBeta; x90c_loaderFuncs[kST_EnvFxDensityController] = ScriptLoader::LoadEnvFxDensityController; diff --git a/src/MetroidPrime/Cameras/CCameraManager.cpp b/src/MetroidPrime/Cameras/CCameraManager.cpp index 504e8c2ef..d9e67b36c 100644 --- a/src/MetroidPrime/Cameras/CCameraManager.cpp +++ b/src/MetroidPrime/Cameras/CCameraManager.cpp @@ -1,5 +1,6 @@ #include "MetroidPrime/Cameras/CCameraManager.hpp" +#include "Kyoto/Math/CVector3f.hpp" #include "MetroidPrime/CExplosion.hpp" #include "MetroidPrime/CFluidPlaneCPU.hpp" #include "MetroidPrime/CRumbleManager.hpp" @@ -501,9 +502,12 @@ void CCameraManager::UseCameraHint(const CScriptCameraHint& hint, CStateManager& if ((hint.GetOverrideFlags() & 0x20) == 0 && (hint.GetBehaviourType() != CBallCamera::kBCB_Default || (oldHint && (!oldHint || oldHint->GetBehaviourType() != CBallCamera::kBCB_Default)))) { - SetupInterpolation(camXf, x80_ballCamera->GetUniqueId(), x80_ballCamera->GetLookAtPosition(), - hint.GetInfo().GetInterpolateTime(), hint.GetInfo().GetClampVelRange(), - hint.GetInfo().GetClampRotRange(), hint.GetInfo().Flagx400(), mgr); + const CVector3f& lookAtPos = x80_ballCamera->GetLookAtPosition(); + float interpTime = hint.GetInfo().GetInterpolateTime(); + float positionSpeed = hint.GetInfo().GetClampVelRange(); + float rotationSpeed = hint.GetInfo().GetClampRotRange(); + SetupInterpolation(camXf, x80_ballCamera->GetUniqueId(), lookAtPos, interpTime, positionSpeed, + rotationSpeed, hint.GetInfo().Flagx400(), mgr); } } diff --git a/src/MetroidPrime/Enemies/CAtomicAlpha.cpp b/src/MetroidPrime/Enemies/CAtomicAlpha.cpp new file mode 100644 index 000000000..184da1c39 --- /dev/null +++ b/src/MetroidPrime/Enemies/CAtomicAlpha.cpp @@ -0,0 +1,37 @@ +#include "MetroidPrime/Enemies/CAtomicAlpha.hpp" + +#include "MetroidPrime/CStateManager.hpp" +#include "MetroidPrime/Enemies/CPatternedInfo.hpp" +#include "MetroidPrime/TCastTo.hpp" + +#include "float.h" + +CAtomicAlpha::CAtomicAlpha(TUniqueId uid, const rstl::string& name, const CEntityInfo& info, + const CTransform4f& xf, const CModelData& mData, + const CActorParameters& actParms, const CPatternedInfo& pInfo, + CAssetId bombWeapon, const CDamageInfo& bombDamage, + float bombDropDelay, float bombReappearDelay, + float bombRappearTime, CAssetId cmdl, bool invisible, + bool applyBeamAttraction) +: CPatterned(kC_AtomicAlpha, uid, name, kFT_Zero, info, xf, mData, pInfo, kMT_Flyer, kCT_One, + kBT_Flyer, actParms, kKBV_Medium) +, x568_24_inRange(false) +, x568_25_invisible(invisible) +, x568_26_applyBeamAttraction(applyBeamAttraction) +, x56c_bombDropDelay(bombDropDelay) +, x570_bombReappearDelay(bombReappearDelay) +, x574_bombRappearTime(bombRappearTime) +, x578_bombTime(0.f) +, x57c_curBomb(0) +, x580_pathFind(NULL, 3, pInfo.GetPathfindingIndex(), 1.f, 1.f) +, x664_steeringBehaviors() +, x668_bombProjectile(bombWeapon, bombDamage) +, x690_bombModel(CStaticRes(cmdl, mData.ScaleCopy())) { + x668_bombProjectile.Token().Lock(); + x6dc_bombLocators.push_back(SBomb(rstl::string_l("bomb1_LCTR"), pas::kLT_Internal10, FLT_MAX)); + x6dc_bombLocators.push_back(SBomb(rstl::string_l("bomb2_LCTR"), pas::kLT_Internal11, FLT_MAX)); + x6dc_bombLocators.push_back(SBomb(rstl::string_l("bomb3_LCTR"), pas::kLT_Internal12, FLT_MAX)); + x6dc_bombLocators.push_back(SBomb(rstl::string_l("bomb4_LCTR"), pas::kLT_Internal13, FLT_MAX)); +} + +void CAtomicAlpha::Accept(IVisitor& visitor) { visitor.Visit(*this); } diff --git a/src/MetroidPrime/Enemies/CAtomicBeta.cpp b/src/MetroidPrime/Enemies/CAtomicBeta.cpp new file mode 100644 index 000000000..8c99f42bd --- /dev/null +++ b/src/MetroidPrime/Enemies/CAtomicBeta.cpp @@ -0,0 +1,62 @@ +#include "MetroidPrime/Enemies/CAtomicBeta.hpp" + +#include "MetroidPrime/CStateManager.hpp" +#include "MetroidPrime/Enemies/CKnockBackController.hpp" +#include "MetroidPrime/Enemies/CPatternedInfo.hpp" +#include "MetroidPrime/TCastTo.hpp" + +#include "Kyoto/Audio/CSfxManager.hpp" +#include "Kyoto/CSimplePool.hpp" + +CAtomicBeta::CAtomicBeta(TUniqueId uid, const rstl::string& name, const CEntityInfo& info, + const CTransform4f& xf, CModelData mData, const CActorParameters& actParms, + const CPatternedInfo& pInfo, CAssetId electricId, CAssetId weaponId, + const CDamageInfo& dInfo, CAssetId particleId, float beamFadeSpeed, + float beamRadius, float beamDamageInterval, + const CDamageVulnerability& frozenDVuln, float moveSpeed, float minSpeed, + float maxSpeed, ushort sId1, ushort sId2, ushort sId3, float speedStep) +: CPatterned(kC_AtomicBeta, uid, name, kFT_Zero, info, xf, mData, pInfo, kMT_Flyer, kCT_One, + kBT_RestrictedFlyer, actParms, kKBV_Small) +, x568_projectileIds() +, x574_beamFired(false) +, x578_minSpeed(minSpeed) +, x57c_maxSpeed(maxSpeed) +, x580_speedStep(speedStep) +, x584_currentSpeed(x578_minSpeed) +, x588_frozenDamage(frozenDVuln) +, x5f0_moveSpeed(moveSpeed) +, x5f4_direction(xf.GetColumn(kDY)) +, x600_electricWeapon(gpSimplePool->GetObj(SObjectTag('ELSC', electricId))) +, x608_weaponDesc(gpSimplePool->GetObj(SObjectTag('WPSC', weaponId))) +, x610_projectileDamage(dInfo) +, x62c_beamParticle(particleId) +, x630_beamFadeSpeed(beamFadeSpeed) +, x634_beamRadius(beamRadius) +, x638_beamDamageInterval(beamDamageInterval) +, x63c_(1.f) +, x640_(10.f) +, x644_sfxId1(CSfxManager::TranslateSFXID(sId1)) +, x646_sfxId2(CSfxManager::TranslateSFXID(sId2)) +, x648_sfxId3(CSfxManager::TranslateSFXID(sId3)) +, x64c_sfxHandle1() +, x650_sfxHandle2() +, x654_sfxHandle3() { + x460_knockBackController.SetAutoResetImpulse(false); + x460_knockBackController.SetEnableFreeze(false); + x460_knockBackController.SetX82_24(false); +} + +CAtomicBeta::~CAtomicBeta() { + DestroyEmitter(x650_sfxHandle2); + DestroyEmitter(x654_sfxHandle3); + DestroyEmitter(x64c_sfxHandle1); +} + +void CAtomicBeta::Accept(IVisitor& visitor) { visitor.Visit(*this); } + +void CAtomicBeta::DestroyEmitter(CSfxHandle& handle) { + if (handle) { + CSfxManager::RemoveEmitter(handle); + handle = CSfxHandle(); + } +} diff --git a/src/MetroidPrime/Enemies/CWarWasp.cpp b/src/MetroidPrime/Enemies/CWarWasp.cpp new file mode 100644 index 000000000..4ff6d00bf --- /dev/null +++ b/src/MetroidPrime/Enemies/CWarWasp.cpp @@ -0,0 +1,62 @@ +#include "MetroidPrime/Enemies/CWarWasp.hpp" + +#include "MetroidPrime/CStateManager.hpp" +#include "MetroidPrime/Enemies/CPatternedInfo.hpp" +#include "MetroidPrime/TCastTo.hpp" + +#include "Kyoto/Audio/CSfxManager.hpp" +#include "Kyoto/CSimplePool.hpp" +#include "Kyoto/Particles/CGenDescription.hpp" +#include "Kyoto/SObjectTag.hpp" + +CWarWasp::CWarWasp(TUniqueId uid, const rstl::string& name, const CEntityInfo& info, + const CTransform4f& xf, const CModelData& mData, const CPatternedInfo& pInfo, + CPatterned::EFlavorType flavor, CPatterned::EColliderType collider, + const CDamageInfo& dInfo, const CActorParameters& actParms, + CAssetId projectileWeapon, const CDamageInfo& projectileDamage, + CAssetId projectileVisorParticle, uint projectileVisorSfx) +: CPatterned(kC_WarWasp, uid, name, flavor, info, xf, mData, pInfo, kMT_Flyer, collider, + kBT_Flyer, actParms, kKBV_Small) +, x568_stateProg(-1) +, x570_cSphere(CSphere(CVector3f(0.f, 0.f, 1.8f), 1.f), GetMaterialList()) +, x590_pfSearch(NULL, 3, pInfo.GetPathfindingIndex(), 1.f, 1.f) +, x674_aiMgr(kInvalidUniqueId) +, x678_targetPos(CVector3f::Zero()) +, x684_contactDamage(dInfo) +, x6a0_initialRot(CQuaternion::NoRotation()) +, x6b0_circleBurstPos(CVector3f::Zero()) +, x6bc_circleBurstDir(CVector3f(0.f, 0.f, 0.f)) +, x6c8_circleBurstRight(CVector3f(0.f, 0.f, 0.f)) +, x6d4_projectileInfo(projectileWeapon, projectileDamage) +, x6fc_initialSpeed(x3b4_speed) +, x700_attackRemTime(0.f) +, x704_dodgeDir(pas::kSD_Invalid) +, x708_circleAttackTeam(-1) +, x70c_initialCircleAttackTeam(-1) +, x710_initialCircleAttackTeamUnit(-1) +, x714_circleTelegraphSeekHeight(0.f) +, x718_circleBurstOffTotemAngle(1.5707964f) +, x72c_projectileVisorSfx(CSfxManager::TranslateSFXID(projectileVisorSfx)) +, x72e_24_jumpBackRepeat(true) +, x72e_25_canApplyDamage(false) +, x72e_26_initiallyInactive(!pInfo.GetActive()) +, x72e_27_teamMatesMelee(false) +, x72e_28_inProjectileAttack(false) +, x72e_29_pathObstructed(false) +, x72e_30_isRetreating(false) +, x72e_31_heardNoise(false) { + if (flavor == kFT_Two) { + x6d4_projectileInfo.Token().Lock(); + } + UpdateTouchBounds(); + SetCoefficientOfRestitutionModifier(0.1f); + if (projectileVisorParticle != kInvalidAssetId) { + x71c_projectileVisorParticle = gpSimplePool->GetObj(SObjectTag('PART', projectileVisorParticle)); + } + x328_29_noPatternShagging = true; + x460_knockBackController.SetAnimationStateRange(kKBAS_KnockBack, kKBAS_KnockBack); +} + +CWarWasp::~CWarWasp() {} + +void CWarWasp::Accept(IVisitor& visitor) { visitor.Visit(*this); } diff --git a/src/MetroidPrime/ScriptLoader.cpp b/src/MetroidPrime/ScriptLoader.cpp index 546753330..fe7fe884f 100644 --- a/src/MetroidPrime/ScriptLoader.cpp +++ b/src/MetroidPrime/ScriptLoader.cpp @@ -1,8 +1,12 @@ #include "MetroidPrime/ScriptLoader.hpp" +#include "Kyoto/Audio/CSfxManager.hpp" +#include "Kyoto/Math/CMath.hpp" +#include "Kyoto/Math/CVector3f.hpp" #include "MetroidPrime/CActorParameters.hpp" #include "MetroidPrime/CAnimRes.hpp" #include "MetroidPrime/CAnimationParameters.hpp" +#include "MetroidPrime/CDamageInfo.hpp" #include "MetroidPrime/CDamageVulnerability.hpp" #include "MetroidPrime/CFluidUVMotion.hpp" #include "MetroidPrime/CGrappleParameters.hpp" @@ -12,28 +16,71 @@ #include "MetroidPrime/CWorld.hpp" #include "MetroidPrime/Player/CPlayerState.hpp" +#include "MetroidPrime/ScriptObjects/CRepulsor.hpp" #include "MetroidPrime/ScriptObjects/CScriptActor.hpp" +#include "MetroidPrime/ScriptObjects/CScriptActorRotate.hpp" +#include "MetroidPrime/ScriptObjects/CScriptAiJumpPoint.hpp" +#include "MetroidPrime/ScriptObjects/CScriptAreaAttributes.hpp" +#include "MetroidPrime/ScriptObjects/CScriptBallTrigger.hpp" +#include "MetroidPrime/ScriptObjects/CScriptCameraHint.hpp" #include "MetroidPrime/ScriptObjects/CScriptCameraHintTrigger.hpp" +#include "MetroidPrime/ScriptObjects/CScriptCameraPitchVolume.hpp" +#include "MetroidPrime/ScriptObjects/CScriptControllerAction.hpp" +#include "MetroidPrime/ScriptObjects/CScriptCounter.hpp" #include "MetroidPrime/ScriptObjects/CScriptDamageableTrigger.hpp" +#include "MetroidPrime/ScriptObjects/CScriptDebugCameraWaypoint.hpp" +#include "MetroidPrime/ScriptObjects/CScriptDistanceFog.hpp" #include "MetroidPrime/ScriptObjects/CScriptDock.hpp" +#include "MetroidPrime/ScriptObjects/CScriptDockAreaChange.hpp" +#include "MetroidPrime/ScriptObjects/CScriptEMPulse.hpp" +#include "MetroidPrime/ScriptObjects/CScriptMazeNode.hpp" +#include "MetroidPrime/ScriptObjects/CScriptMemoryRelay.hpp" +#include "MetroidPrime/ScriptObjects/CScriptMidi.hpp" +#include "MetroidPrime/ScriptObjects/CScriptPickup.hpp" +#include "MetroidPrime/ScriptObjects/CScriptPickupGenerator.hpp" +#include "MetroidPrime/ScriptObjects/CScriptPlayerHint.hpp" +#include "MetroidPrime/ScriptObjects/CScriptPlayerStateChange.hpp" +#include "MetroidPrime/ScriptObjects/CScriptPointOfInterest.hpp" +#include "MetroidPrime/ScriptObjects/CScriptRandomRelay.hpp" +#include "MetroidPrime/ScriptObjects/CScriptRipple.hpp" +#include "MetroidPrime/ScriptObjects/CScriptRoomAcoustics.hpp" #include "MetroidPrime/ScriptObjects/CScriptSpawnPoint.hpp" +#include "MetroidPrime/ScriptObjects/CScriptSpecialFunction.hpp" +#include "MetroidPrime/ScriptObjects/CScriptSpiderBallAttractionSurface.hpp" +#include "MetroidPrime/ScriptObjects/CScriptSteam.hpp" +#include "MetroidPrime/ScriptObjects/CScriptStreamedAudio.hpp" +#include "MetroidPrime/ScriptObjects/CScriptSwitch.hpp" +#include "MetroidPrime/ScriptObjects/CScriptTargetingPoint.hpp" +#include "MetroidPrime/ScriptObjects/CScriptTimer.hpp" #include "MetroidPrime/ScriptObjects/CScriptTrigger.hpp" +#include "MetroidPrime/ScriptObjects/CScriptVisorGoo.hpp" +#include "MetroidPrime/ScriptObjects/CScriptWater.hpp" + +#include "MetroidPrime/CFluidPlaneManager.hpp" #include "Kyoto/Alloc/CMemory.hpp" #include "Kyoto/CResFactory.hpp" +#include "Kyoto/Graphics/CGraphics.hpp" #include "Kyoto/Math/CQuaternion.hpp" #include "Kyoto/Math/CRelAngle.hpp" +#include "Kyoto/Math/CVector2f.hpp" #include "Kyoto/Streams/CInputStream.hpp" +static CAABox GetCollisionBox(CStateManager& stateMgr, TAreaId id, const CVector3f& extent, + const CVector3f& offset) { + const CAABox box(0.5f * -extent.GetX() + offset.GetX(), 0.5f * -extent.GetY() + offset.GetY(), + 0.5f * -extent.GetZ() + offset.GetZ(), 0.5f * extent.GetX() + offset.GetX(), + 0.5f * extent.GetY() + offset.GetY(), 0.5f * extent.GetZ() + offset.GetZ()); + return box.GetTransformedAABox(stateMgr.GetWorld()->GetAreaAlways(id).GetTM().GetRotation()); +} + static CTransform4f ConvertEditorEulerToTransform4f(const CVector3f& orientation, const CVector3f& position) { CQuaternion quat = CQuaternion::ZRotation(CRelAngle::FromDegrees(orientation.GetZ())) * CQuaternion::YRotation(CRelAngle::FromDegrees(orientation.GetY())) * CQuaternion::XRotation(CRelAngle::FromDegrees(orientation.GetX())); - CMatrix3f mat = quat.BuildTransform(); - // return CTransform4f(mat, position); - return CTransform4f(mat.GetRow(kDX), mat.GetRow(kDY), mat.GetRow(kDZ), position); + return CTransform4f(mat.GetColumn(kDX), mat.GetColumn(kDY), mat.GetColumn(kDZ), position); } static CTransform4f LoadEditorTransform(CInputStream& in) { @@ -72,8 +119,8 @@ SScaledActorHead::SScaledActorHead(CInputStream& in, CStateManager& stateMgr) CAnimationParameters LoadAnimationParameters(CInputStream& in) { CAssetId ancs = in.Get< CAssetId >(); - int charIdx = in.Get(); - uint defaultAnim = in.Get(); + int charIdx = in.Get< int >(); + uint defaultAnim = in.Get< int >(); return CAnimationParameters(ancs, charIdx, defaultAnim); } @@ -83,41 +130,31 @@ static CLightParameters LoadLightParameters(CInputStream& in) { return CLightParameters::None(); } - bool a = in.Get< bool >(); - float b = in.Get< float >(); - CLightParameters::EShadowTessellation shadowTess = - CLightParameters::EShadowTessellation(in.Get< int >()); - float d = in.Get< float >(); - float e = in.Get< float >(); + bool castShadow = in.Get< bool >(); + float shadowScale = in.Get< float >(); + CLightParameters::EShadowTessellation shadowTessInt = + CLightParameters::EShadowTessellation(in.ReadLong()); + float shadowAlpha = in.Get< float >(); + float maxShadowHeight = in.Get< float >(); CColor noLightsAmbient(in); - bool makeLights = in.ReadBool(); + bool makeLights = in.Get< bool >(); CLightParameters::EWorldLightingOptions lightOpts = - CLightParameters::EWorldLightingOptions(in.ReadLong()); + CLightParameters::EWorldLightingOptions(in.Get< int >()); CLightParameters::ELightRecalculationOptions recalcOpts = - CLightParameters::ELightRecalculationOptions(in.ReadLong()); + CLightParameters::ELightRecalculationOptions(in.Get< int >()); CVector3f actorPosBias(in); - int maxDynamicLights = -1; - int maxAreaLights = -1; - if (propCount >= 12) { - maxDynamicLights = in.ReadLong(); - maxAreaLights = in.ReadLong(); - } - - bool ambientChannelOverflow = false; - if (propCount >= 13) - ambientChannelOverflow = in.ReadBool(); + int maxDynamicLights = propCount >= 12 ? in.Get< int >() : -1; + int maxAreaLights = propCount >= 12 ? in.Get< int >() : -1; + bool ambientChannelOverflow = propCount >= 13 ? in.Get< bool >() : false; + int layerIdx = propCount >= 14 ? in.Get< int >() : 0; - int layerIdx = 0; - if (propCount >= 14) - layerIdx = in.ReadLong(); - - return CLightParameters(a, b, shadowTess, d, e, noLightsAmbient, makeLights, lightOpts, - recalcOpts, actorPosBias, maxDynamicLights, maxAreaLights, - ambientChannelOverflow, layerIdx); + return CLightParameters(castShadow, shadowScale, shadowTessInt, shadowAlpha, maxShadowHeight, + noLightsAmbient, makeLights, lightOpts, recalcOpts, actorPosBias, + maxDynamicLights, maxAreaLights, ambientChannelOverflow, layerIdx); } static CScannableParameters LoadScannableParameters(CInputStream& in) { @@ -130,21 +167,75 @@ static CScannableParameters LoadScannableParameters(CInputStream& in) { } static CVisorParameters LoadVisorParameters(CInputStream& in) { - u32 propCount = in.ReadLong(); - if (propCount >= 1 && propCount <= 3) { - bool b1 = in.ReadBool(); - bool scanPassthrough = false; - u8 mask = 0xf; - if (propCount > 2) - scanPassthrough = in.ReadBool(); - if (propCount >= 2) - mask = u8(in.ReadLong()); - return CVisorParameters(mask, b1, scanPassthrough); + int propCount = in.ReadLong(); + bool valid = false; + if (propCount >= 1 && propCount <= 3) + valid = true; + if (!valid) + return CVisorParameters(0xf, false, false); + + bool b1 = in.ReadBool(); + bool scanPassthrough = false; + if (propCount > 2) + scanPassthrough = in.ReadBool(); + uint mask; + if (propCount >= 2) { + mask = in.ReadLong(); + } else { + mask = 0xf; } - return CVisorParameters(0xf, false, false); + return CVisorParameters(mask, b1, scanPassthrough); } -static CActorParameters LoadActorParameters(CInputStream&) {} +static CActorParameters LoadActorParameters(CInputStream& in) { + int propCount = in.ReadLong(); + bool valid = false; + if (propCount >= 5 && propCount <= 0xe) + valid = true; + if (!valid) + return CActorParameters::None(); + + CLightParameters lParms = LoadLightParameters(in); + + CScannableParameters sParms = + propCount > 5 ? LoadScannableParameters(in) : CScannableParameters(kInvalidAssetId); + + CAssetId xrayModel = in.Get< CAssetId >(); + CAssetId xraySkin = in.Get< CAssetId >(); + CAssetId infraModel = in.Get< CAssetId >(); + CAssetId infraSkin = in.Get< CAssetId >(); + + bool globalTimeProvider = propCount > 7 ? in.Get< bool >() : true; + float fadeInTime = propCount > 8 ? in.Get< float >() : 1.f; + float fadeOutTime = propCount > 9 ? in.Get< float >() : 1.f; + CVisorParameters vParms = + propCount > 6 ? LoadVisorParameters(in) : CVisorParameters(0xf, false, false); + + bool thermalHeat = propCount > 10 ? in.Get< bool >() : false; + bool renderUnsorted = propCount > 11 ? in.Get< bool >() : false; + bool noSortThermal = propCount > 12 ? in.Get< bool >() : false; + float thermalMag = propCount > 13 ? in.Get< float >() : 1.f; + + uint xrayType = gpResourceFactory->GetResourceTypeById(xrayModel); + uint infraType = gpResourceFactory->GetResourceTypeById(infraModel); + + int flags = 0; + if (xrayType) + flags |= 1; + if (infraType) + flags |= 2; + + rstl::pair< CAssetId, CAssetId > xray = + (flags & 1) ? rstl::pair< CAssetId, CAssetId >(xrayModel, xraySkin) + : rstl::pair< CAssetId, CAssetId >(CAssetId(), CAssetId()); + + rstl::pair< CAssetId, CAssetId > infra = + (flags & 2) ? rstl::pair< CAssetId, CAssetId >(infraModel, infraSkin) + : rstl::pair< CAssetId, CAssetId >(CAssetId(), CAssetId()); + + return CActorParameters(lParms, sParms, xray, infra, vParms, globalTimeProvider, thermalHeat, + renderUnsorted, noSortThermal, fadeInTime, fadeOutTime, thermalMag); +} static CGrappleParameters LoadGrappleParameters(CInputStream& in) { in.ReadLong(); @@ -163,9 +254,9 @@ static CGrappleParameters LoadGrappleParameters(CInputStream& in) { return CGrappleParameters(a, b, c, d, e, f, g, h, i, j, k, l); } -static int LoadParameterFlags(CInputStream& in) { +static uint LoadParameterFlags(CInputStream& in) { int count = in.ReadLong(); - int ret = 0; + uint ret = 0; for (int i = 0; i < count; ++i) if (in.ReadBool()) ret |= 1 << i; @@ -310,6 +401,148 @@ static CFluidUVMotion LoadFluidUVMotion(CInputStream& in) { return CFluidUVMotion(a, (b * M_PIF) / 180.f - M_PIF, colorLayer, pattern1Layer, pattern2Layer); } +CEntity* ScriptLoader::LoadWater(CStateManager& mgr, CInputStream& in, int propCount, + const CEntityInfo& info) { + if (propCount != 63) + return nullptr; + + rstl::string name = mgr.HashInstanceName(in); + CVector3f position(in); + CVector3f extent(in); + CDamageInfo dInfo(in); + CVector3f orientedForce(in); + uint triggerFlags = in.ReadLong() | 0x7fc; + bool thermalCold = in.ReadBool(); + bool displaySurface = in.ReadBool(); + CAssetId patternMap1 = in.Get< CAssetId >(); + CAssetId patternMap2 = in.Get< CAssetId >(); + CAssetId colorMap = in.Get< CAssetId >(); + CAssetId bumpMap = in.Get< CAssetId >(); + CAssetId envMap = in.Get< CAssetId >(); + CAssetId envBumpMap = in.Get< CAssetId >(); + CVector3f bumpLightDir = in.Get< CVector3f >(); + if (!bumpLightDir.CanBeNormalized()) { + bumpLightDir = CVector3f(0.f, 0.f, -1.f); + } + + float bumpScale = 1.f / in.Get< float >(); + float morphInTime = in.Get< float >(); + float morphOutTime = in.Get< float >(); + bool active = in.ReadBool(); + CFluidPlane::EFluidType fluidType = CFluidPlane::EFluidType(in.ReadLong()); + bool b4 = in.ReadBool(); + float alpha = in.Get< float >(); + CFluidUVMotion uvMotion = LoadFluidUVMotion(in); + + float turbSpeed = in.Get< float >(); + float turbDistance = in.Get< float >(); + float turbFreqMax = in.Get< float >(); + float turbFreqMin = in.Get< float >(); + float turbPhaseMax = (M_2PIF * in.Get< float >()) / 360.f; + float turbPhaseMin = (M_2PIF * in.Get< float >()) / 360.f; + float turbAmplitudeMax = in.Get< float >(); + float turbAmplitudeMin = in.Get< float >(); + CColor splashColor(in); + CColor insideFogColor = in.Get< CColor >(); + CAssetId splashParticle1 = in.Get< CAssetId >(); + CAssetId splashParticle2 = in.Get< CAssetId >(); + CAssetId splashParticle3 = in.Get< CAssetId >(); + CAssetId visorRunoffParticle = in.Get< CAssetId >(); + CAssetId unmorphVisorRunoffParticle = in.Get< CAssetId >(); + int visorRunoffSfx = in.ReadLong(); + int unmorphVisorRunoffSfx = in.ReadLong(); + int splashSfx1 = in.ReadLong(); + int splashSfx2 = in.ReadLong(); + int splashSfx3 = in.ReadLong(); + float tileSize = in.Get< float >(); + uint tileSubdivisions = in.ReadLong(); + float specularMin = in.Get< float >(); + float specularMax = in.Get< float >(); + float reflectionSize = in.Get< float >(); + float rippleIntensity = in.Get< float >(); + float reflectionBlend = in.Get< float >(); + float fogBias = in.Get< float >(); + float fogMagnitude = in.Get< float >(); + float fogSpeed = in.Get< float >(); + CColor fogColor = in.Get< CColor >(); + CAssetId lightmap = in.Get< CAssetId >(); + float unitsPerLightmapTexel = in.Get< float >(); + float alphaInTime = in.Get< float >(); + float alphaOutTime = in.Get< float >(); + uint w21 = in.ReadLong(); + uint w22 = in.ReadLong(); + bool b5 = in.ReadBool(); + + int bitVal0 = 0; + int bitVal1 = 0; + uint* bitset = NULL; + + if (b5) { + bitVal0 = (short)in.ReadShort(); + bitVal1 = (short)in.ReadShort(); + int len = (bitVal0 * bitVal1 + 31) / 32; + bitset = rs_new uint[len]; + in.ReadBytes(bitset, len * 4); + } + + float hx = extent.GetX() * 0.5f; + float hy = extent.GetY() * 0.5f; + float hz = extent.GetZ() * 0.5f; + CAABox box = CAABox(CVector3f(-hx, -hy, -hz), CVector3f(hx, hy, hz)); + return rs_new CScriptWater( + mgr, mgr.AllocateUniqueId(), name, info, position, box, dInfo, orientedForce, triggerFlags, + thermalCold, displaySurface, patternMap1, patternMap2, colorMap, bumpMap, + bumpMap == kInvalidAssetId ? envMap : kInvalidAssetId, + bumpMap == kInvalidAssetId ? envBumpMap : kInvalidAssetId, kInvalidAssetId, bumpLightDir, + bumpScale, morphInTime, morphOutTime, active, fluidType, b4, alpha, uvMotion, turbSpeed, + turbDistance, turbFreqMax, turbFreqMin, turbPhaseMax, turbPhaseMin, turbAmplitudeMax, + turbAmplitudeMin, splashColor, insideFogColor, splashParticle1, splashParticle2, + splashParticle3, visorRunoffParticle, unmorphVisorRunoffParticle, visorRunoffSfx, + unmorphVisorRunoffSfx, splashSfx1, splashSfx2, splashSfx3, tileSize, tileSubdivisions, + specularMin, specularMax, reflectionSize, rippleIntensity, reflectionBlend, fogBias, + fogMagnitude, fogSpeed, fogColor, lightmap, unitsPerLightmapTexel, alphaInTime, alphaOutTime, + w21, w22, b5, bitVal0, bitVal1, bitset); +} + +CEntity* ScriptLoader::LoadSteam(CStateManager& mgr, CInputStream& in, int propCount, + const CEntityInfo& info) { + if (propCount != 11) + return nullptr; + + rstl::string name = mgr.HashInstanceName(in); + CVector3f position(in); + CVector3f extent(in); + CDamageInfo dInfo(in); + CVector3f orientedForce(in); + uint triggerFlags = in.ReadLong(); + bool active = in.ReadBool(); + CAssetId texture = in.ReadLong(); + float f1 = in.ReadFloat(); + float f2 = in.ReadFloat(); + float f3 = in.ReadFloat(); + float f4 = in.ReadFloat(); + bool b2 = in.ReadBool(); + + float hx = extent.GetX() * 0.5f; + float hy = extent.GetY() * 0.5f; + float hz = extent.GetZ() * 0.5f; + CAABox aabb = CAABox(CVector3f(-hx, -hy, -hz), CVector3f(hx, hy, hz)); + return rs_new CScriptSteam(mgr.AllocateUniqueId(), name, info, position, aabb, dInfo, + orientedForce, triggerFlags, active, texture, f1, f2, f3, f4, b2); +} + +CEntity* ScriptLoader::LoadRipple(CStateManager& mgr, CInputStream& in, int propCount, + const CEntityInfo& info) { + if (propCount != 4) + return nullptr; + + rstl::string name = mgr.HashInstanceName(in); + CVector3f center(in); + bool active = in.Get< bool >(); + float mag = in.Get< float >(); + return rs_new CScriptRipple(mgr.AllocateUniqueId(), name, info, center, active, mag); +} + CEntity* ScriptLoader::LoadSpawnPoint(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info) { if (propCount < 35) @@ -339,6 +572,11 @@ CEntity* ScriptLoader::LoadSpawnPoint(CStateManager& mgr, CInputStream& in, int morphed); } +CEntity* ScriptLoader::LoadJumpPoint(CStateManager& mgr, CInputStream& in, int propCount, + const CEntityInfo& info) { + return nullptr; +} + CEntity* ScriptLoader::LoadDock(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info) { if (propCount != 7) @@ -355,12 +593,87 @@ CEntity* ScriptLoader::LoadDock(CStateManager& mgr, CInputStream& in, int propCo 0, loadConnected); } -static CAABox GetCollisionBox(CStateManager& stateMgr, TAreaId id, const CVector3f& extent, - const CVector3f& offset) { - const CAABox box(0.5f * -extent.GetX() + offset.GetX(), 0.5f * -extent.GetY() + offset.GetY(), - 0.5f * -extent.GetZ() + offset.GetZ(), 0.5f * extent.GetX() + offset.GetX(), - 0.5f * extent.GetY() + offset.GetY(), 0.5f * extent.GetZ() + offset.GetZ()); - return box.GetTransformedAABox(stateMgr.GetWorld()->GetAreaAlways(id).GetTM().GetRotation()); +CEntity* ScriptLoader::LoadCameraHint(CStateManager& mgr, CInputStream& in, int propCount, + const CEntityInfo& info) { + if (propCount > 25) + return nullptr; + + SActorHead aHead(in, mgr); + bool active = in.Get< bool >(); + int priority = in.Get< int >(); + CBallCamera::EBallCameraBehaviour behaviour = CBallCamera::EBallCameraBehaviour(in.ReadLong()); + uint overrideFlags = LoadParameterFlags(in); + if (in.ReadBool()) + overrideFlags |= 0x400000; + float minDist = in.Get< float >(); + if (in.ReadBool()) + overrideFlags |= 0x800000; + float maxDist = in.Get< float >(); + if (in.ReadBool()) + overrideFlags |= 0x1000000; + float backwardsDist = in.Get< float >(); + if (in.ReadBool()) + overrideFlags |= 0x2000000; + CVector3f lookAtOffset(in); + if (in.ReadBool()) + overrideFlags |= 0x4000000; + CVector3f chaseLookAtOffset(in); + CVector3f ballToCam = CVector3f::Zero(); + ballToCam = CVector3f(in); + if (in.ReadBool()) + overrideFlags |= 0x8000000; + float fov = in.Get< float >(); + if (in.ReadBool()) + overrideFlags |= 0x10000000; + CRelAngle attitudeRange = CRelAngle::FromDegrees(in.ReadFloat()); + if (in.ReadBool()) + overrideFlags |= 0x20000000; + CRelAngle azimuthRange = CRelAngle::FromDegrees(in.ReadFloat()); + if (in.ReadBool()) + overrideFlags |= 0x40000000; + CRelAngle anglePerSecond = CRelAngle::FromDegrees(in.ReadFloat()); + float clampVelRange = in.Get< float >(); + CRelAngle clampRotRange = CRelAngle::FromDegrees(in.ReadFloat()); + if (in.ReadBool()) + overrideFlags |= 0x80000000; + float elevation = in.Get< float >(); + // TODO: the following 2 were swapped in Metaforce, so downstream symbols may be wrong + float clampVelTime = in.Get< float >(); + float interpolateTime = in.Get< float >(); + float controlInterpDur = in.Get< float >(); + + return rs_new CScriptCameraHint( + mgr.AllocateUniqueId(), aHead.x0_name, info, aHead.x10_transform, active, priority, behaviour, + overrideFlags, minDist, maxDist, backwardsDist, lookAtOffset, chaseLookAtOffset, ballToCam, + fov, attitudeRange.AsRadians(), azimuthRange.AsRadians(), anglePerSecond.AsRadians(), + clampVelRange, clampRotRange.AsRadians(), elevation, interpolateTime, clampVelTime, + controlInterpDur); +} + +CEntity* ScriptLoader::LoadPlayerHint(CStateManager& mgr, CInputStream& in, int propCount, + const CEntityInfo& info) { + if (propCount < 6) + return nullptr; + + SActorHead aHead(in, mgr); + bool active = in.Get< bool >(); + uint overrideFlags = LoadParameterFlags(in); + int priority = in.Get< int >(); + return rs_new CScriptPlayerHint(mgr.AllocateUniqueId(), aHead.x0_name, info, aHead.x10_transform, + active, priority, overrideFlags); +} + +CEntity* ScriptLoader::LoadPointOfInterest(CStateManager& mgr, CInputStream& in, int propCount, + const CEntityInfo& info) { + if (propCount != 6) + return nullptr; + + SActorHead aHead(in, mgr); + bool active = in.Get< bool >(); + CScannableParameters sParms = LoadScannableParameters(in); + float pointSize = in.ReadFloat(); + return rs_new CScriptPointOfInterest(mgr.AllocateUniqueId(), aHead.x0_name, info, + aHead.x10_transform, active, sParms, pointSize); } CEntity* ScriptLoader::LoadActor(CStateManager& mgr, CInputStream& in, int propCount, @@ -431,3 +744,597 @@ CEntity* ScriptLoader::LoadActor(CStateManager& mgr, CInputStream& in, int propC hInfo, dVuln, actParms, looping, active, shaderIdx, xrayAlpha, noThermalHotZ, castsShadow, scaleAdvancementDelta, materialFlag54); } + +CEntity* ScriptLoader::LoadPickup(CStateManager& mgr, CInputStream& in, int propCount, + const CEntityInfo& info) { + if (propCount != 18) + return nullptr; + + SScaledActorHead head(in, mgr); + CVector3f extent(in); + CVector3f offset(in); + CPlayerState::EItemType itemType = CPlayerState::EItemType(in.Get< int >()); + int capacity = in.Get< int >(); + int amount = in.Get< int >(); + float possibility = in.Get< float >(); + float lifeTime = in.Get< float >(); + float fadeInTime = in.Get< float >(); + CAssetId staticModel = in.Get< CAssetId >(); + CAnimationParameters aParms = LoadAnimationParameters(in); + CActorParameters actParms = LoadActorParameters(in); + bool active = in.Get< bool >(); + float startDelay = in.Get< float >(); + CAssetId pickupEffect = in.Get< CAssetId >(); + + FourCC staticModelType = gpResourceFactory->GetResourceTypeById(staticModel); + FourCC animType = gpResourceFactory->GetResourceTypeById(aParms.GetACSFile()); + if (staticModelType == 0 && animType == 0) + return nullptr; + + const CTransform4f& xf = head.x0_actorHead.x10_transform; + CAABox aabb = GetCollisionBox(mgr, info.GetAreaId(), extent, offset); + + bool negativeExtent; + if (extent.GetX() < 0.f) + negativeExtent = true; + else if (extent.GetY() < 0.f) + negativeExtent = true; + else if (extent.GetZ() < 0.f) + negativeExtent = true; + else + negativeExtent = false; + + CModelData data(CModelData::CModelDataNull()); + if (animType == 'ANCS') { + data = CModelData(CAnimRes(aParms.GetACSFile(), aParms.GetCharacter(), head.x40_scale, + aParms.GetInitialAnimation(), true)); + } else { + data = CModelData(CStaticRes(staticModel, head.x40_scale)); + } + + if (extent == CVector3f::Zero() || negativeExtent) + aabb = data.GetBounds(xf.GetRotation()); + + return rs_new CScriptPickup(mgr.AllocateUniqueId(), head.x0_actorHead.x0_name, info, xf, data, + actParms, aabb, itemType, amount, capacity, pickupEffect, possibility, + lifeTime, fadeInTime, startDelay, active); +} + +CEntity* ScriptLoader::LoadEnemy(CStateManager& mgr, CInputStream& in, int propCount, + const CEntityInfo& info) { + return nullptr; +} + +CEntity* ScriptLoader::LoadRepulsor(CStateManager& mgr, CInputStream& in, int propCount, + const CEntityInfo& info) { + if (propCount != 4) + return nullptr; + + rstl::string name = mgr.HashInstanceName(in); + CVector3f center(in); + bool active = in.Get< bool >(); + float radius = in.Get< float >(); + return rs_new CRepulsor(mgr.AllocateUniqueId(), active, name, info, center, radius); +} + +CEntity* ScriptLoader::LoadTimer(CStateManager& mgr, CInputStream& in, int propCount, + const CEntityInfo& info) { + if (propCount != 6) + return nullptr; + + rstl::string name = mgr.HashInstanceName(in); + float startTime = in.Get< float >(); + float maxRandDelay = in.Get< float >(); + bool loop = in.Get< bool >(); + bool autoStart = in.Get< bool >(); + bool active = in.Get< bool >(); + return rs_new CScriptTimer(mgr.AllocateUniqueId(), name, info, startTime, maxRandDelay, loop, + autoStart, active); +} + +CEntity* ScriptLoader::LoadCounter(CStateManager& mgr, CInputStream& in, int propCount, + const CEntityInfo& info) { + if (propCount != 5) + return nullptr; + + rstl::string name = mgr.HashInstanceName(in); + int initial = in.Get< int >(); + int max = in.Get< int >(); + bool autoReset = in.Get< bool >(); + bool active = in.Get< bool >(); + return rs_new CScriptCounter(mgr.AllocateUniqueId(), name, info, initial, max, autoReset, active); +} + +CEntity* ScriptLoader::LoadRelay(CStateManager& mgr, CInputStream& in, int propCount, + const CEntityInfo& info) { + bool valid = false; + if (propCount >= 3 && propCount <= 4) + valid = true; + if (!valid) + return nullptr; + + rstl::string name = mgr.HashInstanceName(in); + bool b1 = in.Get< bool >(); + bool b2 = in.Get< bool >(); + bool b3 = false; + if (propCount > 3) + b3 = in.Get< bool >(); + return rs_new CScriptMemoryRelay(mgr.AllocateUniqueId(), name, info, b1, b2, b3); +} + +CEntity* ScriptLoader::LoadRandomRelay(CStateManager& mgr, CInputStream& in, int propCount, + const CEntityInfo& info) { + if (propCount != 5) + return nullptr; + + rstl::string name = mgr.HashInstanceName(in); + int sendSetSize = in.Get< int >(); + int sendSetVariance = in.Get< int >(); + bool percentSize = in.ReadBool(); + bool active = in.ReadBool(); + return rs_new CScriptRandomRelay(mgr.AllocateUniqueId(), name, info, sendSetSize, sendSetVariance, + percentSize, active); +} + +CEntity* ScriptLoader::LoadSwitch(CStateManager& mgr, CInputStream& in, int propCount, + const CEntityInfo& info) { + if (propCount != 4) + return nullptr; + + rstl::string name = mgr.HashInstanceName(in); + bool active = in.Get< bool >(); + bool b2 = in.Get< bool >(); + bool b3 = in.Get< bool >(); + return rs_new CScriptSwitch(mgr.AllocateUniqueId(), name, info, active, b2, b3); +} + +CEntity* ScriptLoader::LoadPlayerStateChange(CStateManager& mgr, CInputStream& in, int propCount, + const CEntityInfo& info) { + if (propCount != 7) + return nullptr; + + rstl::string name = mgr.HashInstanceName(in); + bool active = in.Get< bool >(); + int itemType = in.Get< int >(); + int itemCount = in.Get< int >(); + int itemCapacity = in.Get< int >(); + int ctrl = in.Get< int >(); + int ctrlCmdOpt = in.Get< int >(); + return rs_new CScriptPlayerStateChange( + mgr.AllocateUniqueId(), name, info, active, itemType, itemCount, itemCapacity, + CScriptPlayerStateChange::EControl(ctrl), + CScriptPlayerStateChange::EControlCommandOption(ctrlCmdOpt)); +} + +CEntity* ScriptLoader::LoadDockAreaChange(CStateManager& mgr, CInputStream& in, int propCount, + const CEntityInfo& info) { + if (propCount != 3) + return nullptr; + + rstl::string name = mgr.HashInstanceName(in); + int dock = in.Get< int >(); + bool active = in.Get< bool >(); + return rs_new CScriptDockAreaChange(mgr.AllocateUniqueId(), name, info, dock, active); +} + +CEntity* ScriptLoader::LoadActorRotate(CStateManager& mgr, CInputStream& in, int propCount, + const CEntityInfo& info) { + if (propCount != 6) + return nullptr; + + rstl::string name = mgr.HashInstanceName(in); + CVector3f rotation(in); + float scale = in.Get< float >(); + bool updateActors = in.Get< bool >(); + bool updateOnCreation = in.Get< bool >(); + bool active = in.Get< bool >(); + return rs_new CScriptActorRotate(mgr.AllocateUniqueId(), name, info, rotation, scale, + updateActors, updateOnCreation, active); +} + +CEntity* ScriptLoader::LoadTargetingPoint(CStateManager& mgr, CInputStream& in, int propCount, + const CEntityInfo& info) { + if (propCount != 4) + return nullptr; + + SActorHead aHead(in, mgr); + bool active = in.Get< bool >(); + return rs_new CScriptTargetingPoint(mgr.AllocateUniqueId(), aHead.x0_name, info, + aHead.x10_transform, active); +} + +CEntity* ScriptLoader::LoadPickupGenerator(CStateManager& mgr, CInputStream& in, int propCount, + const CEntityInfo& info) { + if (propCount != 4) + return nullptr; + + rstl::string name = mgr.HashInstanceName(in); + CVector3f pos(in); + bool active = in.Get< bool >(); + float frequency = in.Get< float >(); + return rs_new CScriptPickupGenerator(mgr.AllocateUniqueId(), name, info, pos, frequency, active); +} + +CEntity* ScriptLoader::LoadAiJumpPoint(CStateManager& mgr, CInputStream& in, int propCount, + const CEntityInfo& info) { + if (propCount != 5) + return nullptr; + + SActorHead aHead(in, mgr); + bool active = in.Get< bool >(); + float apex = in.ReadFloat(); + return rs_new CScriptAiJumpPoint(mgr.AllocateUniqueId(), aHead.x0_name, info, aHead.x10_transform, + active, apex); +} + +CEntity* ScriptLoader::LoadDebugCameraWaypoint(CStateManager& mgr, CInputStream& in, int propCount, + const CEntityInfo& info) { + if (propCount != 4) + return nullptr; + + SActorHead aHead(in, mgr); + uint w1 = in.Get< uint >(); + return rs_new CScriptDebugCameraWaypoint(mgr.AllocateUniqueId(), aHead.x0_name, info, + aHead.x10_transform, w1); +} + +CEntity* ScriptLoader::LoadSpiderBallAttractionSurface(CStateManager& mgr, CInputStream& in, + int propCount, const CEntityInfo& info) { + if (propCount != 5) + return nullptr; + + SScaledActorHead aHead(in, mgr); + bool active = in.Get< bool >(); + return rs_new CScriptSpiderBallAttractionSurface( + mgr.AllocateUniqueId(), aHead.x0_actorHead.x0_name, info, aHead.x0_actorHead.x10_transform, + aHead.x40_scale, active); +} + +CEntity* ScriptLoader::LoadAreaAttributes(CStateManager& mgr, CInputStream& in, int propCount, + const CEntityInfo& info) { + if (propCount != 9) + return nullptr; + + int load = in.ReadLong(); + if (load != 1) + return nullptr; + + bool showSkybox = in.ReadBool(); + EEnvFxType fxType = EEnvFxType(in.ReadLong()); + float envFxDensity = in.ReadFloat(); + float thermalHeat = in.ReadFloat(); + float xrayFogDistance = in.ReadFloat(); + float worldLightingLevel = in.ReadFloat(); + CAssetId skybox = in.Get< CAssetId >(); + EPhazonType phazonType = EPhazonType(in.ReadLong()); + + return rs_new CScriptAreaAttributes(mgr.AllocateUniqueId(), info, showSkybox, fxType, + envFxDensity, thermalHeat, xrayFogDistance, + worldLightingLevel, skybox, phazonType); +} + +CEntity* ScriptLoader::LoadDistanceFog(CStateManager& mgr, CInputStream& in, int propCount, + const CEntityInfo& info) { + if (propCount != 8) + return nullptr; + + rstl::string name = mgr.HashInstanceName(in); + uint mode = in.Get< uint >(); + CColor color(in); + CVector2f range(in); + float colorDelta = in.Get< float >(); + CVector2f rangeDelta(in); + bool expl = in.Get< bool >(); + bool active = in.Get< bool >(); + + ERglFogMode fogMode; + switch (mode) { + case 0: + fogMode = kRFM_None; + break; + case 1: + fogMode = kRFM_PerspLin; + break; + case 2: + fogMode = kRFM_PerspExp; + break; + case 3: + fogMode = kRFM_PerspExp2; + break; + case 4: + fogMode = kRFM_PerspRevExp; + break; + case 5: + fogMode = kRFM_PerspRevExp2; + break; + default: + return nullptr; + } + + return rs_new CScriptDistanceFog(mgr.AllocateUniqueId(), name, info, fogMode, color, range, + colorDelta, rangeDelta, expl, active, 0.f, 0.f, 0.f, 0.f); +} + +CEntity* ScriptLoader::LoadMazeNode(CStateManager& mgr, CInputStream& in, int propCount, + const CEntityInfo& info) { + if (propCount != 10) + return nullptr; + + SActorHead aHead(in, mgr); + bool active = in.Get< bool >(); + int col = in.Get< int >(); + int row = in.Get< int >(); + int side = in.Get< int >(); + CVector3f actorPos(in); + CVector3f triggerPos(in); + CVector3f effectPos(in); + + return rs_new CScriptMazeNode(mgr.AllocateUniqueId(), aHead.x0_name, info, aHead.x10_transform, + active, col, row, side, actorPos, triggerPos, effectPos); +} + +CEntity* ScriptLoader::LoadWorldLightFader(CStateManager& mgr, CInputStream& in, int propCount, + const CEntityInfo& info) { + if (propCount != 4) + return nullptr; + + rstl::string name = mgr.HashInstanceName(in); + bool active = in.Get< bool >(); + float f1 = in.Get< float >(); + float f2 = in.Get< float >(); + + return rs_new CScriptDistanceFog(mgr.AllocateUniqueId(), name, info, kRFM_None, CColor::Black(), + CVector2f(0.f, 0.f), 0.f, CVector2f(0.f, 0.f), false, active, + 0.f, 0.f, f1, f2); +} + +CEntity* ScriptLoader::LoadThermalHeatFader(CStateManager& mgr, CInputStream& in, int propCount, + const CEntityInfo& info) { + if (propCount != 4) + return nullptr; + + rstl::string name = mgr.HashInstanceName(in); + bool active = in.Get< bool >(); + float fadedLevel = in.Get< float >(); + float initialLevel = in.Get< float >(); + + return rs_new CScriptDistanceFog(mgr.AllocateUniqueId(), name, info, kRFM_None, CColor::Black(), + CVector2f(0.f, 0.f), 0.f, CVector2f(0.f, 0.f), false, active, + fadedLevel, initialLevel, 0.f, 0.f); +} + +CEntity* ScriptLoader::LoadRumbleEffect(CStateManager& mgr, CInputStream& in, int propCount, + const CEntityInfo& info) { + if (propCount != 6) + return nullptr; + + rstl::string name = mgr.HashInstanceName(in); + CVector3f position(in); + bool active = in.Get< bool >(); + float f1 = in.Get< float >(); + int w1 = in.Get< int >(); + uint pFlags = LoadParameterFlags(in); + + return rs_new CScriptSpecialFunction( + mgr.AllocateUniqueId(), name, info, + ConvertEditorEulerToTransform4f(CVector3f::Zero(), position), + CScriptSpecialFunction::kSF_RumbleEffect, rstl::string_l(""), f1, float(w1), pFlags, 0.f, + CVector3f::Zero(), CColor::Black(), active, CDamageInfo(), -1, -1, CPlayerState::kIT_Invalid, + CSfxManager::kInternalInvalidSfxId, CSfxManager::kInternalInvalidSfxId, + CSfxManager::kInternalInvalidSfxId); +} + +CEntity* ScriptLoader::LoadEnvFxDensityController(CStateManager& mgr, CInputStream& in, + int propCount, const CEntityInfo& info) { + if (propCount != 4) + return nullptr; + + rstl::string name = mgr.HashInstanceName(in); + bool active = in.Get< bool >(); + float density = in.Get< float >(); + int maxDensityDeltaSpeed = in.Get< int >(); + + return rs_new CScriptSpecialFunction( + mgr.AllocateUniqueId(), name, info, CTransform4f::Identity(), + CScriptSpecialFunction::kSF_EnvFxDensityController, rstl::string_l(""), density, + float(maxDensityDeltaSpeed), 0.f, 0.f, CVector3f::Zero(), CColor::Black(), active, + CDamageInfo(), -1, -1, CPlayerState::kIT_Invalid, CSfxManager::kInternalInvalidSfxId, + CSfxManager::kInternalInvalidSfxId, CSfxManager::kInternalInvalidSfxId); +} + +CEntity* ScriptLoader::LoadScriptCameraPitchVolume(CStateManager& mgr, CInputStream& in, + int propCount, const CEntityInfo& info) { + if (propCount != 8) + return nullptr; + + SScaledActorHead aHead(in, mgr); + bool active = in.Get< bool >(); + float upPitch = in.Get< float >(); + float downPitch = in.Get< float >(); + float scale = in.Get< float >(); + + return rs_new CScriptCameraPitchVolume(mgr.AllocateUniqueId(), active, aHead.x0_actorHead.x0_name, + info, aHead.x40_scale, aHead.x0_actorHead.x10_transform, + CRelAngle::FromDegrees(upPitch), + CRelAngle::FromDegrees(downPitch), scale); +} + +CEntity* ScriptLoader::LoadRadialDamage(CStateManager& mgr, CInputStream& in, int propCount, + const CEntityInfo& info) { + if (propCount != 5) + return nullptr; + + rstl::string name = mgr.HashInstanceName(in); + CVector3f center(in); + bool active = in.Get< bool >(); + CDamageInfo dInfo = in.Get< CDamageInfo >(); + float radius = in.Get< float >(); + + return rs_new CScriptSpecialFunction( + mgr.AllocateUniqueId(), name, info, + ConvertEditorEulerToTransform4f(CVector3f::Zero(), center), + CScriptSpecialFunction::kSF_RadialDamage, rstl::string_l(""), radius, 0.f, 0.f, 0.f, + CVector3f::Zero(), CColor::Black(), active, dInfo, -1, -1, CPlayerState::kIT_Invalid, + CSfxManager::kInternalInvalidSfxId, CSfxManager::kInternalInvalidSfxId, + CSfxManager::kInternalInvalidSfxId); +} + +CEntity* ScriptLoader::LoadStreamedMusic(CStateManager& mgr, CInputStream& in, int propCount, + const CEntityInfo& info) { + if (propCount != 9) + return nullptr; + + rstl::string name = mgr.HashInstanceName(in); + bool active = in.Get< bool >(); + rstl::string fileName(in); + bool noStopOnDeactivate = in.Get< bool >(); + float fadeIn = in.Get< float >(); + float fadeOut = in.Get< float >(); + uint volume = in.Get< uint >(); + bool loop = in.Get< uint >() == 0; + bool music = in.Get< bool >(); + + return rs_new CScriptStreamedMusic(mgr.AllocateUniqueId(), info, name, active, fileName, + noStopOnDeactivate, fadeIn, fadeOut, volume, loop, music); +} + +CEntity* ScriptLoader::LoadMidi(CStateManager& mgr, CInputStream& in, int propCount, + const CEntityInfo& info) { + if (propCount != 6) + return nullptr; + + rstl::string name = mgr.HashInstanceName(in); + bool active = in.Get< bool >(); + uint csng = in.Get< uint >(); + float fadeIn = in.Get< float >(); + float fadeOut = in.Get< float >(); + uint vol = in.Get< uint >(); + return rs_new CScriptMidi(mgr.AllocateUniqueId(), info, name, active, csng, fadeIn, fadeOut, vol); +} + +CEntity* ScriptLoader::LoadVisorGoo(CStateManager& mgr, CInputStream& in, int propCount, + const CEntityInfo& info) { + if (propCount != 11) + return nullptr; + + rstl::string name = mgr.HashInstanceName(in); + CTransform4f xf = CTransform4f::Translate(CVector3f(in)); + CAssetId particle = in.Get< CAssetId >(); + CAssetId electric = in.Get< CAssetId >(); + float minDist = in.Get< float >(); + float maxDist = in.Get< float >(); + float nearProb = in.Get< float >(); + float farProb = in.Get< float >(); + CColor color(in); + int sfx = in.Get< int >(); + bool forceShow = in.Get< bool >(); + + if (particle == kInvalidAssetId && electric == kInvalidAssetId) + return nullptr; + return rs_new CScriptVisorGoo(mgr.AllocateUniqueId(), name, info, xf, particle, electric, minDist, + maxDist, nearProb, farProb, color, sfx, forceShow, false); +} + +CEntity* ScriptLoader::LoadSaveStation(CStateManager& mgr, CInputStream& in, int propCount, + const CEntityInfo& info) { + return nullptr; +} + +CEntity* ScriptLoader::LoadControllerAction(CStateManager& mgr, CInputStream& in, int propCount, + const CEntityInfo& info) { + if (!(propCount >= 4 && propCount <= 6 ? true : false)) + return nullptr; + + rstl::string name = mgr.HashInstanceName(in); + bool active = in.Get< bool >(); + CScriptControllerAction::ECommands w1 = CScriptControllerAction::ECommands(in.ReadLong()); + bool b1 = propCount >= 6 ? in.Get< bool >() : false; + uint w2 = propCount >= 6 ? in.Get< uint >() : 0; + bool b2 = in.Get< bool >(); + + return rs_new CScriptControllerAction(mgr.AllocateUniqueId(), name, info, active, w1, b1, w2, b2); +} + +CEntity* ScriptLoader::LoadEMPulse(CStateManager& mgr, CInputStream& in, int propCount, + const CEntityInfo& info) { + if (propCount != 12) + return nullptr; + + SActorHead aHead(in, mgr); + bool active = in.Get< bool >(); + float f1 = in.Get< float >(); + float f2 = in.Get< float >(); + float f3 = in.Get< float >(); + float f4 = in.Get< float >(); + float f5 = in.Get< float >(); + float f6 = in.Get< float >(); + float f7 = in.Get< float >(); + CAssetId particleId = in.Get< CAssetId >(); + + return rs_new CScriptEMPulse(mgr.AllocateUniqueId(), aHead.x0_name, info, aHead.x10_transform, + active, f1, f2, f3, f4, f5, f6, f7, particleId); +} + +CEntity* ScriptLoader::LoadBallTrigger(CStateManager& mgr, CInputStream& in, int propCount, + const CEntityInfo& info) { + if (propCount != 9) + return nullptr; + + rstl::string name(in); + CVector3f pos(in); + CVector3f scale(in); + bool b1 = in.Get< bool >(); + float f1 = in.Get< float >(); + float f2 = in.Get< float >(); + float f3 = in.Get< float >(); + CVector3f vec = in.Get< CVector3f >(); + bool b2 = in.Get< bool >(); + return rs_new CScriptBallTrigger(mgr.AllocateUniqueId(), name, info, pos, scale, b1, f1, f2, f3, + vec, b2); +} + +CEntity* ScriptLoader::LoadRoomAcoustics(CStateManager& mgr, CInputStream& in, int propCount, + const CEntityInfo& info) { + if (propCount != 32) + return nullptr; + + rstl::string name = mgr.HashInstanceName(in); + bool active = in.Get< bool >(); + uint volScale = in.Get< uint >(); + bool revHi = in.Get< bool >(); + bool revHiDis = in.Get< bool >(); + float revHiColoration = in.Get< float >(); + float revHiMix = in.Get< float >(); + float revHiTime = in.Get< float >(); + float revHiDamping = in.Get< float >(); + float revHiPreDelay = in.Get< float >(); + float revHiCrosstalk = in.Get< float >(); + bool chorus = in.Get< bool >(); + float baseDelay = in.Get< float >(); + float variation = in.Get< float >(); + float period = in.Get< float >(); + bool revStd = in.Get< bool >(); + bool revStdDis = in.Get< bool >(); + float revStdColoration = in.Get< float >(); + float revStdMix = in.Get< float >(); + float revStdTime = in.Get< float >(); + float revStdDamping = in.Get< float >(); + float revStdPreDelay = in.Get< float >(); + bool delay = in.Get< bool >(); + uint delayL = in.Get< uint >(); + uint delayR = in.Get< uint >(); + uint delayS = in.Get< uint >(); + uint feedbackL = in.Get< uint >(); + uint feedbackR = in.Get< uint >(); + uint feedbackS = in.Get< uint >(); + uint outputL = in.Get< uint >(); + uint outputR = in.Get< uint >(); + uint outputS = in.Get< uint >(); + + return rs_new CScriptRoomAcoustics( + mgr.AllocateUniqueId(), name, info, active, volScale, revHi, revHiDis, revHiColoration, + revHiMix, revHiTime, revHiDamping, revHiPreDelay, revHiCrosstalk, chorus, baseDelay, + variation, period, revStd, revStdDis, revStdColoration, revStdMix, revStdTime, revStdDamping, + revStdPreDelay, delay, delayL, delayR, delayS, feedbackL, feedbackR, feedbackS, outputL, + outputR, outputS); +} diff --git a/src/MetroidPrime/ScriptObjects/CScriptBallTrigger.cpp b/src/MetroidPrime/ScriptObjects/CScriptBallTrigger.cpp index 38f51c9ba..78ddffdf8 100644 --- a/src/MetroidPrime/ScriptObjects/CScriptBallTrigger.cpp +++ b/src/MetroidPrime/ScriptObjects/CScriptBallTrigger.cpp @@ -15,7 +15,7 @@ static CAABox calculate_ball_aabox() { CScriptBallTrigger::CScriptBallTrigger(const TUniqueId uid, const rstl::string& name, const CEntityInfo& info, const CVector3f& pos, const CVector3f& scale, const bool active, const float f1, - const float f2, const float f3, const CVector3f& vec, + const float f2, const float f3, CVector3f vec, const bool b2) : CScriptTrigger(uid, name, info, pos, calculate_ball_aabox(), CDamageInfo(CWeaponMode::Power(), 0.f, 0.f, 0.f), CVector3f::Zero(), diff --git a/src/MetroidPrime/ScriptObjects/CScriptDistanceFog.cpp b/src/MetroidPrime/ScriptObjects/CScriptDistanceFog.cpp index cd2c4fe5f..7fda97474 100644 --- a/src/MetroidPrime/ScriptObjects/CScriptDistanceFog.cpp +++ b/src/MetroidPrime/ScriptObjects/CScriptDistanceFog.cpp @@ -10,8 +10,8 @@ CScriptDistanceFog::CScriptDistanceFog(TUniqueId uid, const rstl::string& name, const CEntityInfo& info, ERglFogMode mode, const CColor& color, const CVector2f& range, - float colorDelta, const CVector2f& rangeDelta, bool expl, - bool active, float thermalTarget, float thermalSpeed, + float colorDelta, CVector2f rangeDelta, const bool expl, + const bool active, float thermalTarget, float thermalSpeed, float xrayTarget, float xraySpeed) : CEntity(uid, info, active, name) , x34_mode(mode) diff --git a/src/MetroidPrime/ScriptObjects/CScriptWater.cpp b/src/MetroidPrime/ScriptObjects/CScriptWater.cpp new file mode 100644 index 000000000..ece51532a --- /dev/null +++ b/src/MetroidPrime/ScriptObjects/CScriptWater.cpp @@ -0,0 +1,747 @@ +#include "MetroidPrime/ScriptObjects/CScriptWater.hpp" + +#include "MetroidPrime/CActorLights.hpp" +#include "MetroidPrime/CFluidPlaneCPU.hpp" +#include "MetroidPrime/CFluidUVMotion.hpp" +#include "MetroidPrime/CGameArea.hpp" +#include "MetroidPrime/CStateManager.hpp" +#include "MetroidPrime/CWorld.hpp" +#include "MetroidPrime/Cameras/CCameraManager.hpp" +#include "MetroidPrime/Cameras/CGameCamera.hpp" + +#include "MetroidPrime/CRipple.hpp" +#include "MetroidPrime/Player/CPlayerState.hpp" + +#include "Collision/CMaterialFilter.hpp" +#include "Collision/CRayCastResult.hpp" +#include "Kyoto/Alloc/CMemory.hpp" +#include "Kyoto/Audio/CSfxManager.hpp" +#include "Kyoto/CSimplePool.hpp" +#include "Kyoto/Graphics/CColor.hpp" +#include "Kyoto/Graphics/CGraphics.hpp" +#include "Kyoto/Math/CAABox.hpp" +#include "Kyoto/Math/CMath.hpp" +#include "Kyoto/Math/CPlane.hpp" +#include "Kyoto/Math/CTransform4f.hpp" +#include "Kyoto/Math/CVector3f.hpp" +#include "Kyoto/Math/CloseEnough.hpp" +#include "Kyoto/Particles/CGenDescription.hpp" +#include "MetaRender/CCubeRenderer.hpp" + +#include "rstl/math.hpp" + +#include "MetroidPrime/TCastTo.hpp" + +static float kMaxRayLength = 120.f; +static int kMaxTilesPerPatch = 7; + +const float CScriptWater::kSplashScales[6] = { + 1.0f, 3.0f, 0.709f, 1.19f, 0.709f, 1.0f, +}; + +CScriptWater::CScriptWater( + CStateManager& mgr, TUniqueId uid, const rstl::string& name, const CEntityInfo& info, + const CVector3f& pos, const CAABox& box, const CDamageInfo& dInfo, + const CVector3f& orientedForce, uint triggerFlags, bool thermalCold, bool displaySurface, + uint patternMap1, uint patternMap2, uint colorMap, uint bumpMap, uint envMap, uint envBumpMap, + uint unusedMap, const CVector3f& bumpLightDir, float bumpScale, float morphInTime, + float morphOutTime, bool active, CFluidPlane::EFluidType fluidType, bool b4, float alpha, + const CFluidUVMotion& uvMotion, float turbSpeed, float turbDistance, float turbFreqMax, + float turbFreqMin, float turbPhaseMax, float turbPhaseMin, float turbAmplitudeMax, + float turbAmplitudeMin, const CColor& splashColor, const CColor& insideFogColor, + uint splashParticle1, uint splashParticle2, uint splashParticle3, uint visorRunoffParticle, + uint unmorphVisorRunoffParticle, int visorRunoffSfx, int unmorphVisorRunoffSfx, int splashSfx1, + int splashSfx2, int splashSfx3, float tileSize, uint tileSubdivisions, float specularMin, + float specularMax, float reflectionSize, float rippleIntensity, float reflectionBlend, + float fogBias, float fogMagnitude, float fogSpeed, const CColor& fogColor, uint lightmap, + float unitsPerLightmapTexel, float alphaInTime, float alphaOutTime, uint w21, uint w22, bool b5, + int bitVal0, int bitVal1, const uint* bitset) +: CScriptTrigger(uid, name, info, pos, box, dInfo, orientedForce, triggerFlags, active, false, + false) +, x1b4_fluidPlane(NULL) +, x1b8_positionMorphed(pos) +, x1c4_extentMorphed(box.GetWidth(), box.GetHeight(), box.GetDepth()) +, x1d0_morphInTime(morphInTime) +, x1d4_positionOrig(pos) +, x1e0_extentOrig(box.GetWidth(), box.GetHeight(), box.GetDepth()) +, x1ec_damageOrig(dInfo.GetDamage()) +, x1f0_damageMorphed(dInfo.GetDamage()) +, x1f4_morphOutTime(morphOutTime) +, x1f8_morphFactor(0.f) +, x214_fogBias(fogBias) +, x218_fogMagnitude(fogMagnitude) +, x21c_origFogBias(fogBias) +, x220_origFogMagnitude(fogMagnitude) +, x224_fogSpeed(fogSpeed) +, x228_fogColor(fogColor) +, x22c_splashParticle1Id(splashParticle1) +, x230_splashParticle2Id(splashParticle2) +, x234_splashParticle3Id(splashParticle3) +, x238_visorRunoffParticleId(visorRunoffParticle) +, x24c_unmorphVisorRunoffParticleId(unmorphVisorRunoffParticle) +, x260_visorRunoffSfx(CSfxManager::TranslateSFXID(visorRunoffSfx)) +, x262_unmorphVisorRunoffSfx(CSfxManager::TranslateSFXID(unmorphVisorRunoffSfx)) +, x2a4_splashColor(splashColor) +, x2a8_insideFogColor(insideFogColor) +, x2ac_alphaInTime(alphaInTime) +, x2b0_alphaOutTime(alphaOutTime) +, x2b4_alphaInRecip(alphaInTime != 0.f ? 1.f / alphaInTime : 0.f) +, x2b8_alphaOutRecip(alphaOutTime != 0.f ? 1.f / alphaOutTime : 0.f) +, x2bc_alpha(alpha) +, x2c0_tileSize(tileSize) +, x2c4_gridDimX( + (int)CMath::FloorF((x2c0_tileSize + GetTriggerBoundsWR().GetWidth() - 0.01f) / x2c0_tileSize)) +, x2c8_gridDimY((int)CMath::FloorF((x2c0_tileSize + GetTriggerBoundsWR().GetHeight() - 0.01f) / + x2c0_tileSize)) +, x2cc_gridCellCount((x2c4_gridDimX + 1) * (x2c8_gridDimY + 1)) +, x2d0_patchDimX(0) +, x2d4_patchDimY(0) +, x2d8_tileIntersects(NULL) +, x2dc_vertIntersects(NULL) +, x2e0_patchIntersects(NULL) +, x2e4_computedGridCellCount(0) +, x2e8_24_b4(b4) +, x2e8_25_morphIn(false) +, x2e8_26_morphing(false) +, x2e8_27_allowRender(displaySurface) +, x2e8_28_recomputeClipping(true) +, x2e8_29_alphaIn(false) +, x2e8_30_alphaOut(false) { + x1b4_fluidPlane = rs_new CFluidPlaneCPU( + patternMap1, patternMap2, colorMap, bumpMap, envMap, envBumpMap, unitsPerLightmapTexel, + lightmap, tileSubdivisions, fluidType, tileSize, bumpLightDir, alpha, uvMotion, bumpScale, + turbSpeed, turbDistance, turbFreqMax, turbFreqMin, turbPhaseMax, turbPhaseMin, + turbAmplitudeMax, turbAmplitudeMin, specularMin, specularMax, reflectionBlend, reflectionSize, + rippleIntensity); + + delete const_cast< uint* >(bitset); + + x264_splashEffects.push_back(rstl::optional_object< TLockedToken< CGenDescription > >()); + x264_splashEffects.push_back(rstl::optional_object< TLockedToken< CGenDescription > >()); + x264_splashEffects.push_back(rstl::optional_object< TLockedToken< CGenDescription > >()); + + if (x22c_splashParticle1Id != kInvalidAssetId) { + x264_splashEffects[0] = TLockedToken< CGenDescription >(TToken< CGenDescription >( + gpSimplePool->GetObj(SObjectTag('PART', x22c_splashParticle1Id)))); + } + if (x230_splashParticle2Id != kInvalidAssetId) { + x264_splashEffects[1] = TLockedToken< CGenDescription >(TToken< CGenDescription >( + gpSimplePool->GetObj(SObjectTag('PART', x230_splashParticle2Id)))); + } + if (x234_splashParticle3Id != kInvalidAssetId) { + x264_splashEffects[2] = TLockedToken< CGenDescription >(TToken< CGenDescription >( + gpSimplePool->GetObj(SObjectTag('PART', x234_splashParticle3Id)))); + } + + if (x238_visorRunoffParticleId != kInvalidAssetId) { + x23c_visorRunoffEffect = TLockedToken< CGenDescription >(TToken< CGenDescription >( + gpSimplePool->GetObj(SObjectTag('PART', x238_visorRunoffParticleId)))); + } + if (x24c_unmorphVisorRunoffParticleId != kInvalidAssetId) { + x250_unmorphVisorRunoffEffect = TLockedToken< CGenDescription >(TToken< CGenDescription >( + gpSimplePool->GetObj(SObjectTag('PART', x24c_unmorphVisorRunoffParticleId)))); + } + + x298_splashSounds.push_back(CSfxManager::TranslateSFXID(splashSfx1)); + x298_splashSounds.push_back(CSfxManager::TranslateSFXID(splashSfx2)); + x298_splashSounds.push_back(CSfxManager::TranslateSFXID(splashSfx3)); + + SetCalculateLighting(true); + if (lightmap != kInvalidAssetId) { + ActorLights()->SetMaxAreaLights(0); + ActorLights()->SetInArea(false); + } + ActorLights()->SetMaxDynamicLights(4); + ActorLights()->SetCastShadows(false); + ActorLights()->SetAmbienceGenerated(false); + ActorLights()->SetFindNearestDynamicLights(true); + x148_24_detectCamera = true; + CalculateRenderBounds(); + SetThermalFlags(thermalCold ? kTF_Hot : kTF_Cold); + if (!GetActive()) { + x2bc_alpha = 0.f; + x214_fogBias = 0.f; + x218_fogMagnitude = 0.f; + } + SetupGrid(true); +} + +int CScriptWater::GetSplashIndex(float scale) const { + scale *= 3.f; + int idx = static_cast< int >(scale); + if (idx >= 3) { + idx -= 1; + } + return idx; +} + +const rstl::optional_object< TLockedToken< CGenDescription > >& +CScriptWater::GetSplashEffect(float scale) const { + return x264_splashEffects[GetSplashIndex(scale)]; +} + +int CScriptWater::GetSplashSound(float scale) const { + int idx = GetSplashIndex(scale); + return x298_splashSounds[idx]; +} + +float CScriptWater::GetSplashEffectScale(float scale) const { + float eps = Real32::Epsilon(); + if (fabs(scale - 1.f) < eps) { + return kSplashScales[5]; + } + int idx = GetSplashIndex(scale); + scale *= 3.f; + scale = scale - CMath::FloorF(scale); + return (1.f - scale) * kSplashScales[idx * 2] + scale * kSplashScales[idx * 2 + 1]; +} + +void CScriptWater::CalculateRenderBounds() { + const CAABox& bounds = GetTriggerBounds(); + CVector3f translation = GetTranslation(); + float maxZ = bounds.GetMaxPoint().GetZ(); + const CVector3f& boundsMax = + CVector3f(bounds.GetMaxPoint().GetX(), bounds.GetMaxPoint().GetY(), maxZ + 1.f); + CVector3f aabbMax = boundsMax + translation; + const CVector3f& boundsMin = + CVector3f(bounds.GetMinPoint().GetX(), bounds.GetMinPoint().GetY(), maxZ - 1.f); + CVector3f aabbMin = boundsMin + translation; + SetRenderBounds(CAABox(aabbMin, aabbMax)); +} + +CAABox CScriptWater::GetSortingBounds(const CStateManager&) const { + const CAABox& bounds = GetRenderBoundsCached(); + CVector3f maxPoint = bounds.GetMaxPoint(); + float fogZ = x214_fogBias + (maxPoint.GetZ() - 1.f); + fogZ = x218_fogMagnitude + fogZ; + if (fogZ > maxPoint.GetZ()) { + maxPoint[kDZ] = fogZ; + } + return CAABox(bounds.GetMinPoint(), maxPoint); +} + +void CScriptWater::PreRender(CStateManager& mgr, const CFrustumPlanes& frustum) { + if (x2e8_27_allowRender) { + SetPreRenderClipped(!frustum.BoxInFrustumPlanes(GetSortingBounds(mgr))); + if (!GetPreRenderClipped()) { + if (GetCurrentAreaId() != kInvalidAreaId) { + if ((uint)ActorLights()->GetMaxAreaLights() != 0) { + if (GetPreRenderHasMoved() || ActorLights()->GetIsDirty()) { + const CGameArea& area = mgr.GetWorld()->GetAreaAlways(GetCurrentAreaId()); + if (area.IsPostConstructed()) { + ActorLights()->BuildAreaLightList( + mgr, mgr.GetWorld()->GetAreaAlways(GetCurrentAreaId()), GetTriggerBoundsWR()); + SetPreRenderHasMoved(false); + } + } + } + ActorLights()->BuildDynamicLightList(mgr, GetTriggerBoundsWR()); + } + x150_frustum = frustum; + } + } else { + SetPreRenderClipped(true); + } +} + +void CScriptWater::AddToRenderer(const CFrustumPlanes&, const CStateManager& mgr) const { + if (GetPreRenderClipped()) { + return; + } + float transZ = GetTranslation().GetZ(); + float boundsMaxZ = x130_bounds.GetMaxPoint().GetZ(); + CUnitVector3f upVec(0.f, 0.f, 1.f); + upVec.Normalize(); + CPlane plane(boundsMaxZ + transZ, upVec); + mgr.AddDrawableActorPlane(*this, plane, GetSortingBounds(mgr)); +} + +void CScriptWater::Render(const CStateManager& mgr) const { + if (GetActive() && !GetPreRenderClipped()) { + GetActorLights()->ActivateLights(); + float zOffset = 0.5f * (GetRenderBoundsCached().GetMaxPoint().GetZ() + + GetRenderBoundsCached().GetMinPoint().GetZ()) - + GetTransform().Get23(); + CAABox aabb = GetRenderBoundsCached().GetTransformedAABox(CTransform4f::Translate( + -GetTransform().Get03(), -GetTransform().Get13(), -GetTransform().Get23() - zOffset)); + CTransform4f xf(GetTransform()); + xf.AddTranslationZ(zOffset); + CVector3f areaCenter = + mgr.GetWorld()->GetAreaAlways(mgr.GetNextAreaId()).GetAABB().GetCenterPoint(); + rstl::optional_object< CRippleManager > rippleMan( + mgr.GetFluidPlaneManager()->GetRippleManager()); + x1b4_fluidPlane->Render(mgr, x2bc_alpha, aabb, xf, + mgr.GetWorld()->GetAreaAlways(GetCurrentAreaId()).GetTM(), false, + x150_frustum, rippleMan, GetUniqueId(), x2d8_tileIntersects.get(), + x2c4_gridDimX, x2c8_gridDimY, areaCenter); + if (x214_fogBias != 0.f) { + if (mgr.GetPlayerState()->CanVisorSeeFog(mgr)) { + if (sRenderFog) { + float sinVal = CMath::FastSinR(x224_fogSpeed * CGraphics::GetSecondsMod900()); + float fogLevel = mgr.IntegrateVisorFog(x218_fogMagnitude * sinVal + x214_fogBias); + if (fogLevel > 0.f) { + CAABox fogBox = GetTriggerBoundsWR(); + CAABox renderBounds(CVector3f(fogBox.GetMinPoint().GetX(), fogBox.GetMinPoint().GetY(), + fogBox.GetMaxPoint().GetZ()), + CVector3f(fogBox.GetMaxPoint().GetX(), fogBox.GetMaxPoint().GetY(), + fogLevel + fogBox.GetMaxPoint().GetZ())); + CTransform4f scaleMtx = CTransform4f::Scale( + (renderBounds.GetMaxPoint() - renderBounds.GetMinPoint()) * 0.5f); + CTransform4f modelXf(CTransform4f::Translate(renderBounds.GetCenterPoint()) * scaleMtx); + CAABox renderAABB(CVector3f(-1.f, -1.f, -1.f), CVector3f(1.f, 1.f, 1.f)); + gpRender->SetModelMatrix(modelXf); + gpRender->SetAmbientColor(CColor::White()); + gpRender->RenderFogVolume(x228_fogColor, renderAABB, NULL, NULL); + } + } + } + } + CGraphics::DisableAllLights(); + } + CActor::Render(mgr); +} + +EWeaponCollisionResponseTypes CScriptWater::GetCollisionResponseType(const CVector3f&, + const CVector3f&, + const CWeaponMode&, + int) const { + return kWCR_Water; +} + +void CScriptWater::SetMorphing(const bool m) { + if (m != x2e8_26_morphing) { + x2e8_26_morphing = m; + SetupGrid(!m); + } +} + +void CScriptWater::SetupGridClipping(CStateManager& mgr, int computeVerts) { + if (x2e8_28_recomputeClipping) { + x2e4_computedGridCellCount = 0; + x2dc_vertIntersects = (bool*)NULL; + x2e8_28_recomputeClipping = false; + } + + if (x2e4_computedGridCellCount < x2cc_gridCellCount) { + static CMaterialFilter kSolidFilter = + CMaterialFilter::MakeInclude(CMaterialList(SolidMaterial)); + + if (x2dc_vertIntersects.get() == NULL) { + x2dc_vertIntersects = rs_new bool[(x2c4_gridDimX + 1) * (x2c8_gridDimY + 1)]; + } + + CVector3f downVec(0.f, 0.f, -1.f); + CAABox trigBounds = GetTriggerBoundsWR(); + float baseZ = 0.8f + trigBounds.GetMaxPoint().GetZ(); + CAABox trigBounds2 = GetTriggerBoundsWR(); + + int gridDimXP1 = x2c4_gridDimX + 1; + int curCell = x2e4_computedGridCellCount; + int row = curCell / gridDimXP1; + int col = curCell - row * gridDimXP1; + bool* vertPtr = x2dc_vertIntersects.get() + curCell; + float zDiff = x130_bounds.GetMaxPoint().GetZ() - x130_bounds.GetMinPoint().GetZ(); + float baseX = trigBounds2.GetMinPoint().GetX(); + float yOffset = x2c0_tileSize * (float)row; + float baseY = trigBounds2.GetMinPoint().GetY(); + float xOffset = x2c0_tileSize * (float)col; + float mag = 2.f * zDiff + 0.8f; + float useMag = rstl::min_val(mag, kMaxRayLength); + + int i = x2e4_computedGridCellCount; + for (; i < rstl::min_val(x2cc_gridCellCount, x2e4_computedGridCellCount + computeVerts); + ++i, ++vertPtr) { + CVector3f pos(xOffset + baseX, yOffset + baseY, baseZ); + CRayCastResult result = mgr.RayStaticIntersection(pos, downVec, useMag, kSolidFilter); + col += 1; + *vertPtr = result.GetValid(); + xOffset += x2c0_tileSize; + if (col > x2c4_gridDimX) { + yOffset += x2c0_tileSize; + xOffset = 0.f; + col = 0; + } + } + + x2e4_computedGridCellCount += computeVerts; + if (x2e4_computedGridCellCount >= x2cc_gridCellCount) { + x2e4_computedGridCellCount = x2cc_gridCellCount; + x2d8_tileIntersects = rs_new bool[x2c4_gridDimX * x2c8_gridDimY]; + + for (int i = 0; i < x2c8_gridDimY; ++i) { + bool* tileRow = x2d8_tileIntersects.get() + i * x2c4_gridDimX; + const char* vertRow = (const char*)(x2dc_vertIntersects.get()) + i * (x2c4_gridDimX + 1); + for (int j = 0; j < x2c4_gridDimX; ++j, ++tileRow, ++vertRow) { + int dimX = x2c4_gridDimX; + if (vertRow[0] != 0 || vertRow[1] != 0 || vertRow[dimX + 1] != 0 || + vertRow[dimX + 2] != 0) { + *tileRow = true; + } else { + *tileRow = false; + } + } + } + + int tmp = 42 / GetFluidPlane().GetTileSubdivisions(); + const int tilesPerPatch = rstl::min_val(kMaxTilesPerPatch, tmp); + + x2d0_patchDimX = (tilesPerPatch + x2c4_gridDimX - 1) / tilesPerPatch; + x2d4_patchDimY = (tilesPerPatch + x2c8_gridDimY - 1) / tilesPerPatch; + x2e0_patchIntersects = rs_new char[x2d0_patchDimX * x2d4_patchDimY]; + + int curTileY = 0; + int patchIdx = 0; + for (; patchIdx < x2d4_patchDimY; ++patchIdx) { + int nextTileY = curTileY + tilesPerPatch; + int curTileX = 0; + int patchJ = 0; + char* patchRow = x2e0_patchIntersects.get() + patchIdx * x2d0_patchDimX; + for (; patchJ < x2d0_patchDimX; ++patchJ) { + int nextTileX = curTileX + tilesPerPatch; + bool allClear = true; + bool allIntersect = true; + for (int k = curTileY; k < rstl::min_val(x2c8_gridDimY, nextTileY); ++k) { + if (!allClear && !allIntersect) + break; + for (int l = curTileX; l < rstl::min_val(x2c4_gridDimX, nextTileX); ++l) { + if (((const char*)x2d8_tileIntersects.get())[l + k * x2c4_gridDimX] != 0) { + allClear = false; + if (!allIntersect) + break; + } else { + allIntersect = false; + if (!allClear) + break; + } + } + } + + char flag; + if (allIntersect) { + flag = 1; + } else { + flag = 2; + if (allClear) { + flag = 0; + } + } + *patchRow = flag; + curTileX += tilesPerPatch; + patchRow += 1; + } + curTileY += tilesPerPatch; + } + + x2dc_vertIntersects = (bool*)NULL; + } + } +} + +void CScriptWater::SetupGrid(bool recomputeClipping) { + CAABox trigBoundsX = GetTriggerBoundsWR(); + float tileSize = x2c0_tileSize; + int dimX = (int)CMath::FloorF( + (tileSize + (trigBoundsX.GetMaxPoint().GetX() - trigBoundsX.GetMinPoint().GetX()) - 0.01f) / + tileSize); + + CAABox trigBoundsY = GetTriggerBoundsWR(); + tileSize = x2c0_tileSize; + int dimY = (int)CMath::FloorF( + (tileSize + (trigBoundsY.GetMaxPoint().GetY() - trigBoundsY.GetMinPoint().GetY()) - 0.01f) / + tileSize); + + x2cc_gridCellCount = (dimX + 1) * (dimY + 1); + x2e4_computedGridCellCount = x2cc_gridCellCount; + + x2dc_vertIntersects = (bool*)NULL; + + if (x2d8_tileIntersects.get() == NULL || dimX != x2c4_gridDimX || dimY != x2c8_gridDimY) { + x2d8_tileIntersects = rs_new bool[dimX * dimY]; + } + + x2c4_gridDimX = dimX; + x2c8_gridDimY = dimY; + + for (int i = 0; i < x2c8_gridDimY; ++i) { + bool* row = x2d8_tileIntersects.get() + i * x2c4_gridDimX; + for (int j = 0; j < x2c4_gridDimX; ++j, ++row) { + *row = true; + } + } + + if (x2e0_patchIntersects.get() == NULL || x2d0_patchDimX != 0 || x2d4_patchDimY != 0) { + x2e0_patchIntersects = rs_new char[32]; + } + + for (int i = 0; i < 32; ++i) { + x2e0_patchIntersects.get()[i] = 1; + } + + x2d4_patchDimY = 0; + x2d0_patchDimX = 0; + x2e8_28_recomputeClipping = recomputeClipping; +} + +bool CScriptWater::CanRippleAtPoint(const CVector3f& point) const { + if (x2d8_tileIntersects.get() == NULL) { + return true; + } + + int xTile = (int)((point.GetX() - GetTriggerBoundsWR().GetMinPoint().GetX()) / x2c0_tileSize); + if (xTile < 0 || xTile >= x2c4_gridDimX) { + return false; + } + + int yTile = (int)((point.GetY() - GetTriggerBoundsWR().GetMinPoint().GetY()) / x2c0_tileSize); + if (yTile < 0 || yTile >= x2c8_gridDimY) { + return false; + } + + const char* tiles = (const char*)x2d8_tileIntersects.get(); + return tiles[yTile * x2c4_gridDimX + xTile] != 0; +} + +int CScriptWater::GetPatchRenderFlags(int x, int y) const { + return x2e0_patchIntersects.get()[x + y * x2d0_patchDimX]; +} + +CScriptWater::~CScriptWater() {} + +void CScriptWater::Accept(IVisitor& visitor) { visitor.Visit(*this); } + +void CScriptWater::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateManager& mgr) { + switch (msg) { + case kSM_Next: + if (GetActive()) { + x2e8_25_morphIn = !x2e8_25_morphIn; + if (x2e8_25_morphIn) { + rstl::vector< SConnection >::const_iterator conn = GetConnectionList().begin(); + for (; conn != GetConnectionList().end(); ++conn) { + if (conn->x0_state != kSS_Play || conn->x4_msg != kSM_Activate) { + continue; + } + CStateManager::TIdListResult search = mgr.GetIdListForScript(conn->x8_objId); + if (search.first != search.second) { + if (const CScriptTrigger* trig = + TCastToConstPtr< CScriptTrigger >(mgr.GetObjectById(search.first->second))) { + x1b8_positionMorphed = trig->GetTranslation(); + x1c4_extentMorphed = + CVector3f(trig->x130_bounds.GetWidth(), trig->x130_bounds.GetHeight(), + trig->x130_bounds.GetDepth()); + x1f0_damageMorphed = trig->x100_damageInfo.GetDamage(); + x1d4_positionOrig = GetTranslation(); + x1e0_extentOrig = CVector3f(x130_bounds.GetWidth(), x130_bounds.GetHeight(), + x130_bounds.GetDepth()); + x1ec_damageOrig = x100_damageInfo.GetDamage(); + break; + } + } + } + } + SetMorphing(true); + } + break; + case kSM_Activate: + x2e8_30_alphaOut = false; + if (close_enough(x2ac_alphaInTime, 0.f)) { + x2bc_alpha = x1b4_fluidPlane->GetAlpha(); + x214_fogBias = x21c_origFogBias; + x218_fogMagnitude = x220_origFogMagnitude; + } else { + x2e8_29_alphaIn = true; + } + break; + case kSM_Action: + x2e8_29_alphaIn = false; + if (close_enough(x2b0_alphaOutTime, 0.f)) { + x2bc_alpha = 0.f; + x214_fogBias = 0.f; + x218_fogMagnitude = 0.f; + } else { + x2e8_30_alphaOut = true; + } + break; + default: + break; + } + CScriptTrigger::AcceptScriptMsg(msg, uid, mgr); +} + +// Real CVector3f::Lerp doesn't work, and demo map doesn't show anything relevant. +// But this hacked up version works for now +static inline CVector3f FakeLerp(const CVector3f& a, const CVector3f& b, float v) { + float inv = 1.f - v; + float by = v * b.GetY(); + float bz = v * b.GetZ(); + float ay = inv * a.GetY(); + float az = inv * a.GetZ(); + float ax = inv * a.GetX(); + float bx = v * b.GetX(); + float x = ax + bx; + float y = ay + by; + float z = az + bz; + return CVector3f(x, y, z); +} + +void CScriptWater::Think(float dt, CStateManager& mgr) { + if (!GetActive()) { + return; + } + + bool oldCamSubmerged = CameraInside(); + CScriptTrigger::Think(dt, mgr); + + CEntity* curCam = &mgr.CameraManager()->CurrentCamera(mgr); + if (x148_25_camSubmerged && !oldCamSubmerged) { + mgr.DeliverScriptMsg(curCam, GetUniqueId(), kSM_AddSplashInhabitant); + } else if (!x148_25_camSubmerged && oldCamSubmerged) { + mgr.DeliverScriptMsg(curCam, GetUniqueId(), kSM_RemoveSplashInhabitant); + } + + UpdateSplashInhabitants(mgr); + + if (x2e8_30_alphaOut) { + x2bc_alpha -= dt * x1b4_fluidPlane->GetAlpha() * x2b8_alphaOutRecip; + x214_fogBias -= dt * x21c_origFogBias * x2b8_alphaOutRecip; + x218_fogMagnitude -= dt * x220_origFogMagnitude * x2b8_alphaOutRecip; + if (x2bc_alpha <= 0.f) { + x218_fogMagnitude = 0.f; + x214_fogBias = 0.f; + x2bc_alpha = 0.f; + x2e8_30_alphaOut = false; + } + } else if (x2e8_29_alphaIn) { + x2bc_alpha += dt * x1b4_fluidPlane->GetAlpha() * x2b4_alphaInRecip; + x214_fogBias -= dt * x21c_origFogBias * x2b4_alphaInRecip; + x218_fogMagnitude -= dt * x220_origFogMagnitude * x2b4_alphaInRecip; + if (x2bc_alpha > x1b4_fluidPlane->GetAlpha()) { + x2bc_alpha = x1b4_fluidPlane->GetAlpha(); + x214_fogBias = x21c_origFogBias; + x218_fogMagnitude = x220_origFogMagnitude; + x2e8_29_alphaIn = false; + } + } + + if (x2e8_26_morphing) { + bool stillMorphing = true; + if (x2e8_25_morphIn) { + x1f8_morphFactor += dt / x1d0_morphInTime; + if (x1f8_morphFactor > 1.f) { + x1f8_morphFactor = 1.f; + stillMorphing = false; + } + } else { + x1f8_morphFactor -= dt / x1f4_morphOutTime; + if (x1f8_morphFactor < 0.f) { + x1f8_morphFactor = 0.f; + stillMorphing = false; + } + } + + SetTranslation(FakeLerp(x1d4_positionOrig, x1b8_positionMorphed, x1f8_morphFactor)); + + x100_damageInfo.SetDamage(x1ec_damageOrig * (1.f - x1f8_morphFactor) + + x1f0_damageMorphed * x1f8_morphFactor); + + CVector3f lerpExtent = FakeLerp(x1e0_extentOrig, x1c4_extentMorphed, x1f8_morphFactor); + CAABox bounds = CAABox(lerpExtent * -0.5f, lerpExtent * 0.5f); + SetTriggerBounds(bounds); + CalculateRenderBounds(); + + if (!stillMorphing) { + SetMorphing(false); + } else { + SetupGrid(false); + } + } + + SetupGridClipping(mgr, 4); +} + +void CScriptWater::UpdateSplashInhabitants(CStateManager& mgr) { + rstl::list< rstl::pair< TUniqueId, bool > >::iterator it = x1fc_waterInhabitants.begin(); + while (it != x1fc_waterInhabitants.end()) { + rstl::list< rstl::pair< TUniqueId, bool > >::iterator next = it; + ++next; + CActor* act = TCastToPtr< CActor >(mgr.ObjectById(it->first)); + bool intersects = false; + if (act != NULL) { + rstl::optional_object< CAABox > touchBounds = act->GetTouchBounds(); + if (touchBounds) { + CAABox trigBounds = GetTriggerBoundsWR(); + float trigMaxZ = trigBounds.GetMaxPoint().GetZ(); + if (touchBounds.data().GetMinPoint().GetZ() <= trigMaxZ && + touchBounds.data().GetMaxPoint().GetZ() >= trigMaxZ) { + intersects = true; + } + } + } + + if (act != NULL && it->second) { + if (intersects) { + act->FluidFXThink(kFS_InFluid, *this, mgr); + } + mgr.DeliverScriptMsg(act, GetUniqueId(), kSM_UpdateSplashInhabitant); + it->second = false; + } else { + x1fc_waterInhabitants.erase(it); + if (act != NULL) { + if (intersects) { + act->FluidFXThink(kFS_LeftFluid, *this, mgr); + } + mgr.DeliverScriptMsg(act, GetUniqueId(), kSM_RemoveSplashInhabitant); + } + } + it = next; + } +} + +void CScriptWater::Touch(CActor& otherAct, CStateManager& mgr) { + if (!GetActive()) { + return; + } + + CScriptTrigger::Touch(otherAct, mgr); + if (otherAct.GetMaterialList().HasMaterial(kMT_Trigger)) { + return; + } + + rstl::list< rstl::pair< TUniqueId, bool > >::iterator it = x1fc_waterInhabitants.begin(); + for (; it != x1fc_waterInhabitants.end(); ++it) { + if (it->first == otherAct.GetUniqueId()) { + it->second = true; + return; + } + } + + const rstl::optional_object< CAABox >& touchBounds = otherAct.GetTouchBounds(); + if (!touchBounds) { + return; + } + + x1fc_waterInhabitants.push_back(rstl::pair< TUniqueId, bool >(otherAct.GetUniqueId(), true)); + CAABox trigBounds = GetTriggerBoundsWR(); + float trigMaxZ = trigBounds.GetMaxPoint().GetZ(); + if (touchBounds.data().GetMinPoint().GetZ() <= trigMaxZ && + touchBounds.data().GetMaxPoint().GetZ() >= trigMaxZ) { + otherAct.FluidFXThink(kFS_EnteredFluid, *this, mgr); + } + mgr.DeliverScriptMsg(&otherAct, GetUniqueId(), kSM_AddSplashInhabitant); +} + +const CScriptWater* CScriptWater::GetNextConnectedWater(const CStateManager& mgr) const { + rstl::vector< SConnection >::const_iterator conn = GetConnectionList().begin(); + for (; conn != GetConnectionList().end(); ++conn) { + if (conn->x0_state != kSS_Play || conn->x4_msg != kSM_Activate) { + continue; + } + CStateManager::TIdListResult search = mgr.GetIdListForScript(conn->x8_objId); + if (search.first != search.second) { + if (const CScriptWater* water = + TCastToConstPtr< CScriptWater >(mgr.GetObjectById(search.first->second))) { + return water; + } + } + } + return NULL; +} From 8d0fa61f5569c9712c193530c59c5c29b9fa62ed Mon Sep 17 00:00:00 2001 From: Luke Street Date: Wed, 25 Feb 2026 23:42:55 -0700 Subject: [PATCH 2/3] CFluidPlane{CPU,Door,Manager,Render} & CMapArea --- config/GM8E01_00/symbols.txt | 130 +- include/GuiSys/CGuiFrame.hpp | 3 + include/GuiSys/CGuiObject.hpp | 1 + include/GuiSys/CGuiTextSupport.hpp | 4 + include/GuiSys/CGuiWidget.hpp | 2 + include/GuiSys/CGuiWidgetDrawParms.hpp | 1 + include/Kyoto/Basics/CCast.hpp | 9 + include/Kyoto/CToken.hpp | 7 +- include/Kyoto/Graphics/CGraphics.hpp | 1 + include/Kyoto/Math/CMath.hpp | 10 + include/Kyoto/Math/CQuaternion.hpp | 3 +- include/MetaRender/CCubeRenderer.hpp | 2 + include/MetroidPrime/CAutoMapper.hpp | 280 +++ include/MetroidPrime/CFluidPlaneCPU.hpp | 4 +- include/MetroidPrime/CFluidPlaneManager.hpp | 121 +- include/MetroidPrime/CGameArea.hpp | 4 +- include/MetroidPrime/CGameHintInfo.hpp | 57 + include/MetroidPrime/CInGameGuiManager.hpp | 4 + include/MetroidPrime/CMapArea.hpp | 1 + include/MetroidPrime/CMapUniverse.hpp | 81 + include/MetroidPrime/CMapWorld.hpp | 26 +- include/MetroidPrime/CMapWorldInfo.hpp | 4 + include/MetroidPrime/CRipple.hpp | 6 + include/MetroidPrime/CWorld.hpp | 2 +- include/MetroidPrime/Player/CGameOptions.hpp | 1 + include/MetroidPrime/Player/CGameState.hpp | 1 + include/MetroidPrime/Player/CHintOptions.hpp | 20 +- include/MetroidPrime/Player/CPlayer.hpp | 1 + include/MetroidPrime/Player/CSystemState.hpp | 3 + include/MetroidPrime/Player/CWorldState.hpp | 8 + .../ScriptObjects/CScriptWater.hpp | 5 +- .../MetroidPrime/Tweaks/CTweakPlayerRes.hpp | 25 +- include/dolphin/gx/GXVert.h | 6 + include/rstl/string.hpp | 1 + src/MetroidPrime/CAutoMapper.cpp | 2126 +++++++++++++++++ src/MetroidPrime/CFluidPlaneCPU.cpp | 1398 ++++++++++- src/MetroidPrime/CFluidPlaneDoor.cpp | 175 +- src/MetroidPrime/CFluidPlaneManager.cpp | 159 +- src/MetroidPrime/CFluidPlaneRender.cpp | 1373 +++++++++++ src/MetroidPrime/CWorld.cpp | 4 +- 40 files changed, 5894 insertions(+), 175 deletions(-) create mode 100644 include/MetroidPrime/CAutoMapper.hpp create mode 100644 include/MetroidPrime/CGameHintInfo.hpp create mode 100644 include/MetroidPrime/CMapUniverse.hpp create mode 100644 src/MetroidPrime/CAutoMapper.cpp create mode 100644 src/MetroidPrime/CFluidPlaneRender.cpp diff --git a/config/GM8E01_00/symbols.txt b/config/GM8E01_00/symbols.txt index 988b3c66e..f35eb1ee3 100644 --- a/config/GM8E01_00/symbols.txt +++ b/config/GM8E01_00/symbols.txt @@ -112,7 +112,7 @@ GetAverageValue__FPCfi = .text:0x80008894; // type:function size:0xC8 scope:g reserve__Q24rstl42vector<6CToken,Q24rstl17rmemory_allocator>Fi = .text:0x8000895C; // type:function size:0xF0 scope:global reserve__Q24rstl55vector,Q24rstl17rmemory_allocator>Fi = .text:0x80008A4C; // type:function size:0xF8 scope:global reserve__Q24rstl63vector,Q24rstl17rmemory_allocator>Fi = .text:0x80008B44; // type:function size:0xF8 scope:global -fn_80008C3C = .text:0x80008C3C; // type:function size:0x5C +ReleaseData__Q24rstl23rc_ptr<13CMapWorldInfo>Fv = .text:0x80008C3C; // type:function size:0x5C scope:global fn_80008C98 = .text:0x80008C98; // type:function size:0x134 fn_80008DCC = .text:0x80008DCC; // type:function size:0x5C fn_80008E28 = .text:0x80008E28; // type:function size:0xE4 @@ -2103,7 +2103,7 @@ Reset__16CCinematicCameraFRC12CTransform4fR13CStateManager = .text:0x800951DC; / __dt__16CCinematicCameraFv = .text:0x800951E0; // type:function size:0x214 scope:global __ct__16CCinematicCameraF9TUniqueIdRCQ24rstl66basic_string,Q24rstl17rmemory_allocator>RC11CEntityInfoRC12CTransform4fbfffffUi = .text:0x800953F4; // type:function size:0x194 scope:global reserve__Q24rstl48vector<11CQuaternion,Q24rstl17rmemory_allocator>Fi = .text:0x80095588; // type:function size:0x108 scope:global -SetupMiniMapWorld__11CAutoMapperFv = .text:0x80095690; // type:function size:0x54 scope:global +SetupMiniMapWorld__11CAutoMapperFR13CStateManager = .text:0x80095690; // type:function size:0x54 scope:global LeaveMapScreen__11CAutoMapperCFRC13CStateManager = .text:0x800956E4; // type:function size:0x258 scope:global SetCurWorldAssetId__11CAutoMapperFi = .text:0x8009593C; // type:function size:0x48 scope:global TransformRenderStateWorldToUniverse__11CAutoMapperFRQ211CAutoMapper22SAutoMapperRenderState = .text:0x80095984; // type:function size:0x68 scope:global @@ -2134,10 +2134,10 @@ SetShouldZoomingSoundBePlaying__11CAutoMapperFb = .text:0x80096BA4; // type:func SetShouldPanningSoundBePlaying__11CAutoMapperFb = .text:0x80096C28; // type:function size:0x84 scope:global BuildMapScreenUniverseRenderState__11CAutoMapperCFRC13CStateManagerRC11CQuaternioni = .text:0x80096CAC; // type:function size:0x100 scope:global __ct__Q211CAutoMapper22SAutoMapperRenderStateFRCQ211CAutoMapper22SAutoMapperRenderState = .text:0x80096DAC; // type:function size:0xBC scope:global -BuildMapScreenWorldRenderState__11CAutoMapperCFRC13CStateManagerRC11CQuaternioni = .text:0x80096E68; // type:function size:0x130 scope:global +BuildMapScreenWorldRenderState__11CAutoMapperCFRC13CStateManagerRC11CQuaternionib = .text:0x80096E68; // type:function size:0x130 scope:global BuildMiniMapWorldRenderState__11CAutoMapperCFRC13CStateManagerRC11CQuaternioni = .text:0x80096F98; // type:function size:0x1E0 scope:global ResetInterpolationTimer__11CAutoMapperFf = .text:0x80097178; // type:function size:0x10 scope:global -CompleteMapperStateTransition__11CAutoMapperFv = .text:0x80097188; // type:function size:0x1AC scope:global +CompleteMapperStateTransition__11CAutoMapperFRC13CStateManager = .text:0x80097188; // type:function size:0x1AC scope:global BeginMapperStateTransition__11CAutoMapperFQ211CAutoMapper16EAutoMapperStateRC13CStateManager = .text:0x80097334; // type:function size:0x94C scope:global Update__11CAutoMapperFfRC13CStateManager = .text:0x80097C80; // type:function size:0x1298 scope:global GetAreaHintDescriptionString__11CAutoMapperFUi = .text:0x80098F18; // type:function size:0xEC scope:global @@ -2151,27 +2151,27 @@ UpdateHintNavigation__11CAutoMapperFfRC13CStateManager = .text:0x8009B8E4; // ty CheckDummyWorldLoad__11CAutoMapperFRC13CStateManager = .text:0x8009BF9C; // type:function size:0x19C scope:global HasCurrentMapUniverseWorld__11CAutoMapperFv = .text:0x8009C138; // type:function size:0x7C scope:global UnmuteAllLoopedSounds__11CAutoMapperFv = .text:0x8009C1B4; // type:function size:0x64 scope:global -NotHintNavigating__11CAutoMapperFv = .text:0x8009C218; // type:function size:0x14 scope:global +NotHintNavigating__11CAutoMapperCFv = .text:0x8009C218; // type:function size:0x14 scope:global CanLeaveMapScreenInternal__11CAutoMapperCFRC13CStateManager = .text:0x8009C22C; // type:function size:0xB4 scope:global CanLeaveMapScreen__11CAutoMapperCFRC13CStateManager = .text:0x8009C2E0; // type:function size:0x48 scope:global OnNewInGameGuiState__11CAutoMapperF15EInGameGuiStateR13CStateManager = .text:0x8009C328; // type:function size:0x1BC scope:global SetupHintNavigation__11CAutoMapperFv = .text:0x8009C4E4; // type:function size:0x748 scope:global CheckLoadComplete__11CAutoMapperFv = .text:0x8009CC2C; // type:function size:0x2A4 scope:global -fn_8009CED0 = .text:0x8009CED0; // type:function size:0x148 -fn_8009D018 = .text:0x8009D018; // type:function size:0xD0 +__as__Q24rstl61vector,Q24rstl17rmemory_allocator>FRCQ24rstl61vector,Q24rstl17rmemory_allocator> = .text:0x8009CED0; // type:function size:0x148 scope:global +__dt__Q24rstl61vector,Q24rstl17rmemory_allocator>Fv = .text:0x8009D018; // type:function size:0xD0 scope:global __dt__11CAutoMapperFv = .text:0x8009D0E8; // type:function size:0x2E4 scope:global -SetResLockState,2>>__11CAutoMapperFv = .text:0x8009D3CC; // type:function size:0x7C scope:global -SetResLockState,9>>__11CAutoMapperFv = .text:0x8009D448; // type:function size:0x7C scope:global +SetResLockState>__11CAutoMapperFRQ24rstl26reserved_vector<6CToken,2>b = .text:0x8009D3CC; // type:function size:0x7C scope:global +SetResLockState>__11CAutoMapperFRQ24rstl26reserved_vector<6CToken,9>b = .text:0x8009D448; // type:function size:0x7C scope:global __ct__11CAutoMapperFR13CStateManager = .text:0x8009D4C4; // type:function size:0x744 scope:global __ct__Q211CAutoMapper23SAutoMapperHintLocationFUifUi7TAreaId = .text:0x8009DC08; // type:function size:0x14 scope:global ResetInterpolation__Q211CAutoMapper22SAutoMapperRenderStateFv = .text:0x8009DC1C; // type:function size:0x20 scope:global InterpolateWithClamp__Q211CAutoMapper22SAutoMapperRenderStateFRCQ211CAutoMapper22SAutoMapperRenderStateRQ211CAutoMapper22SAutoMapperRenderStateRCQ211CAutoMapper22SAutoMapperRenderStatef = .text:0x8009DC3C; // type:function size:0x418 scope:global __ct__Q211CAutoMapper22SAutoMapperRenderStateFRC9CVector2iRC11CQuaternionffRC9CVector3fffffff = .text:0x8009E054; // type:function size:0x88 scope:global reserve__Q24rstl61vector,Q24rstl17rmemory_allocator>Fi = .text:0x8009E0DC; // type:function size:0x124 scope:global -fn_8009E200 = .text:0x8009E200; // type:function size:0x7C -fn_8009E27C = .text:0x8009E27C; // type:function size:0x78 -fn_8009E2F4 = .text:0x8009E2F4; // type:function size:0x7C -fn_8009E370 = .text:0x8009E370; // type:function size:0x78 +do_erase__Q24rstl69listFPQ34rstl69list4node = .text:0x8009E200; // type:function size:0x7C scope:global +__dt__Q24rstl69listFv = .text:0x8009E27C; // type:function size:0x78 scope:global +do_erase__Q24rstl73listFPQ34rstl73list4node = .text:0x8009E2F4; // type:function size:0x7C scope:global +__dt__Q24rstl73listFv = .text:0x8009E370; // type:function size:0x78 scope:global Accept__14CScriptCounterFR8IVisitor = .text:0x8009E3E8; // type:function size:0x38 scope:global AcceptScriptMsg__14CScriptCounterF20EScriptObjectMessage9TUniqueIdR13CStateManager = .text:0x8009E420; // type:function size:0x1C8 scope:global __dt__14CScriptCounterFv = .text:0x8009E5E8; // type:function size:0x60 scope:global @@ -2207,7 +2207,7 @@ fn_800A14F4 = .text:0x800A14F4; // type:function size:0xC0 __ct__9CMapWorldFR12CInputStream = .text:0x800A15B4; // type:function size:0x284 scope:global __as__Q24rstl36vectorFRCQ24rstl36vector = .text:0x800A1838; // type:function size:0xF8 __ct__Q29CMapWorld15CMapAreaBFSInfoFiiff = .text:0x800A1930; // type:function size:0x14 scope:global -__ct__Q29CMapWorld18CMapWorldDrawParmsFfffffRC13CStateManagerRC12CTransform4fRC12CTransform4fRC6IWorldRC13CMapWorldInfofb = .text:0x800A1944; // type:function size:0x44 scope:global +__ct__Q29CMapWorld18CMapWorldDrawParmsFfffffRC13CStateManagerRC12CTransform4fRC12CTransform4fRC6IWorldRC13CMapWorldInfofbfff = .text:0x800A1944; // type:function size:0x44 scope:global GetMapArea__Q29CMapWorld12CMapAreaDataCFv = .text:0x800A1988; // type:function size:0x64 scope:global IsLoaded__Q29CMapWorld12CMapAreaDataCFv = .text:0x800A19EC; // type:function size:0x84 scope:global Unlock__Q29CMapWorld12CMapAreaDataFv = .text:0x800A1A70; // type:function size:0x2C scope:global @@ -8332,14 +8332,14 @@ CalculateRenderBounds__15CBeamProjectileFv = .text:0x8019938C; // type:function GetTouchBounds__15CBeamProjectileCFv = .text:0x801993F4; // type:function size:0x98 scope:global __ct__15CBeamProjectileFRC28TToken<18CWeaponDescription>RCQ24rstl66basic_string,Q24rstl17rmemory_allocator>11EWeaponTypeRC12CTransform4fiff14EMaterialTypesRC11CDamageInfo9TUniqueId7TAreaId9TUniqueIdQ27CWeapon17EProjectileAttribb = .text:0x8019948C; // type:function size:0x408 scope:global RenderCleanup__14CFluidPlaneCPUCFv = .text:0x80199894; // type:function size:0x1D4 scope:global -Render__14CFluidPlaneCPUCFRC13CStateManagerRC6CAABoxRC12CTransform4fRC12CTransform4fbRC14CFrustumPlanesRCQ24rstl33optional_object<14CRippleManager>9TUniqueIdPCbiiRC9CVector3f = .text:0x80199A68; // type:function size:0x840 scope:global -__ct__Q220CFluidPlaneCPURender10SPatchInfoFRC9CVector3fRC9CVector3fRC9CVector3fffb = .text:0x8019A2A8; // type:function size:0x188 scope:global +Render__14CFluidPlaneCPUCFRC13CStateManagerfRC6CAABoxRC12CTransform4fRC12CTransform4fbRC14CFrustumPlanesRCQ24rstl33optional_object<14CRippleManager>9TUniqueIdPCbiiRC9CVector3f = .text:0x80199A68; // type:function size:0x840 scope:global +__ct__Q220CFluidPlaneCPURender10SPatchInfoFRC9CVector3fRC9CVector3fRC9CVector3ffffiiiiUciiiiPCb = .text:0x8019A2A8; // type:function size:0x188 scope:global RenderSetup__14CFluidPlaneCPUCFRC13CStateManagerfRC12CTransform4fRC12CTransform4fRC6CAABoxP12CScriptWater = .text:0x8019A430; // type:function size:0x15D0 scope:global CalculateLightmapMtx__14CFluidPlaneCPUFRC12CTransform4fRC12CTransform4fRC6CAABoxi = .text:0x8019BA00; // type:function size:0x3A8 scope:global UpdatePatch__FfRQ220CFluidPlaneCPURender10SPatchInfoRC14CFluidPlaneCPURC9CVector3fRC14CRippleManageriiii = .text:0x8019BDA8; // type:function size:0x294 scope:global ApplyTurbulence__FfRA45_A45_Q220CFluidPlaneCPURender13SHFieldSamplePCUcRA256_CfRCQ220CFluidPlaneCPURender10SPatchInfoRC14CFluidPlaneCPURC9CVector3f = .text:0x8019C03C; // type:function size:0x164 scope:global -UpdatePatchNoNormals__FPCUcPCUcQ220CFluidPlaneCPURender10SPatchInfo = .text:0x8019C1A0; // type:function size:0x328 scope:global -UpdatePatchWithNormals__FPCUcPCUcQ220CFluidPlaneCPURender10SPatchInfo = .text:0x8019C4C8; // type:function size:0x66C scope:global +UpdatePatchNoNormals__FPCUcPCUcRCQ220CFluidPlaneCPURender10SPatchInfo = .text:0x8019C1A0; // type:function size:0x328 scope:global +UpdatePatchWithNormals__FPCUcPCUcRCQ220CFluidPlaneCPURender10SPatchInfo = .text:0x8019C4C8; // type:function size:0x66C scope:global __dt__Q214CFluidPlaneCPU11CTurbulenceFv = .text:0x8019CB34; // type:function size:0x54 scope:global __ct__Q214CFluidPlaneCPU11CTurbulenceFffffffff = .text:0x8019CB88; // type:function size:0x238 scope:global __ct__14CFluidPlaneCPUFUiUiUiUiUiUifUiUiQ211CFluidPlane10EFluidTypefRC9CVector3ffRC14CFluidUVMotionffffffffffffff = .text:0x8019CDC0; // type:function size:0x5E0 scope:global @@ -8347,7 +8347,7 @@ AddRipple__15CFluidPlaneDoorFRC7CRippleRC12CScriptWaterR13CStateManager = .text: AddRipple__15CFluidPlaneDoorFf9TUniqueIdRC9CVector3fRC9CVector3fRC12CScriptWaterR13CStateManagerRC9CVector3f = .text:0x8019D3A4; // type:function size:0x4 AddRipple__15CFluidPlaneDoorFf9TUniqueIdRC9CVector3fRC12CScriptWaterR13CStateManager = .text:0x8019D3A8; // type:function size:0x4 RenderCleanup__15CFluidPlaneDoorCFv = .text:0x8019D3AC; // type:function size:0x78 scope:global -Render__15CFluidPlaneDoorCFRC13CStateManagerRC6CAABoxRC14CFrustumPlanesRC14CRippleManagerRC9CVector3f = .text:0x8019D424; // type:function size:0x3E8 scope:global +Render__15CFluidPlaneDoorCFRC13CStateManagerfRC6CAABoxRC12CTransform4fRC12CTransform4fbRC14CFrustumPlanesRCQ24rstl33optional_object<14CRippleManager>9TUniqueIdPCbiiRC9CVector3f = .text:0x8019D424; // type:function size:0x3E8 scope:global RenderSetup__15CFluidPlaneDoorCFRC13CStateManagerfRC12CTransform4fRC6CAABox = .text:0x8019D80C; // type:function size:0x3F4 scope:global __dt__15CFluidPlaneDoorFv = .text:0x8019DC00; // type:function size:0x60 scope:global __ct__15CFluidPlaneDoorFUiUiUifUiQ211CFluidPlane10EFluidTypefRC14CFluidUVMotion = .text:0x8019DC60; // type:function size:0xA4 scope:global @@ -9604,7 +9604,7 @@ __ct__12CMapUniverseFR12CInputStream = .text:0x80202BCC; // type:function size:0 fn_80202D0C = .text:0x80202D0C; // type:function size:0x7C fn_80202D88 = .text:0x80202D88; // type:function size:0x28 fn_80202DB0 = .text:0x80202DB0; // type:function size:0x120 -fn_80202ED0 = .text:0x80202ED0; // type:function size:0x24 +__ct__Q212CMapUniverse21CMapUniverseDrawParmsFfiUiifRC13CStateManagerRC12CTransform4fRC12CTransform4f = .text:0x80202ED0; // type:function size:0x24 scope:global __ct__Q212CMapUniverse18CMapObjectSortInfoFfiii6CColor6CColor = .text:0x80202EF4; // type:function size:0x24 scope:global fn_80202F18 = .text:0x80202F18; // type:function size:0x1D8 fn_802030F0 = .text:0x802030F0; // type:function size:0x70 @@ -11002,18 +11002,18 @@ fn_8026B174 = .text:0x8026B174; // type:function size:0x64 GetRelayIndex__18CWorldSaveGameInfoCFRC9TEditorId = .text:0x8026B1D8; // type:function size:0x50 scope:global __ct__18CWorldSaveGameInfoFR12CinputStream = .text:0x8026B228; // type:function size:0x8D4 scope:global reserve__Q24rstl48vector<11SLayerState,Q24rstl17rmemory_allocator>Fi = .text:0x8026BAFC; // type:function size:0xF4 scope:global -RenderPatch__FRCQ220CFluidPlaneCPURender10SPatchInfob = .text:0x8026BBF0; // type:function size:0xD98 scope:global -ApplyRipples__FRC14CRippleManagerRA43_A43_Q220CFluidPlaneCPURender13SHFieldSampleRA22_A22_UcRA256_CfRQ220CFluidPlaneCPURender10SPatchInfo = .text:0x8026C988; // type:function size:0x328 scope:global -ApplyRipple__FRC7CRippleRA43_A43_Q220CFluidPlaneCPURender13SHFieldSampleRA22_A22_UcRA256_CfRQ220CFluidPlaneCPURender10SPatchInfof = .text:0x8026CCB0; // type:function size:0x840 scope:global -RenderStripWithRipples__FRA45_A45_CQ220CFluidPlaneCPURender13SHFieldSampleRA22_A22_CUcifRCQ220CFluidPlaneCPURender10SPatchInfo = .text:0x8026D4F0; // type:function size:0x1894 scope:global -fn_8026ED84 = .text:0x8026ED84; // type:function size:0x1FC -fn_8026EF80 = .text:0x8026EF80; // type:function size:0x22C -fn_8026F1AC = .text:0x8026F1AC; // type:function size:0x1B0 -RenderTileWithRipplesNBT__fRA45_A45_CQ220CFluidPlaneCPURender13SHFieldSamplefiRCQ220CFluidPlaneCPURender10SPatchInfo = .text:0x8026F35C; // type:function size:0xAC scope:global -fn_8026F408 = .text:0x8026F408; // type:function size:0x140 -RenderTileWithRipplesNormals__fRA45_A45_CQ220CFluidPlaneCPURender13SHFieldSamplefiRCQ220CFluidPlaneCPURender10SPatchInfo = .text:0x8026F548; // type:function size:0xAC scope:global -fn_8026F5F4 = .text:0x8026F5F4; // type:function size:0xD0 -RenderTileWithRipplesNoNormals__fRA45_A45_CQ220CFluidPlaneCPURender13SHFieldSamplefiRCQ220CFluidPlaneCPURender10SPatchInfo = .text:0x8026F6C4; // type:function size:0x140 scope:global +RenderPatch__FRCQ220CFluidPlaneCPURender10SPatchInfobb = .text:0x8026BBF0; // type:function size:0xD98 scope:global +ApplyRipples__FRC14CRippleManagerRA45_A45_Q220CFluidPlaneCPURender13SHFieldSampleRA9_A9_UcRA256_CfRQ220CFluidPlaneCPURender10SPatchInfo = .text:0x8026C988; // type:function size:0x328 scope:global +ApplyRipple__FRC7CRippleRA45_A45_Q220CFluidPlaneCPURender13SHFieldSampleRA9_A9_UcRA256_CfRQ220CFluidPlaneCPURender10SPatchInfo = .text:0x8026CCB0; // type:function size:0x840 scope:global +RenderStripWithRipples__FRA45_A45_CQ220CFluidPlaneCPURender13SHFieldSampleRA9_A9_CUcifRCQ220CFluidPlaneCPURender10SPatchInfo = .text:0x8026D4F0; // type:function size:0x1894 scope:global +RenderStripEndNBT__FPCQ220CFluidPlaneCPURender13SHFieldSampleRCQ220CFluidPlaneCPURender10SPatchInfobfff = .text:0x8026ED84; // type:function size:0x1FC scope:local +RenderStripEndNormals__FPCQ220CFluidPlaneCPURender13SHFieldSampleRCQ220CFluidPlaneCPURender10SPatchInfobfff = .text:0x8026EF80; // type:function size:0x22C scope:local +RenderStripEndNoNormals__FPCQ220CFluidPlaneCPURender13SHFieldSampleRCQ220CFluidPlaneCPURender10SPatchInfobfff = .text:0x8026F1AC; // type:function size:0x1B0 scope:local +RenderTileWithRipplesNBT__FfRA45_A45_CQ220CFluidPlaneCPURender13SHFieldSamplefiRCQ220CFluidPlaneCPURender10SPatchInfo = .text:0x8026F35C; // type:function size:0xAC scope:global +RenderTileStripNBT__FPCQ220CFluidPlaneCPURender13SHFieldSamplefffiRCQ220CFluidPlaneCPURender10SPatchInfo = .text:0x8026F408; // type:function size:0x140 scope:local +RenderTileWithRipplesNormals__FfRA45_A45_CQ220CFluidPlaneCPURender13SHFieldSamplefiRCQ220CFluidPlaneCPURender10SPatchInfo = .text:0x8026F548; // type:function size:0xAC scope:global +RenderTileStripNormals__FPCQ220CFluidPlaneCPURender13SHFieldSamplefffiRCQ220CFluidPlaneCPURender10SPatchInfo = .text:0x8026F5F4; // type:function size:0xD0 scope:local +RenderTileWithRipplesNoNormals__FfRA45_A45_CQ220CFluidPlaneCPURender13SHFieldSamplefiRCQ220CFluidPlaneCPURender10SPatchInfo = .text:0x8026F6C4; // type:function size:0x140 scope:global PrepareRipple__FRC7CRippleRCQ220CFluidPlaneCPURender10SPatchInfoRQ220CFluidPlaneCPURender11SRippleInfo = .text:0x8026F804; // type:function size:0x264 scope:global GetGlobalSineWave__Fv = .text:0x8026FA68; // type:function size:0xC scope:global InitializeSineWave__Fv = .text:0x8026FA74; // type:function size:0xD4 scope:global @@ -12230,9 +12230,9 @@ SetFontColor__15CGuiTextSupportFRC6CColor = .text:0x802C9668; // type:function s SetVerticalJustification__15CGuiTextSupportF22EVerticalJustification = .text:0x802C96B8; // type:function size:0x30 scope:global SetJustification__15CGuiTextSupportF14EJustification = .text:0x802C96E8; // type:function size:0x30 scope:global SetImageBaseline__15CGuiTextSupportFb = .text:0x802C9718; // type:function size:0x34 scope:global -fn_802C974C = .text:0x802C974C; // type:function size:0x34 +SetWordWrap__15CGuiTextSupportFb = .text:0x802C974C; // type:function size:0x34 scope:global AddText__15CGuiTextSupportFQ24rstl7wstring = .text:0x802C9780; // type:function size:0x104 scope:global -SetText__15CGuiTextSupportFRCQ24rstl66basic_string,Q24rstl17rmemory_allocator> = .text:0x802C9884; // type:function size:0xAC scope:global +SetText__15CGuiTextSupportFRCQ24rstl66basic_string,Q24rstl17rmemory_allocator>b = .text:0x802C9884; // type:function size:0xAC scope:global fn_802C9930 = .text:0x802C9930; // type:function size:0x2C fn_802C995C = .text:0x802C995C; // type:function size:0x7C fn_802C99D8 = .text:0x802C99D8; // type:function size:0x130 @@ -14380,9 +14380,9 @@ internal_prepare_to_write__Q24rstl66basic_string,Q24rs internal_dereference__Q24rstl66basic_string,Q24rstl17rmemory_allocator>Fv = .text:0x8033D134; // type:function size:0x48 scope:global internal_allocate__Q24rstl66basic_string,Q24rstl17rmemory_allocator>Fi = .text:0x8033D17C; // type:function size:0x80 scope:global fn_8033D1FC = .text:0x8033D1FC; // type:function size:0x150 -fn_8033D34C = .text:0x8033D34C; // type:function size:0x8C +assign__Q24rstl66basic_string,Q24rstl17rmemory_allocator>FRCQ24rstl66basic_string,Q24rstl17rmemory_allocator> = .text:0x8033D34C; // type:function size:0x8C scope:global append__Q24rstl66basic_string,Q24rstl17rmemory_allocator>Fiw = .text:0x8033D3D8; // type:function size:0x10C scope:global -fn_8033D4E4 = .text:0x8033D4E4; // type:function size:0x168 +append__Q24rstl66basic_string,Q24rstl17rmemory_allocator>FPCwi = .text:0x8033D4E4; // type:function size:0x168 scope:global append__Q24rstl66basic_string,Q24rstl17rmemory_allocator>FRCQ24rstl66basic_string,Q24rstl17rmemory_allocator> = .text:0x8033D64C; // type:function size:0x13C scope:global __ct__Q24rstl66basic_string,Q24rstl17rmemory_allocator>FRCQ24rstl66basic_string,Q24rstl17rmemory_allocator> = .text:0x8033D788; // type:function size:0x34 scope:global __ct__Q24rstl66basic_string,Q24rstl17rmemory_allocator>FPCwiRCQ24rstl17rmemory_allocator = .text:0x8033D7BC; // type:function size:0x174 scope:global @@ -16827,7 +16827,7 @@ lbl_803CD7C0 = .rodata:0x803CD7C0; // type:object size:0x18 lbl_803CD7D8 = .rodata:0x803CD7D8; // type:object size:0x14 data:4byte lbl_803CD7EC = .rodata:0x803CD7EC; // type:object size:0x10 data:4byte lbl_803CD7FC = .rodata:0x803CD7FC; // type:object size:0x10 data:4byte -lbl_803CD80C = .rodata:0x803CD80C; // type:object size:0x154 +@stringBase0 = .rodata:0x803CD80C; // type:object size:0x154 scope:local data:string_table lbl_803CD960 = .rodata:0x803CD960; // type:object size:0x7 data:string lbl_803CD968 = .rodata:0x803CD968; // type:object size:0x7 data:string lbl_803CD96F = .rodata:0x803CD96F; // type:object size:0xE data:string @@ -17115,7 +17115,7 @@ lbl_803CFD08 = .rodata:0x803CFD08; // type:object size:0xB0 lbl_803CFDB8 = .rodata:0x803CFDB8; // type:object size:0x108 lbl_803CFEC0 = .rodata:0x803CFEC0; // type:object size:0xB0 lbl_803CFF70 = .rodata:0x803CFF70; // type:object size:0xC8 -lbl_803D0038 = .rodata:0x803D0038; // type:object size:0x10 +@stringBase0 = .rodata:0x803D0038; // type:object size:0x10 scope:local data:string_table @stringBase0 = .rodata:0x803D0048; // type:object size:0x7 scope:local data:string_table @stringBase0 = .rodata:0x803D0050; // type:object size:0x7 scope:local data:string_table lbl_803D0058 = .rodata:0x803D0058; // type:object size:0x7 data:string @@ -17271,7 +17271,9 @@ lbl_803D10E4 = .rodata:0x803D10E4; // type:object size:0x12C lbl_803D1210 = .rodata:0x803D1210; // type:object size:0x20 lbl_803D1230 = .rodata:0x803D1230; // type:object size:0xC8 data:4byte lbl_803D12F8 = .rodata:0x803D12F8; // type:object size:0x7 data:string -lbl_803D1300 = .rodata:0x803D1300; // type:object size:0x60 data:4byte +@467 = .rodata:0x803D1300; // type:object size:0x20 scope:local data:4byte +@468 = .rodata:0x803D1320; // type:object size:0x20 scope:local data:4byte +@469 = .rodata:0x803D1340; // type:object size:0x20 scope:local data:4byte lbl_803D1360 = .rodata:0x803D1360; // type:object size:0x78 lbl_803D13D8 = .rodata:0x803D13D8; // type:object size:0x6 data:string lbl_803D13DE = .rodata:0x803D13DE; // type:object size:0x7 data:string @@ -18136,7 +18138,7 @@ __vt__10CTweakBall = .data:0x803DAD30; // type:object size:0xC scope:global jumptable_803DAD40 = .data:0x803DAD40; // type:object size:0x94 scope:local __vt__12CScriptTimer = .data:0x803DADD8; // type:object size:0x20 scope:global __vt__16CCinematicCamera = .data:0x803DADF8; // type:object size:0x78 scope:global -lbl_803DAE70 = .data:0x803DAE70; // type:object size:0xC +__vt__11CAutoMapper = .data:0x803DAE70; // type:object size:0xC scope:global jumptable_803DAE7C = .data:0x803DAE7C; // type:object size:0x2C scope:local jumptable_803DAEA8 = .data:0x803DAEA8; // type:object size:0x2C scope:local jumptable_803DAED4 = .data:0x803DAED4; // type:object size:0x1C scope:local @@ -18512,7 +18514,7 @@ lbl_803E4594 = .data:0x803E4594; // type:object size:0x44 lbl_803E45D8 = .data:0x803E45D8; // type:object size:0x70 __vt__15CBeamProjectile = .data:0x803E4648; // type:object size:0x80 scope:global __vt__14CFluidPlaneCPU = .data:0x803E46C8; // type:object size:0x20 scope:global -lbl_803E46E8 = .data:0x803E46E8; // type:object size:0x20 +__vt__15CFluidPlaneDoor = .data:0x803E46E8; // type:object size:0x20 scope:global __vt__20CScriptRoomAcoustics = .data:0x803E4708; // type:object size:0x20 scope:global lbl_803E4728 = .data:0x803E4728; // type:object size:0x2CC jumptable_803E49F4 = .data:0x803E49F4; // type:object size:0x60 scope:local @@ -19353,7 +19355,7 @@ lbl_8046D7E0 = .bss:0x8046D7E0; // type:object size:0x1E0 lbl_8046D9C0 = .bss:0x8046D9C0; // type:object size:0x2D0 data:4byte lbl_8046DC90 = .bss:0x8046DC90; // type:object size:0x18 data:4byte lbl_8046DCA8 = .bss:0x8046DCA8; // type:object size:0x18 data:4byte -lbl_8046DCC0 = .bss:0x8046DCC0; // type:object size:0x400 align:4 data:float +sGlobalSineWave = .bss:0x8046DCC0; // type:object size:0x400 scope:local align:4 data:float lbl_8046E0C0 = .bss:0x8046E0C0; // type:object size:0x18 data:4byte lbl_8046E0D8 = .bss:0x8046E0D8; // type:object size:0x18 data:4byte lbl_8046E0F0 = .bss:0x8046E0F0; // type:object size:0x100 data:4byte @@ -19574,10 +19576,10 @@ mActiveIndexList__13CDecalManager = .bss:0x8056F758; // type:object size:0x104 s lbl_8056F85C = .bss:0x8056F85C; // type:object size:0x18 lbl_8056F894 = .bss:0x8056F894; // type:object size:0x10 lbl_8056F8A4 = .bss:0x8056F8A4; // type:object size:0x10 -lbl_8056F8B4 = .bss:0x8056F8B4; // type:object size:0x18 -lbl_8057094C = .bss:0x8057094C; // type:object size:0x1000 data:byte -lbl_8057194C = .bss:0x8057194C; // type:object size:0x40 data:byte -lbl_8057198C = .bss:0x8057198C; // type:object size:0x40 data:byte +sProfile__18CFluidPlaneManager = .bss:0x8056F8B4; // type:object size:0x18 scope:global +sRippleValues = .bss:0x8057094C; // type:object size:0x1000 scope:global data:byte +sRippleMins = .bss:0x8057194C; // type:object size:0x40 scope:global data:byte +sRippleMaxs = .bss:0x8057198C; // type:object size:0x40 scope:global data:byte lbl_805719CC = .bss:0x805719CC; // type:object size:0x4 lbl_805719DC = .bss:0x805719DC; // type:object size:0xC align:4 data:float skHardRecoil__16CCameraShakeData = .bss:0x805719E8; // type:object size:0xD4 scope:global @@ -20371,11 +20373,11 @@ lbl_805A76B4 = .sdata:0x805A76B4; // type:object size:0x4 data:4byte lbl_805A76B8 = .sdata:0x805A76B8; // type:object size:0x4 align:4 data:float lbl_805A76BC = .sdata:0x805A76BC; // type:object size:0x4 lbl_805A76C0 = .sdata:0x805A76C0; // type:object size:0x8 -lbl_805A76C8 = .sdata:0x805A76C8; // type:object size:0x4 align:4 data:float +kRippleIntensityRange__11CFluidPlane = .sdata:0x805A76C8; // type:object size:0x4 scope:global align:4 data:float lbl_805A76CC = .sdata:0x805A76CC; // type:object size:0x4 align:4 data:float lbl_805A76D0 = .sdata:0x805A76D0; // type:object size:0x8 align:4 data:float -lbl_805A76D8 = .sdata:0x805A76D8; // type:object size:0x4 data:4byte -lbl_805A76DC = .sdata:0x805A76DC; // type:object size:0x4 data:4byte +kMinValue = .sdata:0x805A76D8; // type:object size:0x4 scope:local data:4byte +kMaxValue = .sdata:0x805A76DC; // type:object size:0x4 scope:local data:4byte @284 = .sdata:0x805A76E0; // type:object size:0x4 scope:local data:4byte @499 = .sdata:0x805A76E8; // type:object size:0x4 scope:local align:4 data:float SolidMaterial = .sdata:0x805A76F0; // type:object size:0x4 scope:local data:4byte @@ -20655,7 +20657,7 @@ lbl_805A7B9C = .sdata:0x805A7B9C; // type:object size:0x4 data:4byte lbl_805A7BA0 = .sdata:0x805A7BA0; // type:object size:0x4 data:4byte lbl_805A7BA4 = .sdata:0x805A7BA4; // type:object size:0x4 data:4byte lbl_805A7BA8 = .sdata:0x805A7BA8; // type:object size:0x8 data:4byte -lbl_805A7BB0 = .sdata:0x805A7BB0; // type:object size:0x8 +white$460 = .sdata:0x805A7BB0; // type:object size:0x4 lbl_805A7BB8 = .sdata:0x805A7BB8; // type:object size:0x4 data:4byte lbl_805A7BBC = .sdata:0x805A7BBC; // type:object size:0x4 align:4 data:float lbl_805A7BC0 = .sdata:0x805A7BC0; // type:object size:0x4 align:4 data:float @@ -21160,8 +21162,8 @@ lbl_805A83EC = .sdata:0x805A83EC; // type:object size:0x4 data:4byte lbl_805A83F0 = .sdata:0x805A83F0; // type:object size:0x8 data:4byte lbl_805A83F8 = .sdata:0x805A83F8; // type:object size:0x8 data:4byte lbl_805A8400 = .sdata:0x805A8400; // type:object size:0x8 align:4 data:float -lbl_805A8408 = .sdata:0x805A8408; // type:object size:0x4 data:4byte -lbl_805A840C = .sdata:0x805A840C; // type:object size:0x4 data:4byte +sZeroX = .sdata:0x805A8408; // type:object size:0x4 scope:local data:4byte +sZeroY = .sdata:0x805A840C; // type:object size:0x4 scope:local data:4byte lbl_805A8410 = .sdata:0x805A8410; // type:object size:0x4 data:4byte lbl_805A8414 = .sdata:0x805A8414; // type:object size:0x4 data:4byte lbl_805A8418 = .sdata:0x805A8418; // type:object size:0x4 data:4byte @@ -21918,10 +21920,10 @@ lbl_805A9118 = .sbss:0x805A9118; // type:object size:0x8 data:byte lbl_805A9120 = .sbss:0x805A9120; // type:object size:0x8 data:byte lbl_805A9128 = .sbss:0x805A9128; // type:object size:0x4 data:4byte lbl_805A912C = .sbss:0x805A912C; // type:object size:0x1 data:byte -lbl_805A9130 = .sbss:0x805A9130; // type:object size:0x4 data:4byte -lbl_805A9134 = .sbss:0x805A9134; // type:object size:0x4 data:4byte -lbl_805A9138 = .sbss:0x805A9138; // type:object size:0x4 data:4byte -lbl_805A913C = .sbss:0x805A913C; // type:object size:0x1 data:byte +numTilesInHField__20CFluidPlaneCPURender = .sbss:0x805A9130; // type:object size:0x4 scope:global data:4byte +numSubdivisionsInTile__20CFluidPlaneCPURender = .sbss:0x805A9134; // type:object size:0x4 scope:global data:4byte +numSubdivisionsInHField__20CFluidPlaneCPURender = .sbss:0x805A9138; // type:object size:0x4 scope:global data:4byte +sSineWaveInitialized = .sbss:0x805A913C; // type:object size:0x1 scope:local data:byte lbl_805A9140 = .sbss:0x805A9140; // type:object size:0x1 data:byte lbl_805A9144 = .sbss:0x805A9144; // type:object size:0x4 data:4byte lbl_805A9148 = .sbss:0x805A9148; // type:object size:0x8 data:byte @@ -23972,7 +23974,7 @@ lbl_805AB5F4 = .sdata2:0x805AB5F4; // type:object size:0x4 align:4 data:float lbl_805AB5F8 = .sdata2:0x805AB5F8; // type:object size:0x4 align:4 data:float lbl_805AB5FC = .sdata2:0x805AB5FC; // type:object size:0x4 align:4 data:float lbl_805AB600 = .sdata2:0x805AB600; // type:object size:0x8 align:4 data:float -lbl_805AB608 = .sdata2:0x805AB608; // type:object size:0x1 data:byte +sRenderFog = .sdata2:0x805AB608; // type:object size:0x1 scope:global data:byte lbl_805AB609 = .sdata2:0x805AB609; // type:object size:0x1 data:byte lbl_805AB60A = .sdata2:0x805AB60A; // type:object size:0x1 data:byte lbl_805AB60C = .sdata2:0x805AB60C; // type:object size:0x4 data:4byte @@ -24609,9 +24611,9 @@ skSoundIds__11CPhazonBeam = .sdata2:0x805AC118; // type:object size:0x4 data:2by skPhazonVeins__11CPhazonBeam = .sdata2:0x805AC11C; // type:object size:0x4 data:4byte skPhazonVeins2__11CPhazonBeam = .sdata2:0x805AC120; // type:object size:0x4 data:4byte skScaleLocator__11CPhazonBeam = .sdata2:0x805AC124; // type:object size:0x4 data:4byte -@659 = .sdata2:0x805AC128; // type:object size:0x4 align:4 data:float -@660 = .sdata2:0x805AC12C; // type:object size:0x4 align:4 data:float -@761 = .sdata2:0x805AC130; // type:object size:0x4 align:4 data:float +@659 = .sdata2:0x805AC128; // type:object size:0x4 scope:local align:4 data:float +@660 = .sdata2:0x805AC12C; // type:object size:0x4 scope:local align:4 data:float +@761 = .sdata2:0x805AC130; // type:object size:0x4 scope:local align:4 data:float lbl_805AC134 = .sdata2:0x805AC134; // type:object size:0x4 align:4 data:float lbl_805AC138 = .sdata2:0x805AC138; // type:object size:0x4 align:4 data:float lbl_805AC13C = .sdata2:0x805AC13C; // type:object size:0x4 align:4 data:float @@ -24777,10 +24779,10 @@ lbl_805AC428 = .sdata2:0x805AC428; // type:object size:0x8 align:8 data:double lbl_805AC430 = .sdata2:0x805AC430; // type:object size:0x8 align:8 data:double lbl_805AC438 = .sdata2:0x805AC438; // type:object size:0x4 align:4 data:float lbl_805AC43C = .sdata2:0x805AC43C; // type:object size:0x4 align:4 data:float -lbl_805AC440 = .sdata2:0x805AC440; // type:object size:0x4 align:4 data:float -lbl_805AC444 = .sdata2:0x805AC444; // type:object size:0x4 align:4 data:float -lbl_805AC448 = .sdata2:0x805AC448; // type:object size:0x8 align:8 data:double -lbl_805AC450 = .sdata2:0x805AC450; // type:object size:0x8 align:4 data:float +@659 = .sdata2:0x805AC440; // type:object size:0x4 scope:local align:4 data:float +@660 = .sdata2:0x805AC444; // type:object size:0x4 scope:local align:4 data:float +@667 = .sdata2:0x805AC448; // type:object size:0x8 scope:local align:8 data:double +@732 = .sdata2:0x805AC450; // type:object size:0x4 scope:local align:4 data:float lbl_805AC458 = .sdata2:0x805AC458; // type:object size:0x4 data:4byte lbl_805AC45C = .sdata2:0x805AC45C; // type:object size:0x4 data:4byte lbl_805AC460 = .sdata2:0x805AC460; // type:object size:0x4 data:4byte diff --git a/include/GuiSys/CGuiFrame.hpp b/include/GuiSys/CGuiFrame.hpp index e2dd50143..716c7c29c 100644 --- a/include/GuiSys/CGuiFrame.hpp +++ b/include/GuiSys/CGuiFrame.hpp @@ -8,8 +8,11 @@ class CGuiWidget; class CGuiCamera; class CGuiLight; class CGuiHeadWidget; +class CGuiWidgetDrawParms; class CGuiFrame { public: + void Update(float dt); + void Draw(const CGuiWidgetDrawParms& parms) const; CGuiWidget* FindWidget(const char* name) const; CGuiWidget* FindWidget(const rstl::string& name) const; diff --git a/include/GuiSys/CGuiObject.hpp b/include/GuiSys/CGuiObject.hpp index f98b453ef..fc0127f74 100644 --- a/include/GuiSys/CGuiObject.hpp +++ b/include/GuiSys/CGuiObject.hpp @@ -19,6 +19,7 @@ class CGuiObject { CVector3f GetLocalPosition() const; const CTransform4f& GetWorldTransform() const { return x34_worldXF; } void SetLocalPosition(const CVector3f& pos); + CTransform4f& LocalTransform() { return x4_localXF; } void SetLocalTransform(const CTransform4f& xf); void SetO2WTransform(const CTransform4f& xf); diff --git a/include/GuiSys/CGuiTextSupport.hpp b/include/GuiSys/CGuiTextSupport.hpp index 024d8cafd..01335f4d7 100644 --- a/include/GuiSys/CGuiTextSupport.hpp +++ b/include/GuiSys/CGuiTextSupport.hpp @@ -45,7 +45,11 @@ class CGuiTextSupport { const CColor& outlineCol, const CColor& geomCol, int extX, int extY, CSimplePool* store, int /*CGuiWidget::EGuiModelDrawFlags*/ drawFlags); + void SetText(const rstl::wstring&, bool clearRenderBuffer = false); void SetText(const rstl::string&, bool multipage = false); + void SetWordWrap(bool wordWrap); + void ClearRenderBuffer(); + void SetImageBaseline(bool baseline); bool SetTypeWriteEffectOptions(bool enable, float fadeTime, float rate); void SetOutlineColor(const CColor& col); void SetFontColor(const CColor& col); diff --git a/include/GuiSys/CGuiWidget.hpp b/include/GuiSys/CGuiWidget.hpp index 993860e5d..4d98ea22d 100644 --- a/include/GuiSys/CGuiWidget.hpp +++ b/include/GuiSys/CGuiWidget.hpp @@ -60,6 +60,7 @@ class CGuiWidget : public CGuiObject { virtual void OnActivate(); short GetWorkerId() const { return xb4_workerId; } + void SetDepthTest(bool depthTest) { xb6_31_depthTest = depthTest; } void SetIsVisible(bool visible); void SetIsActive(bool active); void SetColor(const CColor& color); @@ -71,6 +72,7 @@ class CGuiWidget : public CGuiObject { static CGuiWidgetParms ReadWidgetHeader(CGuiFrame* frame, CInputStream& in); + const CTransform4f& GetTransform() const { return x74_transform; } CGuiFrame* GetFrame() { return xb0_frame; } static CGuiWidget* Create(CGuiFrame* frame, CInputStream& in, CSimplePool* sp); diff --git a/include/GuiSys/CGuiWidgetDrawParms.hpp b/include/GuiSys/CGuiWidgetDrawParms.hpp index 86d9a6620..d45c1769c 100644 --- a/include/GuiSys/CGuiWidgetDrawParms.hpp +++ b/include/GuiSys/CGuiWidgetDrawParms.hpp @@ -9,6 +9,7 @@ class CGuiWidgetDrawParms { public: CGuiWidgetDrawParms(float alpha) : mAlpha(alpha), mCameraOffset(0.f, 0.f, 0.f) {} + CGuiWidgetDrawParms(float alpha, const CVector3f& offset) : mAlpha(alpha), mCameraOffset(offset) {} float GetAlpha() const { return mAlpha; } const CVector3f& GetCameraOffset() const { return mCameraOffset; } static CGuiWidgetDrawParms Default() { return sDefaultDrawParms; } diff --git a/include/Kyoto/Basics/CCast.hpp b/include/Kyoto/Basics/CCast.hpp index 385d71cfb..2c849426f 100644 --- a/include/Kyoto/Basics/CCast.hpp +++ b/include/Kyoto/Basics/CCast.hpp @@ -55,12 +55,21 @@ inline ushort FtoUS(register float in) { return *ptr; } +inline float StoF(register const short& in) { + register float r; + asm { + psq_l r, 0(in), 1, OS_FASTCAST_S16 + } + return r; +} + #else inline uchar ToUint8(float in) { return static_cast< uchar >(in); } inline char ToInt8(float in) { return static_cast< char >(in); } inline float ToReal32(uchar in) { return static_cast< float >(in); } inline short FtoS(float in) { return static_cast< short >(in); } inline ushort FtoUS(float in) { return static_cast< ushort >(in); } +inline float StoF(const short& in) { return static_cast< float >(in); } #endif inline uchar ToUint8(int c) { return static_cast< uchar >(c); } diff --git a/include/Kyoto/CToken.hpp b/include/Kyoto/CToken.hpp index 74ee4cab1..6bca2fb8a 100644 --- a/include/Kyoto/CToken.hpp +++ b/include/Kyoto/CToken.hpp @@ -10,7 +10,7 @@ class CObjectReference; class CToken { public: - CToken() {} + CToken() : x0_objRef(NULL), x4_lockHeld_init(0) {} CToken(CObjectReference* ref); CToken(IObj* obj); // : x0_objRef(new CObjectReference(obj)), x4_lockHeld(false) {} CToken(const CToken& other); @@ -31,7 +31,10 @@ class CToken { private: CObjectReference* x0_objRef; - bool x4_lockHeld; + union { + bool x4_lockHeld; + int x4_lockHeld_init; + }; }; #endif // _CTOKEN diff --git a/include/Kyoto/Graphics/CGraphics.hpp b/include/Kyoto/Graphics/CGraphics.hpp index 05318692e..0ad229f08 100644 --- a/include/Kyoto/Graphics/CGraphics.hpp +++ b/include/Kyoto/Graphics/CGraphics.hpp @@ -277,6 +277,7 @@ class CGraphics { static void EnableLight(ERglLight light); static void LoadLight(ERglLight light, const CLight& info); static void SetLightState(uchar lights); + static uchar GetLightMask() { return mLightActive; } static void SetViewMatrix(); static void SetScissor(int left, int bottom, int width, int height); static void SetLineWidth(float w, ERglTexOffset offs); diff --git a/include/Kyoto/Math/CMath.hpp b/include/Kyoto/Math/CMath.hpp index bffac847d..ed9880267 100644 --- a/include/Kyoto/Math/CMath.hpp +++ b/include/Kyoto/Math/CMath.hpp @@ -110,4 +110,14 @@ template < typename T > const T& CMath::Clamp(const T& min, const T& val, const T& max) { return min > val ? min : max < val ? max : val; } + +template < typename T > +const T& CMath::Min(const T& a, const T& b) { + return a < b ? a : b; +} + +template < typename T > +const T& CMath::Max(const T& a, const T& b) { + return a > b ? a : b; +} #endif // _CMATH diff --git a/include/Kyoto/Math/CQuaternion.hpp b/include/Kyoto/Math/CQuaternion.hpp index 59669febf..3c7474a35 100644 --- a/include/Kyoto/Math/CQuaternion.hpp +++ b/include/Kyoto/Math/CQuaternion.hpp @@ -37,9 +37,10 @@ class CQuaternion { static CQuaternion LookAt(const CUnitVector3f&, const CUnitVector3f&, const CRelAngle&); // normalize_angle__Ff // IsValidQuaternion__11CQuaternionCFf + static CQuaternion Slerp(const CQuaternion& a, const CQuaternion& b, float t); static CQuaternion SlerpLocal(const CQuaternion& from, const CQuaternion& to, float t); // AngleFrom__11CQuaternionCFRC11CQuaternion - // BuildEquivalent__11CQuaternionCFv + CQuaternion BuildEquivalent() const; // BuildNormalized__11CQuaternionCFv static CQuaternion AxisAngle(const CUnitVector3f&, const CRelAngle&); CVector3f Transform(const CVector3f&) const; diff --git a/include/MetaRender/CCubeRenderer.hpp b/include/MetaRender/CCubeRenderer.hpp index ff06fd0d9..c00393bd2 100644 --- a/include/MetaRender/CCubeRenderer.hpp +++ b/include/MetaRender/CCubeRenderer.hpp @@ -153,6 +153,8 @@ class CCubeRenderer : public IRenderer, public IWeaponRenderer, public TOneStati void SetRequestRGBA6(bool req) { x318_26_requestRGBA6 = req; } CTexture* GetRealReflection(); const CPlane& GetViewPlane() const { return xb0_viewPlane; } + const CTexture& GetZeroTexture() const { return xe4_blackTex; } + static CCubeRenderer* That() { return sRenderer; } private: CResFactory& x8_factory; diff --git a/include/MetroidPrime/CAutoMapper.hpp b/include/MetroidPrime/CAutoMapper.hpp new file mode 100644 index 000000000..0ef31e8f8 --- /dev/null +++ b/include/MetroidPrime/CAutoMapper.hpp @@ -0,0 +1,280 @@ +#ifndef _CAUTOMAPPER +#define _CAUTOMAPPER + +#include "types.h" + +#include "Kyoto/Audio/CSfxHandle.hpp" +#include "Kyoto/CToken.hpp" +#include "Kyoto/Math/CQuaternion.hpp" +#include "Kyoto/Math/CVector2i.hpp" +#include "Kyoto/Math/CVector3f.hpp" +#include "Kyoto/TToken.hpp" + +#include "rstl/auto_ptr.hpp" +#include "rstl/list.hpp" +#include "rstl/optional_object.hpp" +#include "rstl/reserved_vector.hpp" +#include "rstl/single_ptr.hpp" +#include "rstl/vector.hpp" + +#include "MetroidPrime/CInGameGuiManager.hpp" +#include "MetroidPrime/TGameTypes.hpp" + +class CGuiFrame; +class CGuiTextPane; +class CGuiWidget; +class CMapUniverse; +class CMapWorldInfo; +class CModel; +class CStateManager; +class CStringTable; +class CTexture; +class CUnitVector3f; +class IWorld; +struct CFinalInput; + +class CAutoMapper { +public: + enum ELoadPhase { + kLP_LoadResources, + kLP_LoadUniverse, + kLP_Done, + }; + + enum EAutoMapperState { + kAMS_MiniMap, + kAMS_MapScreen, + kAMS_MapScreenUniverse, + }; + + enum EZoomState { + kZS_None, + kZS_In, + kZS_Out, + }; + + struct SAutoMapperRenderState { + enum Ease { + kE_None, + kE_Linear, + kE_Out, + kE_In, + kE_InOut, + }; + + CVector2i x0_viewportSize; + CQuaternion x8_camOrientation; + float x18_camDist; + float x1c_camAngle; + CVector3f x20_areaPoint; + float x2c_drawDepth1; + float x30_drawDepth2; + float x34_alphaSurfaceVisited; + float x38_alphaOutlineVisited; + float x3c_alphaSurfaceUnvisited; + float x40_alphaOutlineUnvisited; + Ease x44_viewportEase; + Ease x48_camEase; + Ease x4c_pointEase; + Ease x50_depth1Ease; + Ease x54_depth2Ease; + Ease x58_alphaEase; + + SAutoMapperRenderState(const SAutoMapperRenderState& other); + SAutoMapperRenderState(const CVector2i& viewportSize, const CQuaternion& camOrientation, + float camDist, float camAngle, const CVector3f& areaPoint, + float drawDepth1, float drawDepth2, float alphaSurfaceVisited, + float alphaOutlineVisited, float alphaSurfaceUnvisited, + float alphaOutlineUnvisited); + + void ResetInterpolation(); + static void InterpolateWithClamp(const SAutoMapperRenderState& a, + SAutoMapperRenderState& out, + const SAutoMapperRenderState& b, float t); + }; + + struct SAutoMapperHintStep { + enum Type { + kHST_PanToArea, + kHST_PanToWorld, + kHST_SwitchToUniverse, + kHST_SwitchToWorld, + kHST_ShowBeacon, + kHST_ZoomIn, + kHST_ZoomOut, + }; + + Type x0_type; + union { + CAssetId x4_worldId; + TAreaId x4_areaId; + float x4_float; + }; + bool x8_processing; + + SAutoMapperHintStep(Type type, int val) : x0_type(type), x4_areaId(val), x8_processing(false) {} + SAutoMapperHintStep(Type type, float val) : x0_type(type), x4_float(val), x8_processing(false) {} + }; + + struct SAutoMapperHintLocation { + uint x0_showBeacon; + float x4_beaconAlpha; + CAssetId x8_worldId; + TAreaId xc_areaId; + + SAutoMapperHintLocation(uint showBeacon, float beaconAlpha, CAssetId worldId, TAreaId areaId); + }; + + // Virtuals + virtual ~CAutoMapper(); + + // Public methods + explicit CAutoMapper(CStateManager& stateMgr); + bool CheckLoadComplete(); + bool CanLeaveMapScreen(const CStateManager& mgr) const; + float GetMapRotationX() const { return xa8_renderState0.x1c_camAngle; } + TAreaId GetFocusAreaIndex() const { return xa0_curAreaId; } + void SetCurWorldAssetId(int mlvlId); + void MuteAllLoopedSounds(); + void UnmuteAllLoopedSounds(); + void ProcessControllerInput(const CFinalInput& input, const CStateManager& mgr); + bool IsInPlayerControlState() const { + return IsInMapperState(kAMS_MapScreen) || IsInMapperState(kAMS_MapScreenUniverse); + } + void Update(float dt, const CStateManager& mgr); + void Draw(const CStateManager& mgr, const CTransform4f& xf, float alpha) const; + float GetTimeIntoInterpolation() const { return x1c8_interpTime; } + void BeginMapperStateTransition(EAutoMapperState state, const CStateManager& mgr); + void CompleteMapperStateTransition(const CStateManager& mgr); + void ResetInterpolationTimer(float duration); + SAutoMapperRenderState BuildMiniMapWorldRenderState(const CStateManager& stateMgr, + const CQuaternion& rot, + int area) const; + SAutoMapperRenderState BuildMapScreenWorldRenderState(const CStateManager& mgr, + const CQuaternion& rot, + int area, bool doingHint) const; + SAutoMapperRenderState BuildMapScreenUniverseRenderState(const CStateManager& mgr, + const CQuaternion& rot, + int area) const; + void LeaveMapScreenState(); + void ProcessMapScreenInput(const CFinalInput& input, const CStateManager& mgr); + static CQuaternion GetMiniMapCameraOrientation(const CStateManager& stateMgr); + CVector3f GetAreaPointOfInterest(const CStateManager& mgr, int aid) const; + struct SClosestWorldResult { + int x0_worldIdx; + int x4_areaIdx; + }; + + int FindClosestVisibleArea(const CVector3f& point, const CUnitVector3f& camDir, + const CStateManager& mgr, const IWorld& wld, + const CMapWorldInfo& mwInfo) const; + SClosestWorldResult FindClosestVisibleWorld(const CVector3f& point, const CUnitVector3f& camDir, + const CStateManager& mgr) const; + EAutoMapperState GetNextState() const { return x1c0_nextState; } + bool IsInMapperState(EAutoMapperState state) const; + bool IsInMapperStateTransition() const; + bool IsRenderStateInterpolating() const; + bool IsFullyInMiniMapState() const { return IsInMapperState(kAMS_MiniMap); } + bool IsFullyOutOfMiniMapState() const { + return x1bc_state != kAMS_MiniMap && x1c0_nextState != kAMS_MiniMap; + } + void OnNewInGameGuiState(EInGameGuiState state, CStateManager& mgr); + float GetInterp() const { + if (x1c4_interpDur > 0.f) + return x1c8_interpTime / x1c4_interpDur; + return 0.f; + } + +private: + bool NotHintNavigating() const; + bool CanLeaveMapScreenInternal(const CStateManager& mgr) const; + void LeaveMapScreen(const CStateManager& mgr) const; + void SetupMiniMapWorld(CStateManager& mgr); + bool HasCurrentMapUniverseWorld(); + bool CheckDummyWorldLoad(const CStateManager& mgr); + void UpdateHintNavigation(float dt, const CStateManager& mgr); + static CVector2i GetMiniMapViewportSize(); + static CVector2i GetMapScreenViewportSize(); + static float GetMapAreaMiniMapDrawDepth(); + float GetMapAreaMaxDrawDepth(const CStateManager& mgr, int aid) const; + static float GetMapAreaMiniMapDrawAlphaSurfaceVisited(const CStateManager& mgr); + static float GetMapAreaMiniMapDrawAlphaOutlineVisited(const CStateManager& mgr); + static float GetMapAreaMiniMapDrawAlphaSurfaceUnvisited(const CStateManager& mgr); + static float GetMapAreaMiniMapDrawAlphaOutlineUnvisited(const CStateManager& mgr); + float GetDesiredMiniMapCameraDistance(const CStateManager& mgr) const; + float GetBaseMapScreenCameraMoveSpeed() const; + float GetClampedMapScreenCameraDistance(float value) const; + float GetFinalMapScreenCameraMoveSpeed() const; + void ProcessMapRotateInput(const CFinalInput& input, const CStateManager& mgr); + void ProcessMapZoomInput(const CFinalInput& input, const CStateManager& mgr); + void ProcessMapPanInput(const CFinalInput& input, const CStateManager& mgr); + void SetShouldPanningSoundBePlaying(bool shouldBePlaying); + void SetShouldZoomingSoundBePlaying(bool shouldBePlaying); + void SetShouldRotatingSoundBePlaying(bool shouldBePlaying); + void TransformRenderStatesWorldToUniverse(); + void TransformRenderStatesUniverseToWorld(); + void TransformRenderStateWorldToUniverse(SAutoMapperRenderState& state); + void SetupHintNavigation(); + CAssetId GetAreaHintDescriptionString(CAssetId mreaId); + + template < class T > + void SetResLockState(T& list, bool lock); + + ELoadPhase x4_loadPhase; + TCachedToken< CMapUniverse > x8_mapu; + rstl::vector< rstl::auto_ptr< IWorld > > x14_dummyWorlds; + IWorld* x24_world; + rstl::single_ptr< TCachedToken< CGuiFrame > > x28_frmeMapScreen; + CGuiFrame* x2c_frmeInitialized; + TCachedToken< CModel > x30_miniMapSamus; + TCachedToken< CTexture > x3c_hintBeacon; + rstl::reserved_vector< CToken, 5 > x48_mapIcons; + CAssetId x74_areaHintDescId; + rstl::optional_object< TCachedToken< CStringTable > > x78_areaHintDesc; + CAssetId x88_mapAreaStringId; + rstl::optional_object< TCachedToken< CStringTable > > x8c_mapAreaString; + uint x9c_worldIdx; + TAreaId xa0_curAreaId; + TAreaId xa4_otherAreaId; + SAutoMapperRenderState xa8_renderState0; + SAutoMapperRenderState x104_renderState1; + SAutoMapperRenderState x160_renderState2; + EAutoMapperState x1bc_state; + EAutoMapperState x1c0_nextState; + float x1c4_interpDur; + float x1c8_interpTime; + CSfxHandle x1cc_panningSfx; + CSfxHandle x1d0_rotatingSfx; + CSfxHandle x1d4_zoomingSfx; + float x1d8_flashTimer; + float x1dc_playerFlashPulse; + rstl::list< SAutoMapperHintStep > x1e0_hintSteps; + rstl::list< SAutoMapperHintLocation > x1f8_hintLocations; + rstl::reserved_vector< CToken, 9 > x210_lstick; + rstl::reserved_vector< CToken, 9 > x25c_cstick; + rstl::reserved_vector< CToken, 2 > x2a8_ltrigger; + rstl::reserved_vector< CToken, 2 > x2bc_rtrigger; + rstl::reserved_vector< CToken, 2 > x2d0_abutton; + uint x2e4_lStickPos; + uint x2e8_rStickPos; + uint x2ec_lTriggerPos; + uint x2f0_rTriggerPos; + uint x2f4_aButtonPos; + CGuiTextPane* x2f8_textpane_areaname; + CGuiTextPane* x2fc_textpane_hint; + CGuiTextPane* x300_textpane_instructions; + CGuiTextPane* x304_textpane_instructions1; + CGuiTextPane* x308_textpane_instructions2; + CGuiWidget* x30c_basewidget_leftPane; + CGuiWidget* x310_basewidget_yButtonPane; + CGuiWidget* x314_basewidget_bottomPane; + float x318_leftPanePos; + float x31c_yButtonPanePos; + float x320_bottomPanePos; + EZoomState x324_zoomState; + int x328_; + bool x32c_loadingDummyWorld; +}; +CHECK_SIZEOF(CAutoMapper, 0x330) + +#endif // _CAUTOMAPPER diff --git a/include/MetroidPrime/CFluidPlaneCPU.hpp b/include/MetroidPrime/CFluidPlaneCPU.hpp index 65fd837f8..027064965 100644 --- a/include/MetroidPrime/CFluidPlaneCPU.hpp +++ b/include/MetroidPrime/CFluidPlaneCPU.hpp @@ -9,6 +9,8 @@ class CFluidPlaneCPU : public CFluidPlane { public: class CTurbulence { public: + static int kTableSize; + CTurbulence(float speed, float distance, float freqMax, float freqMin, float phaseMax, float phaseMin, float amplitudeMax, float amplitudeMin); ~CTurbulence(); @@ -47,7 +49,7 @@ class CFluidPlaneCPU : public CFluidPlane { float turbAmplitudeMax, float turbAmplitudeMin, float specularMin, float specularMax, float reflectionBlend, float reflectionSize, float rippleIntensity); - ~CFluidPlaneCPU(); + ~CFluidPlaneCPU() {} void Render(const CStateManager& mgr, float alpha, const CAABox& aabb, const CTransform4f& xf, const CTransform4f& areaXf, bool noNormals, const CFrustumPlanes& frustum, diff --git a/include/MetroidPrime/CFluidPlaneManager.hpp b/include/MetroidPrime/CFluidPlaneManager.hpp index 8e5a60b74..b8886c8dc 100644 --- a/include/MetroidPrime/CFluidPlaneManager.hpp +++ b/include/MetroidPrime/CFluidPlaneManager.hpp @@ -35,13 +35,77 @@ class CRippleManager; class CFluidPlaneCPURender { public: - struct SPatchInfo {}; - struct SRippleInfo {}; - struct SHFieldSample {}; + enum ENormalMode { + kNM_None, + kNM_NoNormals, + kNM_Normals, + kNM_NBT + }; + + struct SHFieldSample { + float height; + signed char nx; + signed char ny; + signed char nz; + unsigned char wavecapIntensity; + }; + + struct SPatchInfo { + char x0_xSubdivs; + char x1_ySubdivs; + float x4_localMinX; + float x8_localMinY; + float xc_globalMinX; + float x10_globalMinY; + float x14_tileSize; + float x18_rippleResolution; + float x1c_tileHypRadius; + float x20_ooTileSize; + float x24_ooRippleResolution; + short x28_tileX; + short x2a_gridDimX; + short x2c_gridDimY; + short x2e_tileY; + const bool* x30_gridFlags; + uchar x34_redShift; + uchar x35_greenShift; + uchar x36_blueShift; + uchar x37_normalMode; + float x38_wavecapIntensityScale; + + SPatchInfo(const CVector3f& localMin, const CVector3f& localMax, const CVector3f& pos, + float rippleResolution, float tileSize, float wavecapIntensityScale, + int numSubdivisionsInHField, int normalMode, int redShift, int greenShift, + uchar blueShift, int tileX, int gridDimX, int gridDimY, int tileY, + const bool* gridFlags); + }; + + struct SRippleInfo { + const CRipple* x0_ripple; + int x4_fromX; + int x8_toX; + int xc_fromY; + int x10_toY; + int x14_gfromX; + int x18_gtoX; + int x1c_gfromY; + int x20_gtoY; + + SRippleInfo(const CRipple& ripple, int fromX, int toX, int fromY, int toY) + : x0_ripple(&ripple) + , x14_gfromX(fromX) + , x18_gtoX(toX) + , x1c_gfromY(fromY) + , x20_gtoY(toY) {} + }; + + static int numTilesInHField; + static int numSubdivisionsInTile; + static int numSubdivisionsInHField; }; class CFluidPlane { - static const float kRippleIntensityRange; + static float kRippleIntensityRange; public: enum EFluidType { @@ -111,8 +175,35 @@ extern const bool sRenderFog; class CFluidPlaneManager { public: + class CFluidProfile { + public: + void Clear(); + + private: + float x0_; + float x4_; + float x8_; + float xc_; + float x10_; + }; + + class CSplashRecord { + public: + CSplashRecord(float time, TUniqueId id) : x0_time(time), x4_id(id) {} + void SetTime(float t) { x0_time = t; } + float GetTime() const { return x0_time; } + TUniqueId GetUniqueId() const { return x4_id; } + + private: + float x0_time; + TUniqueId x4_id; + }; + CFluidPlaneManager(); - // TODO + + void Update(float dt); + void StartFrame(bool b) const; + void EndFrame() const; void CreateSplash(TUniqueId splasher, CStateManager& mgr, const CScriptWater& water, const CVector3f& pos, float factor, bool sfx); @@ -122,21 +213,25 @@ class CFluidPlaneManager { float GetLastSplashDeltaTime(TUniqueId uid) const; float GetLastRippleDeltaTime(TUniqueId uid) const; + rstl::reserved_vector< CSplashRecord, 32 >& SplashRecords() { return x18_splashes; } + const rstl::reserved_vector< CSplashRecord, 32 >& GetSplashRecords() const { + return x18_splashes; + } + float GetTime() const { return x11c_uvT; } + void SetTime(float t) { x11c_uvT = t; } + float GetUVTime() const { return x11c_uvT; } - + static uint GetFreqTableIndex(float); + static void SetupRippleMap(); + static CFluidProfile sProfile; private: - class CSplashRecord { - float x0_time; - TUniqueId x4_id; - }; - CRippleManager x0_rippleManager; rstl::reserved_vector< CSplashRecord, 32 > x18_splashes; float x11c_uvT; - bool x120_; - bool x121_; + mutable bool x120_; + mutable bool x121_; }; CHECK_SIZEOF(CFluidPlaneManager, 0x124); diff --git a/include/MetroidPrime/CGameArea.hpp b/include/MetroidPrime/CGameArea.hpp index 928e7f302..a235e356a 100644 --- a/include/MetroidPrime/CGameArea.hpp +++ b/include/MetroidPrime/CGameArea.hpp @@ -192,7 +192,9 @@ class CGameArea : public IGameArea { const u8* x10d4_firstMatPtr; const CScriptAreaAttributes* x10d8_areaAttributes; EOcclusionState x10dc_occlusionState; - uchar x10e0_pad[0x60]; + uchar x10e0_pad[0x48]; + float x1128_worldLightingLevel; + uchar x112c_pad[0x14]; CPostConstructed(); ~CPostConstructed(); diff --git a/include/MetroidPrime/CGameHintInfo.hpp b/include/MetroidPrime/CGameHintInfo.hpp new file mode 100644 index 000000000..902ac3bcb --- /dev/null +++ b/include/MetroidPrime/CGameHintInfo.hpp @@ -0,0 +1,57 @@ +#ifndef _CGAMEHINTINFO +#define _CGAMEHINTINFO + +#include "types.h" + +#include "MetroidPrime/TGameTypes.hpp" +#include "rstl/string.hpp" +#include "rstl/vector.hpp" + +class CInputStream; + +class CGameHintInfo { +public: + struct SHintLocation { + CAssetId x0_mlvlId; + CAssetId x4_mreaId; + TAreaId x8_areaId; + CAssetId xc_stringId; + + SHintLocation(CInputStream& in); + }; + + class CGameHint { + public: + CGameHint(CInputStream& in, uint version); + CGameHint(const CGameHint& other); + ~CGameHint(); + + const rstl::string& GetName() const { return x0_name; } + float GetImmediateTime() const { return x10_immediateTime; } + float GetNormalTime() const { return x14_normalTime; } + CAssetId GetStringId() const { return x18_stringId; } + float GetTextTime() const { return x1c_textTime; } + const rstl::vector< SHintLocation >& GetLocations() const { return x20_locations; } + + rstl::string x0_name; + float x10_immediateTime; + float x14_normalTime; + CAssetId x18_stringId; + float x1c_textTime; + rstl::vector< SHintLocation > x20_locations; + }; + + CGameHintInfo(CInputStream& in, uint version); + + const rstl::vector< CGameHint >& GetHints() const { return x0_hints; } + + static int FindHintIndex(const char* name); + +private: + rstl::vector< CGameHint > x0_hints; +}; + +NESTED_CHECK_SIZEOF(CGameHintInfo, SHintLocation, 0x10) +NESTED_CHECK_SIZEOF(CGameHintInfo, CGameHint, 0x30) + +#endif // _CGAMEHINTINFO diff --git a/include/MetroidPrime/CInGameGuiManager.hpp b/include/MetroidPrime/CInGameGuiManager.hpp index e9de1a157..5cb2edf3b 100644 --- a/include/MetroidPrime/CInGameGuiManager.hpp +++ b/include/MetroidPrime/CInGameGuiManager.hpp @@ -1,6 +1,10 @@ #ifndef _CINGUIGUIMANAGER #define _CINGUIGUIMANAGER +#include "types.h" + +class CStateManager; + enum EInGameGuiState { kIGGS_Zero, kIGGS_InGame, diff --git a/include/MetroidPrime/CMapArea.hpp b/include/MetroidPrime/CMapArea.hpp index c451687bb..89df63239 100644 --- a/include/MetroidPrime/CMapArea.hpp +++ b/include/MetroidPrime/CMapArea.hpp @@ -40,6 +40,7 @@ class CMapArea { void PostConstruct(); bool GetIsVisibleToAutoMapper(bool worldVis, bool areaVis) const; CVector3f GetAreaCenterPoint() const; + const CAABox& GetBoundingBox() const { return x10_box; } CTransform4f GetAreaPostTransform(const IWorld&, int); static const CVector3f& GetAreaPostTranslate(const IWorld&, int); diff --git a/include/MetroidPrime/CMapUniverse.hpp b/include/MetroidPrime/CMapUniverse.hpp new file mode 100644 index 000000000..d70b22b36 --- /dev/null +++ b/include/MetroidPrime/CMapUniverse.hpp @@ -0,0 +1,81 @@ +#ifndef _CMAPUNIVERSE +#define _CMAPUNIVERSE + +#include "types.h" + +#include "Kyoto/CToken.hpp" +#include "Kyoto/Graphics/CColor.hpp" +#include "Kyoto/Math/CTransform4f.hpp" +#include "Kyoto/Math/CVector3f.hpp" +#include "Kyoto/TToken.hpp" + +#include "rstl/string.hpp" +#include "rstl/vector.hpp" + +class CInputStream; +class CMapArea; +class CStateManager; + +class CMapUniverse { +public: + class CMapUniverseDrawParms { + float x0_alpha; + int x4_wldIdx; + CAssetId x8_wldRes; + int xc_closestHex; + float x10_flashPulse; + const CStateManager& x14_mgr; + const CTransform4f& x18_model; + const CTransform4f& x1c_view; + + public: + CMapUniverseDrawParms(float alpha, int wldIdx, CAssetId wldRes, int closestHex, + float flashPulse, const CStateManager& mgr, + const CTransform4f& model, const CTransform4f& view); + }; + + class CMapWorldData { + public: + explicit CMapWorldData(CInputStream& in, uint version); + CAssetId GetWorldAssetId() const { return x10_worldAssetId; } + const CVector3f& GetWorldCenterPoint() const { return x64_centerPoint; } + const rstl::string& GetWorldLabel() const { return x0_label; } + const CTransform4f& GetWorldTransform() const { return x14_transform; } + const CTransform4f& GetMapAreaData(int idx) const { return x44_hexagonXfs[idx]; } + uint GetNumMapAreaDatas() const { return x44_hexagonXfs.size(); } + const CColor& GetOutlineColorUnselected() const { return x60_outlineColorUnselected; } + const CColor& GetOutlineColorSelected() const { return x58_outlineColorSelected; } + const CColor& GetSurfaceColorUnselected() const { return x5c_surfColorUnselected; } + const CColor& GetSurfaceColorSelected() const { return x54_surfColorSelected; } + + private: + rstl::string x0_label; + CAssetId x10_worldAssetId; + CTransform4f x14_transform; + rstl::vector< CTransform4f > x44_hexagonXfs; + CColor x54_surfColorSelected; + CColor x58_outlineColorSelected; + CColor x5c_surfColorUnselected; + CColor x60_outlineColorUnselected; + CVector3f x64_centerPoint; + }; + + explicit CMapUniverse(CInputStream& in, uint version); + + const CMapWorldData& GetMapWorldData(int idx) const { return x10_worldDatas[idx]; } + const CMapWorldData& GetMapWorldDataByWorldId(CAssetId id); + uint GetNumMapWorldDatas() const { return x10_worldDatas.size(); } + float GetMapUniverseRadius() const { return x2c_universeRadius; } + const CVector3f& GetMapUniverseCenterPoint() const { return x20_universeCenter; } + void Draw(const CMapUniverseDrawParms& parms, const CVector3f& pos, float depth1, + float depth2) const; + +private: + CAssetId x0_hexagonId; + TLockedToken< CMapArea > x4_hexagonToken; + rstl::vector< CMapWorldData > x10_worldDatas; + CVector3f x20_universeCenter; + float x2c_universeRadius; +}; + +#endif // _CMAPUNIVERSE diff --git a/include/MetroidPrime/CMapWorld.hpp b/include/MetroidPrime/CMapWorld.hpp index 8c725f683..4f1175a62 100644 --- a/include/MetroidPrime/CMapWorld.hpp +++ b/include/MetroidPrime/CMapWorld.hpp @@ -99,25 +99,11 @@ class CMapWorld { public: CMapWorldDrawParms(float alphaSurfVisited, float alphaOlVisited, float alphaSurfUnvisited, - float alphaOlUnvisited, float alpha, float outlineWidthScale, + float alphaOlUnvisited, float alpha, const CStateManager& mgr, const CTransform4f& modelXf, const CTransform4f& viewXf, const IWorld& wld, const CMapWorldInfo& mwInfo, - float playerFlash, float hintFlash, float objectScale, bool sortDoorSurfs) - : x0_alphaSurfVisited(alphaSurfVisited) - , x4_alphaOlVisited(alphaOlVisited) - , x8_alphaSurfUnvisited(alphaSurfUnvisited) - , xc_alphaOlUnvisited(alphaOlUnvisited) - , x10_alpha(alpha) - , x14_outlineWidthScale(outlineWidthScale) - , x18_mgr(mgr) - , x1c_modelXf(modelXf) - , x20_viewXf(viewXf) - , x24_wld(wld) - , x28_mwInfo(mwInfo) - , x2c_playerFlashIntensity(playerFlash) - , x30_hintFlashIntensity(hintFlash) - , x34_objectScale(objectScale) - , x38_sortDoorSurfs(sortDoorSurfs) {} + float outlineWidthScale, bool sortDoorSurfs, + float playerFlash, float hintFlash, float objectScale); const IWorld& GetWorld() const { return x24_wld; } float GetOutlineWidthScale() const { return x14_outlineWidthScale; } const CTransform4f& GetPlaneProjectionTransform() const { return x1c_modelXf; } @@ -152,16 +138,16 @@ class CMapWorld { const rstl::vector< CMapAreaBFSInfo >& vec) const; void SetWhichMapAreasLoaded(const IWorld& wld, int start, int count); void MoveMapAreaToList(CMapAreaData* data, EMapAreaList list); - int GetCurrentMapAreaDepth(const IWorld& wld, TAreaId aid); + int GetCurrentMapAreaDepth(const IWorld& wld, int aid) const; rstl::vector< int > GetVisibleAreas(const IWorld& wld, const CMapWorldInfo& mwInfo) const; void Draw(const CMapWorldDrawParms& parms, int curArea, int otherArea, float depth1, float depth2, - bool inMapScreen); + bool inMapScreen) const; void DoBFS(const IWorld& wld, int startArea, int areaCount, float surfDepth, float outlineDepth, bool checkLoad, rstl::vector< CMapAreaBFSInfo >& bfsInfos); bool IsMapAreaValid(const IWorld& wld, int areaIdx, bool checkLoad) const; void DrawAreas(const CMapWorldDrawParms& parms, int selArea, const rstl::vector< CMapAreaBFSInfo >& bfsInfos, bool inMapScreen); - void RecalculateWorldSphere(const CMapWorldInfo& mwInfo, const IWorld& wld); + void RecalculateWorldSphere(const CMapWorldInfo& mwInfo, const IWorld& wld) const; CVector3f ConstrainToWorldVolume(const CVector3f& point, const CVector3f& lookVec) const; void ClearTraversedFlags(); void SetWhichMapAreasLoaded(const IWorld& wld, int start, int count, bool load); diff --git a/include/MetroidPrime/CMapWorldInfo.hpp b/include/MetroidPrime/CMapWorldInfo.hpp index 22f2f7256..1cf0a9af3 100644 --- a/include/MetroidPrime/CMapWorldInfo.hpp +++ b/include/MetroidPrime/CMapWorldInfo.hpp @@ -5,7 +5,11 @@ class CMapWorldInfo { public: + bool IsMapped(TAreaId areaId) const; + bool IsAreaVisited(TAreaId areaId) const; bool IsAreaVisible(const TAreaId areaId) const; + bool IsWorldVisible(const TAreaId areaId) const; + bool IsAnythingSet(); bool IsDoorVisited(const TEditorId eid) const; void SetDoorVisited(TEditorId eid, const bool visited); }; diff --git a/include/MetroidPrime/CRipple.hpp b/include/MetroidPrime/CRipple.hpp index 3b556e695..4dbb03923 100644 --- a/include/MetroidPrime/CRipple.hpp +++ b/include/MetroidPrime/CRipple.hpp @@ -31,6 +31,12 @@ class CRipple { void SetTime(float t) { x4_time = t; } float GetTimeFalloff() const { return x14_timeFalloff; } + float GetDistFalloff() const { return x18_distFalloff; } + float GetFrequency() const { return x1c_frequency; } + float GetLookupAmplitude() const { return x24_lookupAmplitude; } + float GetOoTimeFalloff() const { return x28_ooTimeFalloff; } + float GetOoDistFalloff() const { return x2c_ooDistFalloff; } + float GetLookupPhase() const { return x38_lookupPhase; } const CVector3f& GetCenter() const { return x8_center; } static const float kDefaultScale; diff --git a/include/MetroidPrime/CWorld.hpp b/include/MetroidPrime/CWorld.hpp index 2f94f5a9f..ff08e5ef6 100644 --- a/include/MetroidPrime/CWorld.hpp +++ b/include/MetroidPrime/CWorld.hpp @@ -220,7 +220,7 @@ class CDummyWorld : public IWorld { TAreaId x3c_curAreaId; public: - CDummyWorld(CAssetId mlvlId, bool loadMap); + CDummyWorld(CAssetId mlvlId); ~CDummyWorld() override; CAssetId IGetWorldAssetId() const override; CAssetId IGetStringTableAssetId() const override; diff --git a/include/MetroidPrime/Player/CGameOptions.hpp b/include/MetroidPrime/Player/CGameOptions.hpp index 3d8ed1fa0..e0948676f 100644 --- a/include/MetroidPrime/Player/CGameOptions.hpp +++ b/include/MetroidPrime/Player/CGameOptions.hpp @@ -48,6 +48,7 @@ class CGameOptions { const bool GetInvertYAxis() const { return x68_25_invertY; } void SetIsRumbleEnabled(const bool rumble); const bool GetIsRumbleEnabled() const { return x68_26_rumble; } + bool GetIsHintSystemEnabled() const { return x68_28_hintSystem; } private: rstl::reserved_vector< uchar, 64 > x0_; diff --git a/include/MetroidPrime/Player/CGameState.hpp b/include/MetroidPrime/Player/CGameState.hpp index daffe8a29..271d8e4a6 100644 --- a/include/MetroidPrime/Player/CGameState.hpp +++ b/include/MetroidPrime/Player/CGameState.hpp @@ -30,6 +30,7 @@ class CGameState { CAssetId CurrentWorldAssetId() const; void WriteBackupBuf(); + CWorldState& StateForWorld(CAssetId mlvlId); CWorldState& CurrentWorldState(); void ImportPersistentOptions(const CSystemState&); diff --git a/include/MetroidPrime/Player/CHintOptions.hpp b/include/MetroidPrime/Player/CHintOptions.hpp index ab0aae55f..e5e99f32a 100644 --- a/include/MetroidPrime/Player/CHintOptions.hpp +++ b/include/MetroidPrime/Player/CHintOptions.hpp @@ -8,23 +8,31 @@ enum EHintState { kHS_Zero, kHS_Waiting, kHS_Displaying, kHS_Delayed }; -struct SHintState { - EHintState x0_state; - float x4_time; - bool x8_dismissed; -}; - class CHintOptions { public: + struct SHintState { + EHintState x0_state; + float x4_time; + bool x8_dismissed; + + bool CanContinue(); + }; + void SetHintNextTime(); void DelayHint(const rstl::string& name); void ActivateImmediateHintTimer(const rstl::string& name); void ActivateContinueDelayHintTimer(const rstl::string& name); + const SHintState* GetCurrentDisplayedHint() const; + int GetNextHintIdx(); + const rstl::vector< SHintState >& GetHintStates() const { return x0_hintStates; } + private: rstl::vector< SHintState > x0_hintStates; int x10_nextHintIdx; }; +typedef CHintOptions::SHintState SHintState; + #endif // _CHINTOPTIONS diff --git a/include/MetroidPrime/Player/CPlayer.hpp b/include/MetroidPrime/Player/CPlayer.hpp index 06ab540b9..6d42f2b58 100644 --- a/include/MetroidPrime/Player/CPlayer.hpp +++ b/include/MetroidPrime/Player/CPlayer.hpp @@ -388,6 +388,7 @@ class CPlayer : public CPhysicsActor, public TOneStatic< CPlayer > { float GetGravity() const; float GetAttachedActorStruggle() const; + float GetGunAlpha() const { return x494_gunAlpha; } void SetAttachedActorStruggle(float struggle) { xa28_attachedActorStruggle = struggle; } // PlayerHint diff --git a/include/MetroidPrime/Player/CSystemState.hpp b/include/MetroidPrime/Player/CSystemState.hpp index 1f3ad12fa..479b21db7 100644 --- a/include/MetroidPrime/Player/CSystemState.hpp +++ b/include/MetroidPrime/Player/CSystemState.hpp @@ -22,6 +22,9 @@ class CSystemState { bool GetCinematicState(rstl::pair< CAssetId, TEditorId > cineId) const; void SetCinematicState(rstl::pair< CAssetId, TEditorId > cineId, bool state); + int GetAutoMapperKeyState() const { return xbc_autoMapperKeyState; } + void SetAutoMapperKeyState(int state) { xbc_autoMapperKeyState = state; } + bool GetShowPowerBombAmmoMessage() const; void IncrementPowerBombAmmoCount(); diff --git a/include/MetroidPrime/Player/CWorldState.hpp b/include/MetroidPrime/Player/CWorldState.hpp index 0158c1c7c..1ae5982de 100644 --- a/include/MetroidPrime/Player/CWorldState.hpp +++ b/include/MetroidPrime/Player/CWorldState.hpp @@ -3,10 +3,18 @@ #include "Kyoto/SObjectTag.hpp" +namespace rstl { +template < typename T > +class rc_ptr; +} // namespace rstl + +class CMapWorldInfo; + class CWorldState { public: ~CWorldState(); void SetDesiredAreaAssetId(CAssetId id); + rstl::rc_ptr< CMapWorldInfo > GetMapWorldInfo() const; private: uchar pad[0x18]; diff --git a/include/MetroidPrime/ScriptObjects/CScriptWater.hpp b/include/MetroidPrime/ScriptObjects/CScriptWater.hpp index 7ce8c0c38..4c955bebc 100644 --- a/include/MetroidPrime/ScriptObjects/CScriptWater.hpp +++ b/include/MetroidPrime/ScriptObjects/CScriptWater.hpp @@ -58,6 +58,8 @@ class CScriptWater : public CScriptTrigger { void SetupGrid(bool recomputeClipping); void SetupGridClipping(CStateManager&, int computeVerts); int GetPatchRenderFlags(int x, int y) const; + int GetPatchDimensionX() const { return x2d0_patchDimX; } + int GetPatchDimensionY() const { return x2d4_patchDimY; } void SetMorphing(const bool m); const CScriptWater* GetNextConnectedWater(const CStateManager&) const; // RenderSurface__12CScriptWaterFv @@ -82,12 +84,13 @@ class CScriptWater : public CScriptTrigger { ushort GetUnmorphVisorRunoffSfx() const { return x262_unmorphVisorRunoffSfx; } // GetFluidType__12CScriptWaterCFv bool IsMorphing() const { return x2e8_26_morphing; } + float GetMorphFactor() const { return x1f8_morphFactor; } // GetFrustumPlanes__12CScriptWaterCFv int GetSplashIndex(float scale) const; const rstl::optional_object< TLockedToken< CGenDescription > >& GetSplashEffect(float scale) const; int GetSplashSound(float scale) const; float GetSplashEffectScale(float scale) const; - // GetSplashColor__12CScriptWaterCFv + const CColor& GetSplashColor() const { return x2a4_splashColor; } static const float kSplashScales[6]; diff --git a/include/MetroidPrime/Tweaks/CTweakPlayerRes.hpp b/include/MetroidPrime/Tweaks/CTweakPlayerRes.hpp index 178646077..81a26feef 100644 --- a/include/MetroidPrime/Tweaks/CTweakPlayerRes.hpp +++ b/include/MetroidPrime/Tweaks/CTweakPlayerRes.hpp @@ -15,7 +15,30 @@ struct CTweakPlayerRes : public ITweakObject, public TOneStatic< CTweakPlayerRes CAssetId xc_elevatorIcon; CAssetId x10_minesBreakFirstTopIcon; CAssetId x14_minesBreakFirstBottomIcon; - char cls[0xdc]; + CAssetId x18_minesBreakSecondTopIcon; + CAssetId x1c_minesBreakSecondBottomIcon; + CAssetId x20_unk; + CAssetId x24_lStick[9]; + CAssetId x48_unk; + CAssetId x4c_cStick[9]; + CAssetId x70_unk; + CAssetId x74_lTrigger[2]; + CAssetId x7c_unk; + CAssetId x80_rTrigger[2]; + CAssetId x88_unk; + CAssetId x8c_startButton[2]; + CAssetId x94_unk; + CAssetId x98_aButton[2]; + CAssetId xa0_unk; + CAssetId xa4_bButton[2]; + CAssetId xac_unk; + CAssetId xb0_xButton[2]; + CAssetId xb8_unk; + CAssetId xbc_yButton[2]; + CAssetId xc4_ballTransitionsANCS; + CAssetId xc8_ballTransitions[5]; + CAssetId xdc_cineGun[5]; + float xf0_cinematicMoveOutofIntoPlayerDistance; CTweakPlayerRes(CInputStream& in); diff --git a/include/dolphin/gx/GXVert.h b/include/dolphin/gx/GXVert.h index af0eec30a..2b4ae3860 100644 --- a/include/dolphin/gx/GXVert.h +++ b/include/dolphin/gx/GXVert.h @@ -104,6 +104,12 @@ static inline void GXNormal3f32(const f32 x, const f32 y, const f32 z) { GXWGFifo.f32 = z; } +static inline void GXNormal3s8(const s8 x, const s8 y, const s8 z) { + GXWGFifo.s8 = x; + GXWGFifo.s8 = y; + GXWGFifo.s8 = z; +} + static inline void GXColor1u32(const u32 v) { GXWGFifo.u32 = v; } diff --git a/include/rstl/string.hpp b/include/rstl/string.hpp index 75bbb2253..ddf12bc0f 100644 --- a/include/rstl/string.hpp +++ b/include/rstl/string.hpp @@ -102,6 +102,7 @@ class basic_string { void reserve(int len) { internal_prepare_to_write(len, true); } void assign(const basic_string&); + void assign(const _CharTp*, int); basic_string& operator=(const basic_string& other) { assign(other); return *this; diff --git a/src/MetroidPrime/CAutoMapper.cpp b/src/MetroidPrime/CAutoMapper.cpp new file mode 100644 index 000000000..cee840f6a --- /dev/null +++ b/src/MetroidPrime/CAutoMapper.cpp @@ -0,0 +1,2126 @@ +#pragma inline_max_size(200) +#include "MetroidPrime/CAutoMapper.hpp" + +#include "Kyoto/Audio/CSfxManager.hpp" +#include "Kyoto/Basics/CBasics.hpp" +#include "Kyoto/CResFactory.hpp" +#include "Kyoto/CSimplePool.hpp" +#include "Kyoto/Graphics/CGraphics.hpp" +#include "Kyoto/Graphics/CModel.hpp" +#include "Kyoto/Graphics/CModelFlags.hpp" +#include "Kyoto/Graphics/CTexture.hpp" +#include "Kyoto/Math/CMath.hpp" +#include "Kyoto/Math/CRelAngle.hpp" +#include "Kyoto/SObjectTag.hpp" +#include "Kyoto/Text/CStringTable.hpp" + +#include "GuiSys/CGuiFrame.hpp" +#include "GuiSys/CGuiTextPane.hpp" +#include "GuiSys/CGuiWidgetDrawParms.hpp" + +#include "MetaRender/CCubeRenderer.hpp" + +#include "MetroidPrime/CGameArea.hpp" +#include "MetroidPrime/CMapArea.hpp" +#include "MetroidPrime/CControlMapper.hpp" +#include "MetroidPrime/CEulerAngles.hpp" +#include "MetroidPrime/CGameHintInfo.hpp" +#include "MetroidPrime/CMain.hpp" +#include "MetroidPrime/CMapUniverse.hpp" +#include "MetroidPrime/CMapWorld.hpp" +#include "MetroidPrime/CMapWorldInfo.hpp" +#include "MetroidPrime/CMemoryCard.hpp" +#include "MetroidPrime/CStateManager.hpp" +#include "MetroidPrime/Cameras/CCameraManager.hpp" +#include "MetroidPrime/Cameras/CGameCamera.hpp" +#include "MetroidPrime/Player/CGameState.hpp" + +#include "rstl/rc_ptr.hpp" +#include "rstl/StringExtras.hpp" +#include "MetroidPrime/Player/CPlayer.hpp" +#include "MetroidPrime/Tweaks/CTweakAutoMapper.hpp" +#include "MetroidPrime/Tweaks/CTweakGui.hpp" +#include "MetroidPrime/Tweaks/CTweakPlayerRes.hpp" +#include "MetroidPrime/CWorld.hpp" + +static inline float min_val(float a, float b) { return a < b ? a : b; } +static inline float max_val(float a, float b) { return a > b ? a : b; } + +inline float normalize_angle(float angle) { + float rcpTwoPi = 1.f / (2.f * M_PIF); + float twoPi = 2.f * M_PIF; + float ret = angle - static_cast< int >(angle * rcpTwoPi) * twoPi; + if (ret < 0.f) + ret += twoPi; + return ret; +} + +static const char* const skFRME_MapScreen = "FRME_MapScreen"; + +static inline const rstl::vector< CGameHintInfo::CGameHint >& GetGameHints() { + return (*reinterpret_cast< TLockedToken< CGameHintInfo >* >(gpMemoryCard))->GetHints(); +} + +#pragma inline_max_size(0) + +void CAutoMapper::Update(float dt, const CStateManager& mgr) { + if (IsFullyOutOfMiniMapState()) { + x1d8_flashTimer = static_cast< float >(fmod(x1d8_flashTimer + dt, 0.75)); + if (x1d8_flashTimer < 0.375f) { + x1dc_playerFlashPulse = x1d8_flashTimer / 0.375f; + } else { + x1dc_playerFlashPulse = (0.75f - x1d8_flashTimer) / 0.375f; + } + } + + // Initialize frame widgets when map screen frame is loaded + if (x28_frmeMapScreen.get() != NULL && x2c_frmeInitialized == NULL) { + if (x28_frmeMapScreen->TryCache()) { + x2c_frmeInitialized = x28_frmeMapScreen->GetObject(); + + CGuiTextPane* leftPane = static_cast< CGuiTextPane* >(x2c_frmeInitialized->FindWidget("textpane_left")); + leftPane->TextSupport().SetText(rstl::wstring(gpStringTable->GetString(0x2a))); + + CGuiTextPane* yicon = static_cast< CGuiTextPane* >(x2c_frmeInitialized->FindWidget("textpane_yicon")); + yicon->TextSupport().SetText(rstl::wstring(gpStringTable->GetString(0x2b))); + + x2fc_textpane_hint = static_cast< CGuiTextPane* >(x2c_frmeInitialized->FindWidget("textpane_hint")); + x300_textpane_instructions = static_cast< CGuiTextPane* >(x2c_frmeInitialized->FindWidget("textpane_instructions")); + x304_textpane_instructions1 = static_cast< CGuiTextPane* >(x2c_frmeInitialized->FindWidget("textpane_instructions1")); + x308_textpane_instructions2 = static_cast< CGuiTextPane* >(x2c_frmeInitialized->FindWidget("textpane_instructions2")); + + CGuiTextPane* mapLegend = static_cast< CGuiTextPane* >(x2c_frmeInitialized->FindWidget("textpane_mapLegend")); + mapLegend->TextSupport().SetWordWrap(false); + mapLegend->TextSupport().SetImageBaseline(true); + mapLegend->TextSupport().SetText(rstl::wstring(gpStringTable->GetString(0x31))); + + x30c_basewidget_leftPane = x2c_frmeInitialized->FindWidget("basewidget_leftPane"); + x310_basewidget_yButtonPane = x2c_frmeInitialized->FindWidget("basewidget_yButtonPane"); + x314_basewidget_bottomPane = x2c_frmeInitialized->FindWidget("basewidget_bottomPane"); + + x2f8_textpane_areaname = static_cast< CGuiTextPane* >(x2c_frmeInitialized->FindWidget("textpane_areaname")); + x2f8_textpane_areaname->SetDepthTest(false); + } + } + + // Update frame and text panes + if (x2c_frmeInitialized != NULL) { + x2c_frmeInitialized->Update(dt); + + CGuiTextPane* right1 = static_cast< CGuiTextPane* >(x2c_frmeInitialized->FindWidget("textpane_right1")); + const wchar_t imagePrefix[] = L"&image="; + const wchar_t imageSuffix[] = L";"; + rstl::wstring string; + + if (x1bc_state == kAMS_MapScreenUniverse || + (x1bc_state == kAMS_MapScreen && HasCurrentMapUniverseWorld())) { + string.reserve(0x100); + string.append(imagePrefix, -1); + rstl::string hexStr(CBasics::Stringize("%8.8X", gpTweakPlayerRes->x98_aButton[x2f4_aButtonPos])); + rstl::wstring unicodeHex = CStringExtras::ConvertToUNICODE(hexStr); + string.append(unicodeHex); + string.append(imageSuffix, -1); + } else { + string = rstl::wstring_l(L""); + } + right1->TextSupport().SetText(string); + + CGuiTextPane* right = static_cast< CGuiTextPane* >(x2c_frmeInitialized->FindWidget("textpane_right")); + rstl::wstring rightString; + if (x1bc_state == kAMS_MapScreenUniverse) { + rightString = rstl::wstring_l(gpStringTable->GetString(0x2d)); + } else if (x1bc_state == kAMS_MapScreen && HasCurrentMapUniverseWorld()) { + rightString = rstl::wstring_l(gpStringTable->GetString(0x2c)); + } else { + rightString = rstl::wstring_l(L""); + } + right->TextSupport().SetText(rightString); + } + + // Update pane positions + float dt2 = 2.f * dt; + switch (gpGameState->SystemState().GetAutoMapperKeyState()) { + case 0: + x318_leftPanePos -= dt2; + x31c_yButtonPanePos -= dt2; + x320_bottomPanePos -= dt2; + break; + case 1: + x318_leftPanePos += dt2; + x31c_yButtonPanePos -= dt2; + x320_bottomPanePos -= dt2; + break; + case 2: + x318_leftPanePos += dt2; + x31c_yButtonPanePos += dt2; + x320_bottomPanePos += dt2; + break; + default: + break; + } + + if (x318_leftPanePos < 0.f) + x318_leftPanePos = 0.f; + else if (x318_leftPanePos > 1.f) + x318_leftPanePos = 1.f; + if (x31c_yButtonPanePos < 0.f) + x31c_yButtonPanePos = 0.f; + else if (x31c_yButtonPanePos > 1.f) + x31c_yButtonPanePos = 1.f; + if (x320_bottomPanePos < 0.f) + x320_bottomPanePos = 0.f; + else if (x320_bottomPanePos > 1.f) + x320_bottomPanePos = 1.f; + + if (x30c_basewidget_leftPane != NULL) { + x30c_basewidget_leftPane->LocalTransform() = + CTransform4f::Translate(CVector3f(-15.f, 0.f, 0.f) * x318_leftPanePos) * + x30c_basewidget_leftPane->GetTransform(); + x30c_basewidget_leftPane->RecalculateTransforms(); + } + + if (x310_basewidget_yButtonPane != NULL) { + x310_basewidget_yButtonPane->LocalTransform() = + CTransform4f::Translate(CVector3f(0.f, 0.f, -3.5f) * x31c_yButtonPanePos) * + x310_basewidget_yButtonPane->GetTransform(); + x310_basewidget_yButtonPane->RecalculateTransforms(); + } + + if (x314_basewidget_bottomPane != NULL) { + x314_basewidget_bottomPane->LocalTransform() = + CTransform4f::Translate(CVector3f(0.f, 0.f, -7.f) * x320_bottomPanePos) * + x314_basewidget_bottomPane->GetTransform(); + x314_basewidget_bottomPane->RecalculateTransforms(); + } + + // Update camera and area for minimap + if (IsInMapperState(kAMS_MiniMap)) { + xa8_renderState0.x8_camOrientation = GetMiniMapCameraOrientation(mgr); + float camDist = xa8_renderState0.x18_camDist; + float desiredDist = GetDesiredMiniMapCameraDistance(mgr); + if (CMath::AbsF(camDist - desiredDist) < 3.f) { + xa8_renderState0.x18_camDist = desiredDist; + } else if (camDist < desiredDist) { + xa8_renderState0.x18_camDist = camDist + 3.f; + } else { + xa8_renderState0.x18_camDist = camDist - 3.f; + } + + TAreaId curAid = x24_world->IGetCurrentAreaId(); + if (curAid != xa0_curAreaId) { + x160_renderState2 = xa8_renderState0; + x104_renderState1 = xa8_renderState0; + xa4_otherAreaId = xa0_curAreaId; + xa0_curAreaId = curAid; + CVector3f areaPoint = GetAreaPointOfInterest(mgr, xa0_curAreaId.value); + x104_renderState1.x20_areaPoint = areaPoint; + x104_renderState1.x44_viewportEase = SAutoMapperRenderState::kE_None; + x104_renderState1.x48_camEase = SAutoMapperRenderState::kE_None; + x104_renderState1.x4c_pointEase = SAutoMapperRenderState::kE_InOut; + x104_renderState1.x50_depth1Ease = SAutoMapperRenderState::kE_Linear; + x104_renderState1.x54_depth2Ease = SAutoMapperRenderState::kE_Linear; + x104_renderState1.x58_alphaEase = SAutoMapperRenderState::kE_None; + x104_renderState1.x2c_drawDepth1 = GetMapAreaMiniMapDrawDepth(); + x104_renderState1.x30_drawDepth2 = GetMapAreaMiniMapDrawDepth(); + x160_renderState2.x2c_drawDepth1 = GetMapAreaMiniMapDrawDepth() - 1.f; + x160_renderState2.x30_drawDepth2 = GetMapAreaMiniMapDrawDepth() - 1.f; + ResetInterpolationTimer(gpTweakAutoMapper->x6c_hintPanTime); + } + xa8_renderState0.x34_alphaSurfaceVisited = GetMapAreaMiniMapDrawAlphaSurfaceVisited(mgr); + xa8_renderState0.x38_alphaOutlineVisited = GetMapAreaMiniMapDrawAlphaOutlineVisited(mgr); + xa8_renderState0.x3c_alphaSurfaceUnvisited = GetMapAreaMiniMapDrawAlphaSurfaceUnvisited(mgr); + xa8_renderState0.x40_alphaOutlineUnvisited = GetMapAreaMiniMapDrawAlphaOutlineUnvisited(mgr); + } else if (x1c0_nextState == kAMS_MiniMap) { + float camDist = x104_renderState1.x18_camDist; + float desiredDist = GetDesiredMiniMapCameraDistance(mgr); + if (CMath::AbsF(camDist - desiredDist) < 3.f) { + xa8_renderState0.x18_camDist = desiredDist; + } else if (camDist < desiredDist) { + x104_renderState1.x18_camDist = camDist + 3.f; + } else { + x104_renderState1.x18_camDist = camDist - 3.f; + } + } else { + if (x1bc_state != kAMS_MiniMap && x1c0_nextState != kAMS_MiniMap && x24_world != NULL) { + const CMapWorld* mapWorld = x24_world->IGetMapWorld(); + CWorldState& worldState = gpGameState->StateForWorld(x24_world->IGetWorldAssetId()); + CMapWorldInfo* mwInfo = worldState.GetMapWorldInfo().GetPtr(); + mapWorld->RecalculateWorldSphere(*mwInfo, *x24_world); + } + } + + // Update interpolation + if (IsRenderStateInterpolating()) { + float newTime = x1c8_interpTime + dt; + x1c8_interpTime = newTime < x1c4_interpDur ? newTime : x1c4_interpDur; + SAutoMapperRenderState::InterpolateWithClamp(x160_renderState2, xa8_renderState0, + x104_renderState1, + x1c8_interpTime / x1c4_interpDur); + if (x1c8_interpTime == x1c4_interpDur && x328_ == 2) { + SetupMiniMapWorld(const_cast< CStateManager& >(mgr)); + } + } else if (IsInMapperStateTransition()) { + CompleteMapperStateTransition(mgr); + } + + // Update map area string + CAssetId stringId = x88_mapAreaStringId; + if (IsInMapperState(kAMS_MapScreenUniverse)) { + IWorld* dummyWorld = x14_dummyWorlds[x9c_worldIdx].get(); + if (dummyWorld != NULL && dummyWorld->ICheckWorldComplete()) { + stringId = dummyWorld->IGetStringTableAssetId(); + } else if (x24_world != NULL) { + stringId = x24_world->IGetStringTableAssetId(); + } + } else if (x24_world != NULL) { + const IGameArea* area = x24_world->IGetAreaAlways(xa0_curAreaId); + CWorldState& worldState = gpGameState->StateForWorld(x24_world->IGetWorldAssetId()); + CMapWorldInfo* mwInfo = worldState.GetMapWorldInfo().GetPtr(); + bool showName = true; + if (!mwInfo->IsMapped(xa0_curAreaId)) { + if (!mwInfo->IsAreaVisited(xa0_curAreaId)) { + showName = false; + } + } + if (showName) { + stringId = area->IGetStringTableAssetId(); + } else { + stringId = kInvalidAssetId; + } + } + + if (stringId != x88_mapAreaStringId) { + x88_mapAreaStringId = stringId; + if (x88_mapAreaStringId != kInvalidAssetId) { + x8c_mapAreaString = TCachedToken< CStringTable >( + gpSimplePool->GetObj(SObjectTag('STRG', x88_mapAreaStringId))); + x8c_mapAreaString->Lock(); + } else { + x8c_mapAreaString = rstl::optional_object< TCachedToken< CStringTable > >(); + } + } + + if (x2f8_textpane_areaname != NULL) { + if (x8c_mapAreaString) { + if (x8c_mapAreaString->IsLoaded()) { + x2f8_textpane_areaname->TextSupport().SetText( + rstl::wstring(x8c_mapAreaString->GetObject()->GetString(0))); + } + } else { + x2f8_textpane_areaname->TextSupport().SetText(rstl::wstring_l(L"")); + } + } + + // Update hint description for map screen + if (IsInMapperState(kAMS_MapScreen)) { + const IGameArea* area = x24_world->IGetAreaAlways(xa0_curAreaId); + CAssetId hintDescId = GetAreaHintDescriptionString(area->IGetAreaAssetId()); + if (hintDescId != x74_areaHintDescId) { + x74_areaHintDescId = hintDescId; + if (x74_areaHintDescId != kInvalidAssetId) { + x78_areaHintDesc = TCachedToken< CStringTable >( + gpSimplePool->GetObj(SObjectTag('STRG', x74_areaHintDescId))); + x78_areaHintDesc->Lock(); + } else { + x78_areaHintDesc = rstl::optional_object< TCachedToken< CStringTable > >(); + } + } + } + + // Update dummy worlds + for (int i = 0; i < static_cast< int >(x14_dummyWorlds.size()); ++i) { + IWorld* dummyWorld = x14_dummyWorlds[i].get(); + if (dummyWorld != NULL) { + dummyWorld->ICheckWorldComplete(); + } + } +} + +CAssetId CAutoMapper::GetAreaHintDescriptionString(CAssetId mreaId) { + const CHintOptions& hintOpts = gpGameState->HintOptions(); + const rstl::vector< SHintState >& hintStates = hintOpts.GetHintStates(); + for (int i = 0; i < static_cast< int >(hintStates.size()); ++i) { + if (hintStates[i].x0_state != kHS_Displaying) + continue; + const CGameHintInfo::CGameHint& hint = GetGameHints()[i]; + int numLocs = static_cast< int >(hint.x20_locations.size()); + for (int j = 0; j < numLocs; ++j) { + const CGameHintInfo::SHintLocation& loc = hint.x20_locations[j]; + if (loc.x4_mreaId != mreaId) + continue; + rstl::list< SAutoMapperHintLocation >::const_iterator locIt = x1f8_hintLocations.begin(); + for (; locIt != x1f8_hintLocations.end(); ++locIt) { + if (locIt->xc_areaId != loc.x8_areaId) + continue; + if (locIt->x4_beaconAlpha > 0.f) + return loc.xc_stringId; + } + } + } + return kInvalidAssetId; +} + +void CAutoMapper::Draw(const CStateManager& mgr, const CTransform4f& xf, float alpha) const { + alpha *= gpGameState->GameOptions().GetHudAlpha(); + gpRender->SetBlendMode_AlphaBlended(); + CGraphics::SetCullMode(kCM_Front); + + float alphaInterp; + if (IsFullyOutOfMiniMapState()) { + alphaInterp = 1.f; + } else if (IsInMapperState(kAMS_MiniMap)) { + alphaInterp = alpha; + } else if (x1c0_nextState == kAMS_MiniMap) { + float t = GetInterp(); + alphaInterp = alpha * t + (1.f - t); + } else if (x1bc_state == kAMS_MiniMap) { + float t = GetInterp(); + alphaInterp = alpha * (1.f - t) + t; + } else { + alphaInterp = 1.f; + } + + float aspect = static_cast< float >(xa8_renderState0.x0_viewportSize.GetX()) / + static_cast< float >(xa8_renderState0.x0_viewportSize.GetY()); + float camAngleRad = xa8_renderState0.x1c_camAngle * (1.f / 360.f) * (2.f * M_PIF); + float yScale = xa8_renderState0.x18_camDist / + static_cast< float >(tan( + M_PIF / 2.f - 0.5f * camAngleRad)); + + CTransform4f camXf = + xa8_renderState0.x8_camOrientation.BuildTransform4f(xa8_renderState0.x20_areaPoint); + + CTransform4f distScale(1.f / (yScale * aspect), 0.f, 0.f, 0.f, + 0.f, 0.001f, 0.f, 0.f, + 0.f, 0.f, 1.f / yScale, 0.f); + + CTransform4f tweakScale = + CTransform4f::Scale(gpTweakAutoMapper->xc4_mapPlaneScaleX, 0.f, + gpTweakAutoMapper->xc8_mapPlaneScaleZ); + + CTransform4f planeXf = xf * tweakScale * distScale * camXf.GetQuickInverse(); + + float universeInterp = 0.f; + if (x1c0_nextState == kAMS_MapScreenUniverse) { + if (x1bc_state == kAMS_MapScreenUniverse) + universeInterp = 1.f; + else + universeInterp = GetInterp(); + } else if (x1bc_state == kAMS_MapScreenUniverse) { + universeInterp = 1.f - GetInterp(); + } + + const CTransform4f* preXf; + bool isMapScreenUniverse = + (x1bc_state == kAMS_MapScreenUniverse || x1c0_nextState == kAMS_MapScreenUniverse); + if (isMapScreenUniverse) + preXf = &x8_mapu.GetObject()->GetMapWorldData(x9c_worldIdx).GetWorldTransform(); + else + preXf = &CTransform4f::Identity(); + + float objectScale = xa8_renderState0.x18_camDist / gpTweakAutoMapper->xc_minCamDist; + float mapAlpha = alphaInterp * (1.f - universeInterp); + + if (IsFullyOutOfMiniMapState()) { + if (universeInterp < 1.f && x24_world != NULL) { + CWorldState& worldState = gpGameState->StateForWorld(x24_world->IGetWorldAssetId()); + const CMapWorldInfo& mwInfo = *worldState.GetMapWorldInfo().GetPtr(); + const CMapWorld* mw = x24_world->IGetMapWorld(); + + float hintFlash = 0.f; + if (!x1e0_hintSteps.empty() && + x1e0_hintSteps.begin()->x0_type == SAutoMapperHintStep::kHST_ShowBeacon) { + float hintStepFloat = x1e0_hintSteps.begin()->x4_float; + if (xa0_curAreaId == mgr.GetNextAreaId() && x24_world == mgr.GetWorld()) { + float pulseTime = + static_cast< float >(fmod(hintStepFloat * 8.f, 1.0)); + hintFlash = 2.f * (pulseTime < 0.5f ? pulseTime : 1.f - pulseTime); + } else { + rstl::list< SAutoMapperHintLocation >::const_iterator locIt = + x1f8_hintLocations.begin(); + for (; locIt != x1f8_hintLocations.end(); ++locIt) { + if (x24_world->IGetWorldAssetId() != locIt->x8_worldId) + continue; + if (xa0_curAreaId != locIt->xc_areaId) + continue; + float pulseTime = static_cast< float >( + fmod((1.f - max_val(0.f, (hintStepFloat - 0.5f) / 0.5f)) * + 4.f, + 1.0)); + hintFlash = 2.f * (pulseTime < 0.5f ? pulseTime : 1.f - pulseTime); + break; + } + } + } + + int curArea = xa0_curAreaId.value; + CTransform4f modelXf = planeXf * *preXf; + const CMapWorld::CMapWorldDrawParms parms( + xa8_renderState0.x34_alphaSurfaceVisited * alphaInterp, + xa8_renderState0.x38_alphaOutlineVisited * alphaInterp, + xa8_renderState0.x3c_alphaSurfaceUnvisited * alphaInterp, + xa8_renderState0.x40_alphaOutlineUnvisited * alphaInterp, mapAlpha, + mgr, modelXf, camXf, *x24_world, mwInfo, + 2.f, true, x1dc_playerFlashPulse, hintFlash, objectScale); + mw->Draw(parms, curArea, curArea, + xa8_renderState0.x2c_drawDepth1, xa8_renderState0.x30_drawDepth2, true); + } + } else if (IsInMapperState(kAMS_MiniMap)) { + const CMapWorld* mw = x24_world->IGetMapWorld(); + CWorldState& worldState = gpGameState->StateForWorld(x24_world->IGetWorldAssetId()); + const CMapWorldInfo& mwInfo = *worldState.GetMapWorldInfo().GetPtr(); + const CMapWorld::CMapWorldDrawParms parms( + xa8_renderState0.x34_alphaSurfaceVisited * alphaInterp, + xa8_renderState0.x38_alphaOutlineVisited * alphaInterp, + xa8_renderState0.x3c_alphaSurfaceUnvisited * alphaInterp, + xa8_renderState0.x40_alphaOutlineUnvisited * alphaInterp, mapAlpha, + mgr, planeXf, camXf, *x24_world, mwInfo, + 1.f, false, 0.f, 0.f, objectScale); + mw->Draw(parms, xa0_curAreaId.value, xa4_otherAreaId.value, + xa8_renderState0.x2c_drawDepth1, xa8_renderState0.x30_drawDepth2, false); + } else { + int curArea = xa0_curAreaId.value; + const CMapWorld* mw = x24_world->IGetMapWorld(); + CWorldState& worldState = gpGameState->StateForWorld(x24_world->IGetWorldAssetId()); + const CMapWorldInfo& mwInfo = *worldState.GetMapWorldInfo().GetPtr(); + CTransform4f modelXf = planeXf * *preXf; + const CMapWorld::CMapWorldDrawParms parms( + xa8_renderState0.x34_alphaSurfaceVisited * alphaInterp, + xa8_renderState0.x38_alphaOutlineVisited * alphaInterp, + xa8_renderState0.x3c_alphaSurfaceUnvisited * alphaInterp, + xa8_renderState0.x40_alphaOutlineUnvisited * alphaInterp, mapAlpha, + mgr, modelXf, camXf, *x24_world, mwInfo, + 2.f, true, 0.f, 0.f, objectScale); + mw->Draw(parms, curArea, curArea, + xa8_renderState0.x2c_drawDepth1, xa8_renderState0.x30_drawDepth2, false); + } + + if (universeInterp > 0.f) { + const CWorld* wld = mgr.GetWorld(); + CMapWorld* mapWorld = wld->GetMapWorld(); + const CMapArea* mapArea = mapWorld->GetMapArea(mgr.GetNextAreaId().value); + CTransform4f areaXf = + const_cast< CMapArea* >(mapArea)->GetAreaPostTransform(*wld, mgr.GetNextAreaId().value); + + CAssetId curWorldId = gpGameState->CurrentWorldAssetId(); + const CMapUniverse::CMapWorldData& mwData = + x8_mapu.GetObject()->GetMapWorldDataByWorldId(curWorldId); + CTransform4f universeAreaXf = mwData.GetWorldTransform() * areaXf; + + float minMag = FLT_MAX; + int hexIdx = -1; + for (int i = 0; i < static_cast< int >(mwData.GetNumMapAreaDatas()); ++i) { + CVector3f diff = universeAreaXf.GetTranslation() - mwData.GetMapAreaData(i).GetTranslation(); + float mag = diff.Magnitude(); + if (mag < minMag) { + hexIdx = i; + minMag = mag; + } + } + + const CMapUniverse::CMapUniverseDrawParms uParms( + universeInterp, x9c_worldIdx, gpGameState->CurrentWorldAssetId(), hexIdx, + x1dc_playerFlashPulse, mgr, planeXf, camXf); + x8_mapu.GetObject()->Draw(uParms, CVector3f::Zero(), 0.f, 0.f); + } + + if (!IsInMapperState(kAMS_MapScreenUniverse)) { + CTransform4f mapXf = planeXf * *preXf; + if (x24_world == mgr.GetWorld()) { + float func = CMath::Clamp( + 0.f, 0.5f * (1.f + CMath::FastSinR(5.f * CGraphics::GetSecondsMod900() - (M_PIF / 2.f))), + 1.f); + float scale = min_val(0.6f * gpTweakAutoMapper->x10_maxCamDist / + gpTweakAutoMapper->xc_minCamDist, + objectScale); + + CEulerAngles eulers = CEulerAngles::FromTransform( + mgr.GetCameraManager()->GetCurrentCameraTransform(mgr)); + CRelAngle angle = CRelAngle(normalize_angle(eulers.GetZ())); + + CVector3f playerPos = + CMapArea::GetAreaPostTranslate(*x24_world, mgr.GetNextAreaId().value) + + mgr.GetPlayer()->GetTranslation(); + + CTransform4f playerXf(CMatrix3f::RotateZ(angle), playerPos); + + gpRender->SetModelMatrix(mapXf * playerXf * + CTransform4f::Scale(scale * (0.25f * func + 0.75f))); + + float colorAlpha; + if (IsFullyOutOfMiniMapState()) { + colorAlpha = 1.f; + } else { + colorAlpha = xa8_renderState0.x34_alphaSurfaceVisited; + } + colorAlpha *= mapAlpha; + CColor modColor = + gpTweakAutoMapper->xf0_miniMapSamusModColor.WithAlphaModulatedBy(colorAlpha); + CModelFlags flags(CModelFlags::kT_Blend, static_cast< uchar >(0), + static_cast< CModelFlags::EFlags >(CModelFlags::kF_DepthCompare | CModelFlags::kF_DepthGreater), + modColor); + x30_miniMapSamus.GetObject()->Draw(flags); + } + + if (IsInMapperState(kAMS_MapScreen)) { + CAssetId wldMlvl = x24_world->IGetWorldAssetId(); + const CMapWorld* mw = x24_world->IGetMapWorld(); + rstl::list< SAutoMapperHintLocation >::const_iterator locIt = x1f8_hintLocations.begin(); + for (; locIt != x1f8_hintLocations.end(); ++locIt) { + if (locIt->x8_worldId != wldMlvl) + continue; + const CMapArea* mapa = mw->GetMapArea(locIt->xc_areaId.value); + if (mapa == NULL) + continue; + + CTransform4f camRot(camXf.BuildMatrix3f(), CVector3f::Zero()); + CGraphics::SetModelMatrix( + mapXf * + CTransform4f::Translate(const_cast< CMapArea* >(mapa) + ->GetAreaPostTransform(*x24_world, locIt->xc_areaId.value) + .GetTranslation()) * + CTransform4f::Translate(mapa->GetAreaCenterPoint()) * + CTransform4f::Scale(objectScale) * camRot); + + float beaconAlpha = 0.f; + if (locIt->x0_showBeacon == 1) { + beaconAlpha = locIt->x4_beaconAlpha; + } + + if (beaconAlpha > 0.f) { + CGraphics::SetTevOp(kTS_Stage0, CGraphics::kEnvModulate); + x3c_hintBeacon.GetObject()->Load(GX_TEXMAP0, CTexture::kCM_Repeat); + gpRender->SetBlendMode_AdditiveAlpha(); + CGraphics::StreamBegin(kP_TriangleStrip); + float beaconColorAlpha; + if (IsFullyOutOfMiniMapState()) { + beaconColorAlpha = 1.f; + } else { + beaconColorAlpha = xa8_renderState0.x34_alphaSurfaceVisited; + } + CColor beaconColor(0xff, 0xff, 0xff, + static_cast< uchar >(beaconAlpha * beaconColorAlpha * mapAlpha * 255.f)); + CGraphics::StreamColor(beaconColor); + CGraphics::StreamTexcoord(0.f, 1.f); + CGraphics::StreamVertex(CVector3f(-4.f, -8.f, 8.f)); + CGraphics::StreamTexcoord(0.f, 0.f); + CGraphics::StreamVertex(CVector3f(-4.f, -8.f, 0.f)); + CGraphics::StreamTexcoord(1.f, 1.f); + CGraphics::StreamVertex(CVector3f(4.f, -8.f, 8.f)); + CGraphics::StreamTexcoord(1.f, 0.f); + CGraphics::StreamVertex(CVector3f(4.f, -8.f, 0.f)); + CGraphics::StreamEnd(); + } + } + } + } + + gpRender->SetDepthReadWrite(false, false); + gpRender->SetAmbientColor(CColor::White()); + CGraphics::DisableAllLights(); + + if (x2c_frmeInitialized != NULL) { + float frmeAlpha; + if (IsFullyOutOfMiniMapState()) { + frmeAlpha = 1.f; + } else if (x1c0_nextState != kAMS_MiniMap) { + frmeAlpha = 0.f; + if (x1c4_interpDur > 0.f) + frmeAlpha = x1c8_interpTime / x1c4_interpDur; + } else { + frmeAlpha = 0.f; + if (x1c4_interpDur > 0.f) + frmeAlpha = x1c8_interpTime / x1c4_interpDur; + frmeAlpha = 1.f - frmeAlpha; + } + + CGraphics::SetDepthRange(0.f, 0.f); + CGuiWidgetDrawParms parms(frmeAlpha, CVector3f::Zero()); + x2c_frmeInitialized->Draw(parms); + CGraphics::SetDepthRange(0.f, 1.f / 512.f); + } +} + +void CAutoMapper::OnNewInGameGuiState(EInGameGuiState state, CStateManager& mgr) { + if (state == kIGGS_MapScreen) { + CMain::EnsureWorldPaksReady(); + CWorld* wld = mgr.World(); + wld->GetMapWorld()->SetWhichMapAreasLoaded(*wld, 0, 9999); + SetupHintNavigation(); + BeginMapperStateTransition(kAMS_MapScreen, mgr); + x28_frmeMapScreen = rs_new TCachedToken< CGuiFrame >(gpSimplePool->GetObj(skFRME_MapScreen)); + x28_frmeMapScreen->Lock(); + SetResLockState(x210_lstick, true); + SetResLockState(x25c_cstick, true); + SetResLockState(x2a8_ltrigger, true); + SetResLockState(x2bc_rtrigger, true); + SetResLockState(x2d0_abutton, true); + } else { + CMain::EnsureWorldPakReady(gpGameState->CurrentWorldAssetId()); + if (x1bc_state == kAMS_MapScreenUniverse || x24_world == mgr.GetWorld()) { + BeginMapperStateTransition(kAMS_MiniMap, mgr); + x328_ = 0; + } + LeaveMapScreenState(); + } +} + +void CAutoMapper::SetupHintNavigation() { + if (!gpGameState->GameOptions().GetIsHintSystemEnabled()) + return; + + rstl::list< SAutoMapperHintStep >::iterator stepEnd = x1e0_hintSteps.end(); + rstl::list< SAutoMapperHintStep >::iterator stepIt = x1e0_hintSteps.begin(); + while (stepIt != stepEnd) { + stepIt = x1e0_hintSteps.erase(stepIt); + } + + rstl::list< SAutoMapperHintLocation >::iterator locEnd = x1f8_hintLocations.end(); + rstl::list< SAutoMapperHintLocation >::iterator locIt = x1f8_hintLocations.begin(); + while (locIt != locEnd) { + locIt = x1f8_hintLocations.erase(locIt); + } + + CHintOptions& hintOpts = gpGameState->HintOptions(); + const SHintState* curHint = hintOpts.GetCurrentDisplayedHint(); + bool navigating = false; + if (curHint != NULL && const_cast< SHintState* >(curHint)->CanContinue()) { + navigating = true; + x1e0_hintSteps.push_back(SAutoMapperHintStep(SAutoMapperHintStep::kHST_ShowBeacon, 0.75f)); + + int nextIdx = hintOpts.GetNextHintIdx(); + const CGameHintInfo::CGameHint& nextHint = GetGameHints()[nextIdx]; + CAssetId curMlvl = x24_world->IGetWorldAssetId(); + const rstl::vector< CGameHintInfo::SHintLocation >& locs = nextHint.GetLocations(); + for (int i = 0; i < static_cast< int >(locs.size()); ++i) { + const CGameHintInfo::SHintLocation& loc = locs[i]; + if (loc.x0_mlvlId != curMlvl) { + x1e0_hintSteps.push_back(SAutoMapperHintStep(SAutoMapperHintStep::kHST_SwitchToUniverse, 0)); + x1e0_hintSteps.push_back(SAutoMapperHintStep(SAutoMapperHintStep::kHST_PanToWorld, static_cast< int >(loc.x0_mlvlId))); + x1e0_hintSteps.push_back(SAutoMapperHintStep(SAutoMapperHintStep::kHST_SwitchToWorld, static_cast< int >(loc.x0_mlvlId))); + curMlvl = loc.x0_mlvlId; + } else { + x1e0_hintSteps.push_back(SAutoMapperHintStep(SAutoMapperHintStep::kHST_ZoomOut, 0)); + } + x1e0_hintSteps.push_back(SAutoMapperHintStep(SAutoMapperHintStep::kHST_PanToArea, loc.x8_areaId.value)); + x1e0_hintSteps.push_back(SAutoMapperHintStep(SAutoMapperHintStep::kHST_ZoomIn, 0)); + x1e0_hintSteps.push_back(SAutoMapperHintStep(SAutoMapperHintStep::kHST_ShowBeacon, 1.f)); + x1f8_hintLocations.push_back(SAutoMapperHintLocation(0, 0.f, loc.x0_mlvlId, loc.x8_areaId)); + } + } + + const rstl::vector< SHintState >& hintStates = hintOpts.GetHintStates(); + for (int i = 0; i < static_cast< int >(hintStates.size()); ++i) { + if (navigating && hintOpts.GetNextHintIdx() == i) + continue; + if (hintStates[i].x0_state != kHS_Displaying) + continue; + const CGameHintInfo::CGameHint& hint = GetGameHints()[i]; + const rstl::vector< CGameHintInfo::SHintLocation >& locs = hint.GetLocations(); + for (int j = 0; j < static_cast< int >(locs.size()); ++j) { + x1f8_hintLocations.push_back(SAutoMapperHintLocation(1, 1.f, locs[j].x0_mlvlId, locs[j].x8_areaId)); + } + } +} + +bool CAutoMapper::CanLeaveMapScreen(const CStateManager& mgr) const { + bool ret = false; + if (x328_ == 3 && CanLeaveMapScreenInternal(mgr)) + ret = true; + return ret; +} + +bool CAutoMapper::CanLeaveMapScreenInternal(const CStateManager& mgr) const { + if (!NotHintNavigating()) + return false; + if (IsRenderStateInterpolating()) + return false; + if (IsInMapperState(kAMS_MapScreenUniverse)) + return true; + bool ret = false; + if (x24_world == mgr.GetWorld() && IsInMapperState(kAMS_MapScreen)) + ret = true; + return ret; +} + +bool CAutoMapper::CheckDummyWorldLoad(const CStateManager& mgr) { + uint worldIdx = x9c_worldIdx; + IWorld* dummyWorld = x14_dummyWorlds[worldIdx].get(); + const CMapUniverse::CMapWorldData& mapuWld = x8_mapu.GetObject()->GetMapWorldData(worldIdx); + if (dummyWorld != NULL) { + if (dummyWorld->ICheckWorldComplete()) { + CWorldState& worldState = gpGameState->StateForWorld(dummyWorld->IGetWorldAssetId()); + CMapWorldInfo* mwInfo = worldState.GetMapWorldInfo().GetPtr(); + + CVector3f localPoint(mapuWld.GetWorldTransform().GetQuickInverse() * xa8_renderState0.x20_areaPoint); + CMatrix3f camRot(xa8_renderState0.x8_camOrientation.BuildTransform()); + CVector3f camDir = camRot.GetColumn(kDY); + CUnitVector3f unitDir(camDir.GetX(), camDir.GetY(), camDir.GetZ()); + int aid = FindClosestVisibleArea(localPoint, unitDir, mgr, *dummyWorld, *mwInfo); + if (aid != -1) { + xa0_curAreaId = aid; + dummyWorld->IMapWorld()->RecalculateWorldSphere(*mwInfo, *dummyWorld); + x24_world = dummyWorld; + BeginMapperStateTransition(kAMS_MapScreen, mgr); + x32c_loadingDummyWorld = false; + return true; + } + x32c_loadingDummyWorld = false; + return false; + } + return true; + } + x32c_loadingDummyWorld = false; + return false; +} + +void CAutoMapper::UpdateHintNavigation(float dt, const CStateManager& mgr) { + SAutoMapperHintStep& nextStep = *x1e0_hintSteps.begin(); + float beaconTime = nextStep.x4_float; + bool oldProcessing = nextStep.x8_processing; + nextStep.x8_processing = true; + switch (nextStep.x0_type) { + case SAutoMapperHintStep::kHST_PanToArea: { + int areaId = nextStep.x4_areaId.value; + const CMapWorld* mapWorld = x24_world->IGetMapWorld(); + if (mapWorld->GetMapArea(areaId) != NULL) { + x160_renderState2 = xa8_renderState0; + x104_renderState1.x20_areaPoint = GetAreaPointOfInterest(mgr, areaId); + x104_renderState1.ResetInterpolation(); + x104_renderState1.x4c_pointEase = SAutoMapperRenderState::kE_Linear; + ResetInterpolationTimer(2.f * gpTweakAutoMapper->x6c_hintPanTime); + x1e0_hintSteps.pop_front(); + } + break; + } + case SAutoMapperHintStep::kHST_PanToWorld: { + const CMapUniverse::CMapWorldData& mwData = + x8_mapu.GetObject()->GetMapWorldDataByWorldId(nextStep.x4_worldId); + CVector3f centerPoint = mwData.GetWorldCenterPoint(); + x160_renderState2 = xa8_renderState0; + x104_renderState1.x20_areaPoint = centerPoint; + x104_renderState1.ResetInterpolation(); + x104_renderState1.x4c_pointEase = SAutoMapperRenderState::kE_Linear; + ResetInterpolationTimer(2.f * gpTweakAutoMapper->x6c_hintPanTime); + x1e0_hintSteps.pop_front(); + break; + } + case SAutoMapperHintStep::kHST_SwitchToUniverse: { + if (HasCurrentMapUniverseWorld()) { + BeginMapperStateTransition(kAMS_MapScreenUniverse, mgr); + x1e0_hintSteps.pop_front(); + } else { + rstl::list< SAutoMapperHintStep >::iterator end = x1e0_hintSteps.end(); + rstl::list< SAutoMapperHintStep >::iterator it = x1e0_hintSteps.begin(); + while (it != end) { + it = x1e0_hintSteps.erase(it); + } + } + break; + } + case SAutoMapperHintStep::kHST_SwitchToWorld: { + x1e0_hintSteps.pop_front(); + x32c_loadingDummyWorld = true; + if (!CheckDummyWorldLoad(mgr)) { + rstl::list< SAutoMapperHintStep >::iterator end = x1e0_hintSteps.end(); + rstl::list< SAutoMapperHintStep >::iterator it = x1e0_hintSteps.begin(); + while (it != end) { + it = x1e0_hintSteps.erase(it); + } + } + break; + } + case SAutoMapperHintStep::kHST_ShowBeacon: { + if (!oldProcessing) { + if (xa0_curAreaId == mgr.GetNextAreaId() && x24_world == mgr.GetWorld()) { + CSfxManager::SfxStart(0x56a, 127, 64, false, CSfxManager::kMedPriority, false, + CSfxManager::kAllAreas); + } else { + CSfxManager::SfxStart(0x56b, 127, 64, false, CSfxManager::kMedPriority, false, + CSfxManager::kAllAreas); + } + } + beaconTime -= dt; + if (beaconTime <= 0.f) + beaconTime = 0.f; + nextStep.x4_float = beaconTime; + rstl::list< SAutoMapperHintLocation >::iterator locIt = x1f8_hintLocations.begin(); + for (; locIt != x1f8_hintLocations.end(); ++locIt) { + if (x24_world->IGetWorldAssetId() == locIt->x8_worldId && + xa0_curAreaId == locIt->xc_areaId) { + locIt->x0_showBeacon = 1; + float alpha = beaconTime / 0.5f; + if (alpha >= 1.f) + alpha = 1.f; + locIt->x4_beaconAlpha = 1.f - alpha; + break; + } + } + if (0.f == beaconTime) { + x1e0_hintSteps.pop_front(); + } + break; + } + case SAutoMapperHintStep::kHST_ZoomOut: { + x160_renderState2 = xa8_renderState0; + x104_renderState1.x18_camDist = gpTweakAutoMapper->x10_maxCamDist; + x104_renderState1.ResetInterpolation(); + x104_renderState1.x48_camEase = SAutoMapperRenderState::kE_Linear; + ResetInterpolationTimer(0.5f); + x1e0_hintSteps.pop_front(); + break; + } + case SAutoMapperHintStep::kHST_ZoomIn: { + x160_renderState2 = xa8_renderState0; + x104_renderState1.x18_camDist = gpTweakAutoMapper->x8_camDist; + x104_renderState1.ResetInterpolation(); + x104_renderState1.x48_camEase = SAutoMapperRenderState::kE_Linear; + ResetInterpolationTimer(0.5f); + x1e0_hintSteps.pop_front(); + break; + } + default: + break; + } +} + +void CAutoMapper::ProcessControllerInput(const CFinalInput& input, const CStateManager& mgr) { + if (!IsRenderStateInterpolating()) { + bool inPlayerControl = IsInMapperState(kAMS_MapScreen) || IsInMapperState(kAMS_MapScreenUniverse); + if (inPlayerControl) { + if (x32c_loadingDummyWorld) { + CheckDummyWorldLoad(mgr); + } else if (static_cast< int >(x1e0_hintSteps.size()) > 0) { + UpdateHintNavigation(input.Time(), mgr); + } else if (x328_ == 0) { + ProcessMapScreenInput(input, mgr); + } + } + } + + CMatrix3f camRot = xa8_renderState0.x8_camOrientation.BuildTransform(); + if (IsInMapperState(kAMS_MapScreen)) { + CWorldState& worldState = gpGameState->StateForWorld(x24_world->IGetWorldAssetId()); + CMapWorldInfo* mwInfo = worldState.GetMapWorldInfo().GetPtr(); + CVector3f camDir = camRot.GetColumn(kDY); + CUnitVector3f unitDir(camDir.GetX(), camDir.GetY(), camDir.GetZ()); + int aid = FindClosestVisibleArea(xa8_renderState0.x20_areaPoint, + unitDir, mgr, *x24_world, *mwInfo); + if (aid != xa0_curAreaId.value) { + xa0_curAreaId = aid; + xa8_renderState0.x2c_drawDepth1 = GetMapAreaMaxDrawDepth(mgr, xa0_curAreaId.value); + xa8_renderState0.x30_drawDepth2 = GetMapAreaMaxDrawDepth(mgr, xa0_curAreaId.value); + } + } else if (IsInMapperState(kAMS_MapScreenUniverse)) { + CMapUniverse* mapu = x8_mapu.GetObject(); + uint oldWldIdx = x9c_worldIdx; + if (static_cast< int >(x1e0_hintSteps.size()) > 0) { + SAutoMapperHintStep& nextStep = *x1e0_hintSteps.begin(); + if (nextStep.x0_type == SAutoMapperHintStep::kHST_PanToWorld || + nextStep.x0_type == SAutoMapperHintStep::kHST_SwitchToWorld) { + SetCurWorldAssetId(nextStep.x4_worldId); + } else { + CVector3f camDir = camRot.GetColumn(kDY); + CUnitVector3f unitDir(camDir.GetX(), camDir.GetY(), camDir.GetZ()); + SClosestWorldResult result = FindClosestVisibleWorld( + xa8_renderState0.x20_areaPoint, unitDir, mgr); + x9c_worldIdx = result.x0_worldIdx; + } + } else { + CVector3f camDir = camRot.GetColumn(kDY); + CUnitVector3f unitDir(camDir.GetX(), camDir.GetY(), camDir.GetZ()); + SClosestWorldResult result = FindClosestVisibleWorld( + xa8_renderState0.x20_areaPoint, unitDir, mgr); + x9c_worldIdx = result.x0_worldIdx; + } + + if (x9c_worldIdx != oldWldIdx) { + CAssetId curMlvl = gpGameState->CurrentWorldAssetId(); + for (uint i = 0; static_cast< int >(i) < static_cast< int >(x14_dummyWorlds.size()); ++i) { + const CMapUniverse::CMapWorldData& mwData = mapu->GetMapWorldData(i); + if (i == x9c_worldIdx && curMlvl != mwData.GetWorldAssetId()) { + if (gpResourceFactory->CanBuild(SObjectTag('MLVL', mwData.GetWorldAssetId()))) { + CDummyWorld* dw = rs_new CDummyWorld(mwData.GetWorldAssetId()); + rstl::auto_ptr< IWorld > newWorld(dw); + x14_dummyWorlds[i] = newWorld; + } + } else { + rstl::auto_ptr< IWorld > emptyWorld; + x14_dummyWorlds[i] = emptyWorld; + } + } + if (curMlvl == mapu->GetMapWorldData(x9c_worldIdx).GetWorldAssetId()) { + x24_world = const_cast< CWorld* >(mgr.GetWorld()); + } else { + x24_world = NULL; + } + } + } + + if (x300_textpane_instructions != NULL) { + if (x78_areaHintDesc.valid() && x78_areaHintDesc->TryCache()) { + x2fc_textpane_hint->TextSupport().SetText( + rstl::wstring(x78_areaHintDesc->GetObject()->GetString(0))); + x304_textpane_instructions1->TextSupport().SetText(rstl::wstring_l(L"")); + x300_textpane_instructions->TextSupport().SetText(rstl::wstring_l(L"")); + x308_textpane_instructions2->TextSupport().SetText(rstl::wstring_l(L"")); + } else { + x2fc_textpane_hint->TextSupport().SetText(rstl::wstring_l(L"")); + + const wchar_t imagePrefix[] = L"&image="; + const wchar_t imageSuffix[] = L";"; + CStringTable* strTable = gpStringTable; + + rstl::wstring string; + string.reserve(0x100); + string.append(imagePrefix, -1); + { + rstl::string hexStr( + CBasics::Stringize("SI,0.6,1.0,%8.8X", gpTweakPlayerRes->x24_lStick[x2e4_lStickPos])); + string.append(CStringExtras::ConvertToUNICODE(hexStr)); + } + string.append(imageSuffix, -1); + string.append(strTable->GetString(0x2e), -1); + x300_textpane_instructions->TextSupport().SetText(string); + + string.assign(imagePrefix, -1); + { + rstl::string hexStr( + CBasics::Stringize("SI,0.6,1.0,%8.8X", gpTweakPlayerRes->x4c_cStick[x2e8_rStickPos])); + string.append(CStringExtras::ConvertToUNICODE(hexStr)); + } + string.append(imageSuffix, -1); + string.append(strTable->GetString(0x2f), -1); + x304_textpane_instructions1->TextSupport().SetText(string); + + string.assign(imagePrefix, -1); + { + rstl::string hexStr( + CBasics::Stringize("%8.8X", gpTweakPlayerRes->x74_lTrigger[x2ec_lTriggerPos])); + string.append(CStringExtras::ConvertToUNICODE(hexStr)); + } + string.append(imageSuffix, -1); + string.append(strTable->GetString(0x30), -1); + string.append(imagePrefix, -1); + { + rstl::string hexStr( + CBasics::Stringize("%8.8X", gpTweakPlayerRes->x80_rTrigger[x2f0_rTriggerPos])); + string.append(CStringExtras::ConvertToUNICODE(hexStr)); + } + string.append(imageSuffix, -1); + x308_textpane_instructions2->TextSupport().SetText(string); + } + } + + if (input.PY()) { + CSystemState& sysState = gpGameState->SystemState(); + int keyState = sysState.GetAutoMapperKeyState(); + if (keyState == 0) { + sysState.SetAutoMapperKeyState(1); + CSfxManager::SfxStart(0x5ac, 127, 64, false, CSfxManager::kMedPriority, false, + CSfxManager::kAllAreas); + } else if (keyState == 1) { + sysState.SetAutoMapperKeyState(2); + CSfxManager::SfxStart(0x5a6, 127, 64, false, CSfxManager::kMedPriority, false, + CSfxManager::kAllAreas); + } else if (keyState == 2) { + sysState.SetAutoMapperKeyState(0); + CSfxManager::SfxStart(0x5ad, 127, 64, false, CSfxManager::kMedPriority, false, + CSfxManager::kAllAreas); + } + } + + if ((input.PZ() || input.PB()) && x328_ == 0) { + if (CanLeaveMapScreenInternal(mgr)) { + LeaveMapScreen(mgr); + } else if (NotHintNavigating()) { + BeginMapperStateTransition(kAMS_MapScreenUniverse, mgr); + x328_ = 1; + } + } +} + +void CAutoMapper::ProcessMapScreenInput(const CFinalInput& input, const CStateManager& mgr) { + CMatrix3f camRot(xa8_renderState0.x8_camOrientation.BuildTransform()); + if (x1bc_state == kAMS_MapScreen) { + if (input.PA() && x328_ == 0) { + if (HasCurrentMapUniverseWorld()) { + BeginMapperStateTransition(kAMS_MapScreenUniverse, mgr); + } + } + } else if (x1bc_state == kAMS_MapScreenUniverse && input.PA()) { + const CMapUniverse::CMapWorldData& mapuWld = x8_mapu.GetObject()->GetMapWorldData(x9c_worldIdx); + CVector3f pointLocal = mapuWld.GetWorldTransform().GetQuickInverse() * xa8_renderState0.x20_areaPoint; + if (mapuWld.GetWorldAssetId() != gpGameState->CurrentWorldAssetId()) { + x32c_loadingDummyWorld = true; + CheckDummyWorldLoad(mgr); + } else { + x24_world = const_cast< CWorld* >(mgr.GetWorld()); + CWorldState& worldState = gpGameState->StateForWorld(x24_world->IGetWorldAssetId()); + CMapWorldInfo* mwInfo = worldState.GetMapWorldInfo().GetPtr(); + CVector3f camDir = camRot.GetColumn(kDY); + xa0_curAreaId.value = FindClosestVisibleArea( + pointLocal, CUnitVector3f(camDir.GetX(), camDir.GetY(), camDir.GetZ()), + mgr, *x24_world, *mwInfo); + BeginMapperStateTransition(kAMS_MapScreen, mgr); + } + } + + x2f4_aButtonPos = 0; + if (input.PA()) { + x2f4_aButtonPos = 1; + } + + bool inPlayerControl = false; + if (IsInMapperState(kAMS_MapScreen) || IsInMapperState(kAMS_MapScreenUniverse)) { + inPlayerControl = true; + } + if (inPlayerControl) { + x2e4_lStickPos = 0; + x2e8_rStickPos = 0; + x2ec_lTriggerPos = 0; + x2f0_rTriggerPos = 0; + ProcessMapRotateInput(input, mgr); + ProcessMapZoomInput(input, mgr); + ProcessMapPanInput(input, mgr); + } +} + +void CAutoMapper::ProcessMapRotateInput(const CFinalInput& input, const CStateManager& mgr) { + float up = ControlMapper::GetAnalogInput(ControlMapper::kC_MapCircleUp, input); + float down = ControlMapper::GetAnalogInput(ControlMapper::kC_MapCircleDown, input); + float left = ControlMapper::GetAnalogInput(ControlMapper::kC_MapCircleLeft, input); + float right = ControlMapper::GetAnalogInput(ControlMapper::kC_MapCircleRight, input); + + int flags = 0; + if (up > 0.f) + flags += 2; + if (down > 0.f) + flags += 1; + if (left > 0.f) + flags += 4; + if (right > 0.f) + flags += 8; + + switch (flags) { + case 1: + x2e4_lStickPos = 1; + break; + case 2: + x2e4_lStickPos = 5; + break; + case 4: + x2e4_lStickPos = 3; + break; + case 5: + x2e4_lStickPos = 2; + break; + case 6: + x2e4_lStickPos = 4; + break; + case 8: + x2e4_lStickPos = 7; + break; + case 9: + x2e4_lStickPos = 8; + break; + case 10: + x2e4_lStickPos = 6; + break; + default: + break; + } + + float maxMag = up; + int dirSlot = 0; + if (down > up) { + maxMag = down; + dirSlot = 1; + } + if (left > maxMag) { + maxMag = left; + dirSlot = 2; + } + if (right > maxMag) { + maxMag = right; + dirSlot = 3; + } + + float dirs2 = 0.f; + float dirs0 = 0.f; + float dirs1 = 0.f; + float dirs3 = 0.f; + switch (dirSlot) { + case 0: + dirs0 = maxMag; + break; + case 1: + dirs1 = maxMag; + break; + case 2: + dirs2 = maxMag; + break; + case 3: + dirs3 = maxMag; + break; + default: + break; + } + + if (dirs0 > 0.f || dirs1 > 0.f || dirs2 > 0.f || dirs3 > 0.f) { + float deltaFrames = 60.f * input.Time(); + SetShouldRotatingSoundBePlaying(true); + float minCamRotateX = gpTweakAutoMapper->x14_minCamRotateX; + float maxCamRotateX = gpTweakAutoMapper->x18_maxCamRotateX; + CEulerAngles eulers = CEulerAngles::FromQuaternion(xa8_renderState0.x8_camOrientation); + CRelAngle angX(normalize_angle(eulers.GetX())); + CRelAngle angZ(normalize_angle(eulers.GetZ())); + + float dt = deltaFrames * gpTweakAutoMapper->x74_rotateDegPerFrame; + + angZ -= CRelAngle::FromDegrees(dt * dirs2); + angZ = CRelAngle(normalize_angle(angZ.AsRadians())); + angZ += CRelAngle::FromDegrees(dt * dirs3); + angZ = CRelAngle(normalize_angle(angZ.AsRadians())); + + angX -= CRelAngle::FromDegrees(dt * dirs0); + angX = CRelAngle(normalize_angle(angX.AsRadians())); + angX += CRelAngle::FromDegrees(dt * dirs1); + angX = CRelAngle(normalize_angle(angX.AsRadians())); + + float angXDeg = angX.AsDegrees(); + if (angXDeg > 180.f) + angXDeg -= 360.f; + float clampedX = CMath::Clamp(minCamRotateX, angXDeg, maxCamRotateX); + angX = CRelAngle(normalize_angle(CRelAngle::FromDegrees(clampedX).AsRadians())); + + CQuaternion q = CQuaternion::ZRotation(angZ) * + CQuaternion::XRotation(angX) * + CQuaternion::YRotation(CRelAngle(0.f)); + xa8_renderState0.x8_camOrientation = q; + } else { + SetShouldRotatingSoundBePlaying(false); + } +} + +void CAutoMapper::ProcessMapZoomInput(const CFinalInput& input, const CStateManager& mgr) { + bool zoomIn = ControlMapper::GetDigitalInput(ControlMapper::kC_MapZoomIn, input); + bool zoomOut = ControlMapper::GetDigitalInput(ControlMapper::kC_MapZoomOut, input); + + int curState = x324_zoomState; + int nextZoomState = 0; + float oldDist = xa8_renderState0.x18_camDist; + switch (curState) { + case 0: + if (zoomIn) + nextZoomState = 1; + else if (zoomOut) + nextZoomState = 2; + break; + case 1: + if (zoomIn) + nextZoomState = 1; + else if (zoomOut) + nextZoomState = 2; + break; + case 2: + if (zoomOut) + nextZoomState = 2; + else if (zoomIn) + nextZoomState = 1; + break; + default: + break; + } + + x324_zoomState = static_cast< EZoomState >(nextZoomState); + + float deltaFrames = 60.f * input.Time(); + float speedMult = x1bc_state == kAMS_MapScreen ? 1.f : 4.f; + float delta = gpTweakAutoMapper->x70_zoomUnitsPerFrame * (deltaFrames * speedMult); + + if (x324_zoomState == kZS_In) { + xa8_renderState0.x18_camDist = GetClampedMapScreenCameraDistance(xa8_renderState0.x18_camDist - delta); + x2f0_rTriggerPos = 1; + x324_zoomState = kZS_In; + } else if (x324_zoomState == kZS_Out) { + xa8_renderState0.x18_camDist = GetClampedMapScreenCameraDistance(xa8_renderState0.x18_camDist + delta); + x2ec_lTriggerPos = 1; + x324_zoomState = kZS_Out; + } + + if (oldDist == xa8_renderState0.x18_camDist) + SetShouldZoomingSoundBePlaying(false); + else + SetShouldZoomingSoundBePlaying(true); +} + +void CAutoMapper::ProcessMapPanInput(const CFinalInput& input, const CStateManager& mgr) { + float forward = ControlMapper::GetAnalogInput(ControlMapper::kC_MapMoveForward, input); + float back = ControlMapper::GetAnalogInput(ControlMapper::kC_MapMoveBack, input); + float left = ControlMapper::GetAnalogInput(ControlMapper::kC_MapMoveLeft, input); + float right = ControlMapper::GetAnalogInput(ControlMapper::kC_MapMoveRight, input); + + CMatrix3f camRot = xa8_renderState0.x8_camOrientation.BuildTransform(); + if (forward > 0.f || back > 0.f || left > 0.f || right > 0.f) { + float deltaFrames = 60.f * input.Time(); + float speed = GetFinalMapScreenCameraMoveSpeed(); + int flags = 0; + if (forward > 0.f) + flags += 1; + if (back > 0.f) + flags += 2; + if (left > 0.f) + flags += 4; + if (right > 0.f) + flags += 8; + + switch (flags) { + case 1: + x2e8_rStickPos = 1; + break; + case 2: + x2e8_rStickPos = 5; + break; + case 4: + x2e8_rStickPos = 3; + break; + case 5: + x2e8_rStickPos = 2; + break; + case 6: + x2e8_rStickPos = 4; + break; + case 8: + x2e8_rStickPos = 7; + break; + case 9: + x2e8_rStickPos = 8; + break; + case 10: + x2e8_rStickPos = 6; + break; + default: + break; + } + + CVector3f dirVec(right - left, 0.f, forward - back); + CVector3f deltaVec = camRot * (dirVec * deltaFrames * speed); + float newX = xa8_renderState0.x20_areaPoint.GetX() + deltaVec.GetX(); + float newY = xa8_renderState0.x20_areaPoint.GetY() + deltaVec.GetY(); + float newZ = xa8_renderState0.x20_areaPoint.GetZ() + deltaVec.GetZ(); + CVector3f newPoint(newX, newY, newZ); + if (deltaVec.Magnitude() > input.Time()) { + SetShouldPanningSoundBePlaying(true); + } else { + SetShouldPanningSoundBePlaying(false); + } + + if (x1bc_state == kAMS_MapScreen) { + xa8_renderState0.x20_areaPoint = + x24_world->IGetMapWorld()->ConstrainToWorldVolume(newPoint, camRot.GetColumn(kDY)); + } else { + CVector3f localPoint = newPoint - x8_mapu.GetObject()->GetMapUniverseCenterPoint(); + if (localPoint.Magnitude() > x8_mapu.GetObject()->GetMapUniverseRadius()) { + newPoint = x8_mapu.GetObject()->GetMapUniverseCenterPoint() + + localPoint.AsNormalized() * x8_mapu.GetObject()->GetMapUniverseRadius(); + } + xa8_renderState0.x20_areaPoint = newPoint; + } + } else { + SetShouldPanningSoundBePlaying(false); + float speed = gpTweakAutoMapper->xe0_camPanUnitsPerFrame * GetBaseMapScreenCameraMoveSpeed(); + if (x1bc_state == kAMS_MapScreen) { + const CMapArea* area = x24_world->IGetMapWorld()->GetMapArea(xa0_curAreaId.value); + CVector3f worldPoint = const_cast< CMapArea* >(area)->GetAreaPostTransform( + *x24_world, xa0_curAreaId.value) * area->GetAreaCenterPoint(); + CVector3f viewPoint = worldPoint - xa8_renderState0.x20_areaPoint; + if (viewPoint.Magnitude() < speed) { + xa8_renderState0.x20_areaPoint = worldPoint; + } else { + xa8_renderState0.x20_areaPoint += viewPoint.AsNormalized() * speed; + } + } else { + CVector3f camDir = camRot.GetColumn(kDY); + SClosestWorldResult result = FindClosestVisibleWorld( + xa8_renderState0.x20_areaPoint, CUnitVector3f(camDir.GetX(), camDir.GetY(), camDir.GetZ()), mgr); + const CTransform4f& hex = x8_mapu.GetObject()->GetMapWorldData(result.x0_worldIdx).GetMapAreaData(result.x4_areaIdx); + CVector3f areaToHex = hex.GetTranslation() - xa8_renderState0.x20_areaPoint; + if (areaToHex.Magnitude() < speed) { + xa8_renderState0.x20_areaPoint = hex.GetTranslation(); + } else { + xa8_renderState0.x20_areaPoint += areaToHex.AsNormalized() * speed; + } + } + } +} + +#pragma inline_max_size(400) + +bool CAutoMapper::NotHintNavigating() const { return x1e0_hintSteps.empty(); } + +void CAutoMapper::UnmuteAllLoopedSounds() { + CSfxManager::SfxVolume(x1cc_panningSfx, 127); + CSfxManager::SfxVolume(x1d0_rotatingSfx, 127); + CSfxManager::SfxVolume(x1d4_zoomingSfx, 127); +} + +bool CAutoMapper::HasCurrentMapUniverseWorld() { + CMapUniverse* mapu = x8_mapu.GetObject(); + CAssetId mlvlId = x24_world->IGetWorldAssetId(); + int numWorlds = mapu->GetNumMapWorldDatas(); + for (int i = 0; i < numWorlds; ++i) { + if (mapu->GetMapWorldData(i).GetWorldAssetId() == mlvlId) + return true; + } + return false; +} + +CAutoMapper::SAutoMapperRenderState CAutoMapper::BuildMiniMapWorldRenderState( + const CStateManager& stateMgr, const CQuaternion& rot, int area) const { + CQuaternion camOrient = GetMiniMapCameraOrientation(stateMgr); + CQuaternion useOrient = + (CQuaternion::Dot(rot, camOrient) >= 0.f) ? camOrient : camOrient.BuildEquivalent(); + SAutoMapperRenderState ret( + GetMiniMapViewportSize(), useOrient, gpTweakAutoMapper->x28_miniCamDist, + gpTweakAutoMapper->x30_miniCamAngle, GetAreaPointOfInterest(stateMgr, area), + GetMapAreaMiniMapDrawDepth(), GetMapAreaMiniMapDrawDepth(), + GetMapAreaMiniMapDrawAlphaSurfaceVisited(stateMgr), + GetMapAreaMiniMapDrawAlphaOutlineVisited(stateMgr), + GetMapAreaMiniMapDrawAlphaSurfaceUnvisited(stateMgr), + GetMapAreaMiniMapDrawAlphaOutlineUnvisited(stateMgr)); + ret.x44_viewportEase = SAutoMapperRenderState::kE_Out; + ret.x48_camEase = SAutoMapperRenderState::kE_Out; + ret.x4c_pointEase = SAutoMapperRenderState::kE_Out; + ret.x50_depth1Ease = SAutoMapperRenderState::kE_Linear; + ret.x54_depth2Ease = SAutoMapperRenderState::kE_In; + ret.x58_alphaEase = SAutoMapperRenderState::kE_Linear; + return ret; +} + +CAutoMapper::SAutoMapperRenderState CAutoMapper::BuildMapScreenWorldRenderState( + const CStateManager& mgr, const CQuaternion& rot, int area, bool doingHint) const { + float camDist = doingHint ? gpTweakAutoMapper->x10_maxCamDist : gpTweakAutoMapper->x8_camDist; + SAutoMapperRenderState ret( + GetMapScreenViewportSize(), rot, camDist, gpTweakAutoMapper->x1c_camAngle, + GetAreaPointOfInterest(mgr, area), GetMapAreaMaxDrawDepth(mgr, area), + GetMapAreaMaxDrawDepth(mgr, area), gpTweakAutoMapper->x88_alphaSurfaceVisited, + gpTweakAutoMapper->x90_alphaOutlineVisited, gpTweakAutoMapper->x98_alphaSurfaceUnvisited, + gpTweakAutoMapper->xa0_alphaOutlineUnvisited); + ret.x44_viewportEase = SAutoMapperRenderState::kE_Out; + ret.x48_camEase = SAutoMapperRenderState::kE_Linear; + ret.x4c_pointEase = SAutoMapperRenderState::kE_Out; + ret.x50_depth1Ease = SAutoMapperRenderState::kE_Linear; + ret.x54_depth2Ease = SAutoMapperRenderState::kE_Out; + ret.x58_alphaEase = SAutoMapperRenderState::kE_Linear; + return ret; +} + +CAutoMapper::SAutoMapperRenderState CAutoMapper::BuildMapScreenUniverseRenderState( + const CStateManager& mgr, const CQuaternion& rot, int area) const { + SAutoMapperRenderState ret( + GetMapScreenViewportSize(), rot, gpTweakAutoMapper->xd0_universeCamDist, + gpTweakAutoMapper->x1c_camAngle, GetAreaPointOfInterest(mgr, area), + GetMapAreaMaxDrawDepth(mgr, area), GetMapAreaMaxDrawDepth(mgr, area), 0.f, 0.f, 0.f, 0.f); + ret.x44_viewportEase = SAutoMapperRenderState::kE_Out; + ret.x48_camEase = SAutoMapperRenderState::kE_Linear; + ret.x4c_pointEase = SAutoMapperRenderState::kE_Out; + ret.x50_depth1Ease = SAutoMapperRenderState::kE_Linear; + ret.x54_depth2Ease = SAutoMapperRenderState::kE_Out; + ret.x58_alphaEase = SAutoMapperRenderState::kE_Linear; + return ret; +} + +void CAutoMapper::SetShouldPanningSoundBePlaying(bool shouldBePlaying) { + if (shouldBePlaying) { + if (!x1cc_panningSfx) + x1cc_panningSfx = CSfxManager::SfxStart(0x57E, 127, 64, false, + CSfxManager::kMedPriority, true, + CSfxManager::kAllAreas); + } else { + CSfxManager::SfxStop(x1cc_panningSfx); + x1cc_panningSfx.Clear(); + } +} + +void CAutoMapper::SetShouldZoomingSoundBePlaying(bool shouldBePlaying) { + if (shouldBePlaying) { + if (!x1d4_zoomingSfx) + x1d4_zoomingSfx = CSfxManager::SfxStart(0x560, 127, 64, false, + CSfxManager::kMedPriority, true, + CSfxManager::kAllAreas); + } else { + CSfxManager::SfxStop(x1d4_zoomingSfx); + x1d4_zoomingSfx.Clear(); + } +} + +void CAutoMapper::SetShouldRotatingSoundBePlaying(bool shouldBePlaying) { + if (shouldBePlaying) { + if (!x1d0_rotatingSfx) + x1d0_rotatingSfx = CSfxManager::SfxStart(0x55F, 127, 64, false, + CSfxManager::kMedPriority, true, + CSfxManager::kAllAreas); + } else { + CSfxManager::SfxStop(x1d0_rotatingSfx); + x1d0_rotatingSfx.Clear(); + } +} + +void CAutoMapper::ResetInterpolationTimer(float duration) { + x1c4_interpDur = duration; + x1c8_interpTime = 0.f; +} + +void CAutoMapper::CompleteMapperStateTransition(const CStateManager& mgr) { + if (x1bc_state == kAMS_MapScreenUniverse) + TransformRenderStatesUniverseToWorld(); + + if (x1c0_nextState == kAMS_MapScreen) { + const CMapWorld* mw = x24_world->IGetMapWorld(); + mw->IsMapAreasStreaming(); + CWorldState& worldState = gpGameState->StateForWorld(x24_world->IGetWorldAssetId()); + CMapWorldInfo* mwInfo = worldState.GetMapWorldInfo().GetPtr(); + mw->RecalculateWorldSphere(*mwInfo, *x24_world); + x1d8_flashTimer = 0.f; + x1dc_playerFlashPulse = 0.f; + } + + if (x1c0_nextState == kAMS_MiniMap) { + x28_frmeMapScreen = NULL; + x2c_frmeInitialized = NULL; + x2fc_textpane_hint = NULL; + x300_textpane_instructions = NULL; + x304_textpane_instructions1 = NULL; + x308_textpane_instructions2 = NULL; + x2f8_textpane_areaname = NULL; + x30c_basewidget_leftPane = NULL; + x310_basewidget_yButtonPane = NULL; + x314_basewidget_bottomPane = NULL; + SetResLockState(x210_lstick, false); + SetResLockState(x25c_cstick, false); + SetResLockState(x2a8_ltrigger, false); + SetResLockState(x2bc_rtrigger, false); + SetResLockState(x2d0_abutton, false); + } + + if (x1c0_nextState == kAMS_MapScreenUniverse && x328_ == 1) + LeaveMapScreen(mgr); + + x1bc_state = x1c0_nextState; +} + +void CAutoMapper::BeginMapperStateTransition(EAutoMapperState state, const CStateManager& mgr) { + if (state == x1c0_nextState) + return; + + if ((state == kAMS_MiniMap && x1c0_nextState != kAMS_MiniMap) || + (state != kAMS_MiniMap && x1c0_nextState == kAMS_MiniMap)) { + CSfxManager::KillAll(CSfxManager::kSC_PauseScreen); + } + + x1bc_state = x1c0_nextState; + x1c0_nextState = state; + x160_renderState2 = xa8_renderState0; + x104_renderState1 = xa8_renderState0; + + if (x1bc_state == kAMS_MiniMap && state == kAMS_MapScreen) { + x104_renderState1 = + BuildMapScreenWorldRenderState(mgr, xa8_renderState0.x8_camOrientation, xa0_curAreaId.value, false); + ResetInterpolationTimer(gpTweakAutoMapper->x64_openMapScreenTime); + } else if (x1bc_state == kAMS_MapScreen && state == kAMS_MiniMap) { + xa0_curAreaId = x24_world->IGetCurrentAreaId(); + x104_renderState1 = + BuildMiniMapWorldRenderState(mgr, xa8_renderState0.x8_camOrientation, xa0_curAreaId.value); + ResetInterpolationTimer(gpTweakAutoMapper->x68_closeMapScreenTime); + { + rstl::list< SAutoMapperHintLocation >::iterator end = x1f8_hintLocations.end(); + rstl::list< SAutoMapperHintLocation >::iterator it = x1f8_hintLocations.begin(); + while (it != end) { + it = x1f8_hintLocations.erase(it); + } + } + } else if (x1bc_state == kAMS_MapScreen && state == kAMS_MapScreenUniverse) { + CSfxManager::SfxStart(0x592, 127, 64, false, CSfxManager::kMedPriority, false, + CSfxManager::kAllAreas); + x104_renderState1 = + BuildMapScreenUniverseRenderState(mgr, xa8_renderState0.x8_camOrientation, xa0_curAreaId.value); + TransformRenderStatesWorldToUniverse(); + ResetInterpolationTimer(gpTweakAutoMapper->xdc_switchToFromUniverseTime); + } else if (x1bc_state == kAMS_MapScreenUniverse && state == kAMS_MapScreen) { + CSfxManager::SfxStart(0x593, 127, 64, false, CSfxManager::kMedPriority, false, + CSfxManager::kAllAreas); + x104_renderState1 = + BuildMapScreenWorldRenderState(mgr, xa8_renderState0.x8_camOrientation, xa0_curAreaId.value, + x1e0_hintSteps.size()); + TransformRenderStateWorldToUniverse(x104_renderState1); + ResetInterpolationTimer(gpTweakAutoMapper->xdc_switchToFromUniverseTime); + for (int i = 0; i < static_cast< int >(x14_dummyWorlds.size()); ++i) { + if (x14_dummyWorlds[i].get() != x24_world || x24_world == mgr.GetWorld()) { + rstl::auto_ptr< IWorld > empty; + x14_dummyWorlds[i] = empty; + } + } + } else if (x1bc_state == kAMS_MapScreenUniverse && state == kAMS_MiniMap) { + x24_world = const_cast< CWorld* >(mgr.GetWorld()); + xa0_curAreaId = x24_world->IGetCurrentAreaId(); + x104_renderState1 = + BuildMiniMapWorldRenderState(mgr, xa8_renderState0.x8_camOrientation, xa0_curAreaId.value); + SetCurWorldAssetId(x24_world->IGetWorldAssetId()); + TransformRenderStateWorldToUniverse(x104_renderState1); + ResetInterpolationTimer(gpTweakAutoMapper->x68_closeMapScreenTime); + { + rstl::list< SAutoMapperHintLocation >::iterator end = x1f8_hintLocations.end(); + rstl::list< SAutoMapperHintLocation >::iterator it = x1f8_hintLocations.begin(); + while (it != end) { + it = x1f8_hintLocations.erase(it); + } + } + for (int i = 0; i < static_cast< int >(x14_dummyWorlds.size()); ++i) { + rstl::auto_ptr< IWorld > empty; + x14_dummyWorlds[i] = empty; + } + } +} + +void CAutoMapper::LeaveMapScreenState() { + SetShouldPanningSoundBePlaying(false); + SetShouldZoomingSoundBePlaying(false); + SetShouldRotatingSoundBePlaying(false); +} + +CQuaternion CAutoMapper::GetMiniMapCameraOrientation(const CStateManager& stateMgr) { + float miniCamXAngle = gpTweakAutoMapper->x2c_miniCamXAngle; + const CGameCamera& cam = stateMgr.GetCameraManager()->GetCurrentCamera(stateMgr); + CEulerAngles angles = CEulerAngles::FromQuaternion(CQuaternion::FromMatrix(cam.GetTransform())); + CRelAngle yawAngle(normalize_angle(angles.GetZ())); + CRelAngle xAngle = CRelAngle::FromDegrees(miniCamXAngle); + return CQuaternion::ZRotation(yawAngle) * CQuaternion::XRotation(xAngle); +} + +CVector3f CAutoMapper::GetAreaPointOfInterest(const CStateManager& mgr, int aid) const { + const CMapArea* mapa = x24_world->IGetMapWorld()->GetMapArea(aid); + CVector3f center = mapa->GetAreaCenterPoint(); + return const_cast< CMapArea* >(mapa)->GetAreaPostTransform(*x24_world, aid) * center; +} + +int CAutoMapper::FindClosestVisibleArea(const CVector3f& point, const CUnitVector3f& camDir, + const CStateManager& mgr, const IWorld& wld, + const CMapWorldInfo& mwInfo) const { + float minDist = 9999.f; + int closestArea = xa0_curAreaId.value; + const CMapWorld* mw = wld.IGetMapWorld(); + rstl::vector< int > areas = mw->GetVisibleAreas(wld, mwInfo); + if (areas.empty()) { + return -1; + } + for (int i = 0; i < areas.size(); ++i) { + int areaId = areas[i]; + const CMapArea* mapa = mw->GetMapArea(areaId); + CTransform4f xf = const_cast< CMapArea* >(mapa)->GetAreaPostTransform(wld, areaId); + CVector3f xfPoint = xf * mapa->GetAreaCenterPoint(); + CVector3f pointToArea = xfPoint - point; + CVector3f projPoint; + if (pointToArea.CanBeNormalized()) { + CVector3f dir = pointToArea.AsNormalized(); + float dot = CVector3f::Dot(dir, camDir); + float mag = pointToArea.Magnitude(); + projPoint = point + (dot * mag) * camDir; + } else { + projPoint = point; + } + CVector3f diff = projPoint - xfPoint; + float dist = diff.Magnitude(); + if (dist < minDist) { + minDist = dist; + closestArea = areaId; + } + } + return closestArea; +} + +CAutoMapper::SClosestWorldResult CAutoMapper::FindClosestVisibleWorld( + const CVector3f& point, const CUnitVector3f& camDir, const CStateManager& mgr) const { + float minDist = 29999.f; + int closestWorldIdx = x9c_worldIdx; + int closestAreaIdx = xa0_curAreaId.value; + CMapUniverse* mapu = x8_mapu.GetObject(); + for (int w = 0; w < static_cast< int >(mapu->GetNumMapWorldDatas()); ++w) { + const CMapUniverse::CMapWorldData& mwData = mapu->GetMapWorldData(w); + CWorldState& worldState = gpGameState->StateForWorld(mwData.GetWorldAssetId()); + if (!worldState.GetMapWorldInfo().GetPtr()->IsAnythingSet()) + continue; + for (int i = 0; i < static_cast< int >(mwData.GetNumMapAreaDatas()); ++i) { + const CTransform4f& hexXf = mwData.GetMapAreaData(i); + CVector3f mwOrigin = hexXf.GetTranslation(); + CVector3f pointToArea = mwOrigin - point; + CVector3f projPoint; + if (pointToArea.CanBeNormalized()) { + CVector3f dir = pointToArea.AsNormalized(); + float dot = CVector3f::Dot(dir, camDir); + float mag = pointToArea.Magnitude(); + projPoint = point + (dot * mag) * camDir; + } else { + projPoint = point; + } + CVector3f diff = projPoint - mwOrigin; + float dist = diff.Magnitude(); + if (dist < minDist) { + minDist = dist; + closestWorldIdx = w; + closestAreaIdx = i; + } + } + } + SClosestWorldResult result; + result.x0_worldIdx = closestWorldIdx; + result.x4_areaIdx = closestAreaIdx; + return result; +} + +CVector2i CAutoMapper::GetMiniMapViewportSize() { + float scaleX = static_cast< float >(CGraphics::GetViewport().mWidth) / 640.f; + float scaleY = static_cast< float >(CGraphics::GetViewport().mHeight) / 480.f; + return CVector2i(static_cast< int >(scaleX * gpTweakAutoMapper->xb8_miniMapViewportWidth), + static_cast< int >(scaleY * gpTweakAutoMapper->xbc_miniMapViewportHeight)); +} + +CVector2i CAutoMapper::GetMapScreenViewportSize() { + return CVector2i(CGraphics::GetViewport().mWidth, CGraphics::GetViewport().mHeight); +} + +float CAutoMapper::GetMapAreaMiniMapDrawDepth() { return 2.f; } + +float CAutoMapper::GetMapAreaMaxDrawDepth(const CStateManager& mgr, int aid) const { + return static_cast< float >(x24_world->IGetMapWorld()->GetCurrentMapAreaDepth(*x24_world, aid)); +} + +float CAutoMapper::GetMapAreaMiniMapDrawAlphaSurfaceVisited(const CStateManager& mgr) { + float mapAlphaInterp = gpTweakGui->GetMapAlphaInterpolant(); + return gpTweakAutoMapper->x84_miniAlphaSurfaceVisited * + ((1.f - mapAlphaInterp) * mgr.GetPlayer()->GetGunAlpha() + mapAlphaInterp); +} + +float CAutoMapper::GetMapAreaMiniMapDrawAlphaOutlineVisited(const CStateManager& mgr) { + float mapAlphaInterp = gpTweakGui->GetMapAlphaInterpolant(); + return gpTweakAutoMapper->x8c_miniAlphaOutlineVisited * + ((1.f - mapAlphaInterp) * mgr.GetPlayer()->GetGunAlpha() + mapAlphaInterp); +} + +float CAutoMapper::GetMapAreaMiniMapDrawAlphaSurfaceUnvisited(const CStateManager& mgr) { + float mapAlphaInterp = gpTweakGui->GetMapAlphaInterpolant(); + return gpTweakAutoMapper->x94_miniAlphaSurfaceUnvisited * + ((1.f - mapAlphaInterp) * mgr.GetPlayer()->GetGunAlpha() + mapAlphaInterp); +} + +float CAutoMapper::GetMapAreaMiniMapDrawAlphaOutlineUnvisited(const CStateManager& mgr) { + float mapAlphaInterp = gpTweakGui->GetMapAlphaInterpolant(); + return gpTweakAutoMapper->x9c_miniAlphaOutlineUnvisited * + ((1.f - mapAlphaInterp) * mgr.GetPlayer()->GetGunAlpha() + mapAlphaInterp); +} + +float CAutoMapper::GetDesiredMiniMapCameraDistance(const CStateManager& mgr) const { + CWorldState& worldState = gpGameState->StateForWorld(x24_world->IGetWorldAssetId()); + CMapWorldInfo* mwInfo = worldState.GetMapWorldInfo().GetPtr(); + const CMapWorld* mw = x24_world->IGetMapWorld(); + CAABox aabb = CAABox::MakeMaxInvertedBox(); + const IGameArea* area = x24_world->IGetAreaAlways(xa0_curAreaId); + const CMapArea* mapa = mw->GetMapArea(xa0_curAreaId.value); + bool oneMiniMapArea = gpTweakAutoMapper->x4_24_showOneMiniMapArea; + for (int i = -1; i < (oneMiniMapArea ? 0 : static_cast< int >(area->IGetNumAttachedAreas())); ++i) { + TAreaId aid = i == -1 ? xa0_curAreaId : area->IGetAttachedAreaId(i); + const CMapArea* attMapa = mw->GetMapArea(aid.value); + if (attMapa->GetIsVisibleToAutoMapper(mwInfo->IsWorldVisible(aid), mwInfo->IsAreaVisible(aid))) { + CAABox areaAABB(attMapa->GetBoundingBox().GetTransformedAABox( + const_cast< CMapArea* >(attMapa)->GetAreaPostTransform(*x24_world, aid.value))); + aabb.AccumulateBounds(areaAABB.GetMinPoint()); + aabb.AccumulateBounds(areaAABB.GetMaxPoint()); + } + } + + CVector3f xfPoint = const_cast< CMapArea* >(mapa)->GetAreaPostTransform(*x24_world, xa0_curAreaId.value) * + mapa->GetAreaCenterPoint(); + float diffXa = xfPoint.GetX() - aabb.GetMinPoint().GetX(); + float diffXb = aabb.GetMaxPoint().GetX() - xfPoint.GetX(); + float maxX = max_val(diffXa, diffXb); + float diffYa = xfPoint.GetY() - aabb.GetMinPoint().GetY(); + float diffYb = aabb.GetMaxPoint().GetY() - xfPoint.GetY(); + float maxY = max_val(diffYa, diffYb); + float diffZa = xfPoint.GetZ() - aabb.GetMinPoint().GetZ(); + float diffZb = aabb.GetMaxPoint().GetZ() - xfPoint.GetZ(); + float maxZ = max_val(diffZa, diffZb); + CVector3f extent = mapa->GetBoundingBox().GetMaxPoint() - mapa->GetBoundingBox().GetMinPoint(); + CVector3f maxMargin(maxX, maxY, maxZ); + + return (0.5f * (0.5f * extent.Magnitude()) + 0.5f * maxMargin.Magnitude()) * + gpTweakAutoMapper->xc0_miniMapCamDistScale * + static_cast< float >(tan(M_PIF / 2.f - 0.5f * 2.f * M_PIF * + (xa8_renderState0.x1c_camAngle / 360.f))); +} + +float CAutoMapper::GetClampedMapScreenCameraDistance(float value) const { + if (x1bc_state == kAMS_MapScreenUniverse) { + float clamped = CMath::Clamp(gpTweakAutoMapper->xd4_minUniverseCamDist, value, + gpTweakAutoMapper->xd8_maxUniverseCamDist); + return clamped; + } + float clamped = CMath::Clamp(gpTweakAutoMapper->xc_minCamDist, value, + gpTweakAutoMapper->x10_maxCamDist); + return clamped; +} + +float CAutoMapper::GetBaseMapScreenCameraMoveSpeed() const { + return gpTweakAutoMapper->x78_baseMapScreenCameraMoveSpeed; +} + +float CAutoMapper::GetFinalMapScreenCameraMoveSpeed() const { + float ret = GetBaseMapScreenCameraMoveSpeed(); + if (gpTweakAutoMapper->x4_26_scaleMoveSpeedWithCamDist) { + ret = ret * xa8_renderState0.x18_camDist / gpTweakAutoMapper->x8_camDist; + } + return ret; +} + +bool CAutoMapper::IsRenderStateInterpolating() const { + return x1c8_interpTime < x1c4_interpDur; +} + +bool CAutoMapper::IsInMapperStateTransition() const { + return x1bc_state != x1c0_nextState; +} + +bool CAutoMapper::IsInMapperState(EAutoMapperState state) const { + return state == x1bc_state && state == x1c0_nextState; +} + +void CAutoMapper::TransformRenderStatesWorldToUniverse() { + const CTransform4f& xf = + x8_mapu.GetObject()->GetMapWorldData(x9c_worldIdx).GetWorldTransform(); + CQuaternion rot = CQuaternion::FromMatrix(xf); + x160_renderState2.x8_camOrientation = x160_renderState2.x8_camOrientation * rot; + x160_renderState2.x20_areaPoint = xf * x160_renderState2.x20_areaPoint; + xa8_renderState0.x8_camOrientation = xa8_renderState0.x8_camOrientation * rot; + xa8_renderState0.x20_areaPoint = xf * xa8_renderState0.x20_areaPoint; + x104_renderState1.x8_camOrientation = x104_renderState1.x8_camOrientation * rot; + x104_renderState1.x20_areaPoint = xf * x104_renderState1.x20_areaPoint; +} + +void CAutoMapper::TransformRenderStatesUniverseToWorld() { + CTransform4f invXf = + x8_mapu.GetObject()->GetMapWorldData(x9c_worldIdx).GetWorldTransform().GetQuickInverse(); + CQuaternion rot = CQuaternion::FromMatrix(invXf); + x160_renderState2.x8_camOrientation = x160_renderState2.x8_camOrientation * rot; + x160_renderState2.x20_areaPoint = invXf * x160_renderState2.x20_areaPoint; + xa8_renderState0.x8_camOrientation = xa8_renderState0.x8_camOrientation * rot; + xa8_renderState0.x20_areaPoint = invXf * xa8_renderState0.x20_areaPoint; + x104_renderState1.x8_camOrientation = x104_renderState1.x8_camOrientation * rot; + x104_renderState1.x20_areaPoint = invXf * x104_renderState1.x20_areaPoint; +} + +void CAutoMapper::TransformRenderStateWorldToUniverse(SAutoMapperRenderState& state) { + const CTransform4f& xf = + x8_mapu.GetObject()->GetMapWorldData(x9c_worldIdx).GetWorldTransform(); + state.x20_areaPoint = xf * x104_renderState1.x20_areaPoint; +} + +void CAutoMapper::LeaveMapScreen(const CStateManager& mgr) const { + CAutoMapper* self = const_cast< CAutoMapper* >(this); + if (x1c0_nextState == kAMS_MapScreenUniverse) { + float depth = GetMapAreaMiniMapDrawDepth(); + self->x104_renderState1.x2c_drawDepth1 = depth; + self->x104_renderState1.x30_drawDepth2 = depth; + self->xa8_renderState0.x2c_drawDepth1 = depth; + self->xa8_renderState0.x30_drawDepth2 = depth; + self->SetupMiniMapWorld(const_cast< CStateManager& >(mgr)); + } else { + self->x328_ = 2; + self->x104_renderState1 = xa8_renderState0; + self->x160_renderState2 = x104_renderState1; + self->xa0_curAreaId = x24_world->IGetCurrentAreaId(); + self->x104_renderState1.x20_areaPoint = GetAreaPointOfInterest(mgr, xa0_curAreaId.value); + self->x104_renderState1.x4c_pointEase = SAutoMapperRenderState::kE_Linear; + float depth = GetMapAreaMiniMapDrawDepth(); + self->x104_renderState1.x2c_drawDepth1 = depth; + self->x104_renderState1.x30_drawDepth2 = GetMapAreaMiniMapDrawDepth(); + self->x104_renderState1.x50_depth1Ease = SAutoMapperRenderState::kE_Linear; + self->x104_renderState1.x54_depth2Ease = SAutoMapperRenderState::kE_Linear; + self->ResetInterpolationTimer(0.25f); + } +} + +void CAutoMapper::SetupMiniMapWorld(CStateManager& mgr) { + CWorld* wld = mgr.World(); + wld->GetMapWorld()->SetWhichMapAreasLoaded(*wld, wld->GetCurrentAreaId().value, 3); + x328_ = 3; +} + +void CAutoMapper::SetCurWorldAssetId(int mlvlId) { + int numWorlds = x8_mapu.GetObject()->GetNumMapWorldDatas(); + for (int i = 0; i < numWorlds; ++i) { + if (mlvlId == x8_mapu.GetObject()->GetMapWorldData(i).GetWorldAssetId()) { + x9c_worldIdx = i; + return; + } + } +} + +CAutoMapper::SAutoMapperRenderState::SAutoMapperRenderState( + const CVector2i& viewportSize, const CQuaternion& camOrientation, float camDist, + float camAngle, const CVector3f& areaPoint, float drawDepth1, float drawDepth2, + float alphaSurfaceVisited, float alphaOutlineVisited, float alphaSurfaceUnvisited, + float alphaOutlineUnvisited) +: x0_viewportSize(viewportSize) +, x8_camOrientation(camOrientation) +, x18_camDist(camDist) +, x1c_camAngle(camAngle) +, x20_areaPoint(areaPoint) +, x2c_drawDepth1(drawDepth1) +, x30_drawDepth2(drawDepth2) +, x34_alphaSurfaceVisited(alphaSurfaceVisited) +, x38_alphaOutlineVisited(alphaOutlineVisited) +, x3c_alphaSurfaceUnvisited(alphaSurfaceUnvisited) +, x40_alphaOutlineUnvisited(alphaOutlineUnvisited) +, x44_viewportEase(kE_None) +, x48_camEase(kE_None) +, x4c_pointEase(kE_None) +, x50_depth1Ease(kE_None) +, x54_depth2Ease(kE_None) +, x58_alphaEase(kE_None) {} + +void CAutoMapper::SAutoMapperRenderState::InterpolateWithClamp( + const SAutoMapperRenderState& a, SAutoMapperRenderState& out, + const SAutoMapperRenderState& b, float t) { + float ct = CMath::Clamp(0.f, t, 1.f); + float easeIn = CMath::Clamp(0.f, ct * ct * ct, 1.f); + float omt = 1.f - ct; + float omtCubed = omt * omt * omt; + float easeOut = CMath::Clamp(0.f, 1.f - omtCubed, 1.f); + + float easeInOut; + if (ct >= 0.5f) { + easeInOut = CMath::Clamp(0.f, 0.5f * CMath::SqrtF(2.f * ct - 1.f) + 0.5f, 1.f); + } else { + easeInOut = CMath::Clamp(0.f, 1.f - (0.5f * CMath::SqrtF(2.f * omt - 1.f) + 0.5f), 1.f); + } + + float eases[5] = {0.f, ct, easeOut, easeIn, easeInOut}; + + if (b.x44_viewportEase != kE_None) { + float easeB = eases[b.x44_viewportEase]; + float easeA = 1.f - easeB; + out.x0_viewportSize = CVector2i( + static_cast< int >(easeA * a.x0_viewportSize.GetX() + easeB * b.x0_viewportSize.GetX()), + static_cast< int >(easeA * a.x0_viewportSize.GetY() + easeB * b.x0_viewportSize.GetY())); + } + + if (b.x48_camEase != kE_None) { + float easeB = eases[b.x48_camEase]; + out.x8_camOrientation = CQuaternion::Slerp(a.x8_camOrientation, b.x8_camOrientation, easeB); + float easeA = 1.f - easeB; + out.x18_camDist = a.x18_camDist * easeA + b.x18_camDist * easeB; + out.x1c_camAngle = a.x1c_camAngle * easeA + b.x1c_camAngle * easeB; + } + + if (b.x4c_pointEase != kE_None) { + float eB = eases[b.x4c_pointEase]; + float eA = 1.f - eB; + out.x20_areaPoint = CVector3f( + a.x20_areaPoint.GetX() * eA + b.x20_areaPoint.GetX() * eB, + a.x20_areaPoint.GetY() * eA + b.x20_areaPoint.GetY() * eB, + a.x20_areaPoint.GetZ() * eA + b.x20_areaPoint.GetZ() * eB); + } + + if (b.x50_depth1Ease != kE_None) { + float eB = eases[b.x50_depth1Ease]; + out.x2c_drawDepth1 = a.x2c_drawDepth1 * (1.f - eB) + b.x2c_drawDepth1 * eB; + } + + if (b.x54_depth2Ease != kE_None) { + float eB = eases[b.x54_depth2Ease]; + out.x30_drawDepth2 = a.x30_drawDepth2 * (1.f - eB) + b.x30_drawDepth2 * eB; + } + + if (b.x58_alphaEase != kE_None) { + float eB = eases[b.x58_alphaEase]; + float eA = 1.f - eB; + out.x34_alphaSurfaceVisited = a.x34_alphaSurfaceVisited * eA + b.x34_alphaSurfaceVisited * eB; + out.x38_alphaOutlineVisited = a.x38_alphaOutlineVisited * eA + b.x38_alphaOutlineVisited * eB; + out.x3c_alphaSurfaceUnvisited = a.x3c_alphaSurfaceUnvisited * eA + b.x3c_alphaSurfaceUnvisited * eB; + out.x40_alphaOutlineUnvisited = a.x40_alphaOutlineUnvisited * eA + b.x40_alphaOutlineUnvisited * eB; + } +} + +void CAutoMapper::SAutoMapperRenderState::ResetInterpolation() { + x44_viewportEase = kE_None; + x48_camEase = kE_None; + x4c_pointEase = kE_None; + x50_depth1Ease = kE_None; + x54_depth2Ease = kE_None; + x58_alphaEase = kE_None; +} + +CAutoMapper::SAutoMapperHintLocation::SAutoMapperHintLocation( + uint showBeacon, float beaconAlpha, CAssetId worldId, TAreaId areaId) +: x0_showBeacon(showBeacon) +, x4_beaconAlpha(beaconAlpha) +, x8_worldId(worldId) +, xc_areaId(areaId) {} + +CAutoMapper::SAutoMapperRenderState::SAutoMapperRenderState( + const SAutoMapperRenderState& other) +: x0_viewportSize(other.x0_viewportSize) +, x8_camOrientation(other.x8_camOrientation) +, x18_camDist(other.x18_camDist) +, x1c_camAngle(other.x1c_camAngle) +, x20_areaPoint(other.x20_areaPoint) +, x2c_drawDepth1(other.x2c_drawDepth1) +, x30_drawDepth2(other.x30_drawDepth2) +, x34_alphaSurfaceVisited(other.x34_alphaSurfaceVisited) +, x38_alphaOutlineVisited(other.x38_alphaOutlineVisited) +, x3c_alphaSurfaceUnvisited(other.x3c_alphaSurfaceUnvisited) +, x40_alphaOutlineUnvisited(other.x40_alphaOutlineUnvisited) +, x44_viewportEase(other.x44_viewportEase) +, x48_camEase(other.x48_camEase) +, x4c_pointEase(other.x4c_pointEase) +, x50_depth1Ease(other.x50_depth1Ease) +, x54_depth2Ease(other.x54_depth2Ease) +, x58_alphaEase(other.x58_alphaEase) {} + +CAutoMapper::CAutoMapper(CStateManager& stateMgr) +: x4_loadPhase(kLP_LoadResources) +, x8_mapu(gpSimplePool->GetObj("MAPU_MapUniverse")) +, x14_dummyWorlds() +, x24_world(stateMgr.World()) +, x28_frmeMapScreen() +, x30_miniMapSamus(gpSimplePool->GetObj("CMDL_MiniMapSamus")) +, x3c_hintBeacon(gpSimplePool->GetObj("TXTR_HintBeacon")) +, x48_mapIcons() +, x74_areaHintDescId(kInvalidAssetId) +, x78_areaHintDesc() +, x88_mapAreaStringId(kInvalidAssetId) +, x8c_mapAreaString() +, x9c_worldIdx(0) +, xa0_curAreaId(x24_world->IGetCurrentAreaId()) +, xa4_otherAreaId(xa0_curAreaId) +, xa8_renderState0(BuildMiniMapWorldRenderState(stateMgr, + CQuaternion::FromMatrix(stateMgr.GetCameraManager()->GetCurrentCamera(stateMgr).GetTransform()), + xa0_curAreaId.value)) +, x104_renderState1(xa8_renderState0) +, x160_renderState2(xa8_renderState0) +, x1bc_state(kAMS_MiniMap) +, x1c0_nextState(kAMS_MiniMap) +, x1c4_interpDur(0.f) +, x1c8_interpTime(0.f) +, x1cc_panningSfx() +, x1d0_rotatingSfx() +, x1d4_zoomingSfx() +, x1d8_flashTimer(0.f) +, x1dc_playerFlashPulse(0.f) +, x1e0_hintSteps() +, x1f8_hintLocations() +, x210_lstick() +, x25c_cstick() +, x2a8_ltrigger() +, x2bc_rtrigger() +, x2d0_abutton() +, x2e4_lStickPos(0) +, x2e8_rStickPos(0) +, x2ec_lTriggerPos(0) +, x2f0_rTriggerPos(0) +, x2f4_aButtonPos(0) +, x2f8_textpane_areaname(NULL) +, x2fc_textpane_hint(NULL) +, x300_textpane_instructions(NULL) +, x304_textpane_instructions1(NULL) +, x308_textpane_instructions2(NULL) +, x30c_basewidget_leftPane(NULL) +, x310_basewidget_yButtonPane(NULL) +, x314_basewidget_bottomPane(NULL) +, x318_leftPanePos(0.f) +, x31c_yButtonPanePos(0.f) +, x320_bottomPanePos(0.f) +, x324_zoomState(kZS_None) +, x328_(0) +, x32c_loadingDummyWorld(false) { + x8_mapu.Lock(); + x30_miniMapSamus.Lock(); + x3c_hintBeacon.Lock(); + + x48_mapIcons.push_back(gpSimplePool->GetObj(SObjectTag('TXTR', gpTweakPlayerRes->x4_saveStationIcon))); + x48_mapIcons.push_back(gpSimplePool->GetObj(SObjectTag('TXTR', gpTweakPlayerRes->x8_missileStationIcon))); + x48_mapIcons.push_back(gpSimplePool->GetObj(SObjectTag('TXTR', gpTweakPlayerRes->xc_elevatorIcon))); + x48_mapIcons.push_back(gpSimplePool->GetObj(SObjectTag('TXTR', gpTweakPlayerRes->x10_minesBreakFirstTopIcon))); + x48_mapIcons.push_back(gpSimplePool->GetObj(SObjectTag('TXTR', gpTweakPlayerRes->x14_minesBreakFirstBottomIcon))); + + for (CToken* it = x48_mapIcons.begin(); it != x48_mapIcons.end(); ++it) { + it->Lock(); + } + + for (int i = 0; i < 9; ++i) { + x210_lstick.push_back(gpSimplePool->GetObj( + SObjectTag('TXTR', ((const CAssetId*)((const char*)gpTweakPlayerRes + 0x24))[i]))); + x25c_cstick.push_back(gpSimplePool->GetObj( + SObjectTag('TXTR', ((const CAssetId*)((const char*)gpTweakPlayerRes + 0x4c))[i]))); + } + + for (int i = 0; i < 2; ++i) { + x2a8_ltrigger.push_back(gpSimplePool->GetObj( + SObjectTag('TXTR', ((const CAssetId*)((const char*)gpTweakPlayerRes + 0x74))[i]))); + x2bc_rtrigger.push_back(gpSimplePool->GetObj( + SObjectTag('TXTR', ((const CAssetId*)((const char*)gpTweakPlayerRes + 0x80))[i]))); + x2d0_abutton.push_back(gpSimplePool->GetObj( + SObjectTag('TXTR', ((const CAssetId*)((const char*)gpTweakPlayerRes + 0x98))[i]))); + } +} + +template < class T > +void CAutoMapper::SetResLockState(T& list, bool lock) { + CToken* it = list.data(); + for (; it != list.data() + list.size(); ++it) { + if (lock) { + it->Lock(); + } else { + it->Unlock(); + } + } +} + +bool CAutoMapper::CheckLoadComplete() { + switch (x4_loadPhase) { + case kLP_LoadResources: { + CToken* iconIt = x48_mapIcons.data(); + CToken* iconEnd = iconIt + x48_mapIcons.size(); + for (; iconIt != iconEnd; ++iconIt) { + if (!iconIt->IsLoaded()) + return false; + } + if (x30_miniMapSamus.TryCache()) { + if (x3c_hintBeacon.TryCache()) { + x4_loadPhase = kLP_LoadUniverse; + } else { + return false; + } + } else { + return false; + } + } + case kLP_LoadUniverse: { + if (x8_mapu.TryCache()) { + int numWorlds = x8_mapu.GetObject()->GetNumMapWorldDatas(); + rstl::auto_ptr< IWorld > dummy; + rstl::vector< rstl::auto_ptr< IWorld > > worlds(numWorlds, dummy); + x14_dummyWorlds = worlds; + SetCurWorldAssetId(x24_world->IGetWorldAssetId()); + x4_loadPhase = kLP_Done; + } else { + return false; + } + } + case kLP_Done: + return true; + default: + return false; + } +} + +CAutoMapper::~CAutoMapper() { CSfxManager::KillAll(CSfxManager::kSC_PauseScreen); } diff --git a/src/MetroidPrime/CFluidPlaneCPU.cpp b/src/MetroidPrime/CFluidPlaneCPU.cpp index d833b2d7d..5eac8c4fd 100644 --- a/src/MetroidPrime/CFluidPlaneCPU.cpp +++ b/src/MetroidPrime/CFluidPlaneCPU.cpp @@ -2,57 +2,95 @@ #include "Kyoto/CResFactory.hpp" #include "Kyoto/CSimplePool.hpp" +#include "Kyoto/Math/CAABox.hpp" #include "Kyoto/Math/CMath.hpp" +#include "Kyoto/Math/CTransform4f.hpp" #include "Kyoto/Alloc/CMemory.hpp" +#include "Kyoto/Basics/CCast.hpp" -static int kTableSize = 2048; +#include "rstl/math.hpp" -CFluidPlaneCPU::CTurbulence::CTurbulence(float speed, float distance, float freqMax, float freqMin, - float phaseMax, float phaseMin, float amplitudeMax, - float amplitudeMin) -: x0_speed(speed) -, x4_distance(distance) -, x8_freqMax(freqMax) -, xc_freqMin(freqMin) -, x10_phaseMax(phaseMax) -, x14_phaseMin(phaseMin) -, x18_amplitudeMax(amplitudeMax) -, x1c_amplitudeMin(amplitudeMin) -, x20_table(NULL) -, x24_tableCount(0) -, x28_heightSelPitch(0.f) -, x2c_ooTurbSpeed(1.f / x0_speed) -, x30_ooTurbDistance(1.f / x4_distance) -, x34_hasTurbulence(false) { - if (x18_amplitudeMax == 0.f && x1c_amplitudeMin == 0.f) { - return; - } +#include "MetroidPrime/CRipple.hpp" - x24_tableCount = kTableSize; - x28_heightSelPitch = (float)x24_tableCount; - x20_table = rs_new float[x24_tableCount]; +#include "rstl/reserved_vector.hpp" - float anglePitch = M_2PIF / x28_heightSelPitch; - float freqConstant = 0.5f * (x8_freqMax + xc_freqMin); - float freqLinear = 0.5f * (x8_freqMax - xc_freqMin); - float phaseConstant = 0.5f * (x10_phaseMax + x14_phaseMin); - float phaseLinear = 0.5f * (x10_phaseMax - x14_phaseMin); - float amplitudeConstant = 0.5f * (x18_amplitudeMax + x1c_amplitudeMin); - float amplitudeLinear = 0.5f * (x18_amplitudeMax - x1c_amplitudeMin); +#include "dolphin/os/OSCache.h" - float curAng = 0.f; - for (int i = 0; i < x24_tableCount; ++i, curAng += anglePitch) { - float angCos = CMath::FastCosR(curAng); - x20_table[i] = - (amplitudeLinear * angCos + amplitudeConstant) * - CMath::FastSinR((freqLinear * angCos + freqConstant) * curAng + - (phaseLinear * angCos + phaseConstant)); +#include "Kyoto/Graphics/CGX.hpp" +#include "Kyoto/Graphics/CGraphics.hpp" +#include "Kyoto/Graphics/CLight.hpp" +#include "Kyoto/Graphics/CTexture.hpp" + +#include "MetaRender/CCubeRenderer.hpp" +#include "MetroidPrime/CGameArea.hpp" +#include "MetroidPrime/CWorld.hpp" +#include "MetroidPrime/Cameras/CCameraManager.hpp" +#include "MetroidPrime/Cameras/CGameCamera.hpp" +#include "MetroidPrime/ScriptObjects/CScriptWater.hpp" +#include "MetroidPrime/Tweaks/CTweakGame.hpp" + +#include "dolphin/gx/GXBump.h" +#include "dolphin/gx/GXCull.h" +#include "dolphin/gx/GXTransform.h" +#include "dolphin/mtx.h" + +#include + +extern "C" float* InitializeSineWave(); +extern const bool sRenderBumpMaps; +extern const int sFluidEnvMapType; + +struct STexMtx24 { + float m[2][4]; +}; +struct SIndMtx23 { + float m[2][3]; +}; +struct SEnvMtx34 { + float m[3][4]; +}; +struct SFluidTexMtxTable { + STexMtx24 pad; + STexMtx24 color; + STexMtx24 pattern1; + STexMtx24 pattern2; + SIndMtx23 ind; + SEnvMtx34 env; +}; +static const SFluidTexMtxTable kTexMtxTable = {}; + +static uchar sFluidSetupInitOnce; +static uchar sFluidSetupDone; +static int kMaxTilesInHField = 7; + +int CFluidPlaneCPURender::numTilesInHField; +int CFluidPlaneCPURender::numSubdivisionsInTile; +int CFluidPlaneCPURender::numSubdivisionsInHField; + +int CFluidPlaneCPU::CTurbulence::kTableSize = 2048; + +// TODO: these are in CFluidPlaneRender +const float (&GetGlobalSineWave())[256]; +bool PrepareRipple(const CRipple& ripple, const CFluidPlaneCPURender::SPatchInfo& info, + CFluidPlaneCPURender::SRippleInfo& rippleOut); +void ApplyRipples(const CRippleManager& rippleInfos, + CFluidPlaneCPURender::SHFieldSample (&heights)[45][45], + unsigned char (&flags)[9][9], const float (&sineTable)[256], + CFluidPlaneCPURender::SPatchInfo& info); + +#ifdef __MWERKS__ +static inline float fast_sqrt(register float x) { + register float rsq; + asm { + ps_rsqrte rsq, x + ps_mul rsq, rsq, x } - x34_hasTurbulence = true; + return rsq; } - -CFluidPlaneCPU::CTurbulence::~CTurbulence() { delete[] x20_table; } +#else +static inline float fast_sqrt(float x) { return sqrtf(x); } +#endif CFluidPlaneCPU::CFluidPlaneCPU(uint patternMap1, uint patternMap2, uint colorMap, uint bumpMap, uint envMap, uint envBumpMap, float unitsPerLightmapTexel, @@ -101,4 +139,1280 @@ CFluidPlaneCPU::CFluidPlaneCPU(uint patternMap1, uint patternMap2, uint colorMap } } -CFluidPlaneCPU::~CFluidPlaneCPU() {} +CFluidPlaneCPU::CTurbulence::CTurbulence(float speed, float distance, float freqMax, float freqMin, + float phaseMax, float phaseMin, float amplitudeMax, + float amplitudeMin) +: x0_speed(speed) +, x4_distance(distance) +, x8_freqMax(freqMax) +, xc_freqMin(freqMin) +, x10_phaseMax(phaseMax) +, x14_phaseMin(phaseMin) +, x18_amplitudeMax(amplitudeMax) +, x1c_amplitudeMin(amplitudeMin) +, x20_table(NULL) +, x24_tableCount(0) +, x28_heightSelPitch(0.f) +, x2c_ooTurbSpeed(1.f / x0_speed) +, x30_ooTurbDistance(1.f / x4_distance) +, x34_hasTurbulence(false) { + if (x18_amplitudeMax == 0.f && x1c_amplitudeMin == 0.f) { + return; + } + + x24_tableCount = kTableSize; + x28_heightSelPitch = (float)x24_tableCount; + x20_table = rs_new float[x24_tableCount]; + + float freqConstant = 0.5f * (x8_freqMax + xc_freqMin); + float freqLinear = 0.5f * (x8_freqMax - xc_freqMin); + float phaseConstant = 0.5f * (x10_phaseMax + x14_phaseMin); + float phaseLinear = 0.5f * (x10_phaseMax - x14_phaseMin); + float amplitudeConstant = 0.5f * (x18_amplitudeMax + x1c_amplitudeMin); + float amplitudeLinear = 0.5f * (x18_amplitudeMax - x1c_amplitudeMin); + + float curAng = 0.f; + const float anglePitch = M_2PIF / x28_heightSelPitch; + for (int i = 0; i < x24_tableCount; ++i, curAng += anglePitch) { + float angCos = CMath::FastCosR(curAng); + float freq = freqLinear * angCos + freqConstant; + x20_table[i] = (amplitudeLinear * angCos + amplitudeConstant) * + CMath::FastSinR(freq * curAng + (phaseLinear * angCos + phaseConstant)); + } + x34_hasTurbulence = true; +} + +CFluidPlaneCPU::CTurbulence::~CTurbulence() { delete[] x20_table; } + +void UpdatePatchWithNormals(const unsigned char* heights, const unsigned char* flags, + const CFluidPlaneCPURender::SPatchInfo& info) { + typedef CFluidPlaneCPURender::SHFieldSample SHFieldSample; + + float* rowBase = (float*)(heights + 0x170); + float tmp = 2.f * info.x18_rippleResolution; + flags += 9; + float normalScale = -tmp; + float nz = 0.25f * tmp; + + int curGridY = (int)info.x2e_tileY * (int)info.x2a_gridDimX + (int)info.x28_tileX - 1; + + for (int i = 1; i <= (info.x1_ySubdivs + CFluidPlaneCPURender::numSubdivisionsInTile - 2) / + CFluidPlaneCPURender::numSubdivisionsInTile; + ++i) { + int yHi = i * CFluidPlaneCPURender::numSubdivisionsInTile + 1; + static const int sZero = 0; + int yStart = rstl::max_val< int >(sZero, yHi - CFluidPlaneCPURender::numSubdivisionsInTile); + int yEnd = rstl::min_val< int >(yHi, info.x1_ySubdivs + 1); + + float* colBase = rowBase; + const unsigned char* fPtr = flags + 1; + for (int j = 1; j <= (info.x0_xSubdivs + CFluidPlaneCPURender::numSubdivisionsInTile - 2) / + CFluidPlaneCPURender::numSubdivisionsInTile; + ++j, colBase += CFluidPlaneCPURender::numSubdivisionsInTile * 2, ++fPtr) { + int xHi = j * CFluidPlaneCPURender::numSubdivisionsInTile + 1; + int xEnd = rstl::min_val< int >(xHi, info.x0_xSubdivs + 1); + int xStart = xHi - CFluidPlaneCPURender::numSubdivisionsInTile; + + if ((*fPtr & 0x1f) == 0x1f) { + float* pRow = colBase; + for (int k = yStart; k < yEnd; ++k, pRow += 90) { + float* p = pRow; + for (int l = xStart; l < xEnd; ++l, p += 2) { + float dy = normalScale * (p[90] - p[-90]); + float dx = normalScale * (p[2] - p[-2]); + float mag = dy * dy + dx * dx + nz * nz; + if (0.f != mag) + mag = fast_sqrt(mag); + float n = 63.f / mag; + int wc = 0; + ((SHFieldSample*)p)->nx = (signed char)(int)(n * dx); + ((SHFieldSample*)p)->ny = (signed char)(int)(n * dy); + ((SHFieldSample*)p)->nz = (signed char)(int)(n * nz); + if (0.f < p[0]) { + wc = (int)(p[0] * info.x38_wavecapIntensityScale); + if (wc > 255) + wc = 255; + } + ((SHFieldSample*)p)->wavecapIntensity = (unsigned char)wc; + } + } + } else { + if ((info.x30_gridFlags == NULL || info.x30_gridFlags[curGridY + j]) && i > 0 && + i < CFluidPlaneCPURender::numTilesInHField + 1 && j > 0 && + j < CFluidPlaneCPURender::numTilesInHField + 1) { + int halfRow = (CFluidPlaneCPURender::numSubdivisionsInTile * 0x2d) / 2; + int halfCol = CFluidPlaneCPURender::numSubdivisionsInTile / 2; + float* p = colBase + halfCol * 2 + halfRow * 2; + float dy = normalScale * (p[90] - p[-90]); + float dx = normalScale * (p[2] - p[-2]); + float mag = dy * dy + dx * dx + nz * nz; + if (0.f != mag) + mag = fast_sqrt(mag); + int wc = 0; + float n = 63.f / mag; + ((SHFieldSample*)p)->nx = (signed char)(int)(n * dx); + ((SHFieldSample*)p)->ny = (signed char)(int)(n * dy); + ((SHFieldSample*)p)->nz = (signed char)(int)(n * nz); + if (0.f < p[0]) { + wc = (int)(p[0] * info.x38_wavecapIntensityScale); + if (wc > 255) + wc = 255; + } + ((SHFieldSample*)p)->wavecapIntensity = (unsigned char)wc; + } + + if (j != 0 && i != 0) { + if ((*fPtr & 2) == 0 && (fPtr[-10] & 1) == 0 && (*fPtr & 4) == 0 && (fPtr[-1] & 8) == 0) { + // Corner only + float* p = colBase; + float dy = normalScale * (p[90] - p[-90]); + float dx = normalScale * (p[2] - p[-2]); + float mag = dy * dy + dx * dx + nz * nz; + if (0.f != mag) + mag = fast_sqrt(mag); + int wc = 0; + float n = 63.f / mag; + ((SHFieldSample*)p)->nx = (signed char)(int)(n * dx); + ((SHFieldSample*)p)->ny = (signed char)(int)(n * dy); + ((SHFieldSample*)p)->nz = (signed char)(int)(n * nz); + if (0.f < p[0]) { + wc = (int)(p[0] * info.x38_wavecapIntensityScale); + if (wc > 255) + wc = 255; + } + ((SHFieldSample*)p)->wavecapIntensity = (unsigned char)wc; + } else { + // Row edge + float* p = colBase; + for (int l = xStart; l < xEnd; ++l, p += 2) { + float dy = normalScale * (p[90] - p[-90]); + float dx = normalScale * (p[2] - p[-2]); + float mag = dy * dy + dx * dx + nz * nz; + if (0.f != mag) + mag = fast_sqrt(mag); + float n = 63.f / mag; + int wc = 0; + ((SHFieldSample*)p)->nx = (signed char)(int)(n * dx); + ((SHFieldSample*)p)->ny = (signed char)(int)(n * dy); + ((SHFieldSample*)p)->nz = (signed char)(int)(n * nz); + if (0.f < p[0]) { + wc = (int)(p[0] * info.x38_wavecapIntensityScale); + if (wc > 255) + wc = 255; + } + ((SHFieldSample*)p)->wavecapIntensity = (unsigned char)wc; + } + + // Column edge + float* q = colBase + 90; + for (int k = yStart + 1; k < yEnd; ++k, q += 90) { + float dy = normalScale * (q[90] - q[-90]); + float dx = normalScale * (q[2] - q[-2]); + float mag = dy * dy + dx * dx + nz * nz; + if (0.f != mag) + mag = fast_sqrt(mag); + float n = 63.f / mag; + int wc = 0; + ((SHFieldSample*)q)->nx = (signed char)(int)(n * dx); + ((SHFieldSample*)q)->ny = (signed char)(int)(n * dy); + ((SHFieldSample*)q)->nz = (signed char)(int)(n * nz); + if (0.f < q[0]) { + wc = (int)(q[0] * info.x38_wavecapIntensityScale); + if (wc > 255) + wc = 255; + } + ((SHFieldSample*)q)->wavecapIntensity = (unsigned char)wc; + } + } + } + } + } + curGridY += info.x2a_gridDimX; + rowBase += CFluidPlaneCPURender::numSubdivisionsInTile * 90; + flags += 9; + } +} + +void UpdatePatchNoNormals(const unsigned char* heights, const unsigned char* flags, + const CFluidPlaneCPURender::SPatchInfo& info) { + typedef CFluidPlaneCPURender::SHFieldSample SHFieldSample; + + flags += 9; + + for (int i = 1; i <= (info.x1_ySubdivs + CFluidPlaneCPURender::numSubdivisionsInTile - 2) / + CFluidPlaneCPURender::numSubdivisionsInTile; + ++i) { + int yHi = i * CFluidPlaneCPURender::numSubdivisionsInTile + 1; + static const int sYZero = 0; + int yStart = rstl::max_val< int >(sYZero, yHi - CFluidPlaneCPURender::numSubdivisionsInTile); + const int& yEnd = rstl::min_val< int >(yHi, info.x1_ySubdivs + 1); + + float* rowBase = (float*)heights + yStart * 90; + const unsigned char* fPtr = flags + 1; + for (int j = 1; j <= (info.x0_xSubdivs + CFluidPlaneCPURender::numSubdivisionsInTile - 2) / + CFluidPlaneCPURender::numSubdivisionsInTile; + ++j, ++fPtr) { + int xHi = j * CFluidPlaneCPURender::numSubdivisionsInTile + 1; + static const int sXZero = 0; + int xStart = rstl::max_val< int >(sXZero, xHi - CFluidPlaneCPURender::numSubdivisionsInTile); + const int& xEnd = rstl::min_val< int >(xHi, info.x0_xSubdivs + 1); + + if ((*fPtr & 0x1f) == 0x1f) { + float* pRow = rowBase; + for (int k = yStart; k < yEnd; ++k, pRow += 90) { + float* p = pRow + xStart * 2; + for (int l = xStart; l < xEnd; ++l, p += 2) { + int wc = 0; + float scale = info.x38_wavecapIntensityScale; + if (p[0] > 0.f) { + wc = (int)(p[0] * scale); + if (wc > 255) + wc = 255; + } + ((SHFieldSample*)p)->wavecapIntensity = (unsigned char)wc; + } + } + } else { + if (i > 0 && i < CFluidPlaneCPURender::numTilesInHField + 1 && j > 0 && + j < CFluidPlaneCPURender::numTilesInHField + 1) { + int half = CFluidPlaneCPURender::numSubdivisionsInTile / 2; + float* p = (float*)heights + (yStart + half) * 90 + (xStart + half) * 2; + float scale = info.x38_wavecapIntensityScale; + int wc = 0; + if (p[0] > 0.f) { + wc = (int)(p[0] * scale); + if (wc > 255) + wc = 255; + } + ((SHFieldSample*)p)->wavecapIntensity = (unsigned char)wc; + } + + if (i != 0) { + float* p = rowBase + xStart * 2; + for (int l = xStart; l < xEnd; ++l, p += 2) { + int wc = 0; + float scale = info.x38_wavecapIntensityScale; + if (p[0] > 0.f) { + wc = (int)(p[0] * scale); + if (wc > 255) + wc = 255; + } + ((SHFieldSample*)p)->wavecapIntensity = (unsigned char)wc; + } + } + + if (j != 0) { + int k = yStart + 1; + float* q = (float*)heights + k * 90 + xStart * 2; + for (; k < yEnd; ++k, q += 90) { + int wc = 0; + float scale = info.x38_wavecapIntensityScale; + if (q[0] > 0.f) { + wc = (int)(q[0] * scale); + if (wc > 255) + wc = 255; + } + ((SHFieldSample*)q)->wavecapIntensity = (unsigned char)wc; + } + } + } + } + flags += 9; + } +} + +void ApplyTurbulence(float time, CFluidPlaneCPURender::SHFieldSample (&heights)[45][45], + const unsigned char* flags, const float (&sineTable)[256], + const CFluidPlaneCPURender::SPatchInfo& info, const CFluidPlaneCPU& fluidPlane, + const CVector3f& areaCenter) { + bool hasTurb = sRenderFog ? fluidPlane.HasTurbulence() : false; + if (!hasTurb) { + DCZeroRange(&heights, sizeof(heights)); + return; + } + + float ooTurbSpeed = fluidPlane.GetOOTurbulenceSpeed(); + float rippleRes = info.x18_rippleResolution; + float scaledT = time * ooTurbSpeed; + float ooTurbDist = fluidPlane.GetOOTurbulenceDistance(); + float curY = (info.x8_localMinY - rippleRes) - areaCenter.GetY(); + int xDivs = (info.x0_xSubdivs + CFluidPlaneCPURender::numSubdivisionsInTile - 4) / + CFluidPlaneCPURender::numSubdivisionsInTile * + CFluidPlaneCPURender::numSubdivisionsInTile + + 2; + int yDivs = (info.x1_ySubdivs + CFluidPlaneCPURender::numSubdivisionsInTile - 4) / + CFluidPlaneCPURender::numSubdivisionsInTile * + CFluidPlaneCPURender::numSubdivisionsInTile + + 2; + for (int i = 0; i <= yDivs; ++i) { + float curYSq = curY * curY; + float curX = (info.x4_localMinX - rippleRes) - areaCenter.GetX(); + CFluidPlaneCPURender::SHFieldSample* row = &heights[i][0]; + for (int j = 0; j <= xDivs; ++j) { + float distSq = curX * curX + curYSq; + float dist; + if (0.f == distSq) { + dist = distSq; + } else { + dist = fast_sqrt(distSq); + } + float sel = ooTurbDist * dist; + sel += scaledT; + row[j].height = fluidPlane.GetTurbulenceHeight(sel); + curX += info.x18_rippleResolution; + } + curY += info.x18_rippleResolution; + } +} + +bool UpdatePatch(float time, CFluidPlaneCPURender::SPatchInfo& info, + const CFluidPlaneCPU& fluidPlane, const CVector3f& areaCenter, + const CRippleManager& rippleManager, int fromX, int toX, int fromY, int toY) { + typedef CFluidPlaneCPURender::SRippleInfo SRippleInfo; + typedef CFluidPlaneCPURender::SHFieldSample SHFieldSample; + + const float (&sineTable)[256] = GetGlobalSineWave(); + + DCZeroRange((void*)0xe0000040, 0x51); + + rstl::reserved_vector< SRippleInfo, 32 > rippleInfos; + bool noRipples = true; + + if (*((const unsigned char*)&rippleManager + 0x18)) { + const rstl::vector< CRipple >& ripples = rippleManager.GetRipples(); + for (const CRipple* ripple = ripples.data(); ripple != ripples.data() + ripples.size(); + ++ripple) { + if (ripple->GetTime() >= ripple->GetTimeFalloff()) + continue; + SRippleInfo rippleInfo(*ripple, fromX, toX, fromY, toY); + if (PrepareRipple(*ripple, info, rippleInfo)) { + rippleInfos.push_back(rippleInfo); + } + } + if (!rippleInfos.empty()) { + noRipples = false; + } + } + + if (noRipples) { + return noRipples; + } + + SHFieldSample(&heights)[45][45] = *(SHFieldSample(*)[45][45])0xe00000a0; + + ApplyTurbulence(time, heights, (unsigned char*)0xe0000040, sineTable, info, fluidPlane, + areaCenter); + ApplyRipples(reinterpret_cast< const CRippleManager& >(rippleInfos), heights, + *(unsigned char (*)[9][9])0xe0000040, sineTable, info); + + if ((int)info.x37_normalMode == 1) { + UpdatePatchNoNormals((const unsigned char*)0xe00000a0, (const unsigned char*)0xe0000040, info); + } else { + UpdatePatchWithNormals((const unsigned char*)0xe00000a0, (const unsigned char*)0xe0000040, + info); + } + + return noRipples; +} + +void CFluidPlaneCPU::CalculateLightmapMtx(const CTransform4f& areaXf, const CTransform4f& xf, + const CAABox& aabb, int idx) { + int width = GetLightMap()->GetWidth(); + int height = GetLightMap()->GetHeight(); + + CVector3f norm = areaXf.GetRow(kDZ).AsNormalized(); + CTransform4f toLocal = areaXf.GetRotation().GetQuickInverse(); + CAABox areaLocalAABB = aabb.GetTransformedAABox(toLocal); + + float scaleU = (areaLocalAABB.GetMaxPoint().GetX() - areaLocalAABB.GetMinPoint().GetX()) / + (width * x11c_unitsPerLightmapTexel); + float scaleV = (areaLocalAABB.GetMaxPoint().GetY() - areaLocalAABB.GetMinPoint().GetY()) / + (height * x11c_unitsPerLightmapTexel); + + float f24 = + (1.f + fmod(areaLocalAABB.GetMinPoint().GetX() + xf.Get03(), x11c_unitsPerLightmapTexel)) / + width; + float f23 = + (2.f - fmod(areaLocalAABB.GetMaxPoint().GetX() + xf.Get03(), x11c_unitsPerLightmapTexel)) / + width; + float f29 = + (1.f + fmod(areaLocalAABB.GetMinPoint().GetY() + xf.Get13(), x11c_unitsPerLightmapTexel)) / + height; + float f6 = + (2.f - fmod(areaLocalAABB.GetMaxPoint().GetY() + xf.Get13(), x11c_unitsPerLightmapTexel)) / + height; + + CTransform4f texMtx( + (scaleU - f24 - f23) / + (areaLocalAABB.GetMaxPoint().GetX() - areaLocalAABB.GetMinPoint().GetX()), + 0.f, 0.f, + f24 + scaleU * -areaLocalAABB.GetMinPoint().GetX() / + (areaLocalAABB.GetMaxPoint().GetX() - areaLocalAABB.GetMinPoint().GetX()), + 0.f, + (-(scaleV - f29 - f6)) / + (areaLocalAABB.GetMaxPoint().GetY() - areaLocalAABB.GetMinPoint().GetY()), + 0.f, + scaleV * areaLocalAABB.GetMinPoint().GetY() / + (areaLocalAABB.GetMaxPoint().GetY() - areaLocalAABB.GetMinPoint().GetY()) - + f6, + 0.f, 0.f, 0.f, 0.f); + + CTransform4f result(texMtx * toLocal); + float mtx[2][4] = {{result.Get00(), result.Get01(), result.Get02(), result.Get03()}, + {result.Get10(), result.Get11(), result.Get12(), result.Get13()}}; + CGX::LoadTexMtxImm(mtx, idx, GX_MTX2x4); +} + +void CFluidPlaneCPU::RenderSetup(const CStateManager& mgr, float alpha, const CTransform4f& xf, + const CTransform4f& areaXf, const CAABox& aabb, + CScriptWater* water) const { + const SFluidTexMtxTable* tbl = &kTexMtxTable; + + if (!sRenderFog) { + return; + } + + bool hasBumpMap = false; + bool hasDoubleLightmap = false; + float uvT = mgr.GetFluidPlaneManager()->GetTime(); + if (HasBumpMap() && sRenderBumpMaps) { + hasBumpMap = true; + } + uchar hasLightmap = HasLightMap(); + int envMapType; + if (mgr.GetCameraManager()->GetFluidCounter() != 0) { + envMapType = 0; + } else { + uchar hasEnv = HasEnvMap(); + envMapType = sFluidEnvMapType & ((-hasEnv | hasEnv) >> 31); + } + uchar hasEnvBumpMap = HasEnvBumpMap(); + + InitializeSineWave(); + + gpRender->SetModelMatrix(xf); + + const GXColor ambColor = {0, 0, 0, 0}; + const GXColor white = {0xff, 0xff, 0xff, 0xff}; + + if (hasBumpMap) { + CColor bumpLightColor(0.5f, 0.5f, 0.5f, 1.f); + CLight bumpLight = CLight::BuildDirectional(GetBumpLightDir().AsNormalized(), bumpLightColor); + CLight light(bumpLight); + CGraphics::LoadLight(kLight3, light); + CGX::SetNumChans(2); + CGX::SetChanCtrl(CGX::Channel1, true, GX_SRC_REG, GX_SRC_REG, GX_LIGHT3, GX_DF_CLAMP, + GX_AF_SPOT); + CGX::SetChanMatColor(CGX::Channel1, white); + CGX::SetChanAmbColor(CGX::Channel1, ambColor); + CGX::SetChanCtrl(CGX::Channel0, true, GX_SRC_REG, GX_SRC_VTX, GX_LIGHT_NULL, GX_DF_CLAMP, + GX_AF_SPOT); + CGX::SetChanMatColor(CGX::Channel0, white); + CGX::SetChanAmbColor(CGX::Channel0, ambColor); + } else { + CGX::SetNumChans(2); + CGX::SetChanCtrl(CGX::Channel1, true, GX_SRC_REG, GX_SRC_REG, + (GXLightID)CGraphics::GetLightMask(), GX_DF_CLAMP, GX_AF_SPOT); + const GXColor* matColor = &ambColor; + if (CGraphics::GetLightMask() != 0) { + matColor = &white; + } + CGX::SetChanMatColor(CGX::Channel1, *matColor); + if (hasLightmap) { + CGX::SetChanAmbColor(CGX::Channel1, ambColor); + } + CGX::SetChanCtrl(CGX::Channel0, true, GX_SRC_REG, GX_SRC_VTX, GX_LIGHT_NULL, GX_DF_CLAMP, + GX_AF_SPOT); + CGX::SetChanMatColor(CGX::Channel0, white); + CGX::SetChanAmbColor(CGX::Channel0, ambColor); + } + + int nextTexMap = 0; + int nextCoord = 0; + + int texMapIds[8]; + int texCoordIds[8]; + + if (HasTexturePattern1()) { + GetTexturePattern1()->Load((GXTexMapID)nextTexMap, CTexture::kCM_Repeat); + } else { + CCubeRenderer::That()->GetZeroTexture().Load((GXTexMapID)nextTexMap, CTexture::kCM_Repeat); + } + texMapIds[0] = nextTexMap; + nextTexMap = 1; + + if (HasTexturePattern2()) { + GetTexturePattern2()->Load((GXTexMapID)nextTexMap, CTexture::kCM_Repeat); + } else { + CCubeRenderer::That()->GetZeroTexture().Load((GXTexMapID)nextTexMap, CTexture::kCM_Repeat); + } + texMapIds[1] = nextTexMap; + nextTexMap = 2; + + if (HasColorTexture()) { + GetColorTexture()->Load((GXTexMapID)nextTexMap, CTexture::kCM_Repeat); + } else { + CCubeRenderer::That()->GetZeroTexture().Load((GXTexMapID)nextTexMap, CTexture::kCM_Repeat); + } + texMapIds[2] = nextTexMap; + nextTexMap = 3; + + if (hasBumpMap) { + texMapIds[3] = nextTexMap; + GetBumpMap()->Load((GXTexMapID)nextTexMap, CTexture::kCM_Repeat); + nextTexMap = 4; + } + + if (envMapType != 0) { + texMapIds[4] = nextTexMap; + GetEnvMap()->Load((GXTexMapID)nextTexMap, CTexture::kCM_Repeat); + nextTexMap += 1; + } + + if (hasEnvBumpMap) { + texMapIds[5] = nextTexMap; + GetEnvBumpMap()->Load((GXTexMapID)nextTexMap, CTexture::kCM_Repeat); + nextTexMap += 1; + } + + float uvOffsets[3][2]; + GetUVMotion().CalculateFluidTextureOffset(uvT, uvOffsets); + + STexMtx24 colorMtx = tbl->color; + colorMtx.m[0][0] = x4c_uvMotion.GetFluidLayerMotion(CFluidUVMotion::kFM_Circular).x14_uvScale; + colorMtx.m[1][1] = x4c_uvMotion.GetFluidLayerMotion(CFluidUVMotion::kFM_Circular).x14_uvScale; + colorMtx.m[0][3] = uvOffsets[1][0]; + colorMtx.m[1][3] = uvOffsets[1][1]; + + STexMtx24 pattern1Mtx = tbl->pattern1; + pattern1Mtx.m[0][0] = x4c_uvMotion.GetFluidLayerMotion(CFluidUVMotion::kFM_Oscillate).x14_uvScale; + pattern1Mtx.m[1][1] = x4c_uvMotion.GetFluidLayerMotion(CFluidUVMotion::kFM_Oscillate).x14_uvScale; + pattern1Mtx.m[0][3] = uvOffsets[2][0]; + pattern1Mtx.m[1][3] = uvOffsets[2][1]; + + STexMtx24 pattern2Mtx = tbl->pattern2; + pattern2Mtx.m[0][0] = x4c_uvMotion.GetFluidLayerMotion(CFluidUVMotion::kFM_Linear).x14_uvScale; + pattern2Mtx.m[1][1] = x4c_uvMotion.GetFluidLayerMotion(CFluidUVMotion::kFM_Linear).x14_uvScale; + pattern2Mtx.m[0][3] = uvOffsets[0][0]; + pattern2Mtx.m[1][3] = uvOffsets[0][1]; + + GXLoadTexMtxImm(colorMtx.m, GX_TEXMTX0, GX_MTX2x4); + GXLoadTexMtxImm(pattern1Mtx.m, GX_TEXMTX1, GX_MTX2x4); + GXLoadTexMtxImm(pattern2Mtx.m, GX_TEXMTX2, GX_MTX2x4); + + int texMtx = GX_TEXMTX3; + + if (hasBumpMap) { + float bumpScale = GetBumpScale(); + CTransform4f nrmMtxSrc(CGraphics::GetViewMatrix().GetRotation().GetQuickInverse()); + Mtx nrmMtx; + nrmMtx[0][0] = nrmMtxSrc.Get00(); + nrmMtx[0][1] = nrmMtxSrc.Get01(); + nrmMtx[0][2] = nrmMtxSrc.Get02(); + nrmMtx[0][3] = nrmMtxSrc.Get03(); + nrmMtx[1][0] = nrmMtxSrc.Get10(); + nrmMtx[1][1] = nrmMtxSrc.Get11(); + nrmMtx[1][2] = nrmMtxSrc.Get12(); + nrmMtx[1][3] = nrmMtxSrc.Get13(); + nrmMtx[2][0] = nrmMtxSrc.Get20(); + nrmMtx[2][1] = nrmMtxSrc.Get21(); + nrmMtx[2][2] = nrmMtxSrc.Get22(); + nrmMtx[2][3] = nrmMtxSrc.Get23(); + MTXScaleApply(nrmMtx, nrmMtx, bumpScale, bumpScale, bumpScale); + GXLoadNrmMtxImm(nrmMtx, GX_PNMTX0); + } + + texCoordIds[0] = nextCoord; + CGX::SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_POS, GX_TEXMTX0, false, GX_PTIDENTITY); + nextCoord = 1; + texCoordIds[1] = nextCoord; + CGX::SetTexCoordGen(GX_TEXCOORD1, GX_TG_MTX2x4, GX_TG_POS, GX_TEXMTX1, false, GX_PTIDENTITY); + nextCoord = 2; + texCoordIds[2] = nextCoord; + CGX::SetTexCoordGen(GX_TEXCOORD2, GX_TG_MTX2x4, GX_TG_POS, GX_TEXMTX2, false, GX_PTIDENTITY); + nextCoord = 3; + + if (hasBumpMap) { + texCoordIds[3] = nextCoord; + CGX::SetTexCoordGen((GXTexCoordID)nextCoord, GX_TG_MTX2x4, GX_TG_POS, GX_TEXMTX0, false, + GX_PTIDENTITY); + CGX::SetTexCoordGen((GXTexCoordID)(nextCoord + 1), GX_TG_BUMP3, GX_TG_TEXCOORD3, GX_IDENTITY, + false, GX_PTIDENTITY); + nextCoord = 5; + } + + if (hasEnvBumpMap) { + float envBumpScale; + if (envMapType != 0) { + envBumpScale = 0.5f * (1.f - x118_reflectionSize); + } else { + envBumpScale = gpTweakGame->GetFluidEnvBumpScale() * + GetUVMotion().GetFluidLayerMotion(CFluidUVMotion::kFM_Linear).x14_uvScale; + } + + Mtx envBumpMtx; + if (envMapType == 0) { + MTXIdentity(envBumpMtx); + } else { + MTXIdentity(envBumpMtx); + } + GXLoadTexMtxImm(envBumpMtx, GX_TEXMTX3, GX_MTX2x4); + + Mtx postMtx; + MTXScale(postMtx, envBumpScale, -envBumpScale, 1.f); + postMtx[0][3] = 0.5f; + postMtx[1][3] = 0.5f; + GXLoadTexMtxImm(postMtx, GX_PTTEXMTX0, GX_MTX3x4); + + texCoordIds[4] = nextCoord; + int envBumpCoordIdx = nextCoord; + CGX::SetTexCoordGen((GXTexCoordID)nextCoord, GX_TG_MTX2x4, GX_TG_NRM, GX_TEXMTX3, true, + GX_PTTEXMTX0); + nextCoord += 1; + texMtx = GX_TEXMTX4; + + float indScale = 0.5f * (envMapType != 0 ? x118_reflectionSize : 1.f); + SIndMtx23 indMtx = tbl->ind; + indMtx.m[0][0] = indScale; + indMtx.m[1][1] = -indScale; + GXSetIndTexMtx(GX_ITM_0, indMtx.m, 1); + GXSetIndTexCoordScale(GX_INDTEXSTAGE0, GX_ITS_1, GX_ITS_1); + GXSetIndTexOrder(GX_INDTEXSTAGE0, (GXTexCoordID)envBumpCoordIdx, (GXTexMapID)texMapIds[5]); + CGX::SetNumIndStages(1); + } + + if (envMapType != 0) { + float envHeight = aabb.GetMaxPoint().GetZ() - aabb.GetMinPoint().GetZ(); + float envWidth = aabb.GetMaxPoint().GetX() - aabb.GetMinPoint().GetX(); + float maxDim; + if (envWidth < envHeight) { + maxDim = envWidth; + } else { + maxDim = envHeight; + } + float ooMaxDim = 1.f / maxDim; + + SEnvMtx34 envMtx = tbl->env; + envMtx.m[0][0] = ooMaxDim; + envMtx.m[1][1] = ooMaxDim; + envMtx.m[0][3] = 0.5f + (-aabb.GetCenterPoint().GetX()) / maxDim; + envMtx.m[1][3] = 0.5f + (-aabb.GetCenterPoint().GetY()) / maxDim; + + GXLoadTexMtxImm(envMtx.m, texMtx, GX_MTX2x4); + texCoordIds[5] = nextCoord; + CGX::SetTexCoordGen((GXTexCoordID)nextCoord, GX_TG_MTX2x4, GX_TG_POS, (GXTexMtx)texMtx, false, + GX_PTIDENTITY); + nextCoord += 1; + texMtx += 3; + } + + if (hasLightmap) { + TAreaId areaId = mgr.GetNextAreaId(); + float lightmapAlpha = 1.f; + float darkLevel = + mgr.GetWorld()->GetArea(areaId)->GetPostConstructed()->x1128_worldLightingLevel; + + const CScriptWater* nextWater = water->GetNextConnectedWater(mgr); + + if (fabs(water->GetMorphFactor() - 0.f) < FLT_EPSILON || nextWater == NULL || + !nextWater->GetFluidPlane().HasLightMap()) { + texMapIds[6] = nextTexMap; + GetLightMap()->Load((GXTexMapID)nextTexMap, CTexture::kCM_Repeat); + const_cast< CFluidPlaneCPU* >(this)->CalculateLightmapMtx(areaXf, xf, aabb, texMtx); + texCoordIds[6] = nextCoord; + CGX::SetTexCoordGen((GXTexCoordID)nextCoord, GX_TG_MTX2x4, GX_TG_POS, (GXTexMtx)texMtx, false, + GX_PTIDENTITY); + nextCoord += 1; + } else if (nextWater != NULL && nextWater->GetFluidPlane().HasLightMap()) { + if (fabs(water->GetMorphFactor() - 1.f) >= FLT_EPSILON) { + texMapIds[6] = nextTexMap; + GetLightMap()->Load((GXTexMapID)nextTexMap, CTexture::kCM_Repeat); + const_cast< CFluidPlaneCPU* >(this)->CalculateLightmapMtx(areaXf, xf, aabb, texMtx); + + nextTexMap += 1; + texMapIds[7] = nextTexMap; + nextWater->GetFluidPlane().GetLightMap()->Load((GXTexMapID)nextTexMap, + CTexture::kCM_Repeat); + const_cast< CFluidPlaneCPU& >(nextWater->GetFluidPlane()) + .CalculateLightmapMtx(areaXf, xf, aabb, texMtx + 3); + texCoordIds[6] = nextCoord; + CGX::SetTexCoordGen((GXTexCoordID)nextCoord, GX_TG_MTX2x4, GX_TG_POS, (GXTexMtx)texMtx, + false, GX_PTIDENTITY); + texCoordIds[7] = nextCoord + 1; + CGX::SetTexCoordGen((GXTexCoordID)(nextCoord + 1), GX_TG_MTX2x4, GX_TG_POS, + (GXTexMtx)(texMtx + 3), false, GX_PTIDENTITY); + nextCoord += 2; + + float morphVal = darkLevel * water->GetMorphFactor(); + lightmapAlpha = (1.f - water->GetMorphFactor()) / (1.f - morphVal); + CColor kColor3(morphVal, morphVal, morphVal, 1.f); + CGX::SetTevKColor(GX_KCOLOR3, kColor3.GetGXColor()); + hasDoubleLightmap = true; + } else { + texMapIds[6] = nextTexMap; + nextWater->GetFluidPlane().GetLightMap()->Load((GXTexMapID)nextTexMap, + CTexture::kCM_Repeat); + const_cast< CFluidPlaneCPU& >(nextWater->GetFluidPlane()) + .CalculateLightmapMtx(areaXf, xf, aabb, texMtx); + texCoordIds[6] = nextCoord; + CGX::SetTexCoordGen((GXTexCoordID)nextCoord, GX_TG_MTX2x4, GX_TG_POS, (GXTexMtx)texMtx, + false, GX_PTIDENTITY); + nextCoord += 1; + } + } + float lightmapVal = lightmapAlpha * darkLevel; + CColor kColor2(lightmapVal, lightmapVal, lightmapVal, 1.f); + CGX::SetTevKColor(GX_KCOLOR2, kColor2.GetGXColor()); + } + + CVector3f upVec(0.f, 0.f, 1.f); + CVector3f xfUp(xf.TransposeRotate(upVec)); + float xfUpX = xfUp.GetX(); + float xfUpY = xfUp.GetY(); + float xfUpZ = xfUp.GetZ(); + CVector3f camUp(0.f, 1.f, 0.f); + CTransform4f invView(CGraphics::GetViewMatrix().GetQuickInverse()); + CVector3f viewUp(invView.TransposeRotate(camUp)); + float dot = xfUpX * viewUp.GetX() + xfUpY * viewUp.GetY() + xfUpZ * viewUp.GetZ(); + if (dot < 0.f) { + dot = -dot; + } + float specular = (1.f - dot) * (GetSpecularMax() - GetSpecularMin()) + GetSpecularMin(); + float specularAlpha; + if (envMapType == 2) { + specularAlpha = 1.f; + } else { + specularAlpha = alpha; + } + CColor kColor0(specular, specular, specular, specularAlpha); + CGX::SetTevKColor(GX_KCOLOR0, kColor0.GetGXColor()); + + float reflBlend = GetReflectionBlend(); + CColor kColor1(reflBlend, reflBlend, reflBlend, 1.f); + CGX::SetTevKColor(GX_KCOLOR1, kColor1.GetGXColor()); + + CGX::SetNumTexGens((u8)nextCoord); + + int fluidType = GetFluidType(); + int nextStage = 0; + + switch (fluidType) { + case kFT_Lava: { + CGX::SetTevOrder(GX_TEVSTAGE0, (GXTexCoordID)texCoordIds[0], (GXTexMapID)texMapIds[0], + GX_COLOR0A0); + CGX::SetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_TEXC, GX_CC_KONST, GX_CC_RASC); + CGX::SetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, true, GX_TEVPREV); + CGX::SetTevKColorSel(GX_TEVSTAGE0, GX_TEV_KCSEL_K0); + CGX::SetTevOrder(GX_TEVSTAGE1, (GXTexCoordID)texCoordIds[1], (GXTexMapID)texMapIds[1], + GX_COLOR0A0); + CGX::SetTevColorIn(GX_TEVSTAGE1, GX_CC_ZERO, GX_CC_TEXC, GX_CC_CPREV, GX_CC_RASC); + CGX::SetTevColorOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, true, GX_TEVPREV); + CGX::SetTevOrder(GX_TEVSTAGE2, (GXTexCoordID)texCoordIds[2], (GXTexMapID)texMapIds[2], + GX_COLOR_NULL); + CGX::SetTevColorIn(GX_TEVSTAGE2, GX_CC_ZERO, GX_CC_TEXC, GX_CC_ONE, GX_CC_CPREV); + CGX::SetTevColorOp(GX_TEVSTAGE2, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, true, GX_TEVPREV); + nextStage = 3; + if (hasBumpMap) { + CGX::SetTevOrder(GX_TEVSTAGE3, (GXTexCoordID)texCoordIds[3], (GXTexMapID)texMapIds[3], + GX_COLOR_NULL); + CGX::SetTevColorIn(GX_TEVSTAGE3, GX_CC_ZERO, GX_CC_TEXC, GX_CC_ONE, GX_CC_HALF); + CGX::SetTevColorOp(GX_TEVSTAGE3, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, false, GX_TEVREG0); + CGX::SetTevOrder(GX_TEVSTAGE4, (GXTexCoordID)(texCoordIds[3] + 1), (GXTexMapID)texMapIds[3], + GX_COLOR_NULL); + CGX::SetTevColorIn(GX_TEVSTAGE4, GX_CC_ZERO, GX_CC_TEXC, GX_CC_ONE, GX_CC_C0); + CGX::SetTevColorOp(GX_TEVSTAGE4, GX_TEV_SUB, GX_TB_ZERO, GX_CS_SCALE_1, true, GX_TEVREG0); + CGX::SetTevOrder(GX_TEVSTAGE5, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR_NULL); + CGX::SetTevColorIn(GX_TEVSTAGE5, GX_CC_ZERO, GX_CC_CPREV, GX_CC_C0, GX_CC_ZERO); + CGX::SetTevColorOp(GX_TEVSTAGE5, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, true, GX_TEVPREV); + nextStage = 6; + } + break; + } + case kFT_NormalWater: + case kFT_PhazonFluid: + case kFT_Four: { + int curStage = 0; + if (hasLightmap) { + GXChannelID lightmapChan = GX_COLOR1A1; + GXTevColorArg lightmapRasc = GX_CC_RASC; + if (hasDoubleLightmap) { + lightmapChan = GX_COLOR_NULL; + lightmapRasc = GX_CC_ZERO; + } + CGX::SetTevOrder(GX_TEVSTAGE0, (GXTexCoordID)texCoordIds[6], (GXTexMapID)texMapIds[6], + lightmapChan); + CGX::SetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_TEXC, GX_CC_KONST, lightmapRasc); + CGX::SetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, true, GX_TEVREG2); + CGX::SetTevKColorSel(GX_TEVSTAGE0, GX_TEV_KCSEL_K2); + curStage = 1; + if (hasDoubleLightmap) { + CGX::SetTevOrder(GX_TEVSTAGE1, (GXTexCoordID)texCoordIds[7], (GXTexMapID)texMapIds[7], + GX_COLOR1A1); + CGX::SetTevColorIn(GX_TEVSTAGE1, GX_CC_C2, GX_CC_TEXC, GX_CC_KONST, GX_CC_RASC); + CGX::SetTevColorOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, true, GX_TEVREG2); + CGX::SetTevKColorSel(GX_TEVSTAGE1, GX_TEV_KCSEL_K3); + curStage = 2; + } + } + CGX::SetTevOrder((GXTevStageID)curStage, (GXTexCoordID)texCoordIds[0], (GXTexMapID)texMapIds[0], + GX_COLOR1A1); + CGX::SetTevColorIn((GXTevStageID)curStage, GX_CC_ZERO, GX_CC_TEXC, GX_CC_KONST, GX_CC_RASC); + CGX::SetTevColorOp((GXTevStageID)curStage, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, true, + GX_TEVPREV); + CGX::SetTevKColorSel((GXTevStageID)curStage, GX_TEV_KCSEL_K0); + CGX::SetTevOrder((GXTevStageID)(curStage + 1), (GXTexCoordID)texCoordIds[1], + (GXTexMapID)texMapIds[1], GX_COLOR0A0); + CGX::SetTevColorIn((GXTevStageID)(curStage + 1), GX_CC_ZERO, GX_CC_TEXC, GX_CC_CPREV, + GX_CC_RASC); + CGX::SetTevColorOp((GXTevStageID)(curStage + 1), GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, true, + GX_TEVPREV); + CGX::SetTevOrder((GXTevStageID)(curStage + 2), (GXTexCoordID)texCoordIds[2], + (GXTexMapID)texMapIds[2], GX_COLOR1A1); + GXTevColorArg colorRasc = GX_CC_RASC; + if (hasLightmap) { + colorRasc = GX_CC_C2; + } + CGX::SetTevColorIn((GXTevStageID)(curStage + 2), GX_CC_ZERO, GX_CC_TEXC, colorRasc, + GX_CC_CPREV); + CGX::SetTevColorOp((GXTevStageID)(curStage + 2), GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, true, + GX_TEVPREV); + if (envMapType == 0 && hasEnvBumpMap) { + CGX::SetTevIndirect((GXTevStageID)(curStage + 2), GX_INDTEXSTAGE0, GX_ITF_8, GX_ITB_STU, + GX_ITM_0, GX_ITW_OFF, GX_ITW_OFF, false, false, GX_ITBA_OFF); + } + nextStage = curStage + 3; + if (envMapType > 0) { + CGX::SetTevOrder((GXTevStageID)nextStage, (GXTexCoordID)texCoordIds[5], + (GXTexMapID)texMapIds[4], GX_COLOR_NULL); + GXTevColorArg envD = GX_CC_TEXC; + GXTevColorArg envC = GX_CC_ZERO; + GXTevColorArg envB = GX_CC_ZERO; + GXTevColorArg envA = GX_CC_ZERO; + if (envMapType == 1) { + envD = GX_CC_ZERO; + envC = GX_CC_KONST; + envB = GX_CC_TEXC; + envA = GX_CC_CPREV; + } + CGX::SetTevColorIn((GXTevStageID)nextStage, envA, envB, envC, envD); + CGX::SetTevColorOp((GXTevStageID)nextStage, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, true, + GX_TEVPREV); + CGX::SetTevKColorSel((GXTevStageID)nextStage, GX_TEV_KCSEL_K1); + CGX::SetTevIndirect((GXTevStageID)nextStage, GX_INDTEXSTAGE0, GX_ITF_8, GX_ITB_STU, GX_ITM_0, + GX_ITW_OFF, GX_ITW_OFF, false, false, GX_ITBA_OFF); + nextStage = curStage + 4; + } + break; + } + case kFT_PoisonWater: { + if (hasLightmap) { + GXChannelID lightmapChan = GX_COLOR1A1; + GXTevColorArg lightmapRasc = GX_CC_RASC; + if (hasDoubleLightmap) { + lightmapChan = GX_COLOR_NULL; + lightmapRasc = GX_CC_ZERO; + } + CGX::SetTevOrder(GX_TEVSTAGE0, (GXTexCoordID)texCoordIds[6], (GXTexMapID)texMapIds[6], + lightmapChan); + CGX::SetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_TEXC, GX_CC_KONST, lightmapRasc); + CGX::SetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, true, GX_TEVREG2); + CGX::SetTevKColorSel(GX_TEVSTAGE0, GX_TEV_KCSEL_K2); + nextStage = 1; + if (hasDoubleLightmap) { + CGX::SetTevOrder(GX_TEVSTAGE1, (GXTexCoordID)texCoordIds[7], (GXTexMapID)texMapIds[7], + GX_COLOR1A1); + CGX::SetTevColorIn(GX_TEVSTAGE1, GX_CC_C2, GX_CC_TEXC, GX_CC_KONST, GX_CC_RASC); + CGX::SetTevColorOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, true, GX_TEVREG2); + CGX::SetTevKColorSel(GX_TEVSTAGE1, GX_TEV_KCSEL_K3); + nextStage = 2; + } + } + CGX::SetTevOrder((GXTevStageID)nextStage, (GXTexCoordID)texCoordIds[0], + (GXTexMapID)texMapIds[0], GX_COLOR1A1); + CGX::SetTevColorIn((GXTevStageID)nextStage, GX_CC_ZERO, GX_CC_TEXC, GX_CC_KONST, GX_CC_RASC); + CGX::SetTevColorOp((GXTevStageID)nextStage, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, true, + GX_TEVPREV); + CGX::SetTevKColorSel((GXTevStageID)nextStage, GX_TEV_KCSEL_K0); + CGX::SetTevOrder((GXTevStageID)(nextStage + 1), (GXTexCoordID)texCoordIds[1], + (GXTexMapID)texMapIds[1], GX_COLOR0A0); + CGX::SetTevColorIn((GXTevStageID)(nextStage + 1), GX_CC_ZERO, GX_CC_TEXC, GX_CC_CPREV, + GX_CC_RASC); + CGX::SetTevColorOp((GXTevStageID)(nextStage + 1), GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, true, + GX_TEVPREV); + CGX::SetTevOrder((GXTevStageID)(nextStage + 2), (GXTexCoordID)texCoordIds[2], + (GXTexMapID)texMapIds[2], GX_COLOR1A1); + GXTevColorArg pColorRasc = GX_CC_RASC; + if (hasLightmap) { + pColorRasc = GX_CC_C2; + } + CGX::SetTevColorIn((GXTevStageID)(nextStage + 2), GX_CC_ZERO, GX_CC_TEXC, pColorRasc, + GX_CC_CPREV); + CGX::SetTevColorOp((GXTevStageID)(nextStage + 2), GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, true, + GX_TEVPREV); + if (hasEnvBumpMap) { + CGX::SetTevIndirect((GXTevStageID)(nextStage + 2), GX_INDTEXSTAGE0, GX_ITF_8, GX_ITB_STU, + GX_ITM_0, GX_ITW_OFF, GX_ITW_OFF, false, false, GX_ITBA_OFF); + } + nextStage += 3; + break; + } + case kFT_ThickLava: { + CGX::SetTevOrder(GX_TEVSTAGE0, (GXTexCoordID)texCoordIds[0], (GXTexMapID)texMapIds[0], + GX_COLOR0A0); + CGX::SetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_TEXC, GX_CC_KONST, GX_CC_RASC); + CGX::SetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, true, GX_TEVPREV); + CGX::SetTevKColorSel(GX_TEVSTAGE0, GX_TEV_KCSEL_K0); + CGX::SetTevOrder(GX_TEVSTAGE1, (GXTexCoordID)texCoordIds[1], (GXTexMapID)texMapIds[1], + GX_COLOR0A0); + CGX::SetTevColorIn(GX_TEVSTAGE1, GX_CC_ZERO, GX_CC_TEXC, GX_CC_CPREV, GX_CC_RASC); + CGX::SetTevColorOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, true, GX_TEVPREV); + CGX::SetTevOrder(GX_TEVSTAGE2, (GXTexCoordID)texCoordIds[2], (GXTexMapID)texMapIds[2], + GX_COLOR_NULL); + CGX::SetTevColorIn(GX_TEVSTAGE2, GX_CC_ZERO, GX_CC_TEXC, GX_CC_ONE, GX_CC_CPREV); + CGX::SetTevColorOp(GX_TEVSTAGE2, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, true, GX_TEVPREV); + nextStage = 3; + if (hasBumpMap) { + CGX::SetTevOrder(GX_TEVSTAGE3, (GXTexCoordID)texCoordIds[3], (GXTexMapID)texMapIds[3], + GX_COLOR_NULL); + CGX::SetTevColorIn(GX_TEVSTAGE3, GX_CC_ZERO, GX_CC_TEXC, GX_CC_CPREV, GX_CC_ZERO); + CGX::SetTevColorOp(GX_TEVSTAGE3, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, true, GX_TEVPREV); + nextStage = 4; + } + break; + } + default: { + if (!sFluidSetupInitOnce) { + sFluidSetupDone = 0; + sFluidSetupInitOnce = 1; + } + if (!sFluidSetupDone) { + sFluidSetupDone = 1; + } + break; + } + } + + CGX::SetNumTevStages((u8)nextStage); + int lastStage = nextStage - 1; + CGX::SetTevAlphaIn((GXTevStageID)lastStage, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_KONST); + CGX::SetTevAlphaOp((GXTevStageID)lastStage, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, true, + GX_TEVPREV); + CGX::SetTevKAlphaSel((GXTevStageID)lastStage, GX_TEV_KASEL_K0_A); + + if (mgr.GetThermalDrawFlag() != kTD_Hot) { + GXBlendMode bm = (alpha == 1.f) ? GX_BM_NONE : GX_BM_BLEND; + CGX::SetBlendMode(bm, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR); + } else { + CGX::SetBlendMode(GX_BM_BLEND, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR); + } + + CGX::SetZMode(true, GX_LEQUAL, false); + CGX::SetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0); + GXSetCullMode(GX_CULL_NONE); +} + +CFluidPlaneCPURender::SPatchInfo::SPatchInfo(const CVector3f& localMin, const CVector3f& localMax, + const CVector3f& pos, float rippleResolution, + float tileSize, float wavecapIntensityScale, + int numSubdivisionsInHField, int normalMode, + int redShift, int greenShift, uchar blueShift, + int tileX, int gridDimX, int gridDimY, int tileY, + const bool* gridFlags) { + short maxSubsX; + short xSubs = + CCast::FtoS(1.f + (localMax.GetX() - localMin.GetX()) / rippleResolution - FLT_EPSILON) + 2; + maxSubsX = (short)(numSubdivisionsInHField + 2); + x0_xSubdivs = rstl::min_val< short >(maxSubsX, xSubs); + short maxSubsY; + short ySubs = + CCast::FtoS(1.f + (localMax.GetY() - localMin.GetY()) / rippleResolution - FLT_EPSILON) + 2; + maxSubsY = (short)(numSubdivisionsInHField + 2); + x1_ySubdivs = rstl::min_val< short >(maxSubsY, ySubs); + float tileHypRadius = 0.25f * (tileSize * tileSize + tileSize * tileSize); + x4_localMinX = localMin.GetX(); + x8_localMinY = localMin.GetY(); + xc_globalMinX = x4_localMinX + pos.GetX(); + x10_globalMinY = x8_localMinY + pos.GetY(); + x14_tileSize = tileSize; + x18_rippleResolution = rippleResolution; + if (0.f != tileHypRadius) { + tileHypRadius = fast_sqrt(tileHypRadius); + } + x1c_tileHypRadius = tileHypRadius; + x20_ooTileSize = 1.f / x14_tileSize; + x24_ooRippleResolution = 1.f / x18_rippleResolution; + x28_tileX = (short)tileX; + x2a_gridDimX = (short)gridDimX; + x2c_gridDimY = (short)gridDimY; + x2e_tileY = (short)tileY; + x30_gridFlags = gridFlags; + x34_redShift = (uchar)redShift; + x35_greenShift = (uchar)greenShift; + x36_blueShift = (uchar)blueShift; + x37_normalMode = (uchar)normalMode; + x38_wavecapIntensityScale = wavecapIntensityScale; +} + +extern void RenderPatch(const CFluidPlaneCPURender::SPatchInfo& info, bool noRipples); +bool UpdatePatch(float time, CFluidPlaneCPURender::SPatchInfo& info, + const CFluidPlaneCPU& fluidPlane, const CVector3f& areaCenter, + const CRippleManager& rippleManager, int fromX, int toX, int fromY, int toY); + +void CFluidPlaneCPU::Render(const CStateManager& mgr, float alpha, const CAABox& aabb, + const CTransform4f& xf, const CTransform4f& areaXf, bool noNormals, + const CFrustumPlanes& frustum, + const rstl::optional_object< CRippleManager >& rippleManager, + TUniqueId waterId, const bool* gridFlags, int gridDimX, int gridDimY, + const CVector3f& areaCenter) const { + if (!sRenderFog) { + return; + } + + TCastToPtr< CScriptWater > water(const_cast< CEntity* >(mgr.GetObjectById(waterId))); + CScriptWater* waterPtr = water; + RenderSetup(mgr, alpha, xf, areaXf, aabb, waterPtr); + + float time = mgr.GetFluidPlaneManager()->GetTime(); + CGX::ResetVtxDescv(); + + int normalMode; + if (HasBumpMap() && sRenderBumpMaps) { + normalMode = CFluidPlaneCPURender::kNM_NBT; + } else if (!noNormals) { + normalMode = CFluidPlaneCPURender::kNM_Normals; + } else { + normalMode = CFluidPlaneCPURender::kNM_NoNormals; + } + + CGX::SetVtxDesc(GX_VA_POS, GX_DIRECT); + CGX::SetVtxDesc(GX_VA_CLR0, GX_DIRECT); + + switch (normalMode) { + case CFluidPlaneCPURender::kNM_NBT: + GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_NBT, GX_CLR_RGBA, GX_RGB8, 6); + GXSetVtxDesc(GX_VA_NBT, GX_DIRECT); + break; + case CFluidPlaneCPURender::kNM_Normals: + GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_NRM, GX_CLR_RGB, GX_RGB8, 6); + CGX::SetVtxDesc(GX_VA_NRM, GX_DIRECT); + break; + default: + break; + } + + CFluidPlaneCPURender::numSubdivisionsInTile = x104_tileSubdivisions; + float rippleResolution = x108_rippleResolution; + float rippleResolutionRecip = 1.f / rippleResolution; + int numTiles = 42 / CFluidPlaneCPURender::numSubdivisionsInTile; + CFluidPlaneCPURender::numTilesInHField = rstl::min_val(numTiles, kMaxTilesInHField); + CFluidPlaneCPURender::numSubdivisionsInHField = + CFluidPlaneCPURender::numTilesInHField * CFluidPlaneCPURender::numSubdivisionsInTile; + + CVector3f centerPoint(aabb.GetCenterPoint()); + float centerY = centerPoint.GetY(); + CVector3f centerPoint2(aabb.GetCenterPoint()); + CVector2f aabbCenter(centerPoint2.GetX(), centerY); + + float aabbMinX = aabb.GetMinPoint().GetX(); + float aabbMinY = aabb.GetMinPoint().GetY(); + float aabbMinZ = aabb.GetMinPoint().GetZ(); + float aabbMaxX = aabb.GetMaxPoint().GetX(); + float aabbMaxY = aabb.GetMaxPoint().GetY(); + float aabbMaxZ = aabb.GetMaxPoint().GetZ(); + + CVector2f ripplePitch(rippleResolution * CFluidPlaneCPURender::numSubdivisionsInHField, + rippleResolution * CFluidPlaneCPURender::numSubdivisionsInHField); + + int redShift = 0; + int greenShift = 0; + int blueShift = 0; + float wavecapIntensityScale = gpTweakGame->GetWavecapIntensityNormal(); + + switch (x44_fluidType) { + case kFT_PoisonWater: + wavecapIntensityScale = gpTweakGame->GetWavecapIntensityPoison(); + redShift = 1; + blueShift = 1; + break; + case kFT_Lava: + case kFT_ThickLava: + wavecapIntensityScale = gpTweakGame->GetWavecapIntensityLava(); + blueShift = 8; + greenShift = 8; + break; + default: + break; + } + + if (waterPtr != NULL) { + const CGameCamera& camera = mgr.GetCameraManager()->GetCurrentCamera(mgr); + CVector3f camPos(camera.GetTranslation()); + CVector3f upVec(0.f, 0.f, 1.f); + upVec.Normalize(); + CAABox triggerBounds = waterPtr->GetTriggerBoundsWR(); + float upY = upVec.GetY(); + float upX = upVec.GetX(); + float upZ = upVec.GetZ(); + float maxZ = triggerBounds.GetMaxPoint().GetZ(); + float cameraPenetration = + upY * camPos.GetY() + upX * camPos.GetX() + upZ * camPos.GetZ() - maxZ; + bool inRange = false; + if (cameraPenetration >= 0.5f || cameraPenetration < 0.f) { + inRange = true; + } + float scale; + if (inRange) { + scale = 1.f; + } else { + scale = 2.f * cameraPenetration; + } + wavecapIntensityScale *= scale; + } + + bool hasPatchDimX = false; + if (waterPtr != NULL && waterPtr->GetPatchDimensionX() != 0) { + hasPatchDimX = true; + } + int patchDimX; + if (hasPatchDimX) { + patchDimX = waterPtr->GetPatchDimensionX(); + } else { + patchDimX = 128; + } + + bool hasPatchDimY = false; + if (waterPtr != NULL && waterPtr->GetPatchDimensionY() != 0) { + hasPatchDimY = true; + } + int patchDimY; + if (hasPatchDimY) { + patchDimY = waterPtr->GetPatchDimensionY(); + } else { + patchDimY = 128; + } + + uchar blueShiftU = (uchar)blueShift; + uchar greenShiftU = (uchar)greenShift; + uchar redShiftU = (uchar)redShift; + + int tileY = 0; + int i = 0; + float curY = aabbMinY; + while (curY < aabbMaxY && i < patchDimY) { + float remDivsY = rippleResolutionRecip * (aabbMaxY - curY); + int tileX = 0; + int j = 0; + float curX = aabbMinX; + while (curX < aabbMaxX && j < patchDimX) { + int renderFlags = waterPtr->GetPatchRenderFlags(j, i); + if (renderFlags != 0) { + short remDivsXS = CCast::FtoS(rippleResolutionRecip * (aabbMaxX - curX)); + short numSubsS = (short)CFluidPlaneCPURender::numSubdivisionsInHField; + short xSubdivs = rstl::min_val< short >(numSubsS, remDivsXS); + + short remDivsYS = CCast::FtoS(remDivsY); + short numSubsYS = (short)CFluidPlaneCPURender::numSubdivisionsInHField; + short ySubdivs = rstl::min_val< short >(numSubsYS, remDivsYS); + + float localMaxX = rippleResolution * xSubdivs + curX; + float localMaxY = rippleResolution * ySubdivs + curY; + + CVector3f localMax(localMaxX + xf.Get03(), localMaxY + xf.Get13(), aabbMaxZ + xf.Get23()); + CVector3f localMin(curX + xf.Get03(), curY + xf.Get13(), aabbMinZ + xf.Get23()); + + CAABox testAABB(localMin, localMax); + if (frustum.BoxInFrustumPlanes(testAABB)) { + CVector3f pos(xf.Get03(), xf.Get13(), xf.Get23()); + CVector3f patchMin(curX, curY, aabbMinZ); + CVector3f patchMax(localMaxX, localMaxY, aabbMaxZ); + + CFluidPlaneCPURender::SPatchInfo info( + patchMin, patchMax, pos, rippleResolution, x100_tileSize, wavecapIntensityScale, + CFluidPlaneCPURender::numSubdivisionsInHField, normalMode, redShiftU, greenShiftU, + blueShiftU, tileX, gridDimX, gridDimY, tileY, gridFlags); + + CFluidPlaneCPURender::SPatchInfo& lcInfo = + *reinterpret_cast< CFluidPlaneCPURender::SPatchInfo* >(0xe0000000); + lcInfo = info; + + int fromX = ((-tileX | tileX) >> 31) & (2 - CFluidPlaneCPURender::numSubdivisionsInTile); + int toX; + if (tileX != gridDimX - 1) { + toX = lcInfo.x0_xSubdivs + CFluidPlaneCPURender::numSubdivisionsInTile - 2; + } else { + toX = lcInfo.x0_xSubdivs; + } + + int fromY = ((-tileY | tileY) >> 31) & (2 - CFluidPlaneCPURender::numSubdivisionsInTile); + int toY; + if (tileY != gridDimY - 1) { + toY = lcInfo.x1_ySubdivs + CFluidPlaneCPURender::numSubdivisionsInTile - 2; + } else { + toY = lcInfo.x1_ySubdivs; + } + + bool noRipples = + UpdatePatch(time, lcInfo, *this, areaCenter, *rippleManager, fromX, toX, fromY, toY); + RenderPatch(lcInfo, renderFlags == 1); + } + } + curX += ripplePitch.GetX(); + j += 1; + tileX += CFluidPlaneCPURender::numTilesInHField; + } + curY += ripplePitch.GetY(); + i += 1; + tileY += CFluidPlaneCPURender::numTilesInHField; + } + + GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_NBT, GX_CLR_RGBA, GX_F32, 6); + GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_NRM, GX_CLR_RGB, GX_F32, 6); + GXSetVtxDesc(GX_VA_NBT, GX_NONE); + GXSetCullMode(GX_CULL_FRONT); + RenderCleanup(); +} + +void CFluidPlaneCPU::RenderCleanup() const { + if (!sRenderFog) { + return; + } + + LCQueueWait(0); + + CGX::SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX3x4, GX_TG_TEX0, GX_IDENTITY, GX_FALSE, GX_PTIDENTITY); + CGX::SetTexCoordGen(GX_TEXCOORD1, GX_TG_MTX3x4, GX_TG_TEX1, GX_IDENTITY, GX_FALSE, GX_PTIDENTITY); + CGX::SetTexCoordGen(GX_TEXCOORD2, GX_TG_MTX3x4, GX_TG_TEX2, GX_IDENTITY, GX_FALSE, GX_PTIDENTITY); + CGX::SetTexCoordGen(GX_TEXCOORD3, GX_TG_MTX3x4, GX_TG_TEX3, GX_IDENTITY, GX_FALSE, GX_PTIDENTITY); + CGX::SetTexCoordGen(GX_TEXCOORD4, GX_TG_MTX3x4, GX_TG_TEX4, GX_IDENTITY, GX_FALSE, GX_PTIDENTITY); + CGX::SetTexCoordGen(GX_TEXCOORD5, GX_TG_MTX3x4, GX_TG_TEX5, GX_IDENTITY, GX_FALSE, GX_PTIDENTITY); + CGX::SetTexCoordGen(GX_TEXCOORD6, GX_TG_MTX3x4, GX_TG_TEX6, GX_IDENTITY, GX_FALSE, GX_PTIDENTITY); + + CGX::SetTevDirect(GX_TEVSTAGE3); + CGX::SetTevDirect(GX_TEVSTAGE6); + + CGX::SetNumIndStages(0); + + CGX::ResetVtxDescv(); + + float mtx[3][4]; + CTransform4f nrmMtx(CGraphics::GetViewMatrix().GetRotation().GetQuickInverse()); + mtx[0][0] = nrmMtx.Get00(); + mtx[0][1] = nrmMtx.Get01(); + mtx[0][2] = nrmMtx.Get02(); + mtx[0][3] = nrmMtx.Get03(); + mtx[1][0] = nrmMtx.Get10(); + mtx[1][1] = nrmMtx.Get11(); + mtx[1][2] = nrmMtx.Get12(); + mtx[1][3] = nrmMtx.Get13(); + mtx[2][0] = nrmMtx.Get20(); + mtx[2][1] = nrmMtx.Get21(); + mtx[2][2] = nrmMtx.Get22(); + mtx[2][3] = nrmMtx.Get23(); + GXLoadNrmMtxImm(mtx, GX_PNMTX0); + + CGX::SetChanCtrl(CGX::Channel1, GX_FALSE, GX_SRC_REG, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_CLAMP, + GX_AF_SPOT); + CGX::SetNumChans(1); + + CGraphics::SetLightState(CGraphics::GetLightMask()); +} diff --git a/src/MetroidPrime/CFluidPlaneDoor.cpp b/src/MetroidPrime/CFluidPlaneDoor.cpp index 4737fa012..0de56b21a 100644 --- a/src/MetroidPrime/CFluidPlaneDoor.cpp +++ b/src/MetroidPrime/CFluidPlaneDoor.cpp @@ -1,11 +1,24 @@ #include "MetroidPrime/CFluidPlaneDoor.hpp" +#include "Kyoto/Basics/CCast.hpp" +#include "Kyoto/Graphics/CColor.hpp" #include "Kyoto/Graphics/CTexture.hpp" +#include "Kyoto/Math/CAABox.hpp" +#include "Kyoto/Math/CTransform4f.hpp" +#include "Kyoto/Math/CVector2f.hpp" #include "MetaRender/CCubeRenderer.hpp" +#include "MetroidPrime/CFluidUVMotion.hpp" #include "dolphin/gx/GXEnum.h" #include "dolphin/gx/GXTransform.h" #include "dolphin/types.h" +#include "rstl/reserved_vector.hpp" #include +#include +#include +#include + +extern void RenderPatch(const CFluidPlaneCPURender::SPatchInfo& info, bool noRipples, + bool flaggedRendering); CFluidPlaneDoor::CFluidPlaneDoor(const CAssetId texPattern1, const CAssetId texPattern2, const CAssetId texColor, const float tileSize, @@ -40,29 +53,87 @@ void CFluidPlaneDoor::RenderSetup(const CStateManager& mgr, float alpha, const C GetColorTexture()->Load(GX_TEXMAP2, CTexture::kCM_Repeat); } + CGX::SetNumTexGens(3); + float uvs[3][2]; GetUVMotion().CalculateFluidTextureOffset(uvT, uvs); - static const float skZeroMatrix[2][4] = {0.f}; - - float scale0[2][4]; - float scale1[2][4]; - float scale2[2][4]; - - scale1[0][0] = GetUVMotion().GetFluidLayers()[0].x14_uvScale; - scale2[0][0] = GetUVMotion().GetFluidLayers()[1].x14_uvScale; - scale0[0][0] = GetUVMotion().GetFluidLayers()[0].x14_uvScale; - scale1[0][3] = uvs[1][0]; - scale1[1][3] = uvs[1][1]; - scale2[0][3] = uvs[2][0]; - scale2[1][3] = uvs[2][1]; - scale0[0][3] = uvs[0][0]; - scale0[1][3] = uvs[0][1]; + const rstl::reserved_vector< CFluidUVMotion::SFluidLayerMotion, CFluidUVMotion::kFM_NumLayers >& + layers = GetUVMotion().GetFluidLayers(); + float scale1[2][4] = { + { + layers[1].x14_uvScale, + 0.f, + 0.f, + uvs[1][0], + }, + { + 0.f, + layers[1].x14_uvScale, + 0.f, + uvs[1][1], + }, + }; + float scale2[2][4] = { + { + layers[2].x14_uvScale, + 0.f, + 0.f, + uvs[2][0], + }, + { + 0.f, + layers[2].x14_uvScale, + 0.f, + uvs[2][1], + }, + }; + float scale0[2][4] = { + { + layers[0].x14_uvScale, + 0.f, + 0.f, + uvs[0][0], + }, + { + 0.f, + layers[0].x14_uvScale, + 0.f, + uvs[0][1], + }, + }; GXLoadTexMtxImm(scale1, GX_TEXMTX5, GX_MTX2x4); GXLoadTexMtxImm(scale2, GX_TEXMTX6, GX_MTX2x4); GXLoadTexMtxImm(scale0, GX_TEXMTX7, GX_MTX2x4); CGX::SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_POS, GX_TEXMTX5, false, GX_PTIDENTITY); + CGX::SetTexCoordGen(GX_TEXCOORD1, GX_TG_MTX2x4, GX_TG_POS, GX_TEXMTX6, false, GX_PTIDENTITY); + CGX::SetTexCoordGen(GX_TEXCOORD2, GX_TG_MTX2x4, GX_TG_POS, GX_TEXMTX7, false, GX_PTIDENTITY); + + CGX::SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL); + CGX::SetTevOrder(GX_TEVSTAGE1, GX_TEXCOORD1, GX_TEXMAP1, GX_COLOR_NULL); + CGX::SetTevOrder(GX_TEVSTAGE2, GX_TEXCOORD2, GX_TEXMAP2, GX_COLOR_NULL); + + CGX::SetNumTevStages(3); + + CGX::SetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_TEXC, GX_CC_KONST, GX_CC_ZERO); + CGX::SetTevColorIn(GX_TEVSTAGE1, GX_CC_ZERO, GX_CC_TEXC, GX_CC_CPREV, GX_CC_ZERO); + CGX::SetTevColorIn(GX_TEVSTAGE2, GX_CC_ZERO, GX_CC_TEXC, GX_CC_ONE, GX_CC_CPREV); + + CGX::SetTevAlphaIn(GX_TEVSTAGE2, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_KONST); + + CGX::SetTevKColor(GX_KCOLOR0, CColor(1.f, 1.f, 1.f, alpha).GetGXColor()); + CGX::SetTevKColorSel(GX_TEVSTAGE0, GX_TEV_KCSEL_K0); + CGX::SetTevKAlphaSel(GX_TEVSTAGE2, GX_TEV_KASEL_K0_A); + + CGX::SetStandardTevColorAlphaOp(GX_TEVSTAGE0); + CGX::SetStandardTevColorAlphaOp(GX_TEVSTAGE1); + CGX::SetStandardTevColorAlphaOp(GX_TEVSTAGE2); + + CGX::SetBlendMode(alpha == 1.f ? GX_BM_NONE : GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, + GX_LO_CLEAR); + CGX::SetZMode(GX_TRUE, GX_LEQUAL, GX_FALSE); + CGX::SetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0); } void CFluidPlaneDoor::Render(const CStateManager& mgr, float alpha, const CAABox& aabb, @@ -70,6 +141,76 @@ void CFluidPlaneDoor::Render(const CStateManager& mgr, float alpha, const CAABox const CFrustumPlanes& frustum, const rstl::optional_object< CRippleManager >& rippleManager, TUniqueId waterId, const bool* gridFlags, int gridDimX, int gridDimY, - const CVector3f& areaCenter) const {} + const CVector3f& areaCenter) const { + if (!sRenderFog) { + return; + } + + RenderSetup(mgr, alpha, xf, aabb); + CGX::ResetVtxDescv(); + CGX::SetVtxDesc(GX_VA_POS, GX_DIRECT); + + CFluidPlaneCPURender::numSubdivisionsInTile = xa4_tileSubdivisions; + float rippleResolution = xa8_rippleResolution; + CFluidPlaneCPURender::numTilesInHField = 42 / xa4_tileSubdivisions; + float ooSubdivSize = 1.f / rippleResolution; + CFluidPlaneCPURender::numSubdivisionsInHField = + CFluidPlaneCPURender::numTilesInHField * CFluidPlaneCPURender::numSubdivisionsInTile; + + CVector3f center1 = aabb.GetCenterPoint(); + float centerY = center1.GetY(); + CVector3f center2 = aabb.GetCenterPoint(); + CVector2f centerPlane(center2.GetX(), centerY); -void CFluidPlaneDoor::RenderCleanup() const {} + CVector3f aabbMin(aabb.GetMinPoint()); + float maxX = aabb.GetMaxPoint().GetX(); + float maxY = aabb.GetMaxPoint().GetY(); + float maxZ = aabb.GetMaxPoint().GetZ(); + + float patchSizeX = rippleResolution * CCast::LtoF(CFluidPlaneCPURender::numSubdivisionsInHField); + float patchSizeY = rippleResolution * CCast::LtoF(CFluidPlaneCPURender::numSubdivisionsInHField); + CVector2f ripplePitch(patchSizeX, patchSizeY); + + float curX = aabbMin.GetX(); + for (; curX < maxX; curX += ripplePitch.GetX()) { + float remSubdivsX = ooSubdivSize * (maxX - curX); + float curY = aabbMin.GetY(); + for (; curY < maxY; curY += ripplePitch.GetY()) { + short remXi = CCast::FtoS(remSubdivsX); + short clampedX = + rstl::min_val(static_cast< short >(CFluidPlaneCPURender::numSubdivisionsInHField), remXi); + + float remSubdivsY = ooSubdivSize * (maxY - curY); + short remYi = CCast::FtoS(remSubdivsY); + short clampedY = + rstl::min_val(static_cast< short >(CFluidPlaneCPURender::numSubdivisionsInHField), remYi); + + CVector3f localMax(rippleResolution * CCast::StoF(clampedX) + curX, + rippleResolution * CCast::StoF(clampedY) + curY, maxZ); + + CAABox patchAABB(aabbMin, localMax); + + if (frustum.BoxInFrustumPlanes(patchAABB.GetTransformedAABox(xf))) { + CFluidPlaneCPURender::SPatchInfo info(CVector3f(curX, curY, aabbMin.GetZ()), localMax, + xf.GetTranslation(), rippleResolution, xa0_tileSize, + 0.f, CFluidPlaneCPURender::numSubdivisionsInHField, 0, + 0, 0, 0, 0, 0, 0, 0, NULL); + + CFluidPlaneCPURender::SPatchInfo& lcInfo = + *reinterpret_cast< CFluidPlaneCPURender::SPatchInfo* >(0xe0000000); + lcInfo = info; + + RenderPatch(lcInfo, true, true); + } + } + } + + RenderCleanup(); +} + +void CFluidPlaneDoor::RenderCleanup() const { + LCQueueWait(0); + CGX::SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX3x4, GX_TG_TEX0, GX_IDENTITY, GX_FALSE, GX_PTIDENTITY); + CGX::SetTexCoordGen(GX_TEXCOORD1, GX_TG_MTX3x4, GX_TG_TEX1, GX_IDENTITY, GX_FALSE, GX_PTIDENTITY); + CGX::SetTexCoordGen(GX_TEXCOORD2, GX_TG_MTX3x4, GX_TG_TEX2, GX_IDENTITY, GX_FALSE, GX_PTIDENTITY); +} diff --git a/src/MetroidPrime/CFluidPlaneManager.cpp b/src/MetroidPrime/CFluidPlaneManager.cpp index 8f9962bbf..b29adffec 100644 --- a/src/MetroidPrime/CFluidPlaneManager.cpp +++ b/src/MetroidPrime/CFluidPlaneManager.cpp @@ -1,23 +1,84 @@ #include "MetroidPrime/CFluidPlaneManager.hpp" +#include "MetroidPrime/CExplosion.hpp" #include "MetroidPrime/CRipple.hpp" #include "MetroidPrime/CRippleManager.hpp" #include "MetroidPrime/CStateManager.hpp" #include "MetroidPrime/ScriptObjects/CScriptWater.hpp" +#include "Kyoto/Audio/CSfxManager.hpp" +#include "Kyoto/Basics/CCast.hpp" #include "Kyoto/CResFactory.hpp" #include "Kyoto/CSimplePool.hpp" +#include "Kyoto/Graphics/CColor.hpp" #include "Kyoto/Math/CMath.hpp" +#include "Kyoto/Math/CMatrix3f.hpp" +#include "Kyoto/Math/CTransform4f.hpp" #include "Kyoto/SObjectTag.hpp" +#include "MetroidPrime/CEntity.hpp" +#include "MetroidPrime/CEntityInfo.hpp" #include "MetroidPrime/TCastTo.hpp" #include "MetroidPrime/Tweaks/CTweakGame.hpp" #include "rstl/math.hpp" -const float CFluidPlane::kRippleIntensityRange = 1.f; +float CFluidPlane::kRippleIntensityRange = 1.f; const bool sRenderFog = true; +const bool sRenderBumpMaps = true; +const int sFluidEnvMapType = 2; -uint fn_8012F098() { - return 0; +static int kMinValue = 0; +static int kMaxValue = 255; + +uchar sRippleValues[64][64]; +uchar sRippleMins[64]; +uchar sRippleMaxs[64]; + +uint CFluidPlaneManager::GetFreqTableIndex(float) { return 0; } + +void CFluidPlaneManager::SetupRippleMap() { + float curX = 0.f; + for (int i = 0; i < 64; ++i) { + uchar* rowPtr = sRippleValues[i]; + float curY = 0.f; + float minY = 1.f; + float maxY = curY; + for (int j = 64; j > 0; --j) { + float rVal = 1.f - curY; + float minX = curY; + float maxX = 1.25f * (0.25f * rVal + 0.1f) + curY; + if (curY < 0.f) { + minX = 0.f; + } else if (maxX > 1.f) { + maxX = 1.f; + } + float val = 0.f; + if (curX >= minX && curX <= maxX) { + float t = (curX - minX) / (maxX - minX); + if (t < 0.4f) { + val = 2.5f * t; + } else if (t > 0.75f) { + val = 4.f * (1.f - t); + } else { + val = 1.f; + } + } + uchar valA = CCast::ToUint8(255.f * (val * (rVal * rVal))); + *rowPtr = valA; + if (valA != 0 && curY < minY) { + minY = curY; + } + if (valA != 0 && curY > maxY) { + maxY = curY; + } + curY += (1.f / 63.f); + rowPtr++; + } + int valB = static_cast< int >(CCast::ToUint8(255.f * minY)) - 1; + sRippleMins[i] = static_cast< uchar >(rstl::max_val(kMinValue, valB)); + int valC = static_cast< int >(CCast::ToUint8(255.f * maxY)) + 1; + sRippleMaxs[i] = static_cast< uchar >(rstl::min_val(kMaxValue, valC)); + curX += (1.f / 63.f); + } } CFluidPlane::CFluidPlane(const CAssetId texPattern1, const CAssetId texPattern2, @@ -115,3 +176,95 @@ void CFluidPlane::Render(const CStateManager& mgr, const CAABox&, const CFrustum // const CTransform4f&, bool, const CFrustumPlanes&, // const rstl::optional_object< CRippleManager >&, TUniqueId, // const bool*, int, int, const CVector3f&) const {} + +void CFluidPlaneManager::CreateSplash(TUniqueId splasher, CStateManager& mgr, + const CScriptWater& water, const CVector3f& pos, float factor, + bool sfx) { + if (!water.CanRippleAtPoint(pos)) { + return; + } + + float oldestTime = 0.f; + CSplashRecord* oldestRecord = NULL; + for (CSplashRecord* it = SplashRecords().begin(); it != SplashRecords().end(); ++it) { + if (it->GetTime() > oldestTime) { + oldestRecord = it; + oldestTime = it->GetTime(); + } + } + + CSplashRecord newRecord(0.f, splasher); + if (oldestRecord != NULL) { + *oldestRecord = newRecord; + } else { + x18_splashes.push_back(newRecord); + } + + float splashScale = water.GetSplashEffectScale(factor); + if (water.GetSplashEffect(factor)) { + if (CExplosion* expl = rs_new CExplosion( + *water.GetSplashEffect(factor), mgr.AllocateUniqueId(), true, + CEntityInfo(water.GetCurrentAreaId(), CEntity::NullConnectionList), + rstl::string_l("Splash"), CTransform4f(CMatrix3f::Identity(), pos), 1, + CVector3f(splashScale, splashScale, splashScale), water.GetSplashColor())) { + mgr.AddObject(*expl); + } + } + + if (sfx) { + CSfxManager::AddEmitter(water.GetSplashSound(factor), pos, CVector3f(0.f, 0.f, 1.f), true, + false, CSfxManager::kMedPriority, CSfxManager::kAllAreas); + } +} + +float CFluidPlaneManager::GetLastRippleDeltaTime(TUniqueId rippler) const { + return x0_rippleManager.GetLastRippleDeltaTime(rippler); +} + +float CFluidPlaneManager::GetLastSplashDeltaTime(TUniqueId splasher) const { + float newestTime = 9999.f; + for (const CSplashRecord* it = GetSplashRecords().begin(); it != GetSplashRecords().end(); ++it) { + if (splasher == it->GetUniqueId() && newestTime > it->GetTime()) { + newestTime = it->GetTime(); + } + } + return newestTime; +} + +void CFluidPlaneManager::EndFrame() const { x121_ = false; } + +void CFluidPlaneManager::StartFrame(bool b) const { + x121_ = b; + sProfile.Clear(); +} + +void CFluidPlaneManager::Update(float dt) { + x11c_uvT = dt + x11c_uvT; + x0_rippleManager.Update(dt); + for (CSplashRecord* it = SplashRecords().begin(); it != SplashRecords().end(); ++it) { + it->SetTime(dt + it->GetTime()); + if (it->GetTime() > 9999.f) { + it->SetTime(9999.f); + } + } +} + +CFluidPlaneManager::CFluidProfile CFluidPlaneManager::sProfile; + +CFluidPlaneManager::CFluidPlaneManager() +: x0_rippleManager(20, 0.5f), x11c_uvT(0.f), x120_(false), x121_(false) { + sProfile.Clear(); + for (rstl::reserved_vector< CSplashRecord, 32 >::iterator it = x18_splashes.begin(); + it != x18_splashes.end(); ++it) { + it->SetTime(9999.f); + } + SetupRippleMap(); +} + +void CFluidPlaneManager::CFluidProfile::Clear() { + x10_ = 0.f; + xc_ = 0.f; + x8_ = 0.f; + x4_ = 0.f; + x0_ = 0.f; +} diff --git a/src/MetroidPrime/CFluidPlaneRender.cpp b/src/MetroidPrime/CFluidPlaneRender.cpp new file mode 100644 index 000000000..6b0096da3 --- /dev/null +++ b/src/MetroidPrime/CFluidPlaneRender.cpp @@ -0,0 +1,1373 @@ +#include "MetroidPrime/CFluidPlaneManager.hpp" + +#include "MetroidPrime/CRipple.hpp" +#include "MetroidPrime/CRippleManager.hpp" + +#include "Kyoto/Graphics/CGX.hpp" + +#include "rstl/math.hpp" + +#include + +#include "dolphin/gx/GXEnum.h" +#include "dolphin/gx/GXVert.h" +#include "dolphin/os/OSCache.h" + +extern unsigned char sRippleValues[64][64]; +extern unsigned char sRippleMins[64]; +extern unsigned char sRippleMaxs[64]; +static int sZeroX = 0; +static int sZeroY = 0; + +static float sGlobalSineWave[256]; +static bool sSineWaveInitialized; + +#ifdef __MWERKS__ +static inline float fast_sqrt(register float x) { + register float rsq; + asm { + ps_rsqrte rsq, x + ps_mul rsq, rsq, x + } + return rsq; +} +#else +static inline float fast_sqrt(float x) { return sqrtf(x); } +#endif + +float* InitializeSineWave() { + if (!sSineWaveInitialized) { + for (int i = 0; i < 256; ++i) { + sGlobalSineWave[i] = static_cast< float >(sin(M_2PIF * (static_cast< float >(i) / 256))); + } + sSineWaveInitialized = true; + } + return sGlobalSineWave; +} + +float* GetGlobalSineWave() { return sGlobalSineWave; } + +bool PrepareRipple(const CRipple& ripple, const CFluidPlaneCPURender::SPatchInfo& info, + CFluidPlaneCPURender::SRippleInfo& rippleInfo) { + int lifeIdx = static_cast< int >( + 64.f * (1.f - (ripple.GetTimeFalloff() - ripple.GetTime()) / ripple.GetTimeFalloff())); + float scaledDist = ripple.GetDistFalloff() / 255.f; + float centerX = info.x24_ooRippleResolution * (ripple.GetCenter().GetX() - info.xc_globalMinX); + float centerY = info.x24_ooRippleResolution * (ripple.GetCenter().GetY() - info.x10_globalMinY); + float dist = scaledDist * static_cast< float >(static_cast< int >(sRippleMaxs[lifeIdx])); + dist = dist * dist; + if (0.f == dist) { + dist = dist; + } else { + dist = fast_sqrt(dist); + } + dist = info.x24_ooRippleResolution * dist + 1.f; + + int fromX = static_cast< int >(centerX - dist) - 1; + int fromY = static_cast< int >(centerY - dist) - 1; + int toX = static_cast< int >(centerX + dist) + 1; + int toY = static_cast< int >(centerY + dist) + 1; + + rippleInfo.x4_fromX = rstl::max_val(fromX, sZeroX); + rippleInfo.x8_toX = + rstl::min_val(toX, static_cast< int >(static_cast< signed char >(info.x0_xSubdivs))); + rippleInfo.xc_fromY = rstl::max_val(fromY, sZeroY); + rippleInfo.x10_toY = + rstl::min_val(toY, static_cast< int >(static_cast< signed char >(info.x1_ySubdivs))); + + rippleInfo.x14_gfromX = rstl::max_val(fromX, rippleInfo.x14_gfromX); + rippleInfo.x18_gtoX = rstl::min_val(toX, rippleInfo.x18_gtoX); + rippleInfo.x1c_gfromY = rstl::max_val(fromY, rippleInfo.x1c_gfromY); + rippleInfo.x20_gtoY = rstl::min_val(toY, rippleInfo.x20_gtoY); + + if (rippleInfo.x14_gfromX > rippleInfo.x18_gtoX || rippleInfo.x1c_gfromY > rippleInfo.x20_gtoY) { + return false; + } + return true; +} + +void RenderTileWithRipplesNoNormals(float curY, + const CFluidPlaneCPURender::SHFieldSample (&heights)[45][45], + float startX, int startYDiv, + const CFluidPlaneCPURender::SPatchInfo& info) { + const CFluidPlaneCPURender::SHFieldSample* samples = heights[0]; + for (int numSubdivisions = CFluidPlaneCPURender::numSubdivisionsInTile; numSubdivisions > 0; + --numSubdivisions) { + CGX::Begin(GX_TRIANGLESTRIP, GX_VTXFMT0, startYDiv << 1); + float rippleRes = info.x18_rippleResolution; + int count = startYDiv; + float curX = curY; + const CFluidPlaneCPURender::SHFieldSample* s = samples; + for (; count != 0; --count, ++s, curX += rippleRes) { + GXPosition3f32(curX, startX, s->height); + GXColor4u8(static_cast< u8 >(s->wavecapIntensity >> info.x34_redShift), + static_cast< u8 >(s->wavecapIntensity >> info.x35_greenShift), + static_cast< u8 >(s->wavecapIntensity >> info.x36_blueShift), 0xff); + GXPosition3f32(curX, startX + rippleRes, s[45].height); + GXColor4u8(static_cast< u8 >(s[45].wavecapIntensity >> info.x34_redShift), + static_cast< u8 >(s[45].wavecapIntensity >> info.x35_greenShift), + static_cast< u8 >(s[45].wavecapIntensity >> info.x36_blueShift), 0xff); + } + CGX::End(); + samples += 45; + startX += info.x18_rippleResolution; + } +} + +// fn_8026F5F4 +static void RenderTileStripNormals(const CFluidPlaneCPURender::SHFieldSample* samples, float curX, + float curY, float rippleRes, int count, + const CFluidPlaneCPURender::SPatchInfo& info) { + for (; count != 0; --count, ++samples, curX += rippleRes) { + GXPosition3f32(curX, curY, samples->height); + GXNormal3s8(samples->nx, samples->ny, samples->nz); + GXColor4u8(static_cast< u8 >(samples->wavecapIntensity >> info.x34_redShift), + static_cast< u8 >(samples->wavecapIntensity >> info.x35_greenShift), + static_cast< u8 >(samples->wavecapIntensity >> info.x36_blueShift), 0xff); + GXPosition3f32(curX, curY + rippleRes, samples[45].height); + GXNormal3s8(samples[45].nx, samples[45].ny, samples[45].nz); + GXColor4u8(static_cast< u8 >(samples[45].wavecapIntensity >> info.x34_redShift), + static_cast< u8 >(samples[45].wavecapIntensity >> info.x35_greenShift), + static_cast< u8 >(samples[45].wavecapIntensity >> info.x36_blueShift), 0xff); + } +} + +void RenderTileWithRipplesNormals(float curY, + const CFluidPlaneCPURender::SHFieldSample (&heights)[45][45], + float startX, int startYDiv, + const CFluidPlaneCPURender::SPatchInfo& info) { + const CFluidPlaneCPURender::SHFieldSample* samples = heights[0]; + for (int numSubdivisions = CFluidPlaneCPURender::numSubdivisionsInTile; numSubdivisions > 0; + --numSubdivisions) { + CGX::Begin(GX_TRIANGLESTRIP, GX_VTXFMT0, startYDiv << 1); + RenderTileStripNormals(samples, curY, startX, info.x18_rippleResolution, startYDiv, info); + CGX::End(); + samples += 45; + startX += info.x18_rippleResolution; + } +} + +// fn_8026F408 +static void RenderTileStripNBT(const CFluidPlaneCPURender::SHFieldSample* samples, float curX, + float curY, float rippleRes, int count, + const CFluidPlaneCPURender::SPatchInfo& info) { + for (; count != 0; --count, ++samples, curX += rippleRes) { + GXPosition3f32(curX, curY, samples->height); + GXNormal3s8(samples->nx, samples->ny, samples->nz); + GXNormal3s8(samples->nx, samples->nz, -samples->ny); + GXNormal3s8(samples->nz, samples->ny, -samples->nx); + GXColor4u8(static_cast< u8 >(samples->wavecapIntensity >> info.x34_redShift), + static_cast< u8 >(samples->wavecapIntensity >> info.x35_greenShift), + static_cast< u8 >(samples->wavecapIntensity >> info.x36_blueShift), 0xff); + GXPosition3f32(curX, curY + rippleRes, samples[45].height); + GXNormal3s8(samples[45].nx, samples[45].ny, samples[45].nz); + GXNormal3s8(samples[45].nx, samples[45].nz, -samples[45].ny); + GXNormal3s8(samples[45].nz, samples[45].ny, -samples[45].nx); + GXColor4u8(static_cast< u8 >(samples[45].wavecapIntensity >> info.x34_redShift), + static_cast< u8 >(samples[45].wavecapIntensity >> info.x35_greenShift), + static_cast< u8 >(samples[45].wavecapIntensity >> info.x36_blueShift), 0xff); + } +} + +void RenderTileWithRipplesNBT(float curY, + const CFluidPlaneCPURender::SHFieldSample (&heights)[45][45], + float startX, int startYDiv, + const CFluidPlaneCPURender::SPatchInfo& info) { + const CFluidPlaneCPURender::SHFieldSample* samples = heights[0]; + for (int numSubdivisions = CFluidPlaneCPURender::numSubdivisionsInTile; numSubdivisions > 0; + --numSubdivisions) { + CGX::Begin(GX_TRIANGLESTRIP, GX_VTXFMT0, startYDiv << 1); + RenderTileStripNBT(samples, curY, startX, info.x18_rippleResolution, startYDiv, info); + CGX::End(); + samples += 45; + startX += info.x18_rippleResolution; + } +} + +// fn_8026F1AC +static void RenderStripEndNoNormals(const CFluidPlaneCPURender::SHFieldSample* samples, + const CFluidPlaneCPURender::SPatchInfo& info, bool last, + float curX, float curY, float endX) { + if (last) { + for (int i = 0; i <= CFluidPlaneCPURender::numSubdivisionsInTile; ++i) { + GXPosition3f32(curX, curY, samples->height); + GXColor4u8(static_cast< u8 >(samples->wavecapIntensity >> info.x34_redShift), + static_cast< u8 >(samples->wavecapIntensity >> info.x35_greenShift), + static_cast< u8 >(samples->wavecapIntensity >> info.x36_blueShift), 0xff); + samples += 45; + curY += info.x18_rippleResolution; + } + return; + } + GXPosition3f32(curX, curY, samples->height); + GXColor4u8(static_cast< u8 >(samples->wavecapIntensity >> info.x34_redShift), + static_cast< u8 >(samples->wavecapIntensity >> info.x35_greenShift), + static_cast< u8 >(samples->wavecapIntensity >> info.x36_blueShift), 0xff); + samples += CFluidPlaneCPURender::numSubdivisionsInTile * 45; + GXPosition3f32(curX, curY + info.x14_tileSize, samples->height); + GXColor4u8(static_cast< u8 >(samples->wavecapIntensity >> info.x34_redShift), + static_cast< u8 >(samples->wavecapIntensity >> info.x35_greenShift), + static_cast< u8 >(samples->wavecapIntensity >> info.x36_blueShift), 0xff); +} + +// fn_8026EF80 +static void RenderStripEndNormals(const CFluidPlaneCPURender::SHFieldSample* samples, + const CFluidPlaneCPURender::SPatchInfo& info, bool last, + float curX, float curY, float endX) { + if (last) { + for (int i = 0; i <= CFluidPlaneCPURender::numSubdivisionsInTile; ++i) { + GXPosition3f32(curX, curY, samples->height); + GXNormal3s8(samples->nx, samples->ny, samples->nz); + GXColor4u8(static_cast< u8 >(samples->wavecapIntensity >> info.x34_redShift), + static_cast< u8 >(samples->wavecapIntensity >> info.x35_greenShift), + static_cast< u8 >(samples->wavecapIntensity >> info.x36_blueShift), 0xff); + samples += 45; + curY += info.x18_rippleResolution; + } + return; + } + GXPosition3f32(curX, curY, samples->height); + GXNormal3s8(samples->nx, samples->ny, samples->nz); + GXColor4u8(static_cast< u8 >(samples->wavecapIntensity >> info.x34_redShift), + static_cast< u8 >(samples->wavecapIntensity >> info.x35_greenShift), + static_cast< u8 >(samples->wavecapIntensity >> info.x36_blueShift), 0xff); + samples += CFluidPlaneCPURender::numSubdivisionsInTile * 45; + GXPosition3f32(curX, curY + info.x14_tileSize, samples->height); + GXNormal3s8(samples->nx, samples->ny, samples->nz); + GXColor4u8(static_cast< u8 >(samples->wavecapIntensity >> info.x34_redShift), + static_cast< u8 >(samples->wavecapIntensity >> info.x35_greenShift), + static_cast< u8 >(samples->wavecapIntensity >> info.x36_blueShift), 0xff); +} + +// fn_8026ED84 +static void RenderStripEndNBT(const CFluidPlaneCPURender::SHFieldSample* samples, + const CFluidPlaneCPURender::SPatchInfo& info, bool last, float curX, + float curY, float endX) { + if (last) { + for (int i = 0; i <= CFluidPlaneCPURender::numSubdivisionsInTile; ++i) { + GXPosition3f32(curX, curY, samples->height); + GXNormal3s8(samples->nx, samples->ny, samples->nz); + GXNormal3s8(samples->nx, samples->nz, -samples->ny); + GXNormal3s8(samples->nz, samples->ny, -samples->nx); + GXColor4u8(static_cast< u8 >(samples->wavecapIntensity >> info.x34_redShift), + static_cast< u8 >(samples->wavecapIntensity >> info.x35_greenShift), + static_cast< u8 >(samples->wavecapIntensity >> info.x36_blueShift), 0xff); + samples += 45; + curY += info.x18_rippleResolution; + } + return; + } + GXPosition3f32(curX, curY, samples->height); + GXNormal3s8(samples->nx, samples->ny, samples->nz); + GXNormal3s8(samples->nx, samples->nz, -samples->ny); + GXNormal3s8(samples->nz, samples->ny, -samples->nx); + GXColor4u8(static_cast< u8 >(samples->wavecapIntensity >> info.x34_redShift), + static_cast< u8 >(samples->wavecapIntensity >> info.x35_greenShift), + static_cast< u8 >(samples->wavecapIntensity >> info.x36_blueShift), 0xff); + samples += CFluidPlaneCPURender::numSubdivisionsInTile * 45; + GXPosition3f32(curX, curY + info.x14_tileSize, samples->height); + GXNormal3s8(samples->nx, samples->ny, samples->nz); + GXNormal3s8(samples->nx, samples->nz, -samples->ny); + GXNormal3s8(samples->nz, samples->ny, -samples->nx); + GXColor4u8(static_cast< u8 >(samples->wavecapIntensity >> info.x34_redShift), + static_cast< u8 >(samples->wavecapIntensity >> info.x35_greenShift), + static_cast< u8 >(samples->wavecapIntensity >> info.x36_blueShift), 0xff); +} + +void RenderStripWithRipples(const CFluidPlaneCPURender::SHFieldSample (&heights)[45][45], + const unsigned char (&flags)[9][9], int startYDiv, float curY, + const CFluidPlaneCPURender::SPatchInfo& info) { + int numSubdivisions = CFluidPlaneCPURender::numSubdivisionsInTile; + int iDiv = (startYDiv + numSubdivisions - 1) / numSubdivisions; + const CFluidPlaneCPURender::SHFieldSample* heights_00 = &heights[startYDiv][1]; + float halfRes = info.x18_rippleResolution * static_cast< float >(numSubdivisions / 2); + float centerY = halfRes + curY; + float curX = info.x4_localMinX; + int numSubTimesStride = numSubdivisions * 45; + int centerOffset = numSubdivisions / 2 + numSubTimesStride / 2; + int xSubdivs = static_cast< int >(static_cast< signed char >(info.x0_xSubdivs)); + int numTilesX = (xSubdivs + numSubdivisions - 4) / numSubdivisions; + int gridOffset = + static_cast< int >(info.x28_tileX) + + static_cast< int >(info.x2a_gridDimX) * (static_cast< int >(info.x2e_tileY) + iDiv - 1); + const unsigned char* flagsPtr = &flags[0][0] + iDiv * 9; + int bottomStride = numSubTimesStride * 8; + int rightStride = (numSubdivisions / 2 + numSubTimesStride / 2) * 8; + int topStride = (numSubdivisions + numSubTimesStride) * 8; + int sampleStride = numSubdivisions * 8; + int tileIdx = 1; + int xPos = 1; + + for (; xPos < xSubdivs - 2;) { + const bool* gridFlags = info.x30_gridFlags; + int numCombined = 1; + + if (gridFlags != NULL && reinterpret_cast< const char* >(gridFlags)[gridOffset] == 0) { + goto nextTile; + } + + { + const unsigned char* flagByte = flagsPtr + tileIdx; + if ((*flagByte & 0x1f) == 0x1f) { + // Fully rippled tile - check for consecutive fully-rippled tiles + const unsigned char* nextFlag = flagByte + 1; + while (tileIdx + numCombined <= numTilesX) { + if ((*nextFlag & 0x1f) != 0x1f) { + break; + } + if (gridFlags != NULL) { + if (reinterpret_cast< const char* >(gridFlags)[gridOffset + numCombined] == 0) { + break; + } + } + ++nextFlag; + ++numCombined; + } + + int numVerts = numCombined * numSubdivisions + 1; + int normalMode = static_cast< int >(static_cast< signed char >(info.x37_normalMode)); + + switch (normalMode) { + case CFluidPlaneCPURender::kNM_None: { + // Inline rendering for mode 0 + const CFluidPlaneCPURender::SHFieldSample* samples = heights_00; + int subdivsLeft = numSubdivisions; + float stripY = curY; + for (; subdivsLeft > 0; --subdivsLeft) { + CGX::Begin(GX_TRIANGLESTRIP, GX_VTXFMT0, static_cast< ushort >(numVerts * 2)); + float rippleRes = info.x18_rippleResolution; + float endY = stripY + rippleRes; + float startX = curX; + int count = numVerts; + const CFluidPlaneCPURender::SHFieldSample* s = samples; + if (count != 0) { + for (; count != 0; --count) { + GXPosition3f32(startX, stripY, s->height); + GXPosition3f32(startX, endY, s[45].height); + startX += rippleRes; + ++s; + } + } + CGX::End(); + samples += 45; + stripY += info.x18_rippleResolution; + } + break; + } + case CFluidPlaneCPURender::kNM_NoNormals: + RenderTileWithRipplesNoNormals( + curX, + reinterpret_cast< const CFluidPlaneCPURender::SHFieldSample(&)[45][45] >(*heights_00), + curY, numVerts, info); + break; + case CFluidPlaneCPURender::kNM_Normals: + RenderTileWithRipplesNormals( + curX, + reinterpret_cast< const CFluidPlaneCPURender::SHFieldSample(&)[45][45] >(*heights_00), + curY, numVerts, info); + break; + case CFluidPlaneCPURender::kNM_NBT: + RenderTileWithRipplesNBT( + curX, + reinterpret_cast< const CFluidPlaneCPURender::SHFieldSample(&)[45][45] >(*heights_00), + curY, numVerts, info); + break; + default: + break; + } + } else { + // Partial ripple - extract edge flags + unsigned char below = flagByte[9]; + unsigned char left = flagByte[-1]; + unsigned char right = flagByte[1]; + unsigned char above = flagByte[-9]; + + bool hasBelow = (below >> 1) & 1; + bool isLeftEdge = (left >> 3) & 1; + bool hasRight = (right >> 2) & 1; + bool hasAbove = above & 1; + + int bottomCount = 1; + if (hasBelow) { + bottomCount = numSubdivisions; + } + int rightCount = 1; + if (hasRight) { + rightCount = numSubdivisions; + } + int aboveCount = 1; + if (hasAbove) { + aboveCount = numSubdivisions; + } + int leftCount = 1; + if (isLeftEdge) { + leftCount = numSubdivisions; + } + + int totalVerts = bottomCount + 2 + rightCount + aboveCount + leftCount; + + if (totalVerts == 6 && (info.x37_normalMode == CFluidPlaneCPURender::kNM_Normals || + info.x37_normalMode == CFluidPlaneCPURender::kNM_NBT)) { + // Special strip mode - combine consecutive non-rippled non-edge tiles + const unsigned char* nextFlag = flagByte + 1; + while (tileIdx + numCombined <= numTilesX) { + if ((*nextFlag & 0x1f) == 0x1f) { + break; + } + if (gridFlags != NULL) { + if (reinterpret_cast< const char* >(gridFlags)[gridOffset + numCombined] == 0) { + break; + } + } + if ((nextFlag[9] & 2) != 0) { + break; + } + if ((nextFlag[1] & 4) != 0) { + break; + } + if ((nextFlag[-9] & 1) != 0) { + break; + } + ++nextFlag; + ++numCombined; + } + + int stripCount = numCombined + 1; + CGX::Begin(GX_TRIANGLESTRIP, GX_VTXFMT0, static_cast< ushort >(stripCount * 2)); + + int normalMode = static_cast< int >(static_cast< signed char >(info.x37_normalMode)); + if (normalMode == CFluidPlaneCPURender::kNM_NBT) { + int stride = CFluidPlaneCPURender::numSubdivisionsInTile; + int strideBytes = stride * 8; + const CFluidPlaneCPURender::SHFieldSample* topSamples = heights_00; + int bottomOffset = stride * 0x168; + const CFluidPlaneCPURender::SHFieldSample* bottomSamples = + reinterpret_cast< const CFluidPlaneCPURender::SHFieldSample* >( + reinterpret_cast< const char* >(heights_00) + bottomOffset); + float stripX = curX; + for (int n = stripCount; n > 0; --n) { + // Top vertex + GXPosition3f32(stripX, curY, topSamples->height); + GXNormal3s8(topSamples->nx, topSamples->ny, topSamples->nz); + GXNormal3s8(topSamples->nx, topSamples->nz, -topSamples->ny); + GXNormal3s8(topSamples->nz, topSamples->ny, -topSamples->nx); + int wavecap = topSamples->wavecapIntensity; + topSamples = reinterpret_cast< const CFluidPlaneCPURender::SHFieldSample* >( + reinterpret_cast< const char* >(topSamples) + strideBytes); + GXColor4u8(static_cast< u8 >(wavecap >> info.x34_redShift), + static_cast< u8 >(wavecap >> info.x35_greenShift), + static_cast< u8 >(wavecap >> info.x36_blueShift), 0xff); + // Bottom vertex + GXPosition3f32(stripX, info.x14_tileSize + curY, bottomSamples->height); + GXNormal3s8(bottomSamples->nx, bottomSamples->ny, bottomSamples->nz); + GXNormal3s8(bottomSamples->nx, bottomSamples->nz, -bottomSamples->ny); + GXNormal3s8(bottomSamples->nz, bottomSamples->ny, -bottomSamples->nx); + wavecap = bottomSamples->wavecapIntensity; + bottomSamples = reinterpret_cast< const CFluidPlaneCPURender::SHFieldSample* >( + reinterpret_cast< const char* >(bottomSamples) + strideBytes); + GXColor4u8(static_cast< u8 >(wavecap >> info.x34_redShift), + static_cast< u8 >(wavecap >> info.x35_greenShift), + static_cast< u8 >(wavecap >> info.x36_blueShift), 0xff); + stripX += info.x14_tileSize; + } + } else if (normalMode == CFluidPlaneCPURender::kNM_Normals) { + int stride = CFluidPlaneCPURender::numSubdivisionsInTile; + int strideBytes = stride * 8; + const CFluidPlaneCPURender::SHFieldSample* topSamples = heights_00; + int bottomOffset = stride * 0x168; + const CFluidPlaneCPURender::SHFieldSample* bottomSamples = + reinterpret_cast< const CFluidPlaneCPURender::SHFieldSample* >( + reinterpret_cast< const char* >(heights_00) + bottomOffset); + float stripX = curX; + for (int n = stripCount; n > 0; --n) { + GXPosition3f32(stripX, curY, topSamples->height); + GXNormal3s8(topSamples->nx, topSamples->ny, topSamples->nz); + int wavecap = topSamples->wavecapIntensity; + topSamples = reinterpret_cast< const CFluidPlaneCPURender::SHFieldSample* >( + reinterpret_cast< const char* >(topSamples) + strideBytes); + GXColor4u8(static_cast< u8 >(wavecap >> info.x34_redShift), + static_cast< u8 >(wavecap >> info.x35_greenShift), + static_cast< u8 >(wavecap >> info.x36_blueShift), 0xff); + GXPosition3f32(stripX, info.x14_tileSize + curY, bottomSamples->height); + GXNormal3s8(bottomSamples->nx, bottomSamples->ny, bottomSamples->nz); + wavecap = bottomSamples->wavecapIntensity; + bottomSamples = reinterpret_cast< const CFluidPlaneCPURender::SHFieldSample* >( + reinterpret_cast< const char* >(bottomSamples) + strideBytes); + GXColor4u8(static_cast< u8 >(wavecap >> info.x34_redShift), + static_cast< u8 >(wavecap >> info.x35_greenShift), + static_cast< u8 >(wavecap >> info.x36_blueShift), 0xff); + stripX += info.x14_tileSize; + } + } + CGX::End(); + } else { + // Triangle fan rendering + CGX::Begin(GX_TRIANGLEFAN, GX_VTXFMT0, static_cast< ushort >(totalVerts)); + + int normalMode = static_cast< int >(static_cast< signed char >(info.x37_normalMode)); + + switch (normalMode) { + case CFluidPlaneCPURender::kNM_None: { + // Center vertex + float centerX = halfRes + curX; + const CFluidPlaneCPURender::SHFieldSample* centerSample = + reinterpret_cast< const CFluidPlaneCPURender::SHFieldSample* >( + reinterpret_cast< const char* >(heights_00) + rightStride); + GXPosition3f32(centerX, centerY, centerSample->height); + + // Bottom edge (right to left along bottom) + float endY = info.x14_tileSize + curY; + { + const CFluidPlaneCPURender::SHFieldSample* s = + reinterpret_cast< const CFluidPlaneCPURender::SHFieldSample* >( + reinterpret_cast< const char* >(heights_00) + topStride); + float endX = info.x14_tileSize + curX; + int count = 1; + if (hasBelow) { + count = numSubdivisions; + } + if (count > 0) { + for (; count > 0; --count) { + GXPosition3f32(endX, endY, s->height); + endY -= info.x18_rippleResolution; + s -= 45; + } + } + } + + // Right edge (bottom to top) + { + const CFluidPlaneCPURender::SHFieldSample* s = + reinterpret_cast< const CFluidPlaneCPURender::SHFieldSample* >( + reinterpret_cast< const char* >(heights_00) + sampleStride); + float endX = info.x14_tileSize + curX; + endY = info.x14_tileSize + curY; + int count = 1; + if (hasAbove) { + count = numSubdivisions; + } + if (count > 0) { + for (; count > 0; --count) { + GXPosition3f32(endX, endY, s->height); + endX -= info.x18_rippleResolution; + s--; + } + } + } + + // Left edge check + if (!isLeftEdge) { + // Just two vertices + GXPosition3f32(curX, curY, heights_00->height); + GXPosition3f32(curX, curY + info.x14_tileSize, + heights_00[numSubdivisions * 45].height); + } else { + // Left edge (top to bottom) + int count = numSubdivisions + 1; + if (numSubdivisions >= 0) { + const CFluidPlaneCPURender::SHFieldSample* s = heights_00; + float y = curY; + for (; count > 0; --count) { + GXPosition3f32(curX, y, s->height); + s += 45; + y += info.x18_rippleResolution; + } + } + } + break; + } + case CFluidPlaneCPURender::kNM_NoNormals: { + // Center vertex with color + const CFluidPlaneCPURender::SHFieldSample* centerSample = + reinterpret_cast< const CFluidPlaneCPURender::SHFieldSample* >( + reinterpret_cast< const char* >(heights_00) + rightStride); + float centerX = halfRes + curX; + GXPosition3f32(centerX, centerY, centerSample->height); + GXColor4u8(static_cast< u8 >(centerSample->wavecapIntensity >> info.x34_redShift), + static_cast< u8 >(centerSample->wavecapIntensity >> info.x35_greenShift), + static_cast< u8 >(centerSample->wavecapIntensity >> info.x36_blueShift), + 0xff); + + // Bottom edge + { + float endY = info.x14_tileSize + curY; + const CFluidPlaneCPURender::SHFieldSample* s = + reinterpret_cast< const CFluidPlaneCPURender::SHFieldSample* >( + reinterpret_cast< const char* >(heights_00) + bottomStride); + float stripX = curX; + int count = 1; + if (hasBelow) { + count = numSubdivisions; + } + if (count > 0) { + for (; count > 0; --count) { + GXPosition3f32(stripX, endY, s->height); + GXColor4u8(static_cast< u8 >(s->wavecapIntensity >> info.x34_redShift), + static_cast< u8 >(s->wavecapIntensity >> info.x35_greenShift), + static_cast< u8 >(s->wavecapIntensity >> info.x36_blueShift), 0xff); + stripX += info.x18_rippleResolution; + ++s; + } + } + } + + // Right edge + { + float endY = info.x14_tileSize + curY; + float endX = info.x14_tileSize + curX; + const CFluidPlaneCPURender::SHFieldSample* s = + reinterpret_cast< const CFluidPlaneCPURender::SHFieldSample* >( + reinterpret_cast< const char* >(heights_00) + topStride); + int count = 1; + if (hasRight) { + count = numSubdivisions; + } + if (count > 0) { + for (; count > 0; --count) { + GXPosition3f32(endX, endY, s->height); + GXColor4u8(static_cast< u8 >(s->wavecapIntensity >> info.x34_redShift), + static_cast< u8 >(s->wavecapIntensity >> info.x35_greenShift), + static_cast< u8 >(s->wavecapIntensity >> info.x36_blueShift), 0xff); + endY -= info.x18_rippleResolution; + s -= 45; + } + } + } + + // Top edge + { + float endX = info.x14_tileSize + curX; + const CFluidPlaneCPURender::SHFieldSample* s = + reinterpret_cast< const CFluidPlaneCPURender::SHFieldSample* >( + reinterpret_cast< const char* >(heights_00) + sampleStride); + int count = 1; + if (hasAbove) { + count = numSubdivisions; + } + if (count > 0) { + for (; count > 0; --count) { + GXPosition3f32(endX, curY, s->height); + GXColor4u8(static_cast< u8 >(s->wavecapIntensity >> info.x34_redShift), + static_cast< u8 >(s->wavecapIntensity >> info.x35_greenShift), + static_cast< u8 >(s->wavecapIntensity >> info.x36_blueShift), 0xff); + endX -= info.x18_rippleResolution; + --s; + } + } + } + + // Left edge + RenderStripEndNoNormals(heights_00, info, isLeftEdge, curX, curY, 0.f); + break; + } + case CFluidPlaneCPURender::kNM_Normals: { + // Center vertex with normals and color + const CFluidPlaneCPURender::SHFieldSample* centerSample = + reinterpret_cast< const CFluidPlaneCPURender::SHFieldSample* >( + reinterpret_cast< const char* >(heights_00) + rightStride); + float centerX = halfRes + curX; + GXPosition3f32(centerX, centerY, centerSample->height); + GXNormal3s8(centerSample->nx, centerSample->ny, centerSample->nz); + GXColor4u8(static_cast< u8 >(centerSample->wavecapIntensity >> info.x34_redShift), + static_cast< u8 >(centerSample->wavecapIntensity >> info.x35_greenShift), + static_cast< u8 >(centerSample->wavecapIntensity >> info.x36_blueShift), + 0xff); + + // Bottom edge + { + float endY = info.x14_tileSize + curY; + const CFluidPlaneCPURender::SHFieldSample* s = + reinterpret_cast< const CFluidPlaneCPURender::SHFieldSample* >( + reinterpret_cast< const char* >(heights_00) + bottomStride); + float stripX = curX; + int count = 1; + if (hasBelow) { + count = numSubdivisions; + } + if (count > 0) { + for (; count > 0; --count) { + GXPosition3f32(stripX, endY, s->height); + GXNormal3s8(s->nx, s->ny, s->nz); + GXColor4u8(static_cast< u8 >(s->wavecapIntensity >> info.x34_redShift), + static_cast< u8 >(s->wavecapIntensity >> info.x35_greenShift), + static_cast< u8 >(s->wavecapIntensity >> info.x36_blueShift), 0xff); + stripX += info.x18_rippleResolution; + ++s; + } + } + } + + // Right edge + { + float endY = info.x14_tileSize + curY; + float endX = info.x14_tileSize + curX; + const CFluidPlaneCPURender::SHFieldSample* s = + reinterpret_cast< const CFluidPlaneCPURender::SHFieldSample* >( + reinterpret_cast< const char* >(heights_00) + topStride); + int count = 1; + if (hasRight) { + count = numSubdivisions; + } + if (count > 0) { + for (; count > 0; --count) { + GXPosition3f32(endX, endY, s->height); + GXNormal3s8(s->nx, s->ny, s->nz); + GXColor4u8(static_cast< u8 >(s->wavecapIntensity >> info.x34_redShift), + static_cast< u8 >(s->wavecapIntensity >> info.x35_greenShift), + static_cast< u8 >(s->wavecapIntensity >> info.x36_blueShift), 0xff); + endY -= info.x18_rippleResolution; + s -= 45; + } + } + } + + // Top edge + { + float endX = info.x14_tileSize + curX; + const CFluidPlaneCPURender::SHFieldSample* s = + reinterpret_cast< const CFluidPlaneCPURender::SHFieldSample* >( + reinterpret_cast< const char* >(heights_00) + sampleStride); + int count = 1; + if (hasAbove) { + count = numSubdivisions; + } + if (count > 0) { + for (; count > 0; --count) { + GXPosition3f32(endX, curY, s->height); + GXNormal3s8(s->nx, s->ny, s->nz); + GXColor4u8(static_cast< u8 >(s->wavecapIntensity >> info.x34_redShift), + static_cast< u8 >(s->wavecapIntensity >> info.x35_greenShift), + static_cast< u8 >(s->wavecapIntensity >> info.x36_blueShift), 0xff); + endX -= info.x18_rippleResolution; + --s; + } + } + } + + // Left edge + RenderStripEndNormals(heights_00, info, isLeftEdge, curX, curY, 0.f); + break; + } + case CFluidPlaneCPURender::kNM_NBT: { + // Center vertex with NBT and color + const CFluidPlaneCPURender::SHFieldSample* centerSample = + reinterpret_cast< const CFluidPlaneCPURender::SHFieldSample* >( + reinterpret_cast< const char* >(heights_00) + rightStride); + float centerX = halfRes + curX; + GXPosition3f32(centerX, centerY, centerSample->height); + { + GXNormal3s8(centerSample->nx, centerSample->ny, centerSample->nz); + // Binormal + GXNormal3s8(centerSample->nx, centerSample->nz, -centerSample->ny); + // Tangent + GXNormal3s8(centerSample->nz, centerSample->ny, -centerSample->nx); + GXColor4u8(static_cast< u8 >(centerSample->wavecapIntensity >> info.x34_redShift), + static_cast< u8 >(centerSample->wavecapIntensity >> info.x35_greenShift), + static_cast< u8 >(centerSample->wavecapIntensity >> info.x36_blueShift), + 0xff); + } + + // Bottom edge + { + float endY = info.x14_tileSize + curY; + const CFluidPlaneCPURender::SHFieldSample* s = + reinterpret_cast< const CFluidPlaneCPURender::SHFieldSample* >( + reinterpret_cast< const char* >(heights_00) + bottomStride); + float stripX = curX; + int count = 1; + if (hasBelow) { + count = numSubdivisions; + } + if (count > 0) { + for (; count > 0; --count) { + GXPosition3f32(stripX, endY, s->height); + GXNormal3s8(s->nx, s->ny, s->nz); + GXNormal3s8(s->nx, s->nz, -s->ny); + GXNormal3s8(s->nz, s->ny, -s->nx); + GXColor4u8(static_cast< u8 >(s->wavecapIntensity >> info.x34_redShift), + static_cast< u8 >(s->wavecapIntensity >> info.x35_greenShift), + static_cast< u8 >(s->wavecapIntensity >> info.x36_blueShift), 0xff); + stripX += info.x18_rippleResolution; + ++s; + } + } + } + + // Right edge + { + float endY = info.x14_tileSize + curY; + float endX = info.x14_tileSize + curX; + const CFluidPlaneCPURender::SHFieldSample* s = + reinterpret_cast< const CFluidPlaneCPURender::SHFieldSample* >( + reinterpret_cast< const char* >(heights_00) + topStride); + int count = 1; + if (hasRight) { + count = numSubdivisions; + } + if (count > 0) { + for (; count > 0; --count) { + GXPosition3f32(endX, endY, s->height); + GXNormal3s8(s->nx, s->ny, s->nz); + GXNormal3s8(s->nx, s->nz, -s->ny); + GXNormal3s8(s->nz, s->ny, -s->nx); + GXColor4u8(static_cast< u8 >(s->wavecapIntensity >> info.x34_redShift), + static_cast< u8 >(s->wavecapIntensity >> info.x35_greenShift), + static_cast< u8 >(s->wavecapIntensity >> info.x36_blueShift), 0xff); + endY -= info.x18_rippleResolution; + s -= 45; + } + } + } + + // Top edge + { + float endX = info.x14_tileSize + curX; + const CFluidPlaneCPURender::SHFieldSample* s = + reinterpret_cast< const CFluidPlaneCPURender::SHFieldSample* >( + reinterpret_cast< const char* >(heights_00) + sampleStride); + int count = 1; + if (hasAbove) { + count = numSubdivisions; + } + if (count > 0) { + for (; count > 0; --count) { + GXPosition3f32(endX, curY, s->height); + GXNormal3s8(s->nx, s->ny, s->nz); + GXNormal3s8(s->nx, s->nz, -s->ny); + GXNormal3s8(s->nz, s->ny, -s->nx); + GXColor4u8(static_cast< u8 >(s->wavecapIntensity >> info.x34_redShift), + static_cast< u8 >(s->wavecapIntensity >> info.x35_greenShift), + static_cast< u8 >(s->wavecapIntensity >> info.x36_blueShift), 0xff); + endX -= info.x18_rippleResolution; + --s; + } + } + } + + // Left edge + RenderStripEndNBT(heights_00, info, isLeftEdge, curX, curY, 0.f); + break; + } + default: + break; + } + CGX::End(); + } + } + } + + nextTile: + tileIdx += numCombined; + gridOffset += numCombined; + xPos += CFluidPlaneCPURender::numSubdivisionsInTile * numCombined; + heights_00 += CFluidPlaneCPURender::numSubdivisionsInTile * numCombined; + curX += info.x14_tileSize * static_cast< float >(numCombined); + } +} + +void ApplyRipple(const CRipple& ripple, CFluidPlaneCPURender::SHFieldSample (&heights)[45][45], + unsigned char (&flags)[9][9], const float (&sineWave)[256], + CFluidPlaneCPURender::SPatchInfo& info) { + typedef CFluidPlaneCPURender::SRippleInfo SRippleInfo; + typedef CFluidPlaneCPURender::SHFieldSample SHFieldSample; + + const SRippleInfo& rippleInfo = reinterpret_cast< const SRippleInfo& >(ripple); + const CRipple& rip = *rippleInfo.x0_ripple; + + float timeRatio = rip.GetTime() * rip.GetOoTimeFalloff(); + float distMul = (1.f / 255.f) * rip.GetDistFalloff(); + float lookupT = 256.f * (1.f - timeRatio * rip.GetOoTimeFalloff()) * rip.GetFrequency(); + int lifeIdx = static_cast< int >(64.f * timeRatio); + + float minDist = distMul * static_cast< float >(static_cast< int >(sRippleMins[lifeIdx])); + float minDistSq = minDist * minDist; + float minDistR = minDistSq; + if (0.f == minDistSq) { + minDistR = minDistR; + } else { + minDistR = fast_sqrt(minDistSq); + } + + float maxDist = distMul * static_cast< float >(static_cast< int >(sRippleMaxs[lifeIdx])); + float maxDistSq = maxDist * maxDist; + float maxDistR = maxDistSq; + if (0.f == maxDistSq) { + maxDistR = maxDistR; + } else { + maxDistR = fast_sqrt(maxDistSq); + } + + int subdivsInTile = CFluidPlaneCPURender::numSubdivisionsInTile; + int subdivsM1 = subdivsInTile - 1; + + int fromY = (rippleInfo.x1c_gfromY + subdivsM1) / subdivsInTile; + int fromX = (rippleInfo.x14_gfromX + subdivsM1) / subdivsInTile; + int toY = (rippleInfo.x20_gtoY + subdivsM1) / subdivsInTile; + int toX = (rippleInfo.x18_gtoX + subdivsM1) / subdivsInTile; + + float curY = (rip.GetCenter().GetY() - info.x10_globalMinY) - + (0.5f * info.x14_tileSize + static_cast< float >(fromY - 1) * info.x14_tileSize); + + int curGridY = + static_cast< int >(info.x2a_gridDimX) * (static_cast< int >(info.x2e_tileY) + fromY - 1); + int startGridX = static_cast< int >(info.x28_tileX) + fromX - 1; + int gridCells = static_cast< int >(info.x2a_gridDimX) * static_cast< int >(info.x2c_gridDimY); + float distFalloff = 64.f * rip.GetOoDistFalloff(); + int curYDiv = rippleInfo.xc_fromY; + + bool hasGridFlags = info.x30_gridFlags != 0; + + unsigned char* flagsRowStart = &flags[fromY][fromX]; + + for (int i = fromY; i <= toY; ++i, curY -= info.x14_tileSize) { + int nextYDiv = (i + 1) * subdivsInTile; + int curGridX = startGridX; + int curXDiv = rippleInfo.x4_fromX; + float curYSq = curY * curY; + float curX = (rip.GetCenter().GetX() - info.xc_globalMinX) - + (0.5f * info.x14_tileSize + static_cast< float >(fromX - 1) * info.x14_tileSize); + unsigned char* flagsBase = flagsRowStart; + + for (int j = fromX; j <= toX; ++j, curX -= info.x14_tileSize, ++curGridX, ++flagsBase) { + float dist = curX * curX + curYSq; + if (0.f == dist) { + dist = dist; + } else { + dist = fast_sqrt(dist); + } + if (maxDistR < dist - info.x1c_tileHypRadius || minDistR > dist + info.x1c_tileHypRadius) { + continue; + } + + { + bool addedRipple = false; + int nextXDiv = (j + 1) * subdivsInTile; + float curXMod = (rip.GetCenter().GetX() - info.xc_globalMinX) - + info.x18_rippleResolution * static_cast< float >(curXDiv); + float curYMod = (rip.GetCenter().GetY() - info.x10_globalMinY) - + info.x18_rippleResolution * static_cast< float >(curYDiv); + + if (!hasGridFlags || + (curGridY >= 0 && curGridY < gridCells && curGridX >= 0 && + curGridX < static_cast< int >(info.x2a_gridDimX) && + reinterpret_cast< const char* >(info.x30_gridFlags)[curGridY + curGridX])) { + SHFieldSample* rowSamples = &heights[curYDiv][0]; + int k = curYDiv; + for (; k <= rstl::min_val(nextYDiv - 1, rippleInfo.x10_toY); + ++k, curYMod -= info.x18_rippleResolution) { + float tmpXMod = curXMod; + float curYModSq = curYMod * curYMod; + SHFieldSample* sample = rowSamples + curXDiv; + int l = curXDiv; + for (; l <= rstl::min_val(nextXDiv - 1, rippleInfo.x8_toX); + ++l, tmpXMod -= info.x18_rippleResolution, ++sample) { + float divDistSq = tmpXMod * tmpXMod + curYModSq; + if (divDistSq < minDistSq || divDistSq > maxDistSq) { + continue; + } + float divDist = divDistSq; + if (0.f == divDistSq) { + divDist = divDistSq; + } else { + divDist = fast_sqrt(divDistSq); + } + int distIdx = static_cast< int >(divDist * distFalloff); + u8 rippleV = sRippleValues[lifeIdx][distIdx]; + float height; + if (rippleV != 0) { + float phase = divDist * rip.GetLookupPhase() + lookupT; + int sineIdx = static_cast< int >(phase) & 0xFF; + height = static_cast< float >(static_cast< int >(rippleV)) * + rip.GetLookupAmplitude() * sineWave[sineIdx]; + } else { + height = 0.f; + } + sample->height += height; + addedRipple = true; + } + rowSamples += 45; + } + + if (addedRipple) { + *flagsBase = 0x1f; + } + } else { + int yMin = nextYDiv - 1; + int yMax = nextYDiv - subdivsInTile + 1; + int xMin = nextXDiv - 1; + int xMax = nextXDiv - subdivsInTile + 1; + + if (curGridX >= 0 && curGridX < static_cast< int >(info.x2a_gridDimX) && + curGridY - static_cast< int >(info.x2a_gridDimX) >= 0 && + !reinterpret_cast< const char* >( + info.x30_gridFlags)[curGridX + curGridY - + static_cast< int >(info.x2a_gridDimX)]) { + yMax -= 2; + } + if (curGridX >= 0 && curGridX < static_cast< int >(info.x2a_gridDimX) && + curGridY + static_cast< int >(info.x2a_gridDimX) < gridCells && + !reinterpret_cast< const char* >( + info.x30_gridFlags)[curGridX + curGridY + + static_cast< int >(info.x2a_gridDimX)]) { + yMin += 2; + } + if (curGridY >= 0 && curGridY < static_cast< int >(info.x2c_gridDimY) && curGridX > 0 && + !reinterpret_cast< const char* >(info.x30_gridFlags)[curGridY + curGridX - 1]) { + xMax -= 2; + } + if (curGridY >= 0 && curGridY < static_cast< int >(info.x2c_gridDimY) && + curGridX + 1 < static_cast< int >(info.x2a_gridDimX) && + !reinterpret_cast< const char* >(info.x30_gridFlags)[curGridY + curGridX + 1]) { + xMin += 2; + } + + SHFieldSample* rowSamples = &heights[curYDiv][0]; + int k = curYDiv; + for (; k <= rstl::min_val(nextYDiv - 1, rippleInfo.x10_toY); + ++k, curYMod -= info.x18_rippleResolution) { + float tmpXMod = curXMod; + float curYModSq = curYMod * curYMod; + SHFieldSample* sample = rowSamples + curXDiv; + int l = curXDiv; + for (; l <= rstl::min_val(nextXDiv - 1, rippleInfo.x8_toX); + ++l, tmpXMod -= info.x18_rippleResolution, ++sample) { + if (k > yMax && k < yMin && l > xMax && l < xMin) { + continue; + } + float divDistSq = tmpXMod * tmpXMod + curYModSq; + if (divDistSq < minDistSq || divDistSq > maxDistSq) { + continue; + } + float divDist = divDistSq; + if (0.f == divDistSq) { + divDist = divDistSq; + } else { + divDist = fast_sqrt(divDistSq); + } + int distIdx = static_cast< int >(divDist * distFalloff); + u8 rippleV = sRippleValues[lifeIdx][distIdx]; + float height; + if (rippleV != 0) { + float phase = divDist * rip.GetLookupPhase() + lookupT; + int sineIdx = static_cast< int >(phase) & 0xFF; + height = static_cast< float >(static_cast< int >(rippleV)) * + rip.GetLookupAmplitude() * sineWave[sineIdx]; + } else { + height = 0.f; + } + sample->height += height; + addedRipple = true; + } + rowSamples += 45; + } + + if (addedRipple) { + *flagsBase = 0x0f; + } + } + curXDiv = nextXDiv; + } + } + curYDiv = nextYDiv; + curGridY += static_cast< int >(info.x2a_gridDimX); + flagsRowStart += 9; + } +} + +void ApplyRipples(const CRippleManager& rippleManager, + CFluidPlaneCPURender::SHFieldSample (&heights)[45][45], + unsigned char (&flags)[9][9], const float (&sineWave)[256], + CFluidPlaneCPURender::SPatchInfo& info) { + LCQueueWait(0); + + typedef CFluidPlaneCPURender::SRippleInfo SRippleInfo; + const rstl::reserved_vector< SRippleInfo, 32 >& ripples = + reinterpret_cast< const rstl::reserved_vector< SRippleInfo, 32 >& >(rippleManager); + rstl::reserved_vector< SRippleInfo, 32 >::const_iterator it = ripples.begin(); + for (; it != ripples.end(); ++it) { + ApplyRipple(reinterpret_cast< const CRipple& >(*it), heights, flags, sineWave, info); + } + + unsigned char* ptr; + int count; + + count = CFluidPlaneCPURender::numTilesInHField; + ptr = &flags[0][1]; + for (; count != 0; --count) { + *ptr |= 1; + ++ptr; + } + count = CFluidPlaneCPURender::numTilesInHField; + ptr = &flags[1][0]; + for (; count != 0; --count) { + *ptr |= 8; + ptr += 9; + } + count = CFluidPlaneCPURender::numTilesInHField; + ptr = &flags[0][count + 10]; + for (; count != 0; --count) { + *ptr |= 4; + ptr += 9; + } + count = CFluidPlaneCPURender::numTilesInHField; + ptr = &flags[0][0] + (count + 1) * 9 + 1; + for (; count != 0; --count) { + *ptr |= 2; + ++ptr; + } +} + +void RenderPatch(const CFluidPlaneCPURender::SPatchInfo& info, bool noRipples, + bool flaggedGridGen) { + if (noRipples) { + int ySubdivs = static_cast< int >(static_cast< signed char >(info.x1_ySubdivs)); + int xSubdivs = static_cast< int >(static_cast< signed char >(info.x0_xSubdivs)); + int normalMode = static_cast< int >(static_cast< signed char >(info.x37_normalMode)); + float localMinX = info.x4_localMinX; + float localMinY = info.x8_localMinY; + float endY = info.x18_rippleResolution * static_cast< float >(ySubdivs - 2) + localMinY; + float endX = info.x18_rippleResolution * static_cast< float >(xSubdivs - 2) + localMinX; + + switch (normalMode) { + case 0: + CGX::Begin(GX_TRIANGLESTRIP, GX_VTXFMT0, 4); + GXPosition3f32(localMinX, localMinY, 0.f); + GXPosition3f32(localMinX, endY, 0.f); + GXPosition3f32(endX, localMinY, 0.f); + GXPosition3f32(endX, endY, 0.f); + CGX::End(); + break; + case 1: + CGX::Begin(GX_TRIANGLESTRIP, GX_VTXFMT0, 4); + GXPosition3f32(localMinX, localMinY, 0.f); + GXColor4u8(0, 0, 0, 0xff); + GXPosition3f32(localMinX, endY, 0.f); + GXColor4u8(0, 0, 0, 0xff); + GXPosition3f32(endX, localMinY, 0.f); + GXColor4u8(0, 0, 0, 0xff); + GXPosition3f32(endX, endY, 0.f); + GXColor4u8(0, 0, 0, 0xff); + CGX::End(); + break; + case 2: { + xSubdivs = (xSubdivs - 3) / CFluidPlaneCPURender::numSubdivisionsInTile; + int numTilesXP1 = xSubdivs + 1; + int gridOffset = static_cast< int >(info.x2e_tileY) * static_cast< int >(info.x2a_gridDimX) + + static_cast< int >(info.x28_tileX); + ySubdivs = (ySubdivs - 3) / CFluidPlaneCPURender::numSubdivisionsInTile + 1; + endY = localMinY; + + for (int iY = ySubdivs; iY > 0; --iY) { + bool isFirstRow = (ySubdivs - iY) == 0; + bool isLastRow = (1 - iY) == 0; + endX = localMinX; + int iX = 0; + + while (iX < numTilesXP1) { + const bool* gridFlags = info.x30_gridFlags; + if (gridFlags == NULL || + reinterpret_cast< const char* >(gridFlags)[gridOffset + iX] != 0) { + bool isLeftEdge = iX == 0; + bool isRightEdge = (numTilesXP1 - 1 - iX) == 0; + + if (isFirstRow || isLastRow || isLeftEdge || isRightEdge) { + int totalVerts = (isLastRow ? CFluidPlaneCPURender::numSubdivisionsInTile : 1) + 2; + totalVerts += (isRightEdge ? CFluidPlaneCPURender::numSubdivisionsInTile : 1); + totalVerts += (isFirstRow ? CFluidPlaneCPURender::numSubdivisionsInTile : 1); + totalVerts += (isLeftEdge ? CFluidPlaneCPURender::numSubdivisionsInTile : 1); + + CGX::Begin(GX_TRIANGLEFAN, GX_VTXFMT0, static_cast< ushort >(totalVerts)); + + float halfTile = 0.5f * info.x14_tileSize; + GXPosition3f32(endX + halfTile, endY + halfTile, 0.f); + GXNormal3s8(0, 0, 0x3f); + GXColor4u8(0, 0, 0, 0xff); + + { + int count = isLastRow ? CFluidPlaneCPURender::numSubdivisionsInTile : 1; + float edgeX = endX; + for (int e = count; e > 0; --e) { + GXPosition3f32(edgeX, endY + info.x14_tileSize, 0.f); + GXNormal3s8(0, 0, 0x3f); + GXColor4u8(0, 0, 0, 0xff); + edgeX += info.x18_rippleResolution; + } + } + + { + int count = isRightEdge ? CFluidPlaneCPURender::numSubdivisionsInTile : 1; + float edgeY = endY + info.x14_tileSize; + for (int e = count; e > 0; --e) { + GXPosition3f32(endX + info.x14_tileSize, edgeY, 0.f); + GXNormal3s8(0, 0, 0x3f); + GXColor4u8(0, 0, 0, 0xff); + edgeY -= info.x18_rippleResolution; + } + } + + { + int count = isFirstRow ? CFluidPlaneCPURender::numSubdivisionsInTile : 1; + float edgeX = endX + info.x14_tileSize; + for (int e = count; e > 0; --e) { + GXPosition3f32(edgeX, endY, 0.f); + GXNormal3s8(0, 0, 0x3f); + GXColor4u8(0, 0, 0, 0xff); + edgeX -= info.x18_rippleResolution; + } + } + + { + int count = isLeftEdge ? CFluidPlaneCPURender::numSubdivisionsInTile : 1; + float edgeY = endY; + for (int e = count; e > 0; --e) { + GXPosition3f32(endX, edgeY, 0.f); + GXNormal3s8(0, 0, 0x3f); + GXColor4u8(0, 0, 0, 0xff); + edgeY += info.x18_rippleResolution; + } + } + + GXPosition3f32(endX, endY + info.x14_tileSize, 0.f); + GXNormal3s8(0, 0, 0x3f); + GXColor4u8(0, 0, 0, 0xff); + CGX::End(); + ++iX; + endX += info.x14_tileSize; + } else { + int endIX = iX; + do { + ++endIX; + if (numTilesXP1 <= endIX) + break; + } while (gridFlags == NULL || + reinterpret_cast< const char* >(gridFlags)[gridOffset + endIX] != 0); + int runLen = (endIX - iX) + 1; + CGX::Begin(GX_TRIANGLESTRIP, GX_VTXFMT0, static_cast< ushort >(runLen * 2)); + if (runLen > 0) { + do { + GXPosition3f32(endX, endY, 0.f); + GXNormal3s8(0, 0, 0x3f); + GXColor4u8(0, 0, 0, 0xff); + GXPosition3f32(endX, endY + info.x14_tileSize, 0.f); + GXNormal3s8(0, 0, 0x3f); + GXColor4u8(0, 0, 0, 0xff); + endX += info.x14_tileSize; + --runLen; + } while (runLen != 0); + } + CGX::End(); + iX = endIX + 1; + if (iX == numTilesXP1) { + iX = endIX; + endX -= info.x14_tileSize; + } + } + } else { + endX += info.x14_tileSize; + ++iX; + while (iX < numTilesXP1) { + if (reinterpret_cast< const char* >(gridFlags)[gridOffset + iX] != 0) { + break; + } + endX += info.x14_tileSize; + ++iX; + } + } + } + endY += info.x14_tileSize; + gridOffset += static_cast< int >(info.x2a_gridDimX); + } + break; + } + case 3: { + if (flaggedGridGen || info.x30_gridFlags == NULL) { + CGX::Begin(GX_TRIANGLESTRIP, GX_VTXFMT0, 4); + GXPosition3f32(localMinX, localMinY, 0.f); + GXNormal3s8(0, 0, 0x3f); // Normal + GXNormal3s8(0, 0x3f, 0); // Binormal + GXNormal3s8(0x3f, 0, 0); // Tangent + GXColor4u8(0, 0, 0, 0xff); + GXPosition3f32(localMinX, endY, 0.f); + GXNormal3s8(0, 0, 0x3f); + GXNormal3s8(0, 0x3f, 0); + GXNormal3s8(0x3f, 0, 0); + GXColor4u8(0, 0, 0, 0xff); + GXPosition3f32(endX, localMinY, 0.f); + GXNormal3s8(0, 0, 0x3f); + GXNormal3s8(0, 0x3f, 0); + GXNormal3s8(0x3f, 0, 0); + GXColor4u8(0, 0, 0, 0xff); + GXPosition3f32(endX, endY, 0.f); + GXNormal3s8(0, 0, 0x3f); + GXNormal3s8(0, 0x3f, 0); + GXNormal3s8(0x3f, 0, 0); + GXColor4u8(0, 0, 0, 0xff); + CGX::End(); + } else { + int xSub = static_cast< int >(static_cast< signed char >(info.x0_xSubdivs)); + int ySub = static_cast< int >(static_cast< signed char >(info.x1_ySubdivs)); + int numTilesX = (xSub - 3) / CFluidPlaneCPURender::numSubdivisionsInTile + 1; + int gridOffset = static_cast< int >(info.x28_tileX) + + static_cast< int >(info.x2e_tileY) * static_cast< int >(info.x2a_gridDimX); + endY = localMinY; + for (int iY = (ySub - 3) / CFluidPlaneCPURender::numSubdivisionsInTile + 1; iY > 0; --iY) { + endX = localMinX; + int iX = 0; + while (iX < numTilesX) { + const bool* gridFlags = info.x30_gridFlags; + if (reinterpret_cast< const char* >(gridFlags)[iX + gridOffset] != 0) { + int endIX = iX + 1; + const char* ptr = reinterpret_cast< const char* >(gridFlags) + endIX + gridOffset; + while (endIX < numTilesX && *ptr != 0) { + ++endIX; + ++ptr; + } + int runLen = (endIX - iX) + 1; + CGX::Begin(GX_TRIANGLESTRIP, GX_VTXFMT0, static_cast< ushort >(runLen * 2)); + for (int v = runLen; v > 0; --v) { + GXPosition3f32(endX, endY, 0.f); + GXNormal3s8(0, 0, 0x3f); + GXNormal3s8(0, 0x3f, 0); + GXNormal3s8(0x3f, 0, 0); + GXColor4u8(0, 0, 0, 0xff); + GXPosition3f32(endX, endY + info.x14_tileSize, 0.f); + GXNormal3s8(0, 0, 0x3f); + GXNormal3s8(0, 0x3f, 0); + GXNormal3s8(0x3f, 0, 0); + GXColor4u8(0, 0, 0, 0xff); + endX += info.x14_tileSize; + } + CGX::End(); + iX = endIX + 1; + } else { + endX += info.x14_tileSize; + ++iX; + const char* ptr = reinterpret_cast< const char* >(gridFlags) + iX + gridOffset; + while (iX < numTilesX && *ptr == 0) { + endX += info.x14_tileSize; + ++iX; + ++ptr; + } + } + } + endY += info.x14_tileSize; + gridOffset += static_cast< int >(info.x2a_gridDimX); + } + } + break; + } + default: + break; + } + } else { + float curY = info.x8_localMinY; + int startYDiv = 1; + for (; startYDiv < static_cast< int >(static_cast< signed char >(info.x1_ySubdivs)) - 2; + startYDiv += CFluidPlaneCPURender::numSubdivisionsInTile) { + RenderStripWithRipples( + *reinterpret_cast< const CFluidPlaneCPURender::SHFieldSample(*)[45][45] >(0xe00000a0), + *reinterpret_cast< const unsigned char (*)[9][9] >(0xe0000040), startYDiv, curY, info); + curY += info.x14_tileSize; + } + } +} diff --git a/src/MetroidPrime/CWorld.cpp b/src/MetroidPrime/CWorld.cpp index b3541198e..3dffbfdf2 100644 --- a/src/MetroidPrime/CWorld.cpp +++ b/src/MetroidPrime/CWorld.cpp @@ -246,8 +246,8 @@ rstl::string CWorld::IGetDefaultAudioTrack() const { return x84_defAudioTrack; } int CWorld::IGetAreaCount() const { return x18_areas.size(); } -CDummyWorld::CDummyWorld(CAssetId mlvlId, bool loadMap) -: x4_loadMap(loadMap) +CDummyWorld::CDummyWorld(CAssetId mlvlId) +: x4_loadMap(true) , x8_phase(kP_Loading) , xc_mlvlId(mlvlId) #if NONMATCHING From 5032b3a1598bf192228d9be32b131b131359b24c Mon Sep 17 00:00:00 2001 From: Luke Street Date: Thu, 26 Feb 2026 18:26:08 -0700 Subject: [PATCH 3/3] CBSLocomotion, CBSHurled, CBSJump; CAutoMapper cleanup --- config/GM8E01_00/splits.txt | 2 +- config/GM8E01_00/symbols.txt | 56 +- config/GM8E01_01/symbols.txt | 14 +- configure.py | 2 +- include/Kyoto/Basics/CCast.hpp | 1 + include/Kyoto/Math/CQuaternion.hpp | 5 + include/Kyoto/Math/CRelAngle.hpp | 2 +- include/Kyoto/Math/CVector2i.hpp | 9 +- include/Kyoto/Math/CVector3f.hpp | 16 +- include/MetroidPrime/BodyState/CBSHurled.hpp | 42 + include/MetroidPrime/BodyState/CBSJump.hpp | 43 + .../MetroidPrime/BodyState/CBSLocomotion.hpp | 108 +- .../BodyState/CBodyController.hpp | 3 +- .../BodyState/CBodyStateCmdMgr.hpp | 1 + .../MetroidPrime/BodyState/CBodyStateInfo.hpp | 2 +- include/MetroidPrime/CActor.hpp | 3 + include/MetroidPrime/CActorModelParticles.hpp | 142 +- include/MetroidPrime/CFrontEndUI.hpp | 3 +- include/MetroidPrime/CRippleManager.hpp | 13 +- include/MetroidPrime/Enemies/CPatterned.hpp | 1 + include/rstl/map.hpp | 1 - include/rstl/red_black_tree.hpp | 30 +- include/rstl/set.hpp | 1 - libc/limits.h | 7 +- src/Kyoto/Math/CTransform4f.cpp | 2 +- src/MetroidPrime/BodyState/CBSHurled.cpp | 291 ++ src/MetroidPrime/BodyState/CBSJump.cpp | 245 ++ src/MetroidPrime/BodyState/CBSLocomotion.cpp | 535 +++ .../BodyState/CBodyController.cpp | 2 +- src/MetroidPrime/CActor.cpp | 13 +- src/MetroidPrime/CActorModelParticles.cpp | 15 + src/MetroidPrime/CAutoMapper.cpp | 3130 +++++++++-------- src/MetroidPrime/CEulerAngles.cpp | 40 +- src/MetroidPrime/CRippleManager.cpp | 2 - src/MetroidPrime/CStateManager.cpp | 2 + src/MetroidPrime/CWeaponMgr.cpp | 2 +- src/MetroidPrime/Player/CPlayer.cpp | 9 +- src/MetroidPrime/Weapons/CIceImpact.cpp | 2 +- 38 files changed, 3132 insertions(+), 1665 deletions(-) create mode 100644 include/MetroidPrime/BodyState/CBSHurled.hpp create mode 100644 include/MetroidPrime/BodyState/CBSJump.hpp create mode 100644 src/MetroidPrime/BodyState/CBSHurled.cpp create mode 100644 src/MetroidPrime/BodyState/CBSJump.cpp create mode 100644 src/MetroidPrime/BodyState/CBSLocomotion.cpp create mode 100644 src/MetroidPrime/CActorModelParticles.cpp diff --git a/config/GM8E01_00/splits.txt b/config/GM8E01_00/splits.txt index 00de826a5..90ae7871a 100644 --- a/config/GM8E01_00/splits.txt +++ b/config/GM8E01_00/splits.txt @@ -1597,7 +1597,7 @@ MetroidPrime/Enemies/CThardus.cpp: .sbss start:0x805A9078 end:0x805A9080 .sdata2 start:0x805AC9E8 end:0x805ACAA0 -MetroidPrime/CActorParticles.cpp: +MetroidPrime/CActorModelParticles.cpp: .text start:0x801E38A4 end:0x801E7558 .rodata start:0x803D1E98 end:0x803D1F20 .sdata start:0x805A7DE8 end:0x805A7DF8 diff --git a/config/GM8E01_00/symbols.txt b/config/GM8E01_00/symbols.txt index f35eb1ee3..1d22afa05 100644 --- a/config/GM8E01_00/symbols.txt +++ b/config/GM8E01_00/symbols.txt @@ -467,7 +467,7 @@ UpdateMusicVol__11CFrontEndUIFv = .text:0x8001D790; // type:function size:0x80 s OnMessage__11CFrontEndUIFRC20CArchitectureMessageR18CArchitectureQueue = .text:0x8001D810; // type:function size:0xAC scope:global TransitionToFive__11CFrontEndUIFv = .text:0x8001D8BC; // type:function size:0xBC scope:global __dt__11CFrontEndUIFv = .text:0x8001D978; // type:function size:0x260 scope:global -__ct__11CFrontEndUIFR18CArchitectureQueue = .text:0x8001DBD8; // type:function size:0x344 scope:global +__ct__11CFrontEndUIFv = .text:0x8001DBD8; // type:function size:0x344 scope:global CanBuild__11CResFactoryFRC10SObjectTag = .text:0x8001DF1C; // type:function size:0x24 scope:global DoOptionsCancel__17SFusionBonusFrameFPC14CGuiTableGroup = .text:0x8001DF40; // type:function size:0xC8 scope:global DoSelectionChange__17SFusionBonusFrameFPC14CGuiTableGroup = .text:0x8001E008; // type:function size:0xF4 scope:global @@ -1161,8 +1161,8 @@ fn_8004DD40 = .text:0x8004DD40; // type:function size:0x50 fn_8004DD90 = .text:0x8004DD90; // type:function size:0x68 fn_8004DDF8 = .text:0x8004DDF8; // type:function size:0x6C __dt__20CActorModelParticlesFv = .text:0x8004DE64; // type:function size:0xE8 scope:global -fn_8004DF4C = .text:0x8004DF4C; // type:function size:0x50 -fn_8004DF9C = .text:0x8004DF9C; // type:function size:0xA8 +__dt__Q220CActorModelParticles9DGRPArrayFv = .text:0x8004DF4C; // type:function size:0x50 scope:global +__dt__Q220CActorModelParticles10DependencyFv = .text:0x8004DF9C; // type:function size:0xA8 scope:global __dt__Q24rstl36reserved_vector<15CCameraBlurPass,9>Fv = .text:0x8004E044; // type:function size:0x50 scope:global clear__Q24rstl36reserved_vector<15CCameraBlurPass,9>Fv = .text:0x8004E094; // type:function size:0xA0 scope:global __ct__13CStateManagerFRCQ24rstl26ncrc_ptr<14CScriptMailbox>RCQ24rstl25ncrc_ptr<13CMapWorldInfo>RCQ24rstl24ncrc_ptr<12CPlayerState>RCQ24rstl30ncrc_ptr<18CWorldTransManager>RCQ24rstl28ncrc_ptr<16CWorldLayerState> = .text:0x8004E134; // type:function size:0x11AC scope:global @@ -7074,17 +7074,17 @@ Shutdown__14CBSLieOnGroundFR15CBodyController = .text:0x801368D4; // type:functi UpdateBody__14CBSLieOnGroundFfR15CBodyControllerR13CStateManager = .text:0x801368FC; // type:function size:0x20 scope:global Start__14CBSLieOnGroundFR15CBodyControllerR13CStateManager = .text:0x8013691C; // type:function size:0x1C0 scope:global __ct__14CBSLieOnGroundFRC6CActor = .text:0x80136ADC; // type:function size:0x74 scope:global -__dt__28CBSRestrictedFlyerLocomotionFv = .text:0x80136B50; // type:function size:0xDC scope:global +__dt__20CBSFloaterLocomotionFv = .text:0x80136B50; // type:function size:0xDC scope:global IsMoving__23CBSRestrictedLocomotionCFv = .text:0x80136C2C; // type:function size:0x8 scope:global GetLocomotionSpeed__23CBSRestrictedLocomotionCFQ23pas15ELocomotionTypeQ23pas15ELocomotionAnim = .text:0x80136C34; // type:function size:0x8 scope:global CanShoot__13CBSLocomotionCFv = .text:0x80136C3C; // type:function size:0x8 scope:global IsPitchable__13CBSLocomotionCFv = .text:0x80136C44; // type:function size:0x8 scope:global -ApplyLocomotionPhysics__28CBSRestrictedFlyerLocomotionFfR15CBodyController = .text:0x80136C4C; // type:function size:0xBC scope:global -__ct__28CBSRestrictedFlyerLocomotionFR6CActor = .text:0x80136D08; // type:function size:0x3C scope:global +ApplyLocomotionPhysics__20CBSFloaterLocomotionFfR15CBodyController = .text:0x80136C4C; // type:function size:0xBC scope:global +__ct__20CBSFloaterLocomotionFR6CActor = .text:0x80136D08; // type:function size:0x3C scope:global __dt__23CBSRestrictedLocomotionFv = .text:0x80136D44; // type:function size:0xCC scope:global -UpdateLocomotionAnimation__21CBSNewFlyerLocomotionFffR15CBodyControllerb = .text:0x80136E10; // type:function size:0x1C8 scope:global -ApplyLocomotionPhysics__21CBSNewFlyerLocomotionFfR15CBodyController = .text:0x80136FD8; // type:function size:0x68 scope:global -__ct__21CBSNewFlyerLocomotionFR6CActor = .text:0x80137040; // type:function size:0x3C scope:global +UpdateLocomotionAnimation__25CBSAiMovedFlyerLocomotionFffR15CBodyControllerb = .text:0x80136E10; // type:function size:0x1C8 scope:global +ApplyLocomotionPhysics__25CBSAiMovedFlyerLocomotionFfR15CBodyController = .text:0x80136FD8; // type:function size:0x68 scope:global +__ct__25CBSAiMovedFlyerLocomotionFR6CActor = .text:0x80137040; // type:function size:0x3C scope:global __dt__18CBSBiPedLocomotionFv = .text:0x8013707C; // type:function size:0x100 scope:global ApplyLocomotionPhysics__23CBSWallWalkerLocomotionFfR15CBodyController = .text:0x8013717C; // type:function size:0x224 scope:global __ct__23CBSWallWalkerLocomotionFR6CActor = .text:0x801373A0; // type:function size:0x3C scope:global @@ -7112,13 +7112,13 @@ Shutdown__13CBSLocomotionFR15CBodyController = .text:0x80138AE8; // type:functio UpdateBody__13CBSLocomotionFfR15CBodyControllerR13CStateManager = .text:0x80138B10; // type:function size:0xAC scope:global Start__13CBSLocomotionFR15CBodyControllerR13CStateManager = .text:0x80138BBC; // type:function size:0x74 scope:global __ct__13CBSLocomotionFv = .text:0x80138C30; // type:function size:0x24 scope:global -__dt__21CBSNewFlyerLocomotion = .text:0x80138C54; // type:function size:0x60 scope:global +__dt__25CBSAiMovedFlyerLocomotionFv = .text:0x80138C54; // type:function size:0x60 scope:global IsMoving__18CBSBiPedLocomotionCFv = .text:0x80138CB4; // type:function size:0x14 scope:global -__dt__23CBSWallWalkerLocomotion = .text:0x80138CC8; // type:function size:0x60 scope:global -__dt__18CBSFlyerLocomotion = .text:0x80138D28; // type:function size:0x60 scope:global +__dt__23CBSWallWalkerLocomotionFv = .text:0x80138CC8; // type:function size:0x60 scope:global +__dt__18CBSFlyerLocomotionFv = .text:0x80138D28; // type:function size:0x60 scope:global IsBackPedal__18CBSFlyerLocomotionCFR15CBodyController = .text:0x80138D88; // type:function size:0x8 scope:global IsPitchable__18CBSFlyerLocomotionCFv = .text:0x80138D90; // type:function size:0x8 scope:global -__sinit_CBSFlyerLocomotion_cpp = .text:0x80138D98; // type:function size:0xC scope:local +__sinit_CBSLocomotion_cpp = .text:0x80138D98; // type:function size:0xC scope:local __dt__7CBSStepFv = .text:0x80138DA4; // type:function size:0x5C scope:global IsMoving__7CBSStepCFv = .text:0x80138E00; // type:function size:0x8 scope:global CanShoot__7CBSStepCFv = .text:0x80138E08; // type:function size:0x8 scope:global @@ -7151,7 +7151,7 @@ Freeze__15CBodyControllerFfff = .text:0x8013A240; // type:function size:0xBC sco LoopBestAnimation__15CBodyControllerFRC16CPASAnimParmDataR9CRandom16 = .text:0x8013A2FC; // type:function size:0xAC scope:global PlayBestAnimation__15CBodyControllerFRC16CPASAnimParmDataR9CRandom16 = .text:0x8013A3A8; // type:function size:0xAC scope:global GetPASDatabase__15CBodyControllerCFv = .text:0x8013A454; // type:function size:0x14 scope:global -FaceDirection3D__15CBodyControllerFRC9CVector3ff = .text:0x8013A468; // type:function size:0x23C scope:global +FaceDirection3D__15CBodyControllerFRC9CVector3fRC9CVector3ff = .text:0x8013A468; // type:function size:0x23C scope:global FaceDirection__15CBodyControllerFRC9CVector3ff = .text:0x8013A6A4; // type:function size:0x164 scope:global SetDeltaRotation__15CBodyControllerFRC11CQuaternion = .text:0x8013A808; // type:function size:0x58 scope:global MultiplyPlaybackRate__15CBodyControllerFf = .text:0x8013A860; // type:function size:0x2C scope:global @@ -16736,7 +16736,7 @@ lbl_803CC720 = .rodata:0x803CC720; // type:object size:0x15 data:string lbl_803CC735 = .rodata:0x803CC735; // type:object size:0x15 data:string lbl_803CC74A = .rodata:0x803CC74A; // type:object size:0x356 @stringBase0 = .rodata:0x803CCAA0; // type:object size:0x7 scope:local data:string_table -lbl_803CCAA8 = .rodata:0x803CCAA8; // type:object size:0x10 +@stringBase0 = .rodata:0x803CCAA8; // type:object size:0x10 scope:local data:string_table lbl_803CCAB8 = .rodata:0x803CCAB8; // type:object size:0xE data:string lbl_803CCAC6 = .rodata:0x803CCAC6; // type:object size:0xE data:string lbl_803CCAD4 = .rodata:0x803CCAD4; // type:object size:0x44 @@ -18430,15 +18430,15 @@ __vt__7CBSFall = .data:0x803E13E0; // type:object size:0x38 scope:global __vt__8CBSGetup = .data:0x803E1418; // type:object size:0x38 scope:global __vt__12CBSKnockBack = .data:0x803E1450; // type:object size:0x38 scope:global __vt__14CBSLieOnGround = .data:0x803E1488; // type:object size:0x38 scope:global -lbl_803E14C0 = .data:0x803E14C0; // type:object size:0x18 -lbl_803E14D8 = .data:0x803E14D8; // type:object size:0x18 -lbl_803E14F0 = .data:0x803E14F0; // type:object size:0x4C -lbl_803E153C = .data:0x803E153C; // type:object size:0x50 -lbl_803E158C = .data:0x803E158C; // type:object size:0x50 -lbl_803E15DC = .data:0x803E15DC; // type:object size:0x54 -lbl_803E1630 = .data:0x803E1630; // type:object size:0x4C -lbl_803E167C = .data:0x803E167C; // type:object size:0x50 -lbl_803E16CC = .data:0x803E16CC; // type:object size:0x4C +strafes$local = .data:0x803E14C0; // type:object size:0x18 scope:local data:4byte +runStrafes$local = .data:0x803E14D8; // type:object size:0x18 scope:local data:4byte +__vt__20CBSFloaterLocomotion = .data:0x803E14F0; // type:object size:0x4C scope:global +__vt__25CBSAiMovedFlyerLocomotion = .data:0x803E153C; // type:object size:0x50 scope:global +__vt__23CBSWallWalkerLocomotion = .data:0x803E158C; // type:object size:0x50 scope:global +__vt__18CBSFlyerLocomotion = .data:0x803E15DC; // type:object size:0x54 scope:global +__vt__23CBSRestrictedLocomotion = .data:0x803E1630; // type:object size:0x4C scope:global +__vt__18CBSBiPedLocomotion = .data:0x803E167C; // type:object size:0x50 scope:global +__vt__13CBSLocomotion = .data:0x803E16CC; // type:object size:0x4C scope:global __vt__7CBSStep = .data:0x803E1718; // type:object size:0x38 scope:global __vt__12CBSFlyerTurn = .data:0x803E1750; // type:object size:0x3C scope:global __vt__7CBSTurn = .data:0x803E178C; // type:object size:0x3C scope:global @@ -20396,10 +20396,10 @@ lbl_805A7718 = .sdata:0x805A7718; // type:object size:0x4 align:4 data:float lbl_805A771C = .sdata:0x805A771C; // type:object size:0x4 align:4 data:float lbl_805A7720 = .sdata:0x805A7720; // type:object size:0x4 align:4 data:float lbl_805A7724 = .sdata:0x805A7724; // type:object size:0x4 align:4 data:float -lbl_805A7728 = .sdata:0x805A7728; // type:object size:0x4 data:4byte -lbl_805A772C = .sdata:0x805A772C; // type:object size:0x4 align:4 data:float +sCBSLocomotion_DefaultAnimId = .sdata:0x805A7728; // type:object size:0x4 scope:local data:4byte +lbl_805A772C = .sdata:0x805A772C; // type:object size:0x4 scope:local align:4 data:float lbl_805A7730 = .sdata:0x805A7730; // type:object size:0x4 align:4 data:float -lbl_805A7734 = .sdata:0x805A7734; // type:object size:0x4 data:4byte +skInvalidAnimId = .sdata:0x805A7734; // type:object size:0x4 scope:local data:4byte lbl_805A7738 = .sdata:0x805A7738; // type:object size:0x8 align:4 data:float lbl_805A7740 = .sdata:0x805A7740; // type:object size:0x8 align:4 data:float lbl_805A7748 = .sdata:0x805A7748; // type:object size:0x4 @@ -21824,7 +21824,7 @@ kLineOfSightExcludeList = .sbss:0x805A8F68; // type:object size:0x8 data:8byte g_BillboardCount__19CHUDBillboardEffect = .sbss:0x805A8F70; // type:object size:0x4 scope:global data:4byte g_IndirectTexturedBillboardCount__19CHUDBillboardEffect = .sbss:0x805A8F74; // type:object size:0x4 scope:global data:4byte init$507 = .sbss:0x805A8F78; // type:object size:0x1 scope:local data:byte -lbl_805A8F80 = .sbss:0x805A8F80; // type:object size:0x8 align:4 data:float +skMaxPitchAngle = .sbss:0x805A8F80; // type:object size:0x8 scope:local align:4 data:float lbl_805A8F88 = .sbss:0x805A8F88; // type:object size:0x4 align:4 data:float lbl_805A8F8C = .sbss:0x805A8F8C; // type:object size:0x1 data:byte skEndFadeColor__Q29CFireFlea18CDeathCameraEffect = .sbss:0x805A8F90; // type:object size:0x4 data:4byte diff --git a/config/GM8E01_01/symbols.txt b/config/GM8E01_01/symbols.txt index cda0ca092..8c5eb34f8 100644 --- a/config/GM8E01_01/symbols.txt +++ b/config/GM8E01_01/symbols.txt @@ -7074,17 +7074,17 @@ Shutdown__14CBSLieOnGroundFR15CBodyController = .text:0x80136950; // type:functi UpdateBody__14CBSLieOnGroundFfR15CBodyControllerR13CStateManager = .text:0x80136978; // type:function size:0x20 scope:global Start__14CBSLieOnGroundFR15CBodyControllerR13CStateManager = .text:0x80136998; // type:function size:0x1C0 scope:global __ct__14CBSLieOnGroundFRC6CActor = .text:0x80136B58; // type:function size:0x74 scope:global -__dt__28CBSRestrictedFlyerLocomotionFv = .text:0x80136BCC; // type:function size:0xDC scope:global +__dt__20CBSFloaterLocomotionFv = .text:0x80136BCC; // type:function size:0xDC scope:global IsMoving__23CBSRestrictedLocomotionCFv = .text:0x80136CA8; // type:function size:0x8 scope:global GetLocomotionSpeed__23CBSRestrictedLocomotionCFQ23pas15ELocomotionTypeQ23pas15ELocomotionAnim = .text:0x80136CB0; // type:function size:0x8 scope:global CanShoot__13CBSLocomotionCFv = .text:0x80136CB8; // type:function size:0x8 scope:global IsPitchable__13CBSLocomotionCFv = .text:0x80136CC0; // type:function size:0x8 scope:global -ApplyLocomotionPhysics__28CBSRestrictedFlyerLocomotionFfR15CBodyController = .text:0x80136CC8; // type:function size:0xBC scope:global -__ct__28CBSRestrictedFlyerLocomotionFR6CActor = .text:0x80136D84; // type:function size:0x3C scope:global +ApplyLocomotionPhysics__20CBSFloaterLocomotionFfR15CBodyController = .text:0x80136CC8; // type:function size:0xBC scope:global +__ct__20CBSFloaterLocomotionFR6CActor = .text:0x80136D84; // type:function size:0x3C scope:global __dt__23CBSRestrictedLocomotionFv = .text:0x80136DC0; // type:function size:0xCC scope:global -UpdateLocomotionAnimation__21CBSNewFlyerLocomotionFffR15CBodyControllerb = .text:0x80136E8C; // type:function size:0x1C8 scope:global -ApplyLocomotionPhysics__21CBSNewFlyerLocomotionFfR15CBodyController = .text:0x80137054; // type:function size:0x68 scope:global -__ct__21CBSNewFlyerLocomotionFR6CActor = .text:0x801370BC; // type:function size:0x3C scope:global +UpdateLocomotionAnimation__25CBSAiMovedFlyerLocomotionFffR15CBodyControllerb = .text:0x80136E8C; // type:function size:0x1C8 scope:global +ApplyLocomotionPhysics__25CBSAiMovedFlyerLocomotionFfR15CBodyController = .text:0x80137054; // type:function size:0x68 scope:global +__ct__25CBSAiMovedFlyerLocomotionFR6CActor = .text:0x801370BC; // type:function size:0x3C scope:global __dt__18CBSBiPedLocomotionFv = .text:0x801370F8; // type:function size:0x100 scope:global ApplyLocomotionPhysics__23CBSWallWalkerLocomotionFfR15CBodyController = .text:0x801371F8; // type:function size:0x224 scope:global __ct__23CBSWallWalkerLocomotionFR6CActor = .text:0x8013741C; // type:function size:0x3C scope:global @@ -7112,7 +7112,7 @@ Shutdown__13CBSLocomotionFR15CBodyController = .text:0x80138B64; // type:functio UpdateBody__13CBSLocomotionFfR15CBodyControllerR13CStateManager = .text:0x80138B8C; // type:function size:0xAC scope:global Start__13CBSLocomotionFR15CBodyControllerR13CStateManager = .text:0x80138C38; // type:function size:0x74 scope:global __ct__13CBSLocomotionFv = .text:0x80138CAC; // type:function size:0x24 scope:global -__dt__21CBSNewFlyerLocomotion = .text:0x80138CD0; // type:function size:0x60 scope:global +__dt__25CBSAiMovedFlyerLocomotion = .text:0x80138CD0; // type:function size:0x60 scope:global IsMoving__18CBSBiPedLocomotionCFv = .text:0x80138D30; // type:function size:0x14 scope:global __dt__23CBSWallWalkerLocomotion = .text:0x80138D44; // type:function size:0x60 scope:global __dt__18CBSFlyerLocomotion = .text:0x80138DA4; // type:function size:0x60 scope:global diff --git a/configure.py b/configure.py index 7bc66bce4..d128c79af 100755 --- a/configure.py +++ b/configure.py @@ -994,7 +994,7 @@ def MatchingFor(*versions): "MetroidPrime/ScriptObjects/CScriptPlayerStateChange.cpp", ), Object(NonMatching, "MetroidPrime/Enemies/CThardus.cpp"), - Object(NonMatching, "MetroidPrime/CActorParticles.cpp"), + Object(NonMatching, "MetroidPrime/CActorModelParticles.cpp"), Object(NonMatching, "MetroidPrime/Enemies/CWallCrawlerSwarm.cpp"), Object( MatchingFor("GM8E01_00", "GM8E01_01"), diff --git a/include/Kyoto/Basics/CCast.hpp b/include/Kyoto/Basics/CCast.hpp index 2c849426f..0df57c9ae 100644 --- a/include/Kyoto/Basics/CCast.hpp +++ b/include/Kyoto/Basics/CCast.hpp @@ -76,6 +76,7 @@ inline uchar ToUint8(int c) { return static_cast< uchar >(c); } inline int FtoL(float in) { return static_cast< int >(in); } inline float LtoF(int in) { return static_cast< float >(in); } inline float ToReal32(int in) { return static_cast< float >(in); } +inline float ToReal32(double in) { return static_cast< float >(in); } inline int ToInt32(float in) { return static_cast< int >(in); } inline unsigned short ToUint16(short in) { return static_cast< unsigned short >(in); } inline char ToChar(int c) { return ToUint8(c); } diff --git a/include/Kyoto/Math/CQuaternion.hpp b/include/Kyoto/Math/CQuaternion.hpp index 3c7474a35..2c336eacb 100644 --- a/include/Kyoto/Math/CQuaternion.hpp +++ b/include/Kyoto/Math/CQuaternion.hpp @@ -69,12 +69,17 @@ class CQuaternion { return (a.GetW() * b.GetW()) + (a.GetX() * b.GetX()) + (a.GetY() * b.GetY()) + (a.GetZ() * b.GetZ()); } + // TODO: fake float GetW() const { return w; } float GetX() const { return imaginary.GetX(); } float GetY() const { return imaginary.GetY(); } float GetZ() const { return imaginary.GetZ(); } const CVector3f& GetImaginary() const { return imaginary; } + // Real + float GetScalar() const { return w; } + const CVector3f& GetVector() const { return imaginary; } + private: float w; CVector3f imaginary; diff --git a/include/Kyoto/Math/CRelAngle.hpp b/include/Kyoto/Math/CRelAngle.hpp index b8f18fd9f..ce9ade505 100644 --- a/include/Kyoto/Math/CRelAngle.hpp +++ b/include/Kyoto/Math/CRelAngle.hpp @@ -29,7 +29,7 @@ class CRelAngle { return *this; } - // __lt__9CRelAngleCFRC9CRelAngle + bool operator<(const CRelAngle& rhs) const { return x0_angle < rhs.x0_angle; } static CRelAngle FromDegrees(float deg) { return CRelAngle(deg * (M_PIF / 180.f)); } static CRelAngle FromRadians(float rad) { return CRelAngle(rad); } diff --git a/include/Kyoto/Math/CVector2i.hpp b/include/Kyoto/Math/CVector2i.hpp index 45564f62a..34dd651b7 100644 --- a/include/Kyoto/Math/CVector2i.hpp +++ b/include/Kyoto/Math/CVector2i.hpp @@ -20,13 +20,20 @@ class CVector2i { int& operator[](int idx) { return *(&mX + idx); } const int& operator[](int idx) const { return *(&mX + idx); } + static CVector2i Lerp(const CVector2i& a, const CVector2i& b, float v) { + float inv = 1.f - v; + int x = static_cast< int >(a.GetX() * inv + b.GetX() * v); + int y = static_cast< int >(a.GetY() * inv + b.GetY() * v); + return CVector2i(x, y); + } + private: int mX; int mY; }; CVector2i operator+(const CVector2i& lhs, const CVector2i& rhs); -CVector2i operator+(const CVector2i& lhs, const CVector2i& rhs); +CVector2i operator-(const CVector2i& lhs, const CVector2i& rhs); bool operator==(const CVector2i& lhs, const CVector2i& rhs); CVector2i operator*(const CVector2i& lhs, int rhs); CVector2i operator/(const CVector2i& lhs, int rhs); diff --git a/include/Kyoto/Math/CVector3f.hpp b/include/Kyoto/Math/CVector3f.hpp index 1d8ce3579..a819a9e19 100644 --- a/include/Kyoto/Math/CVector3f.hpp +++ b/include/Kyoto/Math/CVector3f.hpp @@ -153,15 +153,13 @@ inline CVector3f operator+(const CVector3f& lhs, const CVector3f& rhs) { return CVector3f(x, y, z); } -// TODO real? -inline CVector3f operator*(const CVector3f& lhs, const CVector3f& rhs) { - float x = lhs.GetX() * rhs.GetX(); - float y = lhs.GetY() * rhs.GetY(); - float z = lhs.GetZ() * rhs.GetZ(); - return CVector3f(x, y, z); -} - - +// Doesn't show up in map; use CVector3f::ByElementMultiply instead +// inline CVector3f operator*(const CVector3f& lhs, const CVector3f& rhs) { +// float x = lhs.GetX() * rhs.GetX(); +// float y = lhs.GetY() * rhs.GetY(); +// float z = lhs.GetZ() * rhs.GetZ(); +// return CVector3f(x, y, z); +// } inline CVector3f operator*(const CVector3f& vec, const float f) { float x = vec.GetX() * f; diff --git a/include/MetroidPrime/BodyState/CBSHurled.hpp b/include/MetroidPrime/BodyState/CBSHurled.hpp new file mode 100644 index 000000000..d764de3c8 --- /dev/null +++ b/include/MetroidPrime/BodyState/CBSHurled.hpp @@ -0,0 +1,42 @@ +#ifndef _CBSHURLED +#define _CBSHURLED + +#include "MetroidPrime/BodyState/CBodyState.hpp" + +#include "Kyoto/Math/CVector3f.hpp" + +class CBSHurled : public CBodyState { +public: + CBSHurled(); + ~CBSHurled() override {}; + + // CBodyState + bool IsMoving() const override; + bool ApplyHeadTracking() const override; + void Start(CBodyController& bc, CStateManager& mgr) override; + pas::EAnimationState UpdateBody(float dt, CBodyController& bc, CStateManager& mgr) override; + void Shutdown(CBodyController& bc) override; + bool IsInAir(const CBodyController& bc) const override; + +private: + pas::EAnimationState GetBodyStateTransition(float dt, CBodyController& bc); + void Recover(CStateManager& mgr, CBodyController& bc, pas::EHurledState state); + void PlayStrikeWallAnimation(CBodyController& bc, CStateManager& mgr); + void PlayLandAnimation(CBodyController& bc, CStateManager& mgr); + bool ShouldStartStrikeWall(CBodyController& bc) const; + bool ShouldStartLand(float dt, CBodyController& bc) const; + + pas::EHurledState x4_state; + float x8_knockAngle; + int xc_animSeries; + float x10_rotateSpeed; + float x14_remTime; + float x18_curTime; + mutable CVector3f x1c_lastTranslation; + mutable float x28_landedDur; + bool x2c_24_needsRecover : 1; +}; + +CHECK_SIZEOF(CBSHurled, 0x30) + +#endif // _CBSHURLED diff --git a/include/MetroidPrime/BodyState/CBSJump.hpp b/include/MetroidPrime/BodyState/CBSJump.hpp new file mode 100644 index 000000000..b461cd661 --- /dev/null +++ b/include/MetroidPrime/BodyState/CBSJump.hpp @@ -0,0 +1,43 @@ +#ifndef _CBSJUMP +#define _CBSJUMP + +#include "MetroidPrime/BodyState/CBodyState.hpp" + +#include "Kyoto/Math/CVector3f.hpp" + +class CBSJump : public CBodyState { +public: + CBSJump(); + ~CBSJump() {} + + // CBodyState + bool IsInAir(const CBodyController& bc) const override; + bool IsMoving() const override; + bool ApplyHeadTracking() const override; + bool ApplyAnimationDeltas() const override; + bool CanShoot() const override; + void Start(CBodyController& bc, CStateManager& mgr) override; + pas::EAnimationState UpdateBody(float dt, CBodyController& bc, CStateManager& mgr) override; + void Shutdown(CBodyController& bc) override; + +private: + pas::EAnimationState GetBodyStateTransition(float dt, CBodyController& bc); + uchar CheckForWallJump(CBodyController& bc, CStateManager& mgr); + void CheckForLand(CBodyController& bc, CStateManager& mgr); + void PlayJumpLoop(CStateManager& mgr, CBodyController& bc); + + pas::EJumpState x4_state; + pas::EJumpType x8_jumpType; + CVector3f xc_waypoint1; + CVector3f x18_velocity; + CVector3f x24_waypoint2; + bool x30_24_applyLaunchVel : 1; + bool x30_25_wallJump : 1; + bool x30_26_wallBounceRight : 1; + bool x30_27_hasWallBounced : 1; + bool x30_28_startInJumpLoop : 1; +}; + +CHECK_SIZEOF(CBSJump, 0x34) + +#endif // _CBSJUMP diff --git a/include/MetroidPrime/BodyState/CBSLocomotion.hpp b/include/MetroidPrime/BodyState/CBSLocomotion.hpp index b81a73a6f..2bd422003 100644 --- a/include/MetroidPrime/BodyState/CBSLocomotion.hpp +++ b/include/MetroidPrime/BodyState/CBSLocomotion.hpp @@ -4,25 +4,117 @@ #include "MetroidPrime/BodyState/CBodyState.hpp" #include "rstl/pair.hpp" +#include "rstl/reserved_vector.hpp" -class CBSLocomotion : public CBodyState { -protected: - pas::ELocomotionType x4_locomotionType; - float GetStartVelocityMagnitude(const CBodyController& bc) const; - void ReStartBodyState(CBodyController& bc, bool maintainVel); - float ComputeWeightPercentage(const rstl::pair& a, const rstl::pair& b, float f) const; +class CActor; +class CBSLocomotion : public CBodyState { public: + CBSLocomotion(); + ~CBSLocomotion() override {} + bool IsMoving() const override = 0; - bool CanShoot() const override { return true; } + bool CanShoot() const override; void Start(CBodyController& bc, CStateManager& mgr) override; pas::EAnimationState UpdateBody(float dt, CBodyController& bc, CStateManager& mgr) override; void Shutdown(CBodyController& bc) override; - virtual bool IsPitchable() const { return false; } + virtual bool IsPitchable() const; virtual float GetLocomotionSpeed(pas::ELocomotionType type, pas::ELocomotionAnim anim) const = 0; virtual float ApplyLocomotionPhysics(float dt, CBodyController& bc); virtual float UpdateLocomotionAnimation(float dt, float velMag, CBodyController& bc, bool init) = 0; virtual pas::EAnimationState GetBodyStateTransition(float dt, CBodyController& bc); + +protected: + pas::ELocomotionType x4_locomotionType; + + float GetStartVelocityMagnitude(CBodyController& bc) const; + void ReStartBodyState(CBodyController& bc, bool maintainVel); + float ComputeWeightPercentage(const rstl::pair< int, float >& a, const rstl::pair< int, float >& b, + float f) const; +}; +CHECK_SIZEOF(CBSLocomotion, 0x8) + +class CBSBiPedLocomotion : public CBSLocomotion { +public: + explicit CBSBiPedLocomotion(CActor& actor); + ~CBSBiPedLocomotion() override; + + bool IsMoving() const override; + void Start(CBodyController& bc, CStateManager& mgr) override; + pas::EAnimationState UpdateBody(float dt, CBodyController& bc, CStateManager& mgr) override; + float GetLocomotionSpeed(pas::ELocomotionType type, pas::ELocomotionAnim anim) const override; + float UpdateLocomotionAnimation(float dt, float velMag, CBodyController& bc, bool init) override; + virtual bool IsStrafing(CBodyController& bc) const; + +protected: + static const float skMinWalkPercent; + + rstl::reserved_vector< rstl::reserved_vector< rstl::pair< int, float >, 8 >, 14 > x8_anims; + pas::ELocomotionAnim x3c4_anim; + float x3c8_primeTime; + + float UpdateRun(float vel, CBodyController& bc, pas::ELocomotionAnim anim); + float UpdateWalk(float vel, CBodyController& bc, pas::ELocomotionAnim anim); + float UpdateStrafe(float vel, CBodyController& bc, pas::ELocomotionAnim anim); + const rstl::pair< int, float >& GetLocoAnimation(pas::ELocomotionType type, pas::ELocomotionAnim anim) const; +}; +CHECK_SIZEOF(CBSBiPedLocomotion, 0x3CC) + +class CBSWallWalkerLocomotion : public CBSBiPedLocomotion { +public: + explicit CBSWallWalkerLocomotion(CActor& actor); + ~CBSWallWalkerLocomotion() override; + + float ApplyLocomotionPhysics(float dt, CBodyController& bc) override; +}; +CHECK_SIZEOF(CBSWallWalkerLocomotion, 0x3CC) + +class CBSAiMovedFlyerLocomotion : public CBSBiPedLocomotion { +public: + explicit CBSAiMovedFlyerLocomotion(CActor& actor); + ~CBSAiMovedFlyerLocomotion() override; + + float ApplyLocomotionPhysics(float dt, CBodyController& bc) override; + float UpdateLocomotionAnimation(float dt, float velMag, CBodyController& bc, bool init) override; +}; +CHECK_SIZEOF(CBSAiMovedFlyerLocomotion, 0x3CC) + +class CBSFlyerLocomotion : public CBSBiPedLocomotion { +public: + explicit CBSFlyerLocomotion(CActor& actor, bool pitchable); + ~CBSFlyerLocomotion() override; + + bool IsPitchable() const override; + float ApplyLocomotionPhysics(float dt, CBodyController& bc) override; + virtual bool IsBackPedal(CBodyController& bc) const; + +private: + bool x3cc_pitchable; +}; +CHECK_SIZEOF(CBSFlyerLocomotion, 0x3D0) + +class CBSRestrictedLocomotion : public CBSLocomotion { +public: + explicit CBSRestrictedLocomotion(CActor& actor); + ~CBSRestrictedLocomotion() override {} + + bool IsMoving() const override; + float GetLocomotionSpeed(pas::ELocomotionType type, pas::ELocomotionAnim anim) const override; + float UpdateLocomotionAnimation(float dt, float velMag, CBodyController& bc, bool init) override; + +private: + rstl::reserved_vector< int, 14 > x8_anims; + pas::ELocomotionAnim x44_anim; +}; +CHECK_SIZEOF(CBSRestrictedLocomotion, 0x48) + +class CBSFloaterLocomotion : public CBSRestrictedLocomotion { +public: + explicit CBSFloaterLocomotion(CActor& actor); + ~CBSFloaterLocomotion() override; + + float ApplyLocomotionPhysics(float dt, CBodyController& bc) override; }; +CHECK_SIZEOF(CBSFloaterLocomotion, 0x48) #endif // _CBSLOCOMOTION diff --git a/include/MetroidPrime/BodyState/CBodyController.hpp b/include/MetroidPrime/BodyState/CBodyController.hpp index 7162bf6b6..a7f8a60ee 100644 --- a/include/MetroidPrime/BodyState/CBodyController.hpp +++ b/include/MetroidPrime/BodyState/CBodyController.hpp @@ -26,7 +26,7 @@ class CBodyController { void SetDeltaRotation(const CQuaternion& q); void SetCurrentAnimation(const CAnimPlaybackParms& parms, bool loop, bool noTrans); void FaceDirection(const CVector3f& v0, float dt); - void FaceDirection3D(const CVector3f& v0, float dt); + void FaceDirection3D(const CVector3f& v0, const CVector3f& v1, float dt); void EnableAnimation(bool enable); void PlayBestAnimation(const CPASAnimParmData& parms, CRandom16& r); void LoopBestAnimation(const CPASAnimParmData& parms, CRandom16& r); @@ -45,6 +45,7 @@ class CBodyController { const CBodyStateInfo& GetBodyStateInfo() const { return x2a4_bodyStateInfo; } CBodyStateInfo& BodyStateInfo() { return x2a4_bodyStateInfo; } pas::ELocomotionType GetLocomotionType() const { return x2ec_locomotionType; } + EBodyType GetBodyType() const { return x2f4_bodyType; } int GetCurrentAnimId() const { return x2f8_curAnim; } void SetTurnSpeed(float speed); bool IsAnimationOver() const { return x300_24_animationOver; } diff --git a/include/MetroidPrime/BodyState/CBodyStateCmdMgr.hpp b/include/MetroidPrime/BodyState/CBodyStateCmdMgr.hpp index b55b3d2bc..99ee8adcb 100644 --- a/include/MetroidPrime/BodyState/CBodyStateCmdMgr.hpp +++ b/include/MetroidPrime/BodyState/CBodyStateCmdMgr.hpp @@ -458,6 +458,7 @@ class CBodyStateCmdMgr { CBodyStateCmd* GetCmd(EBodyStateCmd cmd); const CBodyStateCmd* GetCmd(EBodyStateCmd cmd) const; const CVector3f& GetMoveVector() const { return x0_move; } + CVector3f& MoveVector() { return x0_move; } const CVector3f& GetFaceVector() const { return xc_face; } const CVector3f& GetTargetVector() const { return x18_target; } const CVector3f& GetAdditiveTargetVector() const { return x24_additiveTarget; } diff --git a/include/MetroidPrime/BodyState/CBodyStateInfo.hpp b/include/MetroidPrime/BodyState/CBodyStateInfo.hpp index 20ec4015b..7cbd36da5 100644 --- a/include/MetroidPrime/BodyState/CBodyStateInfo.hpp +++ b/include/MetroidPrime/BodyState/CBodyStateInfo.hpp @@ -18,7 +18,7 @@ class CBodyStateInfo { ~CBodyStateInfo(); void SetBodyController(CBodyController* controller) { x18_bodyController = controller; } - float GetMaximumPitch() const { return x30_maxPitch; } + const float& GetMaximumPitch() const { return x30_maxPitch; } void SetMaximumPitch(float pitch) { x30_maxPitch = pitch; } bool GetLocoAnimChangeAtEndOfAnimOnly() const { return x34_24_changeLocoAtEndOfAnimOnly; } void SetLocoAnimChangeAtEndOfAnimOnly(bool s) { x34_24_changeLocoAtEndOfAnimOnly = s; } diff --git a/include/MetroidPrime/CActor.hpp b/include/MetroidPrime/CActor.hpp index 203d411ab..9041b8ede 100644 --- a/include/MetroidPrime/CActor.hpp +++ b/include/MetroidPrime/CActor.hpp @@ -106,6 +106,9 @@ class CActor : public CEntity { void CreateShadowIfNeeded(); const CTransform4f& GetTransform() const { return x34_transform; } + CVector3f TransformWorldToLocalRotation(const CVector3f& v) const { + return x34_transform.TransposeRotate(v); + } void SetTransform(const CTransform4f& xf) { x34_transform = xf; SetTransformDirty(true); diff --git a/include/MetroidPrime/CActorModelParticles.hpp b/include/MetroidPrime/CActorModelParticles.hpp index 068e680db..27a65c014 100644 --- a/include/MetroidPrime/CActorModelParticles.hpp +++ b/include/MetroidPrime/CActorModelParticles.hpp @@ -3,18 +3,156 @@ #include "types.h" +#include "Kyoto/Audio/CSfxHandle.hpp" +#include "Kyoto/CToken.hpp" +#include "Kyoto/Graphics/CColor.hpp" +#include "Kyoto/Math/CTransform4f.hpp" +#include "Kyoto/Math/CVector3f.hpp" +#include "Kyoto/TToken.hpp" +#include "MetroidPrime/TGameTypes.hpp" + +#include "rstl/auto_ptr.hpp" +#include "rstl/list.hpp" +#include "rstl/pair.hpp" +#include "rstl/reserved_vector.hpp" +#include "rstl/string.hpp" +#include "rstl/vector.hpp" + class CActor; +class CEntity; +class CElementGen; +class CElectricDescription; +class CGenDescription; +class CParticleElectric; +class CRainSplashGenerator; class CStateManager; +class CTexture; class CActorModelParticles { public: + enum EDependency { + kD_OnFire, + kD_Ice, + kD_Ash, + kD_FirePop, + kD_Electric, + kD_IcePop, + }; + + struct Dependency { + rstl::vector< CToken > x0_tokens; + int x10_refCount; + bool x14_loaded; + + void UpdateLoad(); + void Unload(); + void Load(); + void Decrement(); + void Increment(); + }; + + class CItem { + friend class CActorModelParticles; + + private: + TUniqueId x0_id; + TAreaId x4_areaId; + rstl::reserved_vector< rstl::pair< rstl::auto_ptr< CElementGen >, uint >, 8 > x8_onFireGens; + float x6c_onFireDelayTimer; + bool x70_onFire; + CSfxHandle x74_sfx; + rstl::auto_ptr< CElementGen > x78_ashGen; + int x80_ashPointIterator; + int x84_ashMaxParticles; + uint x88_ashSeed; + rstl::reserved_vector< rstl::auto_ptr< CElementGen >, 4 > x8c_iceGens; + int xb0_icePointIterator; + uint xb4_iceSeed; + rstl::auto_ptr< CElementGen > xb8_firePopGen; + rstl::auto_ptr< CParticleElectric > xc0_electricGen; + int xc8_electricPointIterator; + uint xcc_electricSeed; + CColor xd0_electricColor; + rstl::auto_ptr< CRainSplashGenerator > xd4_rainSplashGen; + CToken xdc_ashy; + rstl::auto_ptr< CElementGen > xe4_icePopGen; + CVector3f xec_particleOffsetScale; + CTransform4f xf8_iceXf; + CActorModelParticles* x128_parent; + uchar x12c_flags; + float x130_remTime; + uchar x134_lockDeps; + + bool UpdateOnFire(float dt, CActor& actor, CStateManager& mgr); + bool UpdateAshGen(float dt, CActor& actor, CStateManager& mgr); + bool UpdateIcePop(float dt, CActor& actor); + bool UpdateFirePop(float dt, CActor& actor); + bool UpdateElectric(float dt, CActor& actor, CStateManager& mgr); + void EnsureLoaded(int dep); + + public: + CItem(const CEntity& ent, CActorModelParticles& parent); + + void GeneratePoints(const CVector3f* vertices, const CVector3f* normals, int count); + bool Update(float dt, CStateManager& mgr); + void Unlock(EDependency dep); + }; + CActorModelParticles(); + + CTexture* GetAshyTexture(CActor& actor); + void StartBurnDeath(CActor& actor); void Render(const CStateManager& mgr, const CActor& actor) const; + void AddStragglersToRenderer(const CStateManager& mgr) const; + rstl::list< CItem >::iterator FindSystem(TUniqueId uid); + rstl::list< CItem >::const_iterator FindSystem(TUniqueId uid) const; + rstl::list< CItem >::iterator FindOrCreateSystem(CActor& actor); + void SetupHook(TUniqueId uid) const; + uint PointGenerator(uint seed, const CVector3f* vertices, const CVector3f* normals, int count); + void RemoveRainSplashGenerator(CActor& actor); + void AddRainSplashGenerator(CActor& actor, CStateManager& mgr, int maxSplashes, int genRate, + float minZ); + void StopThermalHotParticles(CActor& actor); + void LightDudeOnFire(CActor& actor); + void StopElectric(CActor& actor); + void LoadAndStartElectric(CActor& actor); + void StartIce(CActor& actor, CStateManager& mgr); + void EnsureElectricLoaded(CActor& actor); + void EnsureFirePopLoaded(CActor& actor); + void EnsureIceBreakLoaded(CActor& actor); + void Update(float dt, CStateManager& mgr); private: - // TODO - uchar x0_pad[0xe8]; + friend class CItem; + + rstl::list< CItem > x0_items; + TToken< CGenDescription > x18_onFire; + TToken< CGenDescription > x20_ash; + TToken< CGenDescription > x28_iceBreak; + TToken< CGenDescription > x30_firePop; + TToken< CGenDescription > x38_icePop; + TToken< CElectricDescription > x40_electric; + TToken< CTexture > x48_ashy; + rstl::reserved_vector< Dependency, 6 > x50_dgrps; + bool xe4_loadingDeps; + bool xe5_justLoadedDeps; + bool xe6_loadedDeps; + + void UpdateLoad(); + void DecrementDependency(EDependency dep); + void IncrementDependency(int dep); + void LoadParticleDGRPs(); + Dependency GetParticleDGRPTokens(const rstl::string& name); + + rstl::auto_ptr< CElementGen > MakeOnFireGen(); + rstl::auto_ptr< CParticleElectric > MakeElectricGen(); + rstl::auto_ptr< CElementGen > MakeIcePopGen(); + rstl::auto_ptr< CElementGen > MakeFirePopGen(); + rstl::auto_ptr< CElementGen > MakeAshGen(); + rstl::auto_ptr< CElementGen > MakeIceGen(); }; +NESTED_CHECK_SIZEOF(CActorModelParticles, Dependency, 0x18) +NESTED_CHECK_SIZEOF(CActorModelParticles, CItem, 0x138) CHECK_SIZEOF(CActorModelParticles, 0xe8); #endif // _CACTORMODELPARTICLES diff --git a/include/MetroidPrime/CFrontEndUI.hpp b/include/MetroidPrime/CFrontEndUI.hpp index 4db00bafe..efcc30d07 100644 --- a/include/MetroidPrime/CFrontEndUI.hpp +++ b/include/MetroidPrime/CFrontEndUI.hpp @@ -3,7 +3,7 @@ #include "MetroidPrime/CIOWin.hpp" -class CFrontEndUI : public CIOWin { +class CFrontEndUI : public CIOWin { public: struct SFusionBonusFrame { struct SGBALinkFrame { @@ -24,7 +24,6 @@ class CFrontEndUI : public CIOWin { }; CFrontEndUI(); - EMessageReturn OnMessage(const CArchitectureMessage& message, CArchitectureQueue& queue); private: diff --git a/include/MetroidPrime/CRippleManager.hpp b/include/MetroidPrime/CRippleManager.hpp index f508415d8..a9d45b233 100644 --- a/include/MetroidPrime/CRippleManager.hpp +++ b/include/MetroidPrime/CRippleManager.hpp @@ -1,12 +1,11 @@ #ifndef _CRIPPLEMANAGER #define _CRIPPLEMANAGER +#include "MetroidPrime/CRipple.hpp" #include "MetroidPrime/TGameTypes.hpp" #include "rstl/vector.hpp" -class CRipple; - class CRippleManager { private: float x0_maxTimeFalloff; @@ -16,8 +15,8 @@ class CRippleManager { public: CRippleManager(int maxRipples, float alpha); CRippleManager(const CRippleManager& other); - ~CRippleManager(); - + ~CRippleManager() {} + void Init(int maxRipples); void Update(float dt); float GetLastRippleDeltaTime(TUniqueId rippler) const; @@ -26,9 +25,9 @@ class CRippleManager { void SetMaxTimeFalloff(float time) { x0_maxTimeFalloff = time; } float GetMaxTimeFalloff() const { return x0_maxTimeFalloff; } - rstl::vector& Ripples() { return x4_ripples; } - const rstl::vector& GetRipples() const { return x4_ripples; } - + rstl::vector< CRipple >& Ripples() { return x4_ripples; } + const rstl::vector< CRipple >& GetRipples() const { return x4_ripples; } + void SetAlpha(float a) { x14_alpha = a; } float GetAlpha() const { return x14_alpha; } }; diff --git a/include/MetroidPrime/Enemies/CPatterned.hpp b/include/MetroidPrime/Enemies/CPatterned.hpp index d9934dd9e..73fe205ee 100644 --- a/include/MetroidPrime/Enemies/CPatterned.hpp +++ b/include/MetroidPrime/Enemies/CPatterned.hpp @@ -252,6 +252,7 @@ class CPatterned : public CAi { float GetAverageAttackTime() const { return x304_averageAttackTime; } float GetAttackTimeVariation() const { return x308_attackTimeVariation; } const bool GetVerticalMovement() const { return x328_25_verticalMovement; } + const bool IsInCollision() const { return x328_26_solidCollision; } void SetVerticalMovement(const bool v) { x328_25_verticalMovement = v; } EAnimState GetAnimationState() const { return x32c_animState; } void SetAnimationState(const EAnimState state) { x32c_animState = state; } diff --git a/include/rstl/map.hpp b/include/rstl/map.hpp index 49fa8ad60..d1593ab66 100644 --- a/include/rstl/map.hpp +++ b/include/rstl/map.hpp @@ -33,7 +33,6 @@ class map { void erase(iterator it) { inner.erase(it); } - rep_type& get_inner() { return inner; } // hack for CWeaponMgr inlining depth private: rep_type inner; }; diff --git a/include/rstl/red_black_tree.hpp b/include/rstl/red_black_tree.hpp index d19b0ee49..7bc76ebf7 100644 --- a/include/rstl/red_black_tree.hpp +++ b/include/rstl/red_black_tree.hpp @@ -208,15 +208,16 @@ class red_black_tree { return n; } - void free_node_and_sub_nodes(node* n) { - if (node* left = n->get_left()) { - free_node_and_sub_nodes(left); - } - if (node* right = n->get_right()) { - free_node_and_sub_nodes(right); - } - free_node(n); - } + void free_node_and_sub_nodes(node* n); + // void free_node_and_sub_nodes(node* n) { + // if (node* left = n->get_left()) { + // free_node_and_sub_nodes(left); + // } + // if (node* right = n->get_right()) { + // free_node_and_sub_nodes(right); + // } + // free_node(n); + // } void free_node(node* n) { n->~node(); @@ -281,6 +282,17 @@ red_black_tree< T, P, U, S, Cmp, Alloc >::insert_into(node* n, const P& item) { } } +template < typename T, typename P, int U, typename S, typename Cmp, typename Alloc > +void red_black_tree< T, P, U, S, Cmp, Alloc >::free_node_and_sub_nodes(node* n) { + if (node* left = n->get_left()) { + free_node_and_sub_nodes(left); + } + if (node* right = n->get_right()) { + free_node_and_sub_nodes(right); + } + free_node(n); +} + }; // namespace rstl #endif // _RSTL_RED_BLACK_TREE diff --git a/include/rstl/set.hpp b/include/rstl/set.hpp index 904aa99e8..bf5fe3e79 100644 --- a/include/rstl/set.hpp +++ b/include/rstl/set.hpp @@ -3,7 +3,6 @@ #include "types.h" -#include "rstl/pair.hpp" #include "rstl/red_black_tree.hpp" #include "rstl/rmemory_allocator.hpp" diff --git a/libc/limits.h b/libc/limits.h index c595fda07..08749e40b 100644 --- a/libc/limits.h +++ b/libc/limits.h @@ -4,13 +4,18 @@ #define SCHAR_MAX 0x7f #define UCHAR_MAX 0xffU -#if defined(__MWERKS__) && __option(unsigned_char) +#if defined(__MWERKS__) +#if __option(unsigned_char) #define CHAR_MIN 0U #define CHAR_MAX UCHAR_MAX #else #define CHAR_MIN SCHAR_MIN #define CHAR_MAX SCHAR_MAX #endif +#else +#define CHAR_MIN SCHAR_MIN +#define CHAR_MAX SCHAR_MAX +#endif #define SHRT_MAX 0x7fff #define SHRT_MIN (~SHRT_MAX) diff --git a/src/Kyoto/Math/CTransform4f.cpp b/src/Kyoto/Math/CTransform4f.cpp index 9c38886ed..431695022 100644 --- a/src/Kyoto/Math/CTransform4f.cpp +++ b/src/Kyoto/Math/CTransform4f.cpp @@ -509,4 +509,4 @@ CVector3f CTransform4f::TransposeRotate(register const CVector3f& in) const{ } return *ret; #endif -} \ No newline at end of file +} diff --git a/src/MetroidPrime/BodyState/CBSHurled.cpp b/src/MetroidPrime/BodyState/CBSHurled.cpp new file mode 100644 index 000000000..6eac01f05 --- /dev/null +++ b/src/MetroidPrime/BodyState/CBSHurled.cpp @@ -0,0 +1,291 @@ +#include "MetroidPrime/BodyState/CBSHurled.hpp" + +#include "Kyoto/Animation/CPASAnimParmData.hpp" +#include "Kyoto/Animation/CPASDatabase.hpp" +#include "Kyoto/Math/CMath.hpp" +#include "Kyoto/Math/CRelAngle.hpp" +#include "Kyoto/Math/CloseEnough.hpp" +#include "MetroidPrime/BodyState/CBodyController.hpp" +#include "MetroidPrime/CActor.hpp" +#include "MetroidPrime/CAnimPlaybackParms.hpp" +#include "MetroidPrime/CPhysicsActor.hpp" +#include "MetroidPrime/CStateManager.hpp" +#include "MetroidPrime/Enemies/CPatterned.hpp" +#include "MetroidPrime/TCastTo.hpp" + +#include "math.h" +#include "rstl/math.hpp" + +CBSHurled::CBSHurled() +: x4_state(pas::kHS_Invalid) +, x8_knockAngle(0.f) +, xc_animSeries(-1) +, x10_rotateSpeed(0.f) +, x14_remTime(0.f) +, x18_curTime(0.f) +, x1c_lastTranslation(CVector3f::Zero()) +, x28_landedDur(0.f) +, x2c_24_needsRecover(false) {} + +bool CBSHurled::IsInAir(const CBodyController&) const { return true; } + +void CBSHurled::Start(CBodyController& bc, CStateManager& mgr) { + const CBCHurledCmd* cmd = static_cast< const CBCHurledCmd* >(bc.CommandMgr().GetCmd(kBSC_Hurled)); + x4_state = pas::EHurledState(cmd->GetSkipLaunchState() != false); + + CActor* owner = &bc.GetOwner(); + + const CVector3f localDir = owner->GetTransform().TransposeRotate(cmd->GetHitDirection()); + float angle = CMath::ClampRadians(atan2(localDir.GetY(), localDir.GetX())); + x8_knockAngle = CMath::Rad2Deg(angle); + + const CPASDatabase& db = bc.GetPASDatabase(); + + const CPASAnimParmData parms(pas::kAS_Hurled, CPASAnimParm::FromInt32(-1), + CPASAnimParm::FromReal32(x8_knockAngle), + CPASAnimParm::FromEnum(x4_state)); + const rstl::pair< float, int > best = db.FindBestAnimation(parms, *mgr.Random(), -1); + + const CAnimPlaybackParms playParms(best.second, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, false, false); + + const CPASAnimState* hurledState = db.GetAnimState(pas::kAS_Hurled); + CPASAnimParm parm0(hurledState->GetAnimParmData(best.second, 0)); + xc_animSeries = parm0.GetInt32Value(); + + mgr.DeliverScriptMsg(owner, kInvalidUniqueId, kSM_Falling); + mgr.DeliverScriptMsg(owner, kInvalidUniqueId, kSM_Jumped); + + if (!close_enough(cmd->GetLaunchVelocity(), CVector3f::Zero(), 0.0001f)) { + CPhysicsActor* actor = TCastToPtr< CPhysicsActor >(*owner); + if (actor != NULL) { + actor->SetConstantForceWR(actor->GetMass() * cmd->GetLaunchVelocity()); + } + } + + CPASAnimParm parm1(hurledState->GetAnimParmData(best.second, 1)); + const float animAngle = (M_PIF / 180.f) * parm1.GetReal32Value(); + const float delta1 = CMath::ClampRadians(angle - animAngle); + const float delta2 = CMath::ClampRadians(animAngle - angle); + const float minAngle = rstl::min_val(delta1, delta2); + const float flippedAngle = + (CMath::ClampRadians(angle - animAngle) > M_PIF) ? -minAngle : minAngle; + x14_remTime = 0.15f * bc.GetAnimTimeRemaining(); + x10_rotateSpeed = (x14_remTime > FLT_EPSILON) ? flippedAngle / x14_remTime : flippedAngle; + x18_curTime = 0.f; + x2c_24_needsRecover = false; +} + +pas::EAnimationState CBSHurled::UpdateBody(float dt, CBodyController& bc, CStateManager& mgr) { + pas::EAnimationState state = GetBodyStateTransition(dt, bc); + if (state == pas::kAS_Invalid) { + x18_curTime += dt; + + if (x14_remTime > 0.f) { + const CRelAngle angle(x10_rotateSpeed * dt); + bc.SetDeltaRotation(CQuaternion::ZRotation(angle)); + x14_remTime -= dt; + } + + if (bc.CommandMgr().GetCmd(kBSC_ExitState) != NULL) { + x2c_24_needsRecover = true; + } + + switch (x4_state) { + case pas::kHS_KnockIntoAir: { + if (bc.IsAnimationOver()) { + const CPASAnimParmData parms(pas::kAS_Hurled, CPASAnimParm::FromInt32(xc_animSeries), + CPASAnimParm::FromReal32(x8_knockAngle), + CPASAnimParm::FromEnum(1)); + bc.LoopBestAnimation(parms, *mgr.Random()); + x4_state = pas::kHS_KnockLoop; + x28_landedDur = 0.f; + } + break; + } + case pas::kHS_KnockLoop: + if (ShouldStartLand(dt, bc)) { + x4_state = pas::kHS_KnockDown; + PlayLandAnimation(bc, mgr); + } else if (ShouldStartStrikeWall(bc)) { + PlayStrikeWallAnimation(bc, mgr); + + CPatterned* actor = TCastToPtr< CPatterned >(&bc.GetOwner()); + if (actor != NULL) { + actor->SetVelocityWR((2.f * dt * actor->GetGravityConstant()) * CVector3f::Down()); + } + } else if (x2c_24_needsRecover) { + Recover(mgr, bc, pas::kHS_Six); + } + break; + case pas::kHS_StrikeWall: + if (bc.IsAnimationOver()) { + x4_state = pas::kHS_StrikeWallFallLoop; + + const CPASAnimParmData parms(pas::kAS_Hurled, CPASAnimParm::FromInt32(xc_animSeries), + CPASAnimParm::FromReal32(x8_knockAngle), + CPASAnimParm::FromEnum(x4_state)); + bc.LoopBestAnimation(parms, *mgr.Random()); + x28_landedDur = 0.f; + } + break; + case pas::kHS_StrikeWallFallLoop: + if (ShouldStartLand(dt, bc)) { + x4_state = pas::kHS_OutOfStrikeWall; + PlayLandAnimation(bc, mgr); + } else if (x2c_24_needsRecover) { + Recover(mgr, bc, pas::kHS_Seven); + } + break; + case pas::kHS_Six: + case pas::kHS_Seven: { + CPhysicsActor* actor = TCastToPtr< CPhysicsActor >(&bc.GetOwner()); + if (actor != NULL) { + actor->SetVelocityWR(actor->GetVelocityWR() * pow(0.9f, 60.f * dt)); + } + + if (bc.IsAnimationOver()) { + x4_state = pas::kHS_Invalid; + state = pas::kAS_Locomotion; + } + + break; + } + case pas::kHS_KnockDown: + case pas::kHS_OutOfStrikeWall: + if (bc.IsAnimationOver()) { + x4_state = pas::kHS_Invalid; + if (bc.GetFallState() == pas::kFS_Zero) { + state = pas::kAS_Locomotion; + } else { + state = pas::kAS_LieOnGround; + } + } + break; + default: + break; + } + } + + return state; +} + +void CBSHurled::Shutdown(CBodyController&) {} + +bool CBSHurled::ShouldStartLand(float dt, CBodyController& bc) const { + bool shouldStartLand = true; + CPatterned* actor = TCastToPtr< CPatterned >(&bc.GetOwner()); + if (actor != NULL) { + shouldStartLand = false; + if (actor->IsOnGround()) { + shouldStartLand = true; + } else { + CVector3f translation = actor->GetTranslation(); + const bool notCloseEnough = !close_enough(translation, x1c_lastTranslation, 0.0001f); + if (notCloseEnough) { + goto resetLandedDur; + } + + if (actor->GetVelocityWR().GetZ() < 0.f) { + x28_landedDur += dt; + if (x28_landedDur >= 0.25f) { + shouldStartLand = true; + } + } else { + resetLandedDur: + x28_landedDur = 0.f; + } + + x1c_lastTranslation = actor->GetTranslation(); + } + } + + return shouldStartLand; +} + +bool CBSHurled::ShouldStartStrikeWall(CBodyController& bc) const { + bool shouldStartStrikeWall = false; + CPatterned* patterned = TCastToPtr< CPatterned >(&bc.GetOwner()); + + if (patterned->IsInCollision() && !patterned->IsOnGround()) { + shouldStartStrikeWall = true; + } + + return shouldStartStrikeWall; +} + +void CBSHurled::PlayLandAnimation(CBodyController& bc, CStateManager& mgr) { + const CPASDatabase& db = bc.GetPASDatabase(); + const CPASAnimParmData parms(pas::kAS_Hurled, CPASAnimParm::FromInt32(xc_animSeries), + CPASAnimParm::FromReal32(x8_knockAngle), + CPASAnimParm::FromEnum(x4_state)); + const rstl::pair< float, int > best = db.FindBestAnimation(parms, *mgr.Random(), -1); + const CAnimPlaybackParms playParms(best.second, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, false, false); + + const CPASAnimState* hurledState = db.GetAnimState(pas::kAS_Hurled); + CPASAnimParm parm3(hurledState->GetAnimParmData(best.second, 3)); + bc.SetFallState(static_cast< pas::EFallState >(parm3.GetEnumValue())); + + CPhysicsActor* actor = TCastToPtr< CPhysicsActor >(&bc.GetOwner()); + if (actor != NULL) { + mgr.DeliverScriptMsg(actor, kInvalidUniqueId, kSM_OnFloor); + } +} + +void CBSHurled::PlayStrikeWallAnimation(CBodyController& bc, CStateManager& mgr) { + const CPASDatabase& db = bc.GetPASDatabase(); + const CPASAnimParmData parms(pas::kAS_Hurled, CPASAnimParm::FromInt32(xc_animSeries), + CPASAnimParm::FromReal32(x8_knockAngle), CPASAnimParm::FromEnum(3)); + const rstl::pair< float, int > best = db.FindBestAnimation(parms, *mgr.Random(), -1); + + if (best.first > FLT_EPSILON) { + const CAnimPlaybackParms playParms(best.second, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, false, false); + x4_state = pas::kHS_StrikeWall; + } +} + +void CBSHurled::Recover(CStateManager& mgr, CBodyController& bc, pas::EHurledState state) { + const CPASDatabase& db = bc.GetPASDatabase(); + const CPASAnimParmData parms(pas::kAS_Hurled, CPASAnimParm::FromInt32(xc_animSeries), + CPASAnimParm::FromReal32(x8_knockAngle), + CPASAnimParm::FromEnum(state)); + const rstl::pair< float, int > best = db.FindBestAnimation(parms, *mgr.Random(), -1); + + if (best.first > FLT_EPSILON) { + const CAnimPlaybackParms playParms(best.second, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, false, false); + x4_state = state; + + CPhysicsActor* actor = TCastToPtr< CPhysicsActor >(&bc.GetOwner()); + if (actor != NULL) { + actor->SetMomentumWR(CVector3f::Zero()); + } + } + + x2c_24_needsRecover = false; +} + +pas::EAnimationState CBSHurled::GetBodyStateTransition(float, CBodyController& bc) { + CBodyStateCmdMgr* cmdMgr = &bc.CommandMgr(); + if (cmdMgr->GetCmd(kBSC_NextState) != NULL) { + return pas::kAS_LieOnGround; + } + + if (x18_curTime > 0.25f) { + const CBodyStateCmd* hurledCmd = + static_cast< const CBodyStateCmdMgr* >(cmdMgr)->GetCmd(kBSC_Hurled); + if (hurledCmd != NULL) { + static_cast< CBCHurledCmd* >(const_cast< CBodyStateCmd* >(hurledCmd)) + ->SetSkipLaunchState(true); + return pas::kAS_Hurled; + } + } + + return pas::kAS_Invalid; +} + +bool CBSHurled::ApplyHeadTracking() const { return false; } + +bool CBSHurled::IsMoving() const { return true; } diff --git a/src/MetroidPrime/BodyState/CBSJump.cpp b/src/MetroidPrime/BodyState/CBSJump.cpp new file mode 100644 index 000000000..f6602f9f0 --- /dev/null +++ b/src/MetroidPrime/BodyState/CBSJump.cpp @@ -0,0 +1,245 @@ +#include "MetroidPrime/BodyState/CBSJump.hpp" + +#include "Kyoto/Animation/CPASAnimParmData.hpp" +#include "Kyoto/Animation/CPASDatabase.hpp" +#include "Kyoto/Math/CMath.hpp" + +#include "Kyoto/Math/CVector3f.hpp" +#include "MetroidPrime/BodyState/CBodyController.hpp" +#include "MetroidPrime/CActor.hpp" +#include "MetroidPrime/CAnimPlaybackParms.hpp" +#include "MetroidPrime/CPhysicsActor.hpp" +#include "MetroidPrime/CStateManager.hpp" +#include "MetroidPrime/Enemies/CPatterned.hpp" +#include "MetroidPrime/TCastTo.hpp" + +CBSJump::CBSJump() +: x4_state(pas::kJS_Invalid) +, xc_waypoint1(CVector3f::Zero()) +, x18_velocity(CVector3f::Zero()) +, x24_waypoint2(CVector3f::Zero()) +, x30_24_applyLaunchVel(false) +, x30_25_wallJump(false) +, x30_26_wallBounceRight(false) +, x30_27_hasWallBounced(false) +, x30_28_startInJumpLoop(false) {} + +bool CBSJump::IsInAir(const CBodyController& bc) const { + return x4_state == pas::kJS_AmbushJump || x4_state == pas::kJS_Loop; +} + +bool CBSJump::ApplyAnimationDeltas() const { + return x4_state != pas::kJS_AmbushJump && x4_state != pas::kJS_Loop; +} + +bool CBSJump::CanShoot() const { + return x4_state == pas::kJS_AmbushJump || x4_state == pas::kJS_Loop; +} + +void CBSJump::Start(CBodyController& bc, CStateManager& mgr) { + const CBCJumpCmd* cmd = static_cast< const CBCJumpCmd* >(bc.CommandMgr().GetCmd(kBSC_Jump)); + x8_jumpType = cmd->GetJumpType(); + xc_waypoint1 = cmd->GetJumpTarget(); + x24_waypoint2 = cmd->GetSecondJumpTarget(); + x30_25_wallJump = cmd->IsWallJump(); + x30_28_startInJumpLoop = cmd->StartInJumpLoop(); + x30_24_applyLaunchVel = false; + x30_27_hasWallBounced = false; + + if (x30_25_wallJump) { + const CVector3f toWall = xc_waypoint1 - bc.GetOwner().GetTranslation(); + const CVector3f toFinal = x24_waypoint2 - xc_waypoint1; + const CVector3f cross = CVector3f::Cross(toWall, CVector3f::Up()); + x30_26_wallBounceRight = CVector3f::Dot(cross, toFinal) < 0.f; + } + + if (!cmd->StartInJumpLoop()) { + x4_state = pas::kJS_IntoJump; + CPASAnimParmData parms(pas::kAS_Jump, CPASAnimParm::FromEnum(x4_state), + CPASAnimParm::FromEnum(x8_jumpType)); + bc.PlayBestAnimation(parms, *mgr.Random()); + } else { + PlayJumpLoop(mgr, bc); + } +} + +void CBSJump::PlayJumpLoop(CStateManager& mgr, CBodyController& bc) { + CPASAnimParmData ambushParms(pas::kAS_Jump, CPASAnimParm::FromEnum(1), + CPASAnimParm::FromEnum(x8_jumpType)); + rstl::pair< float, int > best = + bc.GetPASDatabase().FindBestAnimation(ambushParms, *mgr.Random(), -1); + + if (best.first > FLT_EPSILON) { + x4_state = pas::kJS_AmbushJump; + const CAnimPlaybackParms playParms(best.second, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, false, false); + } else { + x4_state = pas::kJS_Loop; + CPASAnimParmData loopParms(pas::kAS_Jump, CPASAnimParm::FromEnum(x4_state), + CPASAnimParm::FromEnum(x8_jumpType)); + bc.LoopBestAnimation(loopParms, *mgr.Random()); + } + + if (CPhysicsActor* actor = TCastToPtr< CPhysicsActor >(&bc.GetOwner())) { + mgr.DeliverScriptMsg(actor, kInvalidUniqueId, kSM_Falling); + mgr.DeliverScriptMsg(actor, kInvalidUniqueId, kSM_Jumped); + CVector3f vel = actor->GetVelocityWR(); + x30_24_applyLaunchVel = false; + x18_velocity = vel; + } +} + +pas::EAnimationState CBSJump::UpdateBody(float dt, CBodyController& bc, CStateManager& mgr) { + pas::EAnimationState state = GetBodyStateTransition(dt, bc); + if (state == pas::kAS_Invalid) { + switch (x4_state) { + case pas::kJS_IntoJump: + if (bc.IsAnimationOver()) { + PlayJumpLoop(mgr, bc); + } + break; + case pas::kJS_AmbushJump: { + if (!x30_24_applyLaunchVel) { + CPhysicsActor* actor = TCastToPtr< CPhysicsActor >(&bc.GetOwner()); + if (actor != nullptr) { + actor->SetConstantForceWR(actor->GetMass() * x18_velocity); + } + x30_24_applyLaunchVel = true; + } + + CBodyStateCmdMgr& cmdMgr = bc.CommandMgr(); + if (cmdMgr.GetTargetVector().IsNonZero()) { + bc.FaceDirection(cmdMgr.GetTargetVector(), dt); + } + + if (bc.IsAnimationOver()) { + x4_state = pas::kJS_Loop; + CPASAnimParmData parms( + pas::kAS_Jump, CPASAnimParm::FromEnum(x4_state), CPASAnimParm::FromEnum(x8_jumpType), + CPASAnimParm::NoParameter(), CPASAnimParm::NoParameter(), CPASAnimParm::NoParameter(), + CPASAnimParm::NoParameter(), CPASAnimParm::NoParameter(), CPASAnimParm::NoParameter()); + bc.LoopBestAnimation(parms, *mgr.Random()); + } else if (!CheckForWallJump(bc, mgr)) { + CheckForLand(bc, mgr); + } + break; + } + case pas::kJS_Loop: { + if (!x30_24_applyLaunchVel) { + CPhysicsActor* actor = TCastToPtr< CPhysicsActor >(&bc.GetOwner()); + if (actor != nullptr) { + actor->SetConstantForceWR(actor->GetMass() * x18_velocity); + } + x30_24_applyLaunchVel = true; + } + + CBodyStateCmdMgr& cmdMgr = bc.CommandMgr(); + if (cmdMgr.GetTargetVector().IsNonZero()) { + bc.FaceDirection(cmdMgr.GetTargetVector(), dt); + } + + if (!CheckForWallJump(bc, mgr)) { + CheckForLand(bc, mgr); + } + break; + } + case pas::kJS_WallBounceLeft: + case pas::kJS_WallBounceRight: + if (CPhysicsActor* actor = TCastToPtr< CPhysicsActor >(&bc.GetOwner())) { + actor->Stop(); + actor->SetMomentumWR(CVector3f::Zero()); + } + + if (bc.IsAnimationOver()) { + mgr.DeliverScriptMsg(&bc.GetOwner(), kInvalidUniqueId, kSM_Falling); + x4_state = pas::kJS_Loop; + + CPASAnimParmData parms( + pas::kAS_Jump, CPASAnimParm::FromEnum(x4_state), CPASAnimParm::FromEnum(x8_jumpType), + CPASAnimParm::NoParameter(), CPASAnimParm::NoParameter(), CPASAnimParm::NoParameter(), + CPASAnimParm::NoParameter(), CPASAnimParm::NoParameter(), CPASAnimParm::NoParameter()); + bc.LoopBestAnimation(parms, *mgr.Random()); + x30_27_hasWallBounced = true; + + if (CPatterned* actor = TCastToPtr< CPatterned >(&bc.GetOwner())) { + const CVector3f d = x24_waypoint2 - actor->GetTranslation(); + const float factor = CMath::SqrtF(actor->GetGravityConstant() / (2.f * d.GetZ())); + actor->SetVelocityWR(CVector3f(factor * d.GetX(), factor * d.GetY(), 0.f)); + } + } + break; + case pas::kJS_OutOfJump: + if (bc.IsAnimationOver()) { + x4_state = pas::kJS_Invalid; + state = pas::kAS_Locomotion; + } + break; + default: + break; + } + } + + return state; +} + +void CBSJump::Shutdown(CBodyController& bc) {} + +void CBSJump::CheckForLand(CBodyController& bc, CStateManager& mgr) { + if (CPatterned* patterned = TCastToPtr< CPatterned >(&bc.GetOwner())) { + if (patterned->IsInCollision() || patterned->IsOnGround()) { + x4_state = pas::kJS_OutOfJump; + CPASAnimParmData parms(pas::kAS_Jump, CPASAnimParm::FromEnum(x4_state), + CPASAnimParm::FromEnum(x8_jumpType)); + bc.PlayBestAnimation(parms, *mgr.Random()); + mgr.DeliverScriptMsg(patterned, kInvalidUniqueId, kSM_OnFloor); + } + } +} + +uchar CBSJump::CheckForWallJump(CBodyController& bc, CStateManager& mgr) { + int ret = false; + if (x30_25_wallJump && !x30_27_hasWallBounced) { + if (CPatterned* patterned = TCastToPtr< CPatterned >(&bc.GetOwner())) { + const float distToWall = (xc_waypoint1 - patterned->GetTranslation()).Magnitude(); + const float xExtent = 0.5f * patterned->GetBoundingBox().GetWidth(); + + if (distToWall < 1.414f * xExtent || + (patterned->IsInCollision() && distToWall < 3.f * xExtent)) { + pas::EJumpState state = pas::kJS_WallBounceLeft; + if (x30_26_wallBounceRight) { + state = pas::kJS_WallBounceRight; + } + x4_state = state; + + CPASAnimParmData parms( + pas::kAS_Jump, CPASAnimParm::FromEnum(x4_state), CPASAnimParm::FromEnum(x8_jumpType), + CPASAnimParm::NoParameter(), CPASAnimParm::NoParameter(), CPASAnimParm::NoParameter(), + CPASAnimParm::NoParameter(), CPASAnimParm::NoParameter(), CPASAnimParm::NoParameter()); + bc.PlayBestAnimation(parms, *mgr.Random()); + mgr.DeliverScriptMsg(patterned, kInvalidUniqueId, kSM_OnFloor); + ret = true; + } + } + } + return ret; +} + +pas::EAnimationState CBSJump::GetBodyStateTransition(float dt, CBodyController& bc) { + CBodyStateCmdMgr& cmdMgr = bc.CommandMgr(); + if (const CBodyStateCmd* hurledCmd = bc.GetCommandMgr().GetCmd(kBSC_Hurled)) { + CBCHurledCmd* hurled = static_cast< CBCHurledCmd* >(const_cast< CBodyStateCmd* >(hurledCmd)); + hurled->SetSkipLaunchState(true); + return pas::kAS_Hurled; + } + if (cmdMgr.GetCmd(kBSC_KnockDown) != nullptr) { + return pas::kAS_Fall; + } + if (cmdMgr.GetCmd(kBSC_Jump) != nullptr && bc.GetBodyType() == kBT_WallWalker) { + return pas::kAS_Jump; + } + return pas::kAS_Invalid; +} + +bool CBSJump::ApplyHeadTracking() const { return false; } + +bool CBSJump::IsMoving() const { return true; } diff --git a/src/MetroidPrime/BodyState/CBSLocomotion.cpp b/src/MetroidPrime/BodyState/CBSLocomotion.cpp new file mode 100644 index 000000000..ddf82ff30 --- /dev/null +++ b/src/MetroidPrime/BodyState/CBSLocomotion.cpp @@ -0,0 +1,535 @@ +#include "MetroidPrime/BodyState/CBSLocomotion.hpp" + +#include "Kyoto/Math/CVector3f.hpp" +#include "MetroidPrime/BodyState/CBodyController.hpp" +#include "MetroidPrime/BodyState/CBodyStateCmdMgr.hpp" +#include "MetroidPrime/CAnimPlaybackParms.hpp" +#include "MetroidPrime/CPhysicsActor.hpp" +#include "MetroidPrime/TCastTo.hpp" + +#include "Kyoto/Animation/CPASAnimParmData.hpp" +#include "Kyoto/Math/CRelAngle.hpp" +#include "Kyoto/Math/CloseEnough.hpp" + +#include "rstl/math.hpp" + +static float skMaxPitchAngle = CRelAngle::FromDegrees(10.f).AsRadians(); + +const float CBSBiPedLocomotion::skMinWalkPercent = 0.5f; +static int sCBSLocomotion_DefaultAnimId = 0; +static int skInvalidAnimId = -1; + +bool CBSFlyerLocomotion::IsPitchable() const { return x3cc_pitchable; } + +bool CBSFlyerLocomotion::IsBackPedal(CBodyController& bc) const { + (void)bc; + return false; +} + +CBSFlyerLocomotion::~CBSFlyerLocomotion() {} + +CBSWallWalkerLocomotion::~CBSWallWalkerLocomotion() {} + +bool CBSBiPedLocomotion::IsMoving() const { return x3c4_anim != pas::kLA_Idle; } + +CBSAiMovedFlyerLocomotion::~CBSAiMovedFlyerLocomotion() {} + +CBSLocomotion::CBSLocomotion() : x4_locomotionType(pas::kLT_Invalid) {} + +void CBSLocomotion::Start(CBodyController& bc, CStateManager& mgr) { + x4_locomotionType = bc.GetLocomotionType(); + + if (bc.CommandMgr().GetCmd(kBSC_MaintainVelocity)) { + ReStartBodyState(bc, true); + } else { + ReStartBodyState(bc, false); + } + + (void)mgr; +} + +pas::EAnimationState CBSLocomotion::UpdateBody(float dt, CBodyController& bc, CStateManager& mgr) { + const pas::EAnimationState st = GetBodyStateTransition(dt, bc); + if (st == pas::kAS_Invalid) { + const float velMag = ApplyLocomotionPhysics(dt, bc); + UpdateLocomotionAnimation(dt, velMag, bc, false); + } + + (void)mgr; + return st; +} + +void CBSLocomotion::Shutdown(CBodyController& bc) { bc.MultiplyPlaybackRate(1.f); } + +float CBSLocomotion::ApplyLocomotionPhysics(float dt, CBodyController& bc) { + if (const CPhysicsActor* act = TCastToConstPtr< CPhysicsActor >(&bc.GetOwner())) { + const CBodyStateCmdMgr& cmdMgr = bc.GetCommandMgr(); + const CVector3f& moveVec = cmdMgr.GetMoveVector(); + const CVector3f& faceVec = cmdMgr.GetFaceVector(); + const CVector3f vec = + close_enough(faceVec, CVector3f::Zero(), vector3_epsilon()) ? moveVec : faceVec; + + if (vec.CanBeNormalized()) { + if (IsPitchable()) { + CVector3f lookForward = act->GetTransform().GetForward(); + CVector3f lookVec = lookForward; + lookVec[kDZ] = 0.f; + lookVec.Normalize(); + + CVector3f tmp = vec; + tmp[kDZ] = 0.f; + bc.FaceDirection3D(tmp, lookVec, dt); + + CVector3f lookVec2 = lookForward; + lookVec2[kDZ] = vec.GetZ(); + lookVec2.Normalize(); + if (!close_enough(lookVec, lookVec2, vector3_epsilon())) { + CRelAngle pitchAngle(rstl::min_val(CVector3f::GetAngleDiff(vec, tmp), + bc.GetBodyStateInfo().GetMaximumPitch())); + lookVec2 = CVector3f::Slerp(lookVec, lookVec2, pitchAngle); + } + + bc.FaceDirection3D(lookVec2, lookForward, dt); + + CVector3f right = act->GetTransform().GetRight(); + CVector3f lookVec3 = right; + lookVec3[kDZ] = 0.f; + bc.FaceDirection3D(lookVec3, right, dt); + } else { + bc.FaceDirection(vec.AsNormalized(), dt); + } + } + + return rstl::min_val(moveVec.Magnitude(), 1.f); + } + + return 0.f; +} + +void CBSLocomotion::ReStartBodyState(CBodyController& bc, bool maintainVel) { + UpdateLocomotionAnimation(0.f, maintainVel ? GetStartVelocityMagnitude(bc) : 0.f, bc, true); +} + +float CBSLocomotion::GetStartVelocityMagnitude(CBodyController& bc) const { + if (const CPhysicsActor* act = TCastToConstPtr< CPhysicsActor >(&bc.GetOwner())) { + const float velocityMag = act->GetVelocityWR().Magnitude(); + const float maxSpeed = bc.GetBodyStateInfo().GetMaxSpeed(); + float ret = maxSpeed > 0.f ? velocityMag / maxSpeed : 0.f; + ret = rstl::min_val(ret, 1.f); + return ret; + } + return 0.f; +} + +float CBSLocomotion::ComputeWeightPercentage(const rstl::pair< int, float >& a, + const rstl::pair< int, float >& b, float f) const { + const float range = b.second - a.second; + if (range > FLT_EPSILON) { + return rstl::max_val(rstl::min_val((f - a.second) / range, 1.f), 0.f); + } + return 0.f; +} + +pas::EAnimationState CBSLocomotion::GetBodyStateTransition(float dt, CBodyController& bc) { + CBodyStateCmdMgr& cmdMgr = bc.CommandMgr(); + if (cmdMgr.GetCmd(kBSC_Hurled)) { + return pas::kAS_Hurled; + } + if (cmdMgr.GetCmd(kBSC_KnockDown)) { + return pas::kAS_Fall; + } + if (cmdMgr.GetCmd(kBSC_LoopHitReaction)) { + return pas::kAS_LoopReaction; + } + if (cmdMgr.GetCmd(kBSC_KnockBack)) { + return pas::kAS_KnockBack; + } + if (cmdMgr.GetCmd(kBSC_Locomotion)) { + cmdMgr.ClearLocomotionCmds(); + } else { + if (cmdMgr.GetCmd(kBSC_Slide)) { + return pas::kAS_Slide; + } + if (cmdMgr.GetCmd(kBSC_Generate)) { + return pas::kAS_Generate; + } + if (cmdMgr.GetCmd(kBSC_MeleeAttack)) { + return pas::kAS_MeleeAttack; + } + if (cmdMgr.GetCmd(kBSC_ProjectileAttack)) { + return pas::kAS_ProjectileAttack; + } + if (cmdMgr.GetCmd(kBSC_LoopAttack)) { + return pas::kAS_LoopAttack; + } + if (cmdMgr.GetCmd(kBSC_LoopReaction)) { + return pas::kAS_LoopReaction; + } + if (cmdMgr.GetCmd(kBSC_Jump)) { + return pas::kAS_Jump; + } + if (cmdMgr.GetCmd(kBSC_Taunt)) { + return pas::kAS_Taunt; + } + if (cmdMgr.GetCmd(kBSC_Step)) { + return pas::kAS_Step; + } + if (cmdMgr.GetCmd(kBSC_Cover)) { + return pas::kAS_Cover; + } + if (cmdMgr.GetCmd(kBSC_WallHang)) { + return pas::kAS_WallHang; + } + if (cmdMgr.GetCmd(kBSC_Scripted)) { + return pas::kAS_Scripted; + } + if (!cmdMgr.GetMoveVector().IsNonZero()) { + if (cmdMgr.GetFaceVector().IsNonZero()) { + if (!IsMoving()) { + return pas::kAS_Turn; + } + } + } + if (x4_locomotionType != bc.GetLocomotionType()) { + return pas::kAS_Locomotion; + } + } + + (void)dt; + return pas::kAS_Invalid; +} + +CBSBiPedLocomotion::CBSBiPedLocomotion(CActor& actor) +: x8_anims(rstl::reserved_vector< rstl::pair< int, float >, 8 >( + rstl::pair< int, float >(sCBSLocomotion_DefaultAnimId, 0.f))) +, x3c4_anim(pas::kLA_Invalid) { + const CPASDatabase& pasDatabase = actor.GetAnimationData()->GetCharacterInfo().GetPASDatabase(); + for (int i = 0; i < 14; ++i) { + for (int j = 0; j < 8; ++j) { + const CPASAnimParmData parms(pas::kAS_Locomotion, CPASAnimParm::FromEnum(j), + CPASAnimParm::FromEnum(i)); + rstl::pair< float, int > best = pasDatabase.FindBestAnimation(parms, -1); + float avgVel = 0.f; + if (best.second != -1) { + avgVel = actor.GetAverageAnimVelocity(best.second); + avgVel = j != 0 ? avgVel : 0.f; + } + x8_anims[i][j] = rstl::pair< int, float >(best.second, avgVel); + } + } +} + +float CBSBiPedLocomotion::GetLocomotionSpeed(pas::ELocomotionType type, + pas::ELocomotionAnim anim) const { + return GetLocoAnimation(type, anim).second; +} + +void CBSBiPedLocomotion::Start(CBodyController& bc, CStateManager& mgr) { + x3c8_primeTime = 0.f; + CBSLocomotion::Start(bc, mgr); +} + +pas::EAnimationState CBSBiPedLocomotion::UpdateBody(float dt, CBodyController& bc, + CStateManager& mgr) { + if (x3c8_primeTime < 0.2f) { + x3c8_primeTime += dt; + } + return CBSLocomotion::UpdateBody(dt, bc, mgr); +} + +float CBSBiPedLocomotion::UpdateLocomotionAnimation(float dt, float velMag, CBodyController& bc, + bool init) { + float ret = 1.f; + + if (init || x3c8_primeTime >= 0.2f) { + const pas::ELocomotionAnim anim = init ? pas::kLA_Invalid : x3c4_anim; + const float maxSpeed = velMag * GetLocomotionSpeed(x4_locomotionType, pas::kLA_Run); + if (IsStrafing(bc) && velMag >= 0.01f) { + ret = UpdateStrafe(velMag, bc, anim); + } else if (maxSpeed < 0.01f) { + if (anim != pas::kLA_Idle || init) { + if (!bc.GetBodyStateInfo().GetLocoAnimChangeAtEndOfAnimOnly() || + bc.GetAnimTimeRemaining() <= dt || init) { + const rstl::pair< int, float >& best = GetLocoAnimation(x4_locomotionType, pas::kLA_Idle); + if (bc.GetCurrentAnimId() != best.first) { + const CAnimPlaybackParms playParms(best.first, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, true, false); + x3c8_primeTime = 0.f; + } + x3c4_anim = pas::kLA_Idle; + } + } + } else { + const rstl::pair< int, float >& best = GetLocoAnimation(x4_locomotionType, pas::kLA_Walk); + if (maxSpeed < best.second) { + ret = UpdateWalk(maxSpeed, bc, anim); + } else { + ret = UpdateRun(maxSpeed, bc, anim); + } + } + } + + return ret; +} + +const rstl::pair< int, float >& +CBSBiPedLocomotion::GetLocoAnimation(pas::ELocomotionType type, pas::ELocomotionAnim anim) const { + return x8_anims[static_cast< int >(type)][static_cast< int >(anim)]; +} + +bool CBSBiPedLocomotion::IsStrafing(CBodyController& bc) const { + const CBodyStateCmdMgr& cmdMgr = bc.GetCommandMgr(); + const CVector3f& moveVec = cmdMgr.GetMoveVector(); + const CVector3f& faceVec = cmdMgr.GetFaceVector(); + return !close_enough(moveVec, CVector3f::Zero()) && !close_enough(faceVec, CVector3f::Zero()); +} + +float CBSBiPedLocomotion::UpdateStrafe(float vel, CBodyController& bc, pas::ELocomotionAnim anim) { + static pas::ELocomotionAnim strafes[6] = { + pas::kLA_StrafeRight, pas::kLA_StrafeLeft, pas::kLA_Walk, + pas::kLA_BackUp, pas::kLA_StrafeUp, pas::kLA_StrafeDown, + }; + + if (CPhysicsActor* act = TCastToPtr< CPhysicsActor >(&bc.GetOwner())) { + CVector3f localVec = bc.GetCommandMgr().GetMoveVector(); + localVec = act->TransformWorldToLocalRotation(localVec); + const CVector3f localVecSq = CVector3f::ByElementMultiply(localVec, localVec); + int maxComp = 0; + for (int i = 0; i < 3; ++i) { + if (localVecSq[i] >= localVecSq[maxComp]) { + maxComp = i; + } + } + + const int side = localVec[maxComp] > 0.f ? 0 : 1; + const int strafeKey = maxComp * 2 + side; + const pas::ELocomotionAnim strafeType = strafes[strafeKey]; + const float rate = vel * GetLocomotionSpeed(x4_locomotionType, strafeType); + if (anim != strafeType) { + const rstl::pair< int, float >& strafe = GetLocoAnimation(x4_locomotionType, strafeType); + if (bc.GetCurrentAnimId() != strafe.first) { + const CAnimPlaybackParms playParms(strafe.first, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, true, false); + x3c8_primeTime = 0.f; + } + x3c4_anim = strafeType; + } + + const rstl::pair< int, float >& idle = GetLocoAnimation(x4_locomotionType, pas::kLA_Idle); + const rstl::pair< int, float >& strafe = GetLocoAnimation(x4_locomotionType, strafeType); + const float perc = rstl::max_val(skMinWalkPercent, ComputeWeightPercentage(idle, strafe, rate)); + bc.MultiplyPlaybackRate(perc); + } + + return 1.f; +} + +float CBSBiPedLocomotion::UpdateWalk(float vel, CBodyController& bc, pas::ELocomotionAnim anim) { + if (anim != pas::kLA_Walk) { + const rstl::pair< int, float >& walk = GetLocoAnimation(x4_locomotionType, pas::kLA_Walk); + if (bc.GetCurrentAnimId() != walk.first) { + const CAnimPlaybackParms playParms(walk.first, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, true, false); + x3c8_primeTime = 0.f; + } + x3c4_anim = pas::kLA_Walk; + } + + const rstl::pair< int, float >& idle = GetLocoAnimation(x4_locomotionType, pas::kLA_Idle); + const rstl::pair< int, float >& walk = GetLocoAnimation(x4_locomotionType, pas::kLA_Walk); + const float perc = rstl::max_val(skMinWalkPercent, ComputeWeightPercentage(idle, walk, vel)); + bc.MultiplyPlaybackRate(perc); + return perc; +} + +float CBSBiPedLocomotion::UpdateRun(float vel, CBodyController& bc, pas::ELocomotionAnim anim) { + const rstl::pair< int, float >& walk = GetLocoAnimation(x4_locomotionType, pas::kLA_Walk); + const rstl::pair< int, float >& run = GetLocoAnimation(x4_locomotionType, pas::kLA_Run); + const float perc = ComputeWeightPercentage(walk, run, vel); + const int walkAnim = walk.first; + const int runAnim = run.first; + float rate; + + if (perc < 0.4f) { + rate = walk.second > 0.f ? vel / walk.second : 1.f; + if (anim != pas::kLA_Walk && bc.GetCurrentAnimId() != walkAnim) { + const CAnimPlaybackParms playParms(walkAnim, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, true, false); + x3c8_primeTime = 0.f; + } + bc.MultiplyPlaybackRate(rate); + x3c4_anim = pas::kLA_Walk; + } else { + rate = rstl::min_val(vel / run.second, 1.f); + if (anim != pas::kLA_Run && bc.GetCurrentAnimId() != runAnim) { + const CAnimPlaybackParms playParms(runAnim, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, true, false); + x3c8_primeTime = 0.f; + } + bc.MultiplyPlaybackRate(rate); + x3c4_anim = pas::kLA_Run; + } + + return rate; +} + +CBSRestrictedLocomotion::CBSRestrictedLocomotion(CActor& actor) +: x8_anims(skInvalidAnimId), x44_anim(pas::kLA_Invalid) { + const CPASDatabase& pasDatabase = actor.GetAnimationData()->GetCharacterInfo().GetPASDatabase(); + for (int i = 0; i < 14; ++i) { + CPASAnimParmData parms(pas::kAS_Locomotion, CPASAnimParm::FromEnum(0), + CPASAnimParm::FromEnum(i)); + rstl::pair< float, int > best = pasDatabase.FindBestAnimation(parms, -1); + x8_anims[i] = best.second; + } +} + +float CBSRestrictedLocomotion::UpdateLocomotionAnimation(float dt, float velMag, + CBodyController& bc, bool init) { + const pas::ELocomotionAnim anim = init ? pas::kLA_Invalid : x44_anim; + if (anim != pas::kLA_Idle) { + const int newAnim = x8_anims[static_cast< int >(x4_locomotionType)]; + if (newAnim != bc.GetCurrentAnimId()) { + const CAnimPlaybackParms playParms(newAnim, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, true, false); + } + x44_anim = pas::kLA_Idle; + } + + (void)dt; + (void)velMag; + return 1.f; +} + +CBSFlyerLocomotion::CBSFlyerLocomotion(CActor& actor, const bool pitchable) +: CBSBiPedLocomotion(actor), x3cc_pitchable(pitchable) {} + +float CBSFlyerLocomotion::ApplyLocomotionPhysics(float dt, CBodyController& bc) { + const float ret = CBSLocomotion::ApplyLocomotionPhysics(dt, bc); + + if (CPhysicsActor* act = TCastToPtr< CPhysicsActor >(&bc.GetOwner())) { + if (CMath::AbsF(bc.GetCommandMgr().GetMoveVector()[kDZ]) > 0.01f && + (!x3cc_pitchable || bc.GetBodyStateInfo().GetMaximumPitch() < skMaxPitchAngle)) { + const float maxSpeed = bc.GetBodyStateInfo().GetMaxSpeed(); + CVector3f dir(0.f, 0.f, dt * (maxSpeed * bc.GetCommandMgr().GetMoveVector()[kDZ])); + CVector3f impulse = act->GetMoveToORImpulseWR(dir, dt); + act->ApplyImpulseWR(impulse, CAxisAngle::Identity()); + } + } + + return ret; +} + +CBSWallWalkerLocomotion::CBSWallWalkerLocomotion(CActor& actor) : CBSBiPedLocomotion(actor) {} + +float CBSWallWalkerLocomotion::ApplyLocomotionPhysics(float dt, CBodyController& bc) { + if (CPhysicsActor* act = TCastToPtr< CPhysicsActor >(&bc.GetOwner())) { + const float maxSpeed = bc.GetBodyStateInfo().GetMaxSpeed(); + const CVector3f scaledMove = bc.CommandMgr().GetMoveVector() * maxSpeed; + + const CVector3f tmp = + CVector3f::GetAngleDiff(bc.CommandMgr().GetFaceVector(), scaledMove) < M_PIF / 2.f + ? scaledMove + : bc.CommandMgr().GetFaceVector(); + if (tmp.CanBeNormalized()) { + bc.FaceDirection3D(scaledMove.AsNormalized(), act->GetTransform().GetForward(), dt); + } + + const CVector3f moveDt = scaledMove * dt; + CVector3f moveImpulse = + act->GetMoveToORImpulseWR(act->TransformWorldToLocalRotation(moveDt), dt); + CVector3f impulse; + impulse = act->GetMass() > FLT_EPSILON ? moveImpulse / act->GetMass() + : CVector3f(0.f, act->GetVelocityWR().Magnitude(), 0.f); + + if (maxSpeed > FLT_EPSILON) { + return rstl::min_val(impulse.Magnitude() / maxSpeed, 1.f); + } + } + + return 0.f; +} + +CBSBiPedLocomotion::~CBSBiPedLocomotion() {} + +CBSAiMovedFlyerLocomotion::CBSAiMovedFlyerLocomotion(CActor& actor) : CBSBiPedLocomotion(actor) {} + +float CBSAiMovedFlyerLocomotion::ApplyLocomotionPhysics(float dt, CBodyController& bc) { + if (TCastToPtr< CPhysicsActor >(&bc.GetOwner())) { + bc.FaceDirection(bc.CommandMgr().GetFaceVector(), dt); + } + + return 0.f; +} + +float CBSAiMovedFlyerLocomotion::UpdateLocomotionAnimation(float dt, float velMag, CBodyController& bc, + bool init) { + (void)dt; + (void)velMag; + + static pas::ELocomotionAnim runStrafes[6] = { + pas::kLA_StrafeRight, pas::kLA_StrafeLeft, pas::kLA_Run, + pas::kLA_BackUp, pas::kLA_StrafeUp, pas::kLA_StrafeDown, + }; + + if (CPhysicsActor* act = TCastToPtr< CPhysicsActor >(&bc.GetOwner())) { + pas::ELocomotionAnim strafeType = pas::kLA_Idle; + const CBodyStateCmdMgr& cmdMgr = bc.GetCommandMgr(); + if (cmdMgr.GetMoveVector().CanBeNormalized()) { + CVector3f localVec = act->TransformWorldToLocalRotation(cmdMgr.GetMoveVector()); + CVector3f localVecSq = CVector3f::ByElementMultiply(localVec, localVec); + int maxComp = 0; + for (int i = 0; i < 3; ++i) { + if (localVecSq[i] >= localVecSq[maxComp]) { + maxComp = i; + } + } + + const int side = localVec[maxComp] > 0.f ? 0 : 1; + const int strafeKey = maxComp * 2 + side; + strafeType = runStrafes[strafeKey]; + } + + if (init || strafeType != x3c4_anim) { + const rstl::pair< int, float >& strafe = GetLocoAnimation(x4_locomotionType, strafeType); + const int anim = strafe.first; + if (init || bc.GetCurrentAnimId() != anim) { + const CAnimPlaybackParms playParms(anim, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, true, false); + } + x3c4_anim = strafeType; + } + } + + return 1.f; +} + +CBSFloaterLocomotion::CBSFloaterLocomotion(CActor& actor) +: CBSRestrictedLocomotion(actor) {} + +float CBSFloaterLocomotion::ApplyLocomotionPhysics(float dt, CBodyController& bc) { + if (CPhysicsActor* act = TCastToPtr< CPhysicsActor >(bc.GetOwner())) { + bc.FaceDirection(bc.GetCommandMgr().GetFaceVector(), dt); + const float moveSpeed = bc.GetRestrictedFlyerMoveSpeed(); + const float mass = act->GetMass(); + CVector3f impulse = bc.GetCommandMgr().GetMoveVector() * moveSpeed * mass; + act->ApplyImpulseWR(impulse, CAxisAngle::Identity()); + } + + return 0.f; +} + +bool CBSLocomotion::IsPitchable() const { return false; } + +bool CBSLocomotion::CanShoot() const { return true; } + +float CBSRestrictedLocomotion::GetLocomotionSpeed(pas::ELocomotionType type, + pas::ELocomotionAnim anim) const { + (void)type; + (void)anim; + return 0.f; +} + +bool CBSRestrictedLocomotion::IsMoving() const { return false; } + +CBSFloaterLocomotion::~CBSFloaterLocomotion() {} diff --git a/src/MetroidPrime/BodyState/CBodyController.cpp b/src/MetroidPrime/BodyState/CBodyController.cpp index 4ed1a5fe1..a45f98084 100644 --- a/src/MetroidPrime/BodyState/CBodyController.cpp +++ b/src/MetroidPrime/BodyState/CBodyController.cpp @@ -139,7 +139,7 @@ void CBodyController::SetDeltaRotation(const CQuaternion& q) { x2dc_rot = x2dc_r void CBodyController::FaceDirection(const CVector3f& v0, float dt) {} -void CBodyController::FaceDirection3D(const CVector3f& v0, float dt) {} +void CBodyController::FaceDirection3D(const CVector3f& v0, const CVector3f& v1, float dt) {} const CPASDatabase& CBodyController::GetPASDatabase() const { return GetOwner().GetModelData()->GetAnimationData()->GetPASDatabase(); diff --git a/src/MetroidPrime/CActor.cpp b/src/MetroidPrime/CActor.cpp index 5914046b5..2acdeae51 100644 --- a/src/MetroidPrime/CActor.cpp +++ b/src/MetroidPrime/CActor.cpp @@ -22,6 +22,7 @@ #include "Kyoto/Math/CFrustumPlanes.hpp" #include "Kyoto/Math/CMath.hpp" +#include "MetroidPrime/TGameTypes.hpp" #include "rstl/math.hpp" #pragma inline_max_size(250) @@ -592,7 +593,7 @@ void CActor::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateMana RemoveMaterial(kMT_Scannable, mgr); } if (HasAnimation()) { - AnimationData()->InitializeEffects(mgr, GetAreaId(), GetModelData()->GetScale()); + AnimationData()->InitializeEffects(mgr, GetAreaId(), GetModelData()->ScaleCopy()); } break; } @@ -704,12 +705,8 @@ void CActor::ProcessSoundEvent(const int sfxId, const float weight, const int fl const CVector3f& position, const int aid, CStateManager& mgr, const bool translateId) { if (toListener.MagSquared() < maxDist * maxDist) { - ushort id; - if (translateId) { - id = CSfxManager::TranslateSFXID(sfxId); - } else { - id = static_cast(sfxId); - } + const TSfxId id = + translateId ? CSfxManager::TranslateSFXID(sfxId) : static_cast< TSfxId >(sfxId); uint musyxFlags = 0x1; // Continuous parameter update if (flags & 0x8) { @@ -728,7 +725,7 @@ void CActor::ProcessSoundEvent(const int sfxId, const float weight, const int fl if (mgr.Random()->Float() <= weight) { if (looping) { - ushort curId = x88_sfxId; + const TSfxId curId = x88_sfxId; if (!x8c_loopingSfxHandle) { CSfxHandle handle; if (nonEmitter) { diff --git a/src/MetroidPrime/CActorModelParticles.cpp b/src/MetroidPrime/CActorModelParticles.cpp new file mode 100644 index 000000000..1262531ea --- /dev/null +++ b/src/MetroidPrime/CActorModelParticles.cpp @@ -0,0 +1,15 @@ +#include "MetroidPrime/CActorModelParticles.hpp" + +#include "Kyoto/CResFactory.hpp" +#include "Kyoto/CSimplePool.hpp" + +CActorModelParticles::CActorModelParticles() +: x18_onFire(gpSimplePool->GetObj(*gpResourceFactory->GetResourceIdByName("Effect_OnFire"))) +, x20_ash(gpSimplePool->GetObj(*gpResourceFactory->GetResourceIdByName("Effect_Ash"))) +, x28_iceBreak(gpSimplePool->GetObj(*gpResourceFactory->GetResourceIdByName("Effect_IceBreak"))) +, x30_firePop(gpSimplePool->GetObj(*gpResourceFactory->GetResourceIdByName("Effect_FirePop"))) +, x38_icePop(gpSimplePool->GetObj(*gpResourceFactory->GetResourceIdByName("Effect_IcePop"))) +, x40_electric(gpSimplePool->GetObj(*gpResourceFactory->GetResourceIdByName("Effect_Electric"))) +, x48_ashy(gpSimplePool->GetObj(*gpResourceFactory->GetResourceIdByName("TXTR_Ashy"))) { + LoadParticleDGRPs(); +} diff --git a/src/MetroidPrime/CAutoMapper.cpp b/src/MetroidPrime/CAutoMapper.cpp index cee840f6a..f51f0d99a 100644 --- a/src/MetroidPrime/CAutoMapper.cpp +++ b/src/MetroidPrime/CAutoMapper.cpp @@ -1,5 +1,5 @@ -#pragma inline_max_size(200) #include "MetroidPrime/CAutoMapper.hpp" +#include "rstl/math.hpp" #include "Kyoto/Audio/CSfxManager.hpp" #include "Kyoto/Basics/CBasics.hpp" @@ -20,12 +20,12 @@ #include "MetaRender/CCubeRenderer.hpp" -#include "MetroidPrime/CGameArea.hpp" -#include "MetroidPrime/CMapArea.hpp" #include "MetroidPrime/CControlMapper.hpp" #include "MetroidPrime/CEulerAngles.hpp" +#include "MetroidPrime/CGameArea.hpp" #include "MetroidPrime/CGameHintInfo.hpp" #include "MetroidPrime/CMain.hpp" +#include "MetroidPrime/CMapArea.hpp" #include "MetroidPrime/CMapUniverse.hpp" #include "MetroidPrime/CMapWorld.hpp" #include "MetroidPrime/CMapWorldInfo.hpp" @@ -35,16 +35,13 @@ #include "MetroidPrime/Cameras/CGameCamera.hpp" #include "MetroidPrime/Player/CGameState.hpp" -#include "rstl/rc_ptr.hpp" -#include "rstl/StringExtras.hpp" +#include "MetroidPrime/CWorld.hpp" #include "MetroidPrime/Player/CPlayer.hpp" #include "MetroidPrime/Tweaks/CTweakAutoMapper.hpp" #include "MetroidPrime/Tweaks/CTweakGui.hpp" #include "MetroidPrime/Tweaks/CTweakPlayerRes.hpp" -#include "MetroidPrime/CWorld.hpp" - -static inline float min_val(float a, float b) { return a < b ? a : b; } -static inline float max_val(float a, float b) { return a > b ? a : b; } +#include "rstl/StringExtras.hpp" +#include "rstl/rc_ptr.hpp" inline float normalize_angle(float angle) { float rcpTwoPi = 1.f / (2.f * M_PIF); @@ -61,1323 +58,1689 @@ static inline const rstl::vector< CGameHintInfo::CGameHint >& GetGameHints() { return (*reinterpret_cast< TLockedToken< CGameHintInfo >* >(gpMemoryCard))->GetHints(); } -#pragma inline_max_size(0) +CAutoMapper::SAutoMapperRenderState::SAutoMapperRenderState( + const CVector2i& viewportSize, const CQuaternion& camOrientation, float camDist, float camAngle, + const CVector3f& areaPoint, float drawDepth1, float drawDepth2, float alphaSurfaceVisited, + float alphaOutlineVisited, float alphaSurfaceUnvisited, float alphaOutlineUnvisited) +: x0_viewportSize(viewportSize) +, x8_camOrientation(camOrientation) +, x18_camDist(camDist) +, x1c_camAngle(camAngle) +, x20_areaPoint(areaPoint) +, x2c_drawDepth1(drawDepth1) +, x30_drawDepth2(drawDepth2) +, x34_alphaSurfaceVisited(alphaSurfaceVisited) +, x38_alphaOutlineVisited(alphaOutlineVisited) +, x3c_alphaSurfaceUnvisited(alphaSurfaceUnvisited) +, x40_alphaOutlineUnvisited(alphaOutlineUnvisited) +, x44_viewportEase(kE_None) +, x48_camEase(kE_None) +, x4c_pointEase(kE_None) +, x50_depth1Ease(kE_None) +, x54_depth2Ease(kE_None) +, x58_alphaEase(kE_None) {} -void CAutoMapper::Update(float dt, const CStateManager& mgr) { - if (IsFullyOutOfMiniMapState()) { - x1d8_flashTimer = static_cast< float >(fmod(x1d8_flashTimer + dt, 0.75)); - if (x1d8_flashTimer < 0.375f) { - x1dc_playerFlashPulse = x1d8_flashTimer / 0.375f; - } else { - x1dc_playerFlashPulse = (0.75f - x1d8_flashTimer) / 0.375f; - } - } +// TODO: what is this? +static inline float Lerp(float a, float b, float t) { return a * (1.f - t) + b * t; } - // Initialize frame widgets when map screen frame is loaded - if (x28_frmeMapScreen.get() != NULL && x2c_frmeInitialized == NULL) { - if (x28_frmeMapScreen->TryCache()) { - x2c_frmeInitialized = x28_frmeMapScreen->GetObject(); +void CAutoMapper::SAutoMapperRenderState::InterpolateWithClamp(const SAutoMapperRenderState& a, + SAutoMapperRenderState& out, + const SAutoMapperRenderState& b, + float t) { + float ct = CMath::Clamp(0.f, t, 1.f); + float easeIn = CMath::Clamp(0.f, ct * ct * ct, 1.f); + float omt = 1.f - ct; + float omtCubed = omt * omt * omt; + float easeOut = CMath::Clamp(0.f, 1.f - omtCubed, 1.f); - CGuiTextPane* leftPane = static_cast< CGuiTextPane* >(x2c_frmeInitialized->FindWidget("textpane_left")); - leftPane->TextSupport().SetText(rstl::wstring(gpStringTable->GetString(0x2a))); + float easeInOut; + if (ct >= 0.5f) { + easeInOut = CMath::Clamp(0.f, 0.5f * CMath::SqrtF(2.f * ct - 1.f) + 0.5f, 1.f); + } else { + easeInOut = CMath::Clamp(0.f, 1.f - (0.5f * CMath::SqrtF(2.f * omt - 1.f) + 0.5f), 1.f); + } - CGuiTextPane* yicon = static_cast< CGuiTextPane* >(x2c_frmeInitialized->FindWidget("textpane_yicon")); - yicon->TextSupport().SetText(rstl::wstring(gpStringTable->GetString(0x2b))); + float eases[5] = {0.f, ct, easeOut, easeIn, easeInOut}; - x2fc_textpane_hint = static_cast< CGuiTextPane* >(x2c_frmeInitialized->FindWidget("textpane_hint")); - x300_textpane_instructions = static_cast< CGuiTextPane* >(x2c_frmeInitialized->FindWidget("textpane_instructions")); - x304_textpane_instructions1 = static_cast< CGuiTextPane* >(x2c_frmeInitialized->FindWidget("textpane_instructions1")); - x308_textpane_instructions2 = static_cast< CGuiTextPane* >(x2c_frmeInitialized->FindWidget("textpane_instructions2")); + if (b.x44_viewportEase != kE_None) { + float easeB = eases[b.x44_viewportEase]; + out.x0_viewportSize = CVector2i::Lerp(a.x0_viewportSize, b.x0_viewportSize, easeB); + } - CGuiTextPane* mapLegend = static_cast< CGuiTextPane* >(x2c_frmeInitialized->FindWidget("textpane_mapLegend")); - mapLegend->TextSupport().SetWordWrap(false); - mapLegend->TextSupport().SetImageBaseline(true); - mapLegend->TextSupport().SetText(rstl::wstring(gpStringTable->GetString(0x31))); + if (b.x48_camEase != kE_None) { + float easeB = eases[b.x48_camEase]; + out.x8_camOrientation = CQuaternion::Slerp(a.x8_camOrientation, b.x8_camOrientation, easeB); + out.x18_camDist = Lerp(a.x18_camDist, b.x18_camDist, easeB); + out.x1c_camAngle = Lerp(a.x1c_camAngle, b.x1c_camAngle, easeB); + } - x30c_basewidget_leftPane = x2c_frmeInitialized->FindWidget("basewidget_leftPane"); - x310_basewidget_yButtonPane = x2c_frmeInitialized->FindWidget("basewidget_yButtonPane"); - x314_basewidget_bottomPane = x2c_frmeInitialized->FindWidget("basewidget_bottomPane"); + if (b.x4c_pointEase != kE_None) { + float eB = eases[b.x4c_pointEase]; + out.x20_areaPoint = CVector3f::Lerp(a.x20_areaPoint, b.x20_areaPoint, eB); + } - x2f8_textpane_areaname = static_cast< CGuiTextPane* >(x2c_frmeInitialized->FindWidget("textpane_areaname")); - x2f8_textpane_areaname->SetDepthTest(false); - } + if (b.x50_depth1Ease != kE_None) { + float eB = eases[b.x50_depth1Ease]; + out.x2c_drawDepth1 = Lerp(a.x2c_drawDepth1, b.x2c_drawDepth1, eB); } - // Update frame and text panes - if (x2c_frmeInitialized != NULL) { - x2c_frmeInitialized->Update(dt); + if (b.x54_depth2Ease != kE_None) { + float eB = eases[b.x54_depth2Ease]; + out.x30_drawDepth2 = Lerp(a.x30_drawDepth2, b.x30_drawDepth2, eB); + } - CGuiTextPane* right1 = static_cast< CGuiTextPane* >(x2c_frmeInitialized->FindWidget("textpane_right1")); - const wchar_t imagePrefix[] = L"&image="; - const wchar_t imageSuffix[] = L";"; - rstl::wstring string; + if (b.x58_alphaEase != kE_None) { + float eB = eases[b.x58_alphaEase]; + out.x34_alphaSurfaceVisited = Lerp(a.x34_alphaSurfaceVisited, b.x34_alphaSurfaceVisited, eB); + out.x38_alphaOutlineVisited = Lerp(a.x38_alphaOutlineVisited, b.x38_alphaOutlineVisited, eB); + out.x3c_alphaSurfaceUnvisited = + Lerp(a.x3c_alphaSurfaceUnvisited, b.x3c_alphaSurfaceUnvisited, eB); + out.x40_alphaOutlineUnvisited = + Lerp(a.x40_alphaOutlineUnvisited, b.x40_alphaOutlineUnvisited, eB); + } +} - if (x1bc_state == kAMS_MapScreenUniverse || - (x1bc_state == kAMS_MapScreen && HasCurrentMapUniverseWorld())) { - string.reserve(0x100); - string.append(imagePrefix, -1); - rstl::string hexStr(CBasics::Stringize("%8.8X", gpTweakPlayerRes->x98_aButton[x2f4_aButtonPos])); - rstl::wstring unicodeHex = CStringExtras::ConvertToUNICODE(hexStr); - string.append(unicodeHex); - string.append(imageSuffix, -1); - } else { - string = rstl::wstring_l(L""); - } - right1->TextSupport().SetText(string); +void CAutoMapper::SAutoMapperRenderState::ResetInterpolation() { + x44_viewportEase = kE_None; + x48_camEase = kE_None; + x4c_pointEase = kE_None; + x50_depth1Ease = kE_None; + x54_depth2Ease = kE_None; + x58_alphaEase = kE_None; +} - CGuiTextPane* right = static_cast< CGuiTextPane* >(x2c_frmeInitialized->FindWidget("textpane_right")); - rstl::wstring rightString; - if (x1bc_state == kAMS_MapScreenUniverse) { - rightString = rstl::wstring_l(gpStringTable->GetString(0x2d)); - } else if (x1bc_state == kAMS_MapScreen && HasCurrentMapUniverseWorld()) { - rightString = rstl::wstring_l(gpStringTable->GetString(0x2c)); - } else { - rightString = rstl::wstring_l(L""); - } - right->TextSupport().SetText(rightString); - } +CAutoMapper::SAutoMapperHintLocation::SAutoMapperHintLocation(uint showBeacon, float beaconAlpha, + CAssetId worldId, TAreaId areaId) +: x0_showBeacon(showBeacon), x4_beaconAlpha(beaconAlpha), x8_worldId(worldId), xc_areaId(areaId) {} - // Update pane positions - float dt2 = 2.f * dt; - switch (gpGameState->SystemState().GetAutoMapperKeyState()) { - case 0: - x318_leftPanePos -= dt2; - x31c_yButtonPanePos -= dt2; - x320_bottomPanePos -= dt2; - break; - case 1: - x318_leftPanePos += dt2; - x31c_yButtonPanePos -= dt2; - x320_bottomPanePos -= dt2; - break; - case 2: - x318_leftPanePos += dt2; - x31c_yButtonPanePos += dt2; - x320_bottomPanePos += dt2; - break; - default: - break; - } +CAutoMapper::CAutoMapper(CStateManager& stateMgr) +: x4_loadPhase(kLP_LoadResources) +, x8_mapu(gpSimplePool->GetObj("MAPU_MapUniverse")) +, x14_dummyWorlds() +, x24_world(stateMgr.World()) +, x28_frmeMapScreen() +, x2c_frmeInitialized(nullptr) +, x30_miniMapSamus(gpSimplePool->GetObj("CMDL_MiniMapSamus")) +, x3c_hintBeacon(gpSimplePool->GetObj("TXTR_HintBeacon")) +, x48_mapIcons() +, x74_areaHintDescId(kInvalidAssetId) +, x78_areaHintDesc() +, x88_mapAreaStringId(kInvalidAssetId) +, x8c_mapAreaString() +, x9c_worldIdx(0) +, xa0_curAreaId(x24_world->IGetCurrentAreaId()) +, xa4_otherAreaId(xa0_curAreaId) +, xa8_renderState0(BuildMiniMapWorldRenderState( + stateMgr, + CQuaternion::FromMatrix( + stateMgr.GetCameraManager()->GetCurrentCamera(stateMgr).GetTransform()), + xa0_curAreaId.value)) +, x104_renderState1(xa8_renderState0) +, x160_renderState2(xa8_renderState0) +, x1bc_state(kAMS_MiniMap) +, x1c0_nextState(kAMS_MiniMap) +, x1c4_interpDur(0.f) +, x1c8_interpTime(0.f) +, x1cc_panningSfx() +, x1d0_rotatingSfx() +, x1d4_zoomingSfx() +, x1d8_flashTimer(0.f) +, x1dc_playerFlashPulse(0.f) +, x1e0_hintSteps() +, x1f8_hintLocations() +, x210_lstick() +, x25c_cstick() +, x2a8_ltrigger() +, x2bc_rtrigger() +, x2d0_abutton() +, x2e4_lStickPos(0) +, x2e8_rStickPos(0) +, x2ec_lTriggerPos(0) +, x2f0_rTriggerPos(0) +, x2f4_aButtonPos(0) +, x2f8_textpane_areaname(NULL) +, x2fc_textpane_hint(NULL) +, x300_textpane_instructions(NULL) +, x304_textpane_instructions1(NULL) +, x308_textpane_instructions2(NULL) +, x30c_basewidget_leftPane(NULL) +, x310_basewidget_yButtonPane(NULL) +, x314_basewidget_bottomPane(NULL) +, x318_leftPanePos(0.f) +, x31c_yButtonPanePos(0.f) +, x320_bottomPanePos(0.f) +, x324_zoomState(kZS_None) +, x328_(0) +, x32c_loadingDummyWorld(false) { + x8_mapu.Lock(); + x30_miniMapSamus.Lock(); + x3c_hintBeacon.Lock(); - if (x318_leftPanePos < 0.f) - x318_leftPanePos = 0.f; - else if (x318_leftPanePos > 1.f) - x318_leftPanePos = 1.f; - if (x31c_yButtonPanePos < 0.f) - x31c_yButtonPanePos = 0.f; - else if (x31c_yButtonPanePos > 1.f) - x31c_yButtonPanePos = 1.f; - if (x320_bottomPanePos < 0.f) - x320_bottomPanePos = 0.f; - else if (x320_bottomPanePos > 1.f) - x320_bottomPanePos = 1.f; + x48_mapIcons.push_back( + gpSimplePool->GetObj(SObjectTag('TXTR', gpTweakPlayerRes->x4_saveStationIcon))); + x48_mapIcons.push_back( + gpSimplePool->GetObj(SObjectTag('TXTR', gpTweakPlayerRes->x8_missileStationIcon))); + x48_mapIcons.push_back( + gpSimplePool->GetObj(SObjectTag('TXTR', gpTweakPlayerRes->xc_elevatorIcon))); + x48_mapIcons.push_back( + gpSimplePool->GetObj(SObjectTag('TXTR', gpTweakPlayerRes->x10_minesBreakFirstTopIcon))); + x48_mapIcons.push_back( + gpSimplePool->GetObj(SObjectTag('TXTR', gpTweakPlayerRes->x14_minesBreakFirstBottomIcon))); - if (x30c_basewidget_leftPane != NULL) { - x30c_basewidget_leftPane->LocalTransform() = - CTransform4f::Translate(CVector3f(-15.f, 0.f, 0.f) * x318_leftPanePos) * - x30c_basewidget_leftPane->GetTransform(); - x30c_basewidget_leftPane->RecalculateTransforms(); + for (CToken* it = x48_mapIcons.begin(); it != x48_mapIcons.end(); ++it) { + it->Lock(); } - if (x310_basewidget_yButtonPane != NULL) { - x310_basewidget_yButtonPane->LocalTransform() = - CTransform4f::Translate(CVector3f(0.f, 0.f, -3.5f) * x31c_yButtonPanePos) * - x310_basewidget_yButtonPane->GetTransform(); - x310_basewidget_yButtonPane->RecalculateTransforms(); + for (int i = 0; i < 9; ++i) { + x210_lstick.push_back(gpSimplePool->GetObj( + SObjectTag('TXTR', ((const CAssetId*)((const char*)gpTweakPlayerRes + 0x24))[i]))); + x25c_cstick.push_back(gpSimplePool->GetObj( + SObjectTag('TXTR', ((const CAssetId*)((const char*)gpTweakPlayerRes + 0x4c))[i]))); } - if (x314_basewidget_bottomPane != NULL) { - x314_basewidget_bottomPane->LocalTransform() = - CTransform4f::Translate(CVector3f(0.f, 0.f, -7.f) * x320_bottomPanePos) * - x314_basewidget_bottomPane->GetTransform(); - x314_basewidget_bottomPane->RecalculateTransforms(); + for (int i = 0; i < 2; ++i) { + x2a8_ltrigger.push_back(gpSimplePool->GetObj( + SObjectTag('TXTR', ((const CAssetId*)((const char*)gpTweakPlayerRes + 0x74))[i]))); + x2bc_rtrigger.push_back(gpSimplePool->GetObj( + SObjectTag('TXTR', ((const CAssetId*)((const char*)gpTweakPlayerRes + 0x80))[i]))); + x2d0_abutton.push_back(gpSimplePool->GetObj( + SObjectTag('TXTR', ((const CAssetId*)((const char*)gpTweakPlayerRes + 0x98))[i]))); } +} - // Update camera and area for minimap - if (IsInMapperState(kAMS_MiniMap)) { - xa8_renderState0.x8_camOrientation = GetMiniMapCameraOrientation(mgr); - float camDist = xa8_renderState0.x18_camDist; - float desiredDist = GetDesiredMiniMapCameraDistance(mgr); - if (CMath::AbsF(camDist - desiredDist) < 3.f) { - xa8_renderState0.x18_camDist = desiredDist; - } else if (camDist < desiredDist) { - xa8_renderState0.x18_camDist = camDist + 3.f; +template < class T > +void CAutoMapper::SetResLockState(T& list, bool lock) { + CToken* it = list.data(); + for (; it != list.data() + list.size(); ++it) { + if (lock) { + it->Lock(); } else { - xa8_renderState0.x18_camDist = camDist - 3.f; + it->Unlock(); } + } +} - TAreaId curAid = x24_world->IGetCurrentAreaId(); - if (curAid != xa0_curAreaId) { - x160_renderState2 = xa8_renderState0; - x104_renderState1 = xa8_renderState0; - xa4_otherAreaId = xa0_curAreaId; - xa0_curAreaId = curAid; - CVector3f areaPoint = GetAreaPointOfInterest(mgr, xa0_curAreaId.value); - x104_renderState1.x20_areaPoint = areaPoint; - x104_renderState1.x44_viewportEase = SAutoMapperRenderState::kE_None; - x104_renderState1.x48_camEase = SAutoMapperRenderState::kE_None; - x104_renderState1.x4c_pointEase = SAutoMapperRenderState::kE_InOut; - x104_renderState1.x50_depth1Ease = SAutoMapperRenderState::kE_Linear; - x104_renderState1.x54_depth2Ease = SAutoMapperRenderState::kE_Linear; - x104_renderState1.x58_alphaEase = SAutoMapperRenderState::kE_None; - x104_renderState1.x2c_drawDepth1 = GetMapAreaMiniMapDrawDepth(); - x104_renderState1.x30_drawDepth2 = GetMapAreaMiniMapDrawDepth(); - x160_renderState2.x2c_drawDepth1 = GetMapAreaMiniMapDrawDepth() - 1.f; - x160_renderState2.x30_drawDepth2 = GetMapAreaMiniMapDrawDepth() - 1.f; - ResetInterpolationTimer(gpTweakAutoMapper->x6c_hintPanTime); - } - xa8_renderState0.x34_alphaSurfaceVisited = GetMapAreaMiniMapDrawAlphaSurfaceVisited(mgr); - xa8_renderState0.x38_alphaOutlineVisited = GetMapAreaMiniMapDrawAlphaOutlineVisited(mgr); - xa8_renderState0.x3c_alphaSurfaceUnvisited = GetMapAreaMiniMapDrawAlphaSurfaceUnvisited(mgr); - xa8_renderState0.x40_alphaOutlineUnvisited = GetMapAreaMiniMapDrawAlphaOutlineUnvisited(mgr); - } else if (x1c0_nextState == kAMS_MiniMap) { - float camDist = x104_renderState1.x18_camDist; - float desiredDist = GetDesiredMiniMapCameraDistance(mgr); - if (CMath::AbsF(camDist - desiredDist) < 3.f) { - xa8_renderState0.x18_camDist = desiredDist; - } else if (camDist < desiredDist) { - x104_renderState1.x18_camDist = camDist + 3.f; - } else { - x104_renderState1.x18_camDist = camDist - 3.f; - } - } else { - if (x1bc_state != kAMS_MiniMap && x1c0_nextState != kAMS_MiniMap && x24_world != NULL) { - const CMapWorld* mapWorld = x24_world->IGetMapWorld(); - CWorldState& worldState = gpGameState->StateForWorld(x24_world->IGetWorldAssetId()); - CMapWorldInfo* mwInfo = worldState.GetMapWorldInfo().GetPtr(); - mapWorld->RecalculateWorldSphere(*mwInfo, *x24_world); - } - } - - // Update interpolation - if (IsRenderStateInterpolating()) { - float newTime = x1c8_interpTime + dt; - x1c8_interpTime = newTime < x1c4_interpDur ? newTime : x1c4_interpDur; - SAutoMapperRenderState::InterpolateWithClamp(x160_renderState2, xa8_renderState0, - x104_renderState1, - x1c8_interpTime / x1c4_interpDur); - if (x1c8_interpTime == x1c4_interpDur && x328_ == 2) { - SetupMiniMapWorld(const_cast< CStateManager& >(mgr)); - } - } else if (IsInMapperStateTransition()) { - CompleteMapperStateTransition(mgr); - } +CAutoMapper::~CAutoMapper() { CSfxManager::KillAll(CSfxManager::kSC_PauseScreen); } - // Update map area string - CAssetId stringId = x88_mapAreaStringId; - if (IsInMapperState(kAMS_MapScreenUniverse)) { - IWorld* dummyWorld = x14_dummyWorlds[x9c_worldIdx].get(); - if (dummyWorld != NULL && dummyWorld->ICheckWorldComplete()) { - stringId = dummyWorld->IGetStringTableAssetId(); - } else if (x24_world != NULL) { - stringId = x24_world->IGetStringTableAssetId(); +bool CAutoMapper::CheckLoadComplete() { + switch (x4_loadPhase) { + case kLP_LoadResources: { + CToken* iconIt = x48_mapIcons.data(); + CToken* iconEnd = iconIt + x48_mapIcons.size(); + for (; iconIt != iconEnd; ++iconIt) { + if (!iconIt->IsLoaded()) + return false; } - } else if (x24_world != NULL) { - const IGameArea* area = x24_world->IGetAreaAlways(xa0_curAreaId); - CWorldState& worldState = gpGameState->StateForWorld(x24_world->IGetWorldAssetId()); - CMapWorldInfo* mwInfo = worldState.GetMapWorldInfo().GetPtr(); - bool showName = true; - if (!mwInfo->IsMapped(xa0_curAreaId)) { - if (!mwInfo->IsAreaVisited(xa0_curAreaId)) { - showName = false; + if (x30_miniMapSamus.TryCache()) { + if (x3c_hintBeacon.TryCache()) { + x4_loadPhase = kLP_LoadUniverse; + } else { + return false; } - } - if (showName) { - stringId = area->IGetStringTableAssetId(); } else { - stringId = kInvalidAssetId; + return false; } } - - if (stringId != x88_mapAreaStringId) { - x88_mapAreaStringId = stringId; - if (x88_mapAreaStringId != kInvalidAssetId) { - x8c_mapAreaString = TCachedToken< CStringTable >( - gpSimplePool->GetObj(SObjectTag('STRG', x88_mapAreaStringId))); - x8c_mapAreaString->Lock(); + case kLP_LoadUniverse: { + if (x8_mapu.TryCache()) { + int numWorlds = x8_mapu.GetObject()->GetNumMapWorldDatas(); + rstl::auto_ptr< IWorld > dummy; + rstl::vector< rstl::auto_ptr< IWorld > > worlds(numWorlds, dummy); + x14_dummyWorlds = worlds; + SetCurWorldAssetId(x24_world->IGetWorldAssetId()); + x4_loadPhase = kLP_Done; } else { - x8c_mapAreaString = rstl::optional_object< TCachedToken< CStringTable > >(); + return false; } } + case kLP_Done: + return true; + default: + return false; + } +} - if (x2f8_textpane_areaname != NULL) { - if (x8c_mapAreaString) { - if (x8c_mapAreaString->IsLoaded()) { - x2f8_textpane_areaname->TextSupport().SetText( - rstl::wstring(x8c_mapAreaString->GetObject()->GetString(0))); - } - } else { - x2f8_textpane_areaname->TextSupport().SetText(rstl::wstring_l(L"")); - } +void CAutoMapper::SetupHintNavigation() { + if (!gpGameState->GameOptions().GetIsHintSystemEnabled()) + return; + + rstl::list< SAutoMapperHintStep >::iterator stepEnd = x1e0_hintSteps.end(); + rstl::list< SAutoMapperHintStep >::iterator stepIt = x1e0_hintSteps.begin(); + while (stepIt != stepEnd) { + stepIt = x1e0_hintSteps.erase(stepIt); } - // Update hint description for map screen - if (IsInMapperState(kAMS_MapScreen)) { - const IGameArea* area = x24_world->IGetAreaAlways(xa0_curAreaId); - CAssetId hintDescId = GetAreaHintDescriptionString(area->IGetAreaAssetId()); - if (hintDescId != x74_areaHintDescId) { - x74_areaHintDescId = hintDescId; - if (x74_areaHintDescId != kInvalidAssetId) { - x78_areaHintDesc = TCachedToken< CStringTable >( - gpSimplePool->GetObj(SObjectTag('STRG', x74_areaHintDescId))); - x78_areaHintDesc->Lock(); - } else { - x78_areaHintDesc = rstl::optional_object< TCachedToken< CStringTable > >(); - } - } + rstl::list< SAutoMapperHintLocation >::iterator locEnd = x1f8_hintLocations.end(); + rstl::list< SAutoMapperHintLocation >::iterator locIt = x1f8_hintLocations.begin(); + while (locIt != locEnd) { + locIt = x1f8_hintLocations.erase(locIt); } - // Update dummy worlds - for (int i = 0; i < static_cast< int >(x14_dummyWorlds.size()); ++i) { - IWorld* dummyWorld = x14_dummyWorlds[i].get(); - if (dummyWorld != NULL) { - dummyWorld->ICheckWorldComplete(); + CHintOptions& hintOpts = gpGameState->HintOptions(); + const SHintState* curHint = hintOpts.GetCurrentDisplayedHint(); + bool navigating = false; + if (curHint != NULL && const_cast< SHintState* >(curHint)->CanContinue()) { + navigating = true; + x1e0_hintSteps.push_back(SAutoMapperHintStep(SAutoMapperHintStep::kHST_ShowBeacon, 0.75f)); + + int nextIdx = hintOpts.GetNextHintIdx(); + const CGameHintInfo::CGameHint& nextHint = GetGameHints()[nextIdx]; + CAssetId curMlvl = x24_world->IGetWorldAssetId(); + const rstl::vector< CGameHintInfo::SHintLocation >& locs = nextHint.GetLocations(); + for (int i = 0; i < static_cast< int >(locs.size()); ++i) { + const CGameHintInfo::SHintLocation& loc = locs[i]; + if (loc.x0_mlvlId != curMlvl) { + x1e0_hintSteps.push_back( + SAutoMapperHintStep(SAutoMapperHintStep::kHST_SwitchToUniverse, 0)); + x1e0_hintSteps.push_back(SAutoMapperHintStep(SAutoMapperHintStep::kHST_PanToWorld, + static_cast< int >(loc.x0_mlvlId))); + x1e0_hintSteps.push_back(SAutoMapperHintStep(SAutoMapperHintStep::kHST_SwitchToWorld, + static_cast< int >(loc.x0_mlvlId))); + curMlvl = loc.x0_mlvlId; + } else { + x1e0_hintSteps.push_back(SAutoMapperHintStep(SAutoMapperHintStep::kHST_ZoomOut, 0)); + } + x1e0_hintSteps.push_back( + SAutoMapperHintStep(SAutoMapperHintStep::kHST_PanToArea, loc.x8_areaId.value)); + x1e0_hintSteps.push_back(SAutoMapperHintStep(SAutoMapperHintStep::kHST_ZoomIn, 0)); + x1e0_hintSteps.push_back(SAutoMapperHintStep(SAutoMapperHintStep::kHST_ShowBeacon, 1.f)); + x1f8_hintLocations.push_back(SAutoMapperHintLocation(0, 0.f, loc.x0_mlvlId, loc.x8_areaId)); } } -} -CAssetId CAutoMapper::GetAreaHintDescriptionString(CAssetId mreaId) { - const CHintOptions& hintOpts = gpGameState->HintOptions(); const rstl::vector< SHintState >& hintStates = hintOpts.GetHintStates(); for (int i = 0; i < static_cast< int >(hintStates.size()); ++i) { + if (navigating && hintOpts.GetNextHintIdx() == i) + continue; if (hintStates[i].x0_state != kHS_Displaying) continue; const CGameHintInfo::CGameHint& hint = GetGameHints()[i]; - int numLocs = static_cast< int >(hint.x20_locations.size()); - for (int j = 0; j < numLocs; ++j) { - const CGameHintInfo::SHintLocation& loc = hint.x20_locations[j]; - if (loc.x4_mreaId != mreaId) - continue; - rstl::list< SAutoMapperHintLocation >::const_iterator locIt = x1f8_hintLocations.begin(); - for (; locIt != x1f8_hintLocations.end(); ++locIt) { - if (locIt->xc_areaId != loc.x8_areaId) - continue; - if (locIt->x4_beaconAlpha > 0.f) - return loc.xc_stringId; - } + const rstl::vector< CGameHintInfo::SHintLocation >& locs = hint.GetLocations(); + for (int j = 0; j < static_cast< int >(locs.size()); ++j) { + x1f8_hintLocations.push_back( + SAutoMapperHintLocation(1, 1.f, locs[j].x0_mlvlId, locs[j].x8_areaId)); } } - return kInvalidAssetId; } -void CAutoMapper::Draw(const CStateManager& mgr, const CTransform4f& xf, float alpha) const { - alpha *= gpGameState->GameOptions().GetHudAlpha(); - gpRender->SetBlendMode_AlphaBlended(); - CGraphics::SetCullMode(kCM_Front); - - float alphaInterp; - if (IsFullyOutOfMiniMapState()) { - alphaInterp = 1.f; - } else if (IsInMapperState(kAMS_MiniMap)) { - alphaInterp = alpha; - } else if (x1c0_nextState == kAMS_MiniMap) { - float t = GetInterp(); - alphaInterp = alpha * t + (1.f - t); - } else if (x1bc_state == kAMS_MiniMap) { - float t = GetInterp(); - alphaInterp = alpha * (1.f - t) + t; +void CAutoMapper::OnNewInGameGuiState(EInGameGuiState state, CStateManager& mgr) { + if (state == kIGGS_MapScreen) { + CMain::EnsureWorldPaksReady(); + CWorld* wld = mgr.World(); + wld->GetMapWorld()->SetWhichMapAreasLoaded(*wld, 0, 9999); + SetupHintNavigation(); + BeginMapperStateTransition(kAMS_MapScreen, mgr); + x28_frmeMapScreen = rs_new TCachedToken< CGuiFrame >(gpSimplePool->GetObj(skFRME_MapScreen)); + x28_frmeMapScreen->Lock(); + SetResLockState(x210_lstick, true); + SetResLockState(x25c_cstick, true); + SetResLockState(x2a8_ltrigger, true); + SetResLockState(x2bc_rtrigger, true); + SetResLockState(x2d0_abutton, true); } else { - alphaInterp = 1.f; + CMain::EnsureWorldPakReady(gpGameState->CurrentWorldAssetId()); + if (x1bc_state == kAMS_MapScreenUniverse || x24_world == mgr.GetWorld()) { + BeginMapperStateTransition(kAMS_MiniMap, mgr); + x328_ = 0; + } + LeaveMapScreenState(); } +} - float aspect = static_cast< float >(xa8_renderState0.x0_viewportSize.GetX()) / - static_cast< float >(xa8_renderState0.x0_viewportSize.GetY()); - float camAngleRad = xa8_renderState0.x1c_camAngle * (1.f / 360.f) * (2.f * M_PIF); - float yScale = xa8_renderState0.x18_camDist / - static_cast< float >(tan( - M_PIF / 2.f - 0.5f * camAngleRad)); - - CTransform4f camXf = - xa8_renderState0.x8_camOrientation.BuildTransform4f(xa8_renderState0.x20_areaPoint); - - CTransform4f distScale(1.f / (yScale * aspect), 0.f, 0.f, 0.f, - 0.f, 0.001f, 0.f, 0.f, - 0.f, 0.f, 1.f / yScale, 0.f); +bool CAutoMapper::CanLeaveMapScreen(const CStateManager& mgr) const { + bool ret = false; + if (x328_ == 3 && CanLeaveMapScreenInternal(mgr)) + ret = true; + return ret; +} - CTransform4f tweakScale = - CTransform4f::Scale(gpTweakAutoMapper->xc4_mapPlaneScaleX, 0.f, - gpTweakAutoMapper->xc8_mapPlaneScaleZ); +bool CAutoMapper::CanLeaveMapScreenInternal(const CStateManager& mgr) const { + if (!NotHintNavigating()) + return false; + if (IsRenderStateInterpolating()) + return false; + if (IsInMapperState(kAMS_MapScreenUniverse)) + return true; + bool ret = false; + if (x24_world == mgr.GetWorld() && IsInMapperState(kAMS_MapScreen)) + ret = true; + return ret; +} - CTransform4f planeXf = xf * tweakScale * distScale * camXf.GetQuickInverse(); +bool CAutoMapper::NotHintNavigating() const { return x1e0_hintSteps.empty(); } - float universeInterp = 0.f; - if (x1c0_nextState == kAMS_MapScreenUniverse) { - if (x1bc_state == kAMS_MapScreenUniverse) - universeInterp = 1.f; - else - universeInterp = GetInterp(); - } else if (x1bc_state == kAMS_MapScreenUniverse) { - universeInterp = 1.f - GetInterp(); - } - - const CTransform4f* preXf; - bool isMapScreenUniverse = - (x1bc_state == kAMS_MapScreenUniverse || x1c0_nextState == kAMS_MapScreenUniverse); - if (isMapScreenUniverse) - preXf = &x8_mapu.GetObject()->GetMapWorldData(x9c_worldIdx).GetWorldTransform(); - else - preXf = &CTransform4f::Identity(); +void CAutoMapper::UnmuteAllLoopedSounds() { + CSfxManager::SfxVolume(x1cc_panningSfx, 127); + CSfxManager::SfxVolume(x1d0_rotatingSfx, 127); + CSfxManager::SfxVolume(x1d4_zoomingSfx, 127); +} - float objectScale = xa8_renderState0.x18_camDist / gpTweakAutoMapper->xc_minCamDist; - float mapAlpha = alphaInterp * (1.f - universeInterp); +bool CAutoMapper::HasCurrentMapUniverseWorld() { + CMapUniverse* mapu = x8_mapu.GetObject(); + CAssetId mlvlId = x24_world->IGetWorldAssetId(); + int numWorlds = mapu->GetNumMapWorldDatas(); + for (int i = 0; i < numWorlds; ++i) { + if (mapu->GetMapWorldData(i).GetWorldAssetId() == mlvlId) + return true; + } + return false; +} - if (IsFullyOutOfMiniMapState()) { - if (universeInterp < 1.f && x24_world != NULL) { - CWorldState& worldState = gpGameState->StateForWorld(x24_world->IGetWorldAssetId()); - const CMapWorldInfo& mwInfo = *worldState.GetMapWorldInfo().GetPtr(); - const CMapWorld* mw = x24_world->IGetMapWorld(); +bool CAutoMapper::CheckDummyWorldLoad(const CStateManager& mgr) { + uint worldIdx = x9c_worldIdx; + IWorld* dummyWorld = x14_dummyWorlds[worldIdx].get(); + const CMapUniverse::CMapWorldData& mapuWld = x8_mapu.GetObject()->GetMapWorldData(worldIdx); + if (dummyWorld != NULL) { + if (dummyWorld->ICheckWorldComplete()) { + CWorldState& worldState = gpGameState->StateForWorld(dummyWorld->IGetWorldAssetId()); + CMapWorldInfo* mwInfo = worldState.GetMapWorldInfo().GetPtr(); - float hintFlash = 0.f; - if (!x1e0_hintSteps.empty() && - x1e0_hintSteps.begin()->x0_type == SAutoMapperHintStep::kHST_ShowBeacon) { - float hintStepFloat = x1e0_hintSteps.begin()->x4_float; - if (xa0_curAreaId == mgr.GetNextAreaId() && x24_world == mgr.GetWorld()) { - float pulseTime = - static_cast< float >(fmod(hintStepFloat * 8.f, 1.0)); - hintFlash = 2.f * (pulseTime < 0.5f ? pulseTime : 1.f - pulseTime); - } else { - rstl::list< SAutoMapperHintLocation >::const_iterator locIt = - x1f8_hintLocations.begin(); - for (; locIt != x1f8_hintLocations.end(); ++locIt) { - if (x24_world->IGetWorldAssetId() != locIt->x8_worldId) - continue; - if (xa0_curAreaId != locIt->xc_areaId) - continue; - float pulseTime = static_cast< float >( - fmod((1.f - max_val(0.f, (hintStepFloat - 0.5f) / 0.5f)) * - 4.f, - 1.0)); - hintFlash = 2.f * (pulseTime < 0.5f ? pulseTime : 1.f - pulseTime); - break; - } - } + CVector3f localPoint(mapuWld.GetWorldTransform().GetQuickInverse() * + xa8_renderState0.x20_areaPoint); + CMatrix3f camRot(xa8_renderState0.x8_camOrientation.BuildTransform()); + CVector3f camDir = camRot.GetColumn(kDY); + CUnitVector3f unitDir(camDir.GetX(), camDir.GetY(), camDir.GetZ()); + int aid = FindClosestVisibleArea(localPoint, unitDir, mgr, *dummyWorld, *mwInfo); + if (aid != -1) { + xa0_curAreaId = aid; + dummyWorld->IMapWorld()->RecalculateWorldSphere(*mwInfo, *dummyWorld); + x24_world = dummyWorld; + BeginMapperStateTransition(kAMS_MapScreen, mgr); + x32c_loadingDummyWorld = false; + return true; } - - int curArea = xa0_curAreaId.value; - CTransform4f modelXf = planeXf * *preXf; - const CMapWorld::CMapWorldDrawParms parms( - xa8_renderState0.x34_alphaSurfaceVisited * alphaInterp, - xa8_renderState0.x38_alphaOutlineVisited * alphaInterp, - xa8_renderState0.x3c_alphaSurfaceUnvisited * alphaInterp, - xa8_renderState0.x40_alphaOutlineUnvisited * alphaInterp, mapAlpha, - mgr, modelXf, camXf, *x24_world, mwInfo, - 2.f, true, x1dc_playerFlashPulse, hintFlash, objectScale); - mw->Draw(parms, curArea, curArea, - xa8_renderState0.x2c_drawDepth1, xa8_renderState0.x30_drawDepth2, true); + x32c_loadingDummyWorld = false; + return false; } - } else if (IsInMapperState(kAMS_MiniMap)) { - const CMapWorld* mw = x24_world->IGetMapWorld(); - CWorldState& worldState = gpGameState->StateForWorld(x24_world->IGetWorldAssetId()); - const CMapWorldInfo& mwInfo = *worldState.GetMapWorldInfo().GetPtr(); - const CMapWorld::CMapWorldDrawParms parms( - xa8_renderState0.x34_alphaSurfaceVisited * alphaInterp, - xa8_renderState0.x38_alphaOutlineVisited * alphaInterp, - xa8_renderState0.x3c_alphaSurfaceUnvisited * alphaInterp, - xa8_renderState0.x40_alphaOutlineUnvisited * alphaInterp, mapAlpha, - mgr, planeXf, camXf, *x24_world, mwInfo, - 1.f, false, 0.f, 0.f, objectScale); - mw->Draw(parms, xa0_curAreaId.value, xa4_otherAreaId.value, - xa8_renderState0.x2c_drawDepth1, xa8_renderState0.x30_drawDepth2, false); - } else { - int curArea = xa0_curAreaId.value; - const CMapWorld* mw = x24_world->IGetMapWorld(); - CWorldState& worldState = gpGameState->StateForWorld(x24_world->IGetWorldAssetId()); - const CMapWorldInfo& mwInfo = *worldState.GetMapWorldInfo().GetPtr(); - CTransform4f modelXf = planeXf * *preXf; - const CMapWorld::CMapWorldDrawParms parms( - xa8_renderState0.x34_alphaSurfaceVisited * alphaInterp, - xa8_renderState0.x38_alphaOutlineVisited * alphaInterp, - xa8_renderState0.x3c_alphaSurfaceUnvisited * alphaInterp, - xa8_renderState0.x40_alphaOutlineUnvisited * alphaInterp, mapAlpha, - mgr, modelXf, camXf, *x24_world, mwInfo, - 2.f, true, 0.f, 0.f, objectScale); - mw->Draw(parms, curArea, curArea, - xa8_renderState0.x2c_drawDepth1, xa8_renderState0.x30_drawDepth2, false); + return true; } + x32c_loadingDummyWorld = false; + return false; +} - if (universeInterp > 0.f) { - const CWorld* wld = mgr.GetWorld(); - CMapWorld* mapWorld = wld->GetMapWorld(); - const CMapArea* mapArea = mapWorld->GetMapArea(mgr.GetNextAreaId().value); - CTransform4f areaXf = - const_cast< CMapArea* >(mapArea)->GetAreaPostTransform(*wld, mgr.GetNextAreaId().value); - - CAssetId curWorldId = gpGameState->CurrentWorldAssetId(); +void CAutoMapper::UpdateHintNavigation(float dt, const CStateManager& mgr) { + SAutoMapperHintStep& nextStep = *x1e0_hintSteps.begin(); + float beaconTime = nextStep.x4_float; + bool oldProcessing = nextStep.x8_processing; + nextStep.x8_processing = true; + switch (nextStep.x0_type) { + case SAutoMapperHintStep::kHST_PanToArea: { + int areaId = nextStep.x4_areaId.value; + const CMapWorld* mapWorld = x24_world->IGetMapWorld(); + if (mapWorld->GetMapArea(areaId) != NULL) { + x160_renderState2 = xa8_renderState0; + x104_renderState1.x20_areaPoint = GetAreaPointOfInterest(mgr, areaId); + x104_renderState1.ResetInterpolation(); + x104_renderState1.x4c_pointEase = SAutoMapperRenderState::kE_Linear; + ResetInterpolationTimer(2.f * gpTweakAutoMapper->x6c_hintPanTime); + x1e0_hintSteps.pop_front(); + } + break; + } + case SAutoMapperHintStep::kHST_PanToWorld: { const CMapUniverse::CMapWorldData& mwData = - x8_mapu.GetObject()->GetMapWorldDataByWorldId(curWorldId); - CTransform4f universeAreaXf = mwData.GetWorldTransform() * areaXf; - - float minMag = FLT_MAX; - int hexIdx = -1; - for (int i = 0; i < static_cast< int >(mwData.GetNumMapAreaDatas()); ++i) { - CVector3f diff = universeAreaXf.GetTranslation() - mwData.GetMapAreaData(i).GetTranslation(); - float mag = diff.Magnitude(); - if (mag < minMag) { - hexIdx = i; - minMag = mag; + x8_mapu.GetObject()->GetMapWorldDataByWorldId(nextStep.x4_worldId); + CVector3f centerPoint = mwData.GetWorldCenterPoint(); + x160_renderState2 = xa8_renderState0; + x104_renderState1.x20_areaPoint = centerPoint; + x104_renderState1.ResetInterpolation(); + x104_renderState1.x4c_pointEase = SAutoMapperRenderState::kE_Linear; + ResetInterpolationTimer(2.f * gpTweakAutoMapper->x6c_hintPanTime); + x1e0_hintSteps.pop_front(); + break; + } + case SAutoMapperHintStep::kHST_SwitchToUniverse: { + if (HasCurrentMapUniverseWorld()) { + BeginMapperStateTransition(kAMS_MapScreenUniverse, mgr); + x1e0_hintSteps.pop_front(); + } else { + rstl::list< SAutoMapperHintStep >::iterator end = x1e0_hintSteps.end(); + rstl::list< SAutoMapperHintStep >::iterator it = x1e0_hintSteps.begin(); + while (it != end) { + it = x1e0_hintSteps.erase(it); } } - - const CMapUniverse::CMapUniverseDrawParms uParms( - universeInterp, x9c_worldIdx, gpGameState->CurrentWorldAssetId(), hexIdx, - x1dc_playerFlashPulse, mgr, planeXf, camXf); - x8_mapu.GetObject()->Draw(uParms, CVector3f::Zero(), 0.f, 0.f); + break; } + case SAutoMapperHintStep::kHST_SwitchToWorld: { + x1e0_hintSteps.pop_front(); + x32c_loadingDummyWorld = true; + if (!CheckDummyWorldLoad(mgr)) { + rstl::list< SAutoMapperHintStep >::iterator end = x1e0_hintSteps.end(); + rstl::list< SAutoMapperHintStep >::iterator it = x1e0_hintSteps.begin(); + while (it != end) { + it = x1e0_hintSteps.erase(it); + } + } + break; + } + case SAutoMapperHintStep::kHST_ShowBeacon: { + if (!oldProcessing) { + if (xa0_curAreaId == mgr.GetNextAreaId() && x24_world == mgr.GetWorld()) { + CSfxManager::SfxStart(0x56a, 127, 64, false, CSfxManager::kMedPriority, false, + CSfxManager::kAllAreas); + } else { + CSfxManager::SfxStart(0x56b, 127, 64, false, CSfxManager::kMedPriority, false, + CSfxManager::kAllAreas); + } + } + beaconTime -= dt; + if (beaconTime <= 0.f) + beaconTime = 0.f; + nextStep.x4_float = beaconTime; + rstl::list< SAutoMapperHintLocation >::iterator locIt = x1f8_hintLocations.begin(); + for (; locIt != x1f8_hintLocations.end(); ++locIt) { + if (x24_world->IGetWorldAssetId() == locIt->x8_worldId && xa0_curAreaId == locIt->xc_areaId) { + locIt->x0_showBeacon = 1; + float alpha = beaconTime / 0.5f; + if (alpha >= 1.f) + alpha = 1.f; + locIt->x4_beaconAlpha = 1.f - alpha; + break; + } + } + if (0.f == beaconTime) { + x1e0_hintSteps.pop_front(); + } + break; + } + case SAutoMapperHintStep::kHST_ZoomOut: { + x160_renderState2 = xa8_renderState0; + x104_renderState1.x18_camDist = gpTweakAutoMapper->x10_maxCamDist; + x104_renderState1.ResetInterpolation(); + x104_renderState1.x48_camEase = SAutoMapperRenderState::kE_Linear; + ResetInterpolationTimer(0.5f); + x1e0_hintSteps.pop_front(); + break; + } + case SAutoMapperHintStep::kHST_ZoomIn: { + x160_renderState2 = xa8_renderState0; + x104_renderState1.x18_camDist = gpTweakAutoMapper->x8_camDist; + x104_renderState1.ResetInterpolation(); + x104_renderState1.x48_camEase = SAutoMapperRenderState::kE_Linear; + ResetInterpolationTimer(0.5f); + x1e0_hintSteps.pop_front(); + break; + } + default: + break; + } +} - if (!IsInMapperState(kAMS_MapScreenUniverse)) { - CTransform4f mapXf = planeXf * *preXf; - if (x24_world == mgr.GetWorld()) { - float func = CMath::Clamp( - 0.f, 0.5f * (1.f + CMath::FastSinR(5.f * CGraphics::GetSecondsMod900() - (M_PIF / 2.f))), - 1.f); - float scale = min_val(0.6f * gpTweakAutoMapper->x10_maxCamDist / - gpTweakAutoMapper->xc_minCamDist, - objectScale); - - CEulerAngles eulers = CEulerAngles::FromTransform( - mgr.GetCameraManager()->GetCurrentCameraTransform(mgr)); - CRelAngle angle = CRelAngle(normalize_angle(eulers.GetZ())); - - CVector3f playerPos = - CMapArea::GetAreaPostTranslate(*x24_world, mgr.GetNextAreaId().value) + - mgr.GetPlayer()->GetTranslation(); - - CTransform4f playerXf(CMatrix3f::RotateZ(angle), playerPos); +void CAutoMapper::ProcessControllerInput(const CFinalInput& input, const CStateManager& mgr) { + if (!IsRenderStateInterpolating()) { + bool inPlayerControl = + IsInMapperState(kAMS_MapScreen) || IsInMapperState(kAMS_MapScreenUniverse); + if (inPlayerControl) { + if (x32c_loadingDummyWorld) { + CheckDummyWorldLoad(mgr); + } else if (static_cast< int >(x1e0_hintSteps.size()) > 0) { + UpdateHintNavigation(input.Time(), mgr); + } else if (x328_ == 0) { + ProcessMapScreenInput(input, mgr); + } + } + } - gpRender->SetModelMatrix(mapXf * playerXf * - CTransform4f::Scale(scale * (0.25f * func + 0.75f))); + CMatrix3f camRot = xa8_renderState0.x8_camOrientation.BuildTransform(); + if (IsInMapperState(kAMS_MapScreen)) { + CWorldState& worldState = gpGameState->StateForWorld(x24_world->IGetWorldAssetId()); + CMapWorldInfo* mwInfo = worldState.GetMapWorldInfo().GetPtr(); + CVector3f camDir = camRot.GetColumn(kDY); + CUnitVector3f unitDir(camDir.GetX(), camDir.GetY(), camDir.GetZ()); + int aid = + FindClosestVisibleArea(xa8_renderState0.x20_areaPoint, unitDir, mgr, *x24_world, *mwInfo); + if (aid != xa0_curAreaId.value) { + xa0_curAreaId = aid; + xa8_renderState0.x2c_drawDepth1 = GetMapAreaMaxDrawDepth(mgr, xa0_curAreaId.value); + xa8_renderState0.x30_drawDepth2 = GetMapAreaMaxDrawDepth(mgr, xa0_curAreaId.value); + } + } else if (IsInMapperState(kAMS_MapScreenUniverse)) { + CMapUniverse* mapu = x8_mapu.GetObject(); + uint oldWldIdx = x9c_worldIdx; + if (static_cast< int >(x1e0_hintSteps.size()) > 0) { + SAutoMapperHintStep& nextStep = *x1e0_hintSteps.begin(); + if (nextStep.x0_type == SAutoMapperHintStep::kHST_PanToWorld || + nextStep.x0_type == SAutoMapperHintStep::kHST_SwitchToWorld) { + SetCurWorldAssetId(nextStep.x4_worldId); + } else { + CVector3f camDir = camRot.GetColumn(kDY); + CUnitVector3f unitDir(camDir.GetX(), camDir.GetY(), camDir.GetZ()); + SClosestWorldResult result = + FindClosestVisibleWorld(xa8_renderState0.x20_areaPoint, unitDir, mgr); + x9c_worldIdx = result.x0_worldIdx; + } + } else { + CVector3f camDir = camRot.GetColumn(kDY); + CUnitVector3f unitDir(camDir.GetX(), camDir.GetY(), camDir.GetZ()); + SClosestWorldResult result = + FindClosestVisibleWorld(xa8_renderState0.x20_areaPoint, unitDir, mgr); + x9c_worldIdx = result.x0_worldIdx; + } - float colorAlpha; - if (IsFullyOutOfMiniMapState()) { - colorAlpha = 1.f; + if (x9c_worldIdx != oldWldIdx) { + CAssetId curMlvl = gpGameState->CurrentWorldAssetId(); + for (uint i = 0; static_cast< int >(i) < static_cast< int >(x14_dummyWorlds.size()); ++i) { + const CMapUniverse::CMapWorldData& mwData = mapu->GetMapWorldData(i); + if (i == x9c_worldIdx && curMlvl != mwData.GetWorldAssetId()) { + if (gpResourceFactory->CanBuild(SObjectTag('MLVL', mwData.GetWorldAssetId()))) { + CDummyWorld* dw = rs_new CDummyWorld(mwData.GetWorldAssetId()); + rstl::auto_ptr< IWorld > newWorld(dw); + x14_dummyWorlds[i] = newWorld; + } + } else { + rstl::auto_ptr< IWorld > emptyWorld; + x14_dummyWorlds[i] = emptyWorld; + } + } + if (curMlvl == mapu->GetMapWorldData(x9c_worldIdx).GetWorldAssetId()) { + x24_world = const_cast< CWorld* >(mgr.GetWorld()); } else { - colorAlpha = xa8_renderState0.x34_alphaSurfaceVisited; + x24_world = NULL; } - colorAlpha *= mapAlpha; - CColor modColor = - gpTweakAutoMapper->xf0_miniMapSamusModColor.WithAlphaModulatedBy(colorAlpha); - CModelFlags flags(CModelFlags::kT_Blend, static_cast< uchar >(0), - static_cast< CModelFlags::EFlags >(CModelFlags::kF_DepthCompare | CModelFlags::kF_DepthGreater), - modColor); - x30_miniMapSamus.GetObject()->Draw(flags); } + } - if (IsInMapperState(kAMS_MapScreen)) { - CAssetId wldMlvl = x24_world->IGetWorldAssetId(); - const CMapWorld* mw = x24_world->IGetMapWorld(); - rstl::list< SAutoMapperHintLocation >::const_iterator locIt = x1f8_hintLocations.begin(); - for (; locIt != x1f8_hintLocations.end(); ++locIt) { - if (locIt->x8_worldId != wldMlvl) - continue; - const CMapArea* mapa = mw->GetMapArea(locIt->xc_areaId.value); - if (mapa == NULL) - continue; + if (x300_textpane_instructions != NULL) { + if (x78_areaHintDesc.valid() && x78_areaHintDesc->TryCache()) { + x2fc_textpane_hint->TextSupport().SetText( + rstl::wstring(x78_areaHintDesc->GetObject()->GetString(0))); + x304_textpane_instructions1->TextSupport().SetText(rstl::wstring_l(L"")); + x300_textpane_instructions->TextSupport().SetText(rstl::wstring_l(L"")); + x308_textpane_instructions2->TextSupport().SetText(rstl::wstring_l(L"")); + } else { + x2fc_textpane_hint->TextSupport().SetText(rstl::wstring_l(L"")); - CTransform4f camRot(camXf.BuildMatrix3f(), CVector3f::Zero()); - CGraphics::SetModelMatrix( - mapXf * - CTransform4f::Translate(const_cast< CMapArea* >(mapa) - ->GetAreaPostTransform(*x24_world, locIt->xc_areaId.value) - .GetTranslation()) * - CTransform4f::Translate(mapa->GetAreaCenterPoint()) * - CTransform4f::Scale(objectScale) * camRot); + const wchar_t imagePrefix[] = L"&image="; + const wchar_t imageSuffix[] = L";"; + CStringTable* strTable = gpStringTable; - float beaconAlpha = 0.f; - if (locIt->x0_showBeacon == 1) { - beaconAlpha = locIt->x4_beaconAlpha; - } + rstl::wstring string; + string.reserve(0x100); + string.append(imagePrefix, -1); + { + rstl::string hexStr( + CBasics::Stringize("SI,0.6,1.0,%8.8X", gpTweakPlayerRes->x24_lStick[x2e4_lStickPos])); + string.append(CStringExtras::ConvertToUNICODE(hexStr)); + } + string.append(imageSuffix, -1); + string.append(strTable->GetString(0x2e), -1); + x300_textpane_instructions->TextSupport().SetText(string); - if (beaconAlpha > 0.f) { - CGraphics::SetTevOp(kTS_Stage0, CGraphics::kEnvModulate); - x3c_hintBeacon.GetObject()->Load(GX_TEXMAP0, CTexture::kCM_Repeat); - gpRender->SetBlendMode_AdditiveAlpha(); - CGraphics::StreamBegin(kP_TriangleStrip); - float beaconColorAlpha; - if (IsFullyOutOfMiniMapState()) { - beaconColorAlpha = 1.f; - } else { - beaconColorAlpha = xa8_renderState0.x34_alphaSurfaceVisited; - } - CColor beaconColor(0xff, 0xff, 0xff, - static_cast< uchar >(beaconAlpha * beaconColorAlpha * mapAlpha * 255.f)); - CGraphics::StreamColor(beaconColor); - CGraphics::StreamTexcoord(0.f, 1.f); - CGraphics::StreamVertex(CVector3f(-4.f, -8.f, 8.f)); - CGraphics::StreamTexcoord(0.f, 0.f); - CGraphics::StreamVertex(CVector3f(-4.f, -8.f, 0.f)); - CGraphics::StreamTexcoord(1.f, 1.f); - CGraphics::StreamVertex(CVector3f(4.f, -8.f, 8.f)); - CGraphics::StreamTexcoord(1.f, 0.f); - CGraphics::StreamVertex(CVector3f(4.f, -8.f, 0.f)); - CGraphics::StreamEnd(); - } + string.assign(imagePrefix, -1); + { + rstl::string hexStr( + CBasics::Stringize("SI,0.6,1.0,%8.8X", gpTweakPlayerRes->x4c_cStick[x2e8_rStickPos])); + string.append(CStringExtras::ConvertToUNICODE(hexStr)); + } + string.append(imageSuffix, -1); + string.append(strTable->GetString(0x2f), -1); + x304_textpane_instructions1->TextSupport().SetText(string); + + string.assign(imagePrefix, -1); + { + rstl::string hexStr( + CBasics::Stringize("%8.8X", gpTweakPlayerRes->x74_lTrigger[x2ec_lTriggerPos])); + string.append(CStringExtras::ConvertToUNICODE(hexStr)); } + string.append(imageSuffix, -1); + string.append(strTable->GetString(0x30), -1); + string.append(imagePrefix, -1); + { + rstl::string hexStr( + CBasics::Stringize("%8.8X", gpTweakPlayerRes->x80_rTrigger[x2f0_rTriggerPos])); + string.append(CStringExtras::ConvertToUNICODE(hexStr)); + } + string.append(imageSuffix, -1); + x308_textpane_instructions2->TextSupport().SetText(string); } } - gpRender->SetDepthReadWrite(false, false); - gpRender->SetAmbientColor(CColor::White()); - CGraphics::DisableAllLights(); + if (input.PY()) { + CSystemState& sysState = gpGameState->SystemState(); + int keyState = sysState.GetAutoMapperKeyState(); + if (keyState == 0) { + sysState.SetAutoMapperKeyState(1); + CSfxManager::SfxStart(0x5ac, 127, 64, false, CSfxManager::kMedPriority, false, + CSfxManager::kAllAreas); + } else if (keyState == 1) { + sysState.SetAutoMapperKeyState(2); + CSfxManager::SfxStart(0x5a6, 127, 64, false, CSfxManager::kMedPriority, false, + CSfxManager::kAllAreas); + } else if (keyState == 2) { + sysState.SetAutoMapperKeyState(0); + CSfxManager::SfxStart(0x5ad, 127, 64, false, CSfxManager::kMedPriority, false, + CSfxManager::kAllAreas); + } + } - if (x2c_frmeInitialized != NULL) { - float frmeAlpha; - if (IsFullyOutOfMiniMapState()) { - frmeAlpha = 1.f; - } else if (x1c0_nextState != kAMS_MiniMap) { - frmeAlpha = 0.f; - if (x1c4_interpDur > 0.f) - frmeAlpha = x1c8_interpTime / x1c4_interpDur; + if ((input.PZ() || input.PB()) && x328_ == 0) { + if (CanLeaveMapScreenInternal(mgr)) { + LeaveMapScreen(mgr); + } else if (NotHintNavigating()) { + BeginMapperStateTransition(kAMS_MapScreenUniverse, mgr); + x328_ = 1; + } + } +} + +void CAutoMapper::ProcessMapScreenInput(const CFinalInput& input, const CStateManager& mgr) { + CMatrix3f camRot(xa8_renderState0.x8_camOrientation.BuildTransform()); + if (x1bc_state == kAMS_MapScreen) { + if (input.PA() && x328_ == 0) { + if (HasCurrentMapUniverseWorld()) { + BeginMapperStateTransition(kAMS_MapScreenUniverse, mgr); + } + } + } else if (x1bc_state == kAMS_MapScreenUniverse && input.PA()) { + const CMapUniverse::CMapWorldData& mapuWld = x8_mapu.GetObject()->GetMapWorldData(x9c_worldIdx); + CVector3f pointLocal = + mapuWld.GetWorldTransform().GetQuickInverse() * xa8_renderState0.x20_areaPoint; + if (mapuWld.GetWorldAssetId() != gpGameState->CurrentWorldAssetId()) { + x32c_loadingDummyWorld = true; + CheckDummyWorldLoad(mgr); } else { - frmeAlpha = 0.f; - if (x1c4_interpDur > 0.f) - frmeAlpha = x1c8_interpTime / x1c4_interpDur; - frmeAlpha = 1.f - frmeAlpha; + x24_world = const_cast< CWorld* >(mgr.GetWorld()); + CWorldState& worldState = gpGameState->StateForWorld(x24_world->IGetWorldAssetId()); + CMapWorldInfo* mwInfo = worldState.GetMapWorldInfo().GetPtr(); + CVector3f camDir = camRot.GetColumn(kDY); + xa0_curAreaId.value = FindClosestVisibleArea( + pointLocal, CUnitVector3f(camDir.GetX(), camDir.GetY(), camDir.GetZ()), mgr, *x24_world, + *mwInfo); + BeginMapperStateTransition(kAMS_MapScreen, mgr); } + } - CGraphics::SetDepthRange(0.f, 0.f); - CGuiWidgetDrawParms parms(frmeAlpha, CVector3f::Zero()); - x2c_frmeInitialized->Draw(parms); - CGraphics::SetDepthRange(0.f, 1.f / 512.f); + x2f4_aButtonPos = 0; + if (input.PA()) { + x2f4_aButtonPos = 1; } -} -void CAutoMapper::OnNewInGameGuiState(EInGameGuiState state, CStateManager& mgr) { - if (state == kIGGS_MapScreen) { - CMain::EnsureWorldPaksReady(); - CWorld* wld = mgr.World(); - wld->GetMapWorld()->SetWhichMapAreasLoaded(*wld, 0, 9999); - SetupHintNavigation(); - BeginMapperStateTransition(kAMS_MapScreen, mgr); - x28_frmeMapScreen = rs_new TCachedToken< CGuiFrame >(gpSimplePool->GetObj(skFRME_MapScreen)); - x28_frmeMapScreen->Lock(); - SetResLockState(x210_lstick, true); - SetResLockState(x25c_cstick, true); - SetResLockState(x2a8_ltrigger, true); - SetResLockState(x2bc_rtrigger, true); - SetResLockState(x2d0_abutton, true); - } else { - CMain::EnsureWorldPakReady(gpGameState->CurrentWorldAssetId()); - if (x1bc_state == kAMS_MapScreenUniverse || x24_world == mgr.GetWorld()) { - BeginMapperStateTransition(kAMS_MiniMap, mgr); - x328_ = 0; - } - LeaveMapScreenState(); + bool inPlayerControl = false; + if (IsInMapperState(kAMS_MapScreen) || IsInMapperState(kAMS_MapScreenUniverse)) { + inPlayerControl = true; + } + if (inPlayerControl) { + x2e4_lStickPos = 0; + x2e8_rStickPos = 0; + x2ec_lTriggerPos = 0; + x2f0_rTriggerPos = 0; + ProcessMapRotateInput(input, mgr); + ProcessMapZoomInput(input, mgr); + ProcessMapPanInput(input, mgr); } } -void CAutoMapper::SetupHintNavigation() { - if (!gpGameState->GameOptions().GetIsHintSystemEnabled()) - return; +void CAutoMapper::ProcessMapRotateInput(const CFinalInput& input, const CStateManager& mgr) { + float up = ControlMapper::GetAnalogInput(ControlMapper::kC_MapCircleUp, input); + float down = ControlMapper::GetAnalogInput(ControlMapper::kC_MapCircleDown, input); + float left = ControlMapper::GetAnalogInput(ControlMapper::kC_MapCircleLeft, input); + float right = ControlMapper::GetAnalogInput(ControlMapper::kC_MapCircleRight, input); - rstl::list< SAutoMapperHintStep >::iterator stepEnd = x1e0_hintSteps.end(); - rstl::list< SAutoMapperHintStep >::iterator stepIt = x1e0_hintSteps.begin(); - while (stepIt != stepEnd) { - stepIt = x1e0_hintSteps.erase(stepIt); - } + int flags = 0; + if (up > 0.f) + flags += 2; + if (down > 0.f) + flags += 1; + if (left > 0.f) + flags += 4; + if (right > 0.f) + flags += 8; - rstl::list< SAutoMapperHintLocation >::iterator locEnd = x1f8_hintLocations.end(); - rstl::list< SAutoMapperHintLocation >::iterator locIt = x1f8_hintLocations.begin(); - while (locIt != locEnd) { - locIt = x1f8_hintLocations.erase(locIt); + switch (flags) { + case 1: + x2e4_lStickPos = 1; + break; + case 2: + x2e4_lStickPos = 5; + break; + case 4: + x2e4_lStickPos = 3; + break; + case 5: + x2e4_lStickPos = 2; + break; + case 6: + x2e4_lStickPos = 4; + break; + case 8: + x2e4_lStickPos = 7; + break; + case 9: + x2e4_lStickPos = 8; + break; + case 10: + x2e4_lStickPos = 6; + break; + default: + break; } - CHintOptions& hintOpts = gpGameState->HintOptions(); - const SHintState* curHint = hintOpts.GetCurrentDisplayedHint(); - bool navigating = false; - if (curHint != NULL && const_cast< SHintState* >(curHint)->CanContinue()) { - navigating = true; - x1e0_hintSteps.push_back(SAutoMapperHintStep(SAutoMapperHintStep::kHST_ShowBeacon, 0.75f)); - - int nextIdx = hintOpts.GetNextHintIdx(); - const CGameHintInfo::CGameHint& nextHint = GetGameHints()[nextIdx]; - CAssetId curMlvl = x24_world->IGetWorldAssetId(); - const rstl::vector< CGameHintInfo::SHintLocation >& locs = nextHint.GetLocations(); - for (int i = 0; i < static_cast< int >(locs.size()); ++i) { - const CGameHintInfo::SHintLocation& loc = locs[i]; - if (loc.x0_mlvlId != curMlvl) { - x1e0_hintSteps.push_back(SAutoMapperHintStep(SAutoMapperHintStep::kHST_SwitchToUniverse, 0)); - x1e0_hintSteps.push_back(SAutoMapperHintStep(SAutoMapperHintStep::kHST_PanToWorld, static_cast< int >(loc.x0_mlvlId))); - x1e0_hintSteps.push_back(SAutoMapperHintStep(SAutoMapperHintStep::kHST_SwitchToWorld, static_cast< int >(loc.x0_mlvlId))); - curMlvl = loc.x0_mlvlId; - } else { - x1e0_hintSteps.push_back(SAutoMapperHintStep(SAutoMapperHintStep::kHST_ZoomOut, 0)); - } - x1e0_hintSteps.push_back(SAutoMapperHintStep(SAutoMapperHintStep::kHST_PanToArea, loc.x8_areaId.value)); - x1e0_hintSteps.push_back(SAutoMapperHintStep(SAutoMapperHintStep::kHST_ZoomIn, 0)); - x1e0_hintSteps.push_back(SAutoMapperHintStep(SAutoMapperHintStep::kHST_ShowBeacon, 1.f)); - x1f8_hintLocations.push_back(SAutoMapperHintLocation(0, 0.f, loc.x0_mlvlId, loc.x8_areaId)); - } + float maxMag = up; + int dirSlot = 0; + if (down > up) { + maxMag = down; + dirSlot = 1; + } + if (left > maxMag) { + maxMag = left; + dirSlot = 2; + } + if (right > maxMag) { + maxMag = right; + dirSlot = 3; } - const rstl::vector< SHintState >& hintStates = hintOpts.GetHintStates(); - for (int i = 0; i < static_cast< int >(hintStates.size()); ++i) { - if (navigating && hintOpts.GetNextHintIdx() == i) - continue; - if (hintStates[i].x0_state != kHS_Displaying) - continue; - const CGameHintInfo::CGameHint& hint = GetGameHints()[i]; - const rstl::vector< CGameHintInfo::SHintLocation >& locs = hint.GetLocations(); - for (int j = 0; j < static_cast< int >(locs.size()); ++j) { - x1f8_hintLocations.push_back(SAutoMapperHintLocation(1, 1.f, locs[j].x0_mlvlId, locs[j].x8_areaId)); - } + float dirs2 = 0.f; + float dirs0 = 0.f; + float dirs1 = 0.f; + float dirs3 = 0.f; + switch (dirSlot) { + case 0: + dirs0 = maxMag; + break; + case 1: + dirs1 = maxMag; + break; + case 2: + dirs2 = maxMag; + break; + case 3: + dirs3 = maxMag; + break; + default: + break; } -} -bool CAutoMapper::CanLeaveMapScreen(const CStateManager& mgr) const { - bool ret = false; - if (x328_ == 3 && CanLeaveMapScreenInternal(mgr)) - ret = true; - return ret; -} + if (dirs0 > 0.f || dirs1 > 0.f || dirs2 > 0.f || dirs3 > 0.f) { + float deltaFrames = 60.f * input.Time(); + SetShouldRotatingSoundBePlaying(true); + float minCamRotateX = gpTweakAutoMapper->x14_minCamRotateX; + float maxCamRotateX = gpTweakAutoMapper->x18_maxCamRotateX; + CEulerAngles eulers = CEulerAngles::FromQuaternion(xa8_renderState0.x8_camOrientation); + CRelAngle angX(normalize_angle(eulers.GetX())); + CRelAngle angZ(normalize_angle(eulers.GetZ())); -bool CAutoMapper::CanLeaveMapScreenInternal(const CStateManager& mgr) const { - if (!NotHintNavigating()) - return false; - if (IsRenderStateInterpolating()) - return false; - if (IsInMapperState(kAMS_MapScreenUniverse)) - return true; - bool ret = false; - if (x24_world == mgr.GetWorld() && IsInMapperState(kAMS_MapScreen)) - ret = true; - return ret; + float dt = deltaFrames * gpTweakAutoMapper->x74_rotateDegPerFrame; + + angZ -= CRelAngle::FromDegrees(dt * dirs2); + angZ = CRelAngle(normalize_angle(angZ.AsRadians())); + angZ += CRelAngle::FromDegrees(dt * dirs3); + angZ = CRelAngle(normalize_angle(angZ.AsRadians())); + + angX -= CRelAngle::FromDegrees(dt * dirs0); + angX = CRelAngle(normalize_angle(angX.AsRadians())); + angX += CRelAngle::FromDegrees(dt * dirs1); + angX = CRelAngle(normalize_angle(angX.AsRadians())); + + float angXDeg = angX.AsDegrees(); + if (angXDeg > 180.f) + angXDeg -= 360.f; + float clampedX = CMath::Clamp(minCamRotateX, angXDeg, maxCamRotateX); + angX = CRelAngle(normalize_angle(CRelAngle::FromDegrees(clampedX).AsRadians())); + + CQuaternion q = CQuaternion::ZRotation(angZ) * CQuaternion::XRotation(angX) * + CQuaternion::YRotation(CRelAngle(0.f)); + xa8_renderState0.x8_camOrientation = q; + } else { + SetShouldRotatingSoundBePlaying(false); + } } -bool CAutoMapper::CheckDummyWorldLoad(const CStateManager& mgr) { - uint worldIdx = x9c_worldIdx; - IWorld* dummyWorld = x14_dummyWorlds[worldIdx].get(); - const CMapUniverse::CMapWorldData& mapuWld = x8_mapu.GetObject()->GetMapWorldData(worldIdx); - if (dummyWorld != NULL) { - if (dummyWorld->ICheckWorldComplete()) { - CWorldState& worldState = gpGameState->StateForWorld(dummyWorld->IGetWorldAssetId()); - CMapWorldInfo* mwInfo = worldState.GetMapWorldInfo().GetPtr(); +void CAutoMapper::ProcessMapZoomInput(const CFinalInput& input, const CStateManager& mgr) { + bool zoomIn = ControlMapper::GetDigitalInput(ControlMapper::kC_MapZoomIn, input); + bool zoomOut = ControlMapper::GetDigitalInput(ControlMapper::kC_MapZoomOut, input); - CVector3f localPoint(mapuWld.GetWorldTransform().GetQuickInverse() * xa8_renderState0.x20_areaPoint); - CMatrix3f camRot(xa8_renderState0.x8_camOrientation.BuildTransform()); - CVector3f camDir = camRot.GetColumn(kDY); - CUnitVector3f unitDir(camDir.GetX(), camDir.GetY(), camDir.GetZ()); - int aid = FindClosestVisibleArea(localPoint, unitDir, mgr, *dummyWorld, *mwInfo); - if (aid != -1) { - xa0_curAreaId = aid; - dummyWorld->IMapWorld()->RecalculateWorldSphere(*mwInfo, *dummyWorld); - x24_world = dummyWorld; - BeginMapperStateTransition(kAMS_MapScreen, mgr); - x32c_loadingDummyWorld = false; - return true; - } - x32c_loadingDummyWorld = false; - return false; - } - return true; + int curState = x324_zoomState; + int nextZoomState = 0; + float oldDist = xa8_renderState0.x18_camDist; + switch (curState) { + case 0: + if (zoomIn) + nextZoomState = 1; + else if (zoomOut) + nextZoomState = 2; + break; + case 1: + if (zoomIn) + nextZoomState = 1; + else if (zoomOut) + nextZoomState = 2; + break; + case 2: + if (zoomOut) + nextZoomState = 2; + else if (zoomIn) + nextZoomState = 1; + break; + default: + break; } - x32c_loadingDummyWorld = false; - return false; + + x324_zoomState = static_cast< EZoomState >(nextZoomState); + + float deltaFrames = 60.f * input.Time(); + float speedMult = x1bc_state == kAMS_MapScreen ? 1.f : 4.f; + float delta = gpTweakAutoMapper->x70_zoomUnitsPerFrame * (deltaFrames * speedMult); + + if (x324_zoomState == kZS_In) { + xa8_renderState0.x18_camDist = + GetClampedMapScreenCameraDistance(xa8_renderState0.x18_camDist - delta); + x2f0_rTriggerPos = 1; + x324_zoomState = kZS_In; + } else if (x324_zoomState == kZS_Out) { + xa8_renderState0.x18_camDist = + GetClampedMapScreenCameraDistance(xa8_renderState0.x18_camDist + delta); + x2ec_lTriggerPos = 1; + x324_zoomState = kZS_Out; + } + + if (oldDist == xa8_renderState0.x18_camDist) + SetShouldZoomingSoundBePlaying(false); + else + SetShouldZoomingSoundBePlaying(true); } -void CAutoMapper::UpdateHintNavigation(float dt, const CStateManager& mgr) { - SAutoMapperHintStep& nextStep = *x1e0_hintSteps.begin(); - float beaconTime = nextStep.x4_float; - bool oldProcessing = nextStep.x8_processing; - nextStep.x8_processing = true; - switch (nextStep.x0_type) { - case SAutoMapperHintStep::kHST_PanToArea: { - int areaId = nextStep.x4_areaId.value; - const CMapWorld* mapWorld = x24_world->IGetMapWorld(); - if (mapWorld->GetMapArea(areaId) != NULL) { - x160_renderState2 = xa8_renderState0; - x104_renderState1.x20_areaPoint = GetAreaPointOfInterest(mgr, areaId); - x104_renderState1.ResetInterpolation(); - x104_renderState1.x4c_pointEase = SAutoMapperRenderState::kE_Linear; - ResetInterpolationTimer(2.f * gpTweakAutoMapper->x6c_hintPanTime); - x1e0_hintSteps.pop_front(); +void CAutoMapper::ProcessMapPanInput(const CFinalInput& input, const CStateManager& mgr) { + float forward = ControlMapper::GetAnalogInput(ControlMapper::kC_MapMoveForward, input); + float back = ControlMapper::GetAnalogInput(ControlMapper::kC_MapMoveBack, input); + float left = ControlMapper::GetAnalogInput(ControlMapper::kC_MapMoveLeft, input); + float right = ControlMapper::GetAnalogInput(ControlMapper::kC_MapMoveRight, input); + + CMatrix3f camRot = xa8_renderState0.x8_camOrientation.BuildTransform(); + if (forward > 0.f || back > 0.f || left > 0.f || right > 0.f) { + float deltaFrames = 60.f * input.Time(); + float speed = GetFinalMapScreenCameraMoveSpeed(); + int flags = 0; + if (forward > 0.f) + flags += 1; + if (back > 0.f) + flags += 2; + if (left > 0.f) + flags += 4; + if (right > 0.f) + flags += 8; + + switch (flags) { + case 1: + x2e8_rStickPos = 1; + break; + case 2: + x2e8_rStickPos = 5; + break; + case 4: + x2e8_rStickPos = 3; + break; + case 5: + x2e8_rStickPos = 2; + break; + case 6: + x2e8_rStickPos = 4; + break; + case 8: + x2e8_rStickPos = 7; + break; + case 9: + x2e8_rStickPos = 8; + break; + case 10: + x2e8_rStickPos = 6; + break; + default: + break; } - break; - } - case SAutoMapperHintStep::kHST_PanToWorld: { - const CMapUniverse::CMapWorldData& mwData = - x8_mapu.GetObject()->GetMapWorldDataByWorldId(nextStep.x4_worldId); - CVector3f centerPoint = mwData.GetWorldCenterPoint(); - x160_renderState2 = xa8_renderState0; - x104_renderState1.x20_areaPoint = centerPoint; - x104_renderState1.ResetInterpolation(); - x104_renderState1.x4c_pointEase = SAutoMapperRenderState::kE_Linear; - ResetInterpolationTimer(2.f * gpTweakAutoMapper->x6c_hintPanTime); - x1e0_hintSteps.pop_front(); - break; - } - case SAutoMapperHintStep::kHST_SwitchToUniverse: { - if (HasCurrentMapUniverseWorld()) { - BeginMapperStateTransition(kAMS_MapScreenUniverse, mgr); - x1e0_hintSteps.pop_front(); + + CVector3f dirVec(right - left, 0.f, forward - back); + CVector3f deltaVec = camRot * (dirVec * deltaFrames * speed); + float newX = xa8_renderState0.x20_areaPoint.GetX() + deltaVec.GetX(); + float newY = xa8_renderState0.x20_areaPoint.GetY() + deltaVec.GetY(); + float newZ = xa8_renderState0.x20_areaPoint.GetZ() + deltaVec.GetZ(); + CVector3f newPoint(newX, newY, newZ); + if (deltaVec.Magnitude() > input.Time()) { + SetShouldPanningSoundBePlaying(true); } else { - rstl::list< SAutoMapperHintStep >::iterator end = x1e0_hintSteps.end(); - rstl::list< SAutoMapperHintStep >::iterator it = x1e0_hintSteps.begin(); - while (it != end) { - it = x1e0_hintSteps.erase(it); - } + SetShouldPanningSoundBePlaying(false); } - break; - } - case SAutoMapperHintStep::kHST_SwitchToWorld: { - x1e0_hintSteps.pop_front(); - x32c_loadingDummyWorld = true; - if (!CheckDummyWorldLoad(mgr)) { - rstl::list< SAutoMapperHintStep >::iterator end = x1e0_hintSteps.end(); - rstl::list< SAutoMapperHintStep >::iterator it = x1e0_hintSteps.begin(); - while (it != end) { - it = x1e0_hintSteps.erase(it); + + if (x1bc_state == kAMS_MapScreen) { + xa8_renderState0.x20_areaPoint = + x24_world->IGetMapWorld()->ConstrainToWorldVolume(newPoint, camRot.GetColumn(kDY)); + } else { + CVector3f localPoint = newPoint - x8_mapu.GetObject()->GetMapUniverseCenterPoint(); + if (localPoint.Magnitude() > x8_mapu.GetObject()->GetMapUniverseRadius()) { + newPoint = x8_mapu.GetObject()->GetMapUniverseCenterPoint() + + localPoint.AsNormalized() * x8_mapu.GetObject()->GetMapUniverseRadius(); } + xa8_renderState0.x20_areaPoint = newPoint; } - break; - } - case SAutoMapperHintStep::kHST_ShowBeacon: { - if (!oldProcessing) { - if (xa0_curAreaId == mgr.GetNextAreaId() && x24_world == mgr.GetWorld()) { - CSfxManager::SfxStart(0x56a, 127, 64, false, CSfxManager::kMedPriority, false, - CSfxManager::kAllAreas); + } else { + SetShouldPanningSoundBePlaying(false); + float speed = gpTweakAutoMapper->xe0_camPanUnitsPerFrame * GetBaseMapScreenCameraMoveSpeed(); + if (x1bc_state == kAMS_MapScreen) { + const CMapArea* area = x24_world->IGetMapWorld()->GetMapArea(xa0_curAreaId.value); + CVector3f worldPoint = + const_cast< CMapArea* >(area)->GetAreaPostTransform(*x24_world, xa0_curAreaId.value) * + area->GetAreaCenterPoint(); + CVector3f viewPoint = worldPoint - xa8_renderState0.x20_areaPoint; + if (viewPoint.Magnitude() < speed) { + xa8_renderState0.x20_areaPoint = worldPoint; } else { - CSfxManager::SfxStart(0x56b, 127, 64, false, CSfxManager::kMedPriority, false, - CSfxManager::kAllAreas); + xa8_renderState0.x20_areaPoint += viewPoint.AsNormalized() * speed; } - } - beaconTime -= dt; - if (beaconTime <= 0.f) - beaconTime = 0.f; - nextStep.x4_float = beaconTime; - rstl::list< SAutoMapperHintLocation >::iterator locIt = x1f8_hintLocations.begin(); - for (; locIt != x1f8_hintLocations.end(); ++locIt) { - if (x24_world->IGetWorldAssetId() == locIt->x8_worldId && - xa0_curAreaId == locIt->xc_areaId) { - locIt->x0_showBeacon = 1; - float alpha = beaconTime / 0.5f; - if (alpha >= 1.f) - alpha = 1.f; - locIt->x4_beaconAlpha = 1.f - alpha; - break; + } else { + CVector3f camDir = camRot.GetColumn(kDY); + SClosestWorldResult result = + FindClosestVisibleWorld(xa8_renderState0.x20_areaPoint, + CUnitVector3f(camDir.GetX(), camDir.GetY(), camDir.GetZ()), mgr); + const CTransform4f& hex = x8_mapu.GetObject() + ->GetMapWorldData(result.x0_worldIdx) + .GetMapAreaData(result.x4_areaIdx); + CVector3f areaToHex = hex.GetTranslation() - xa8_renderState0.x20_areaPoint; + if (areaToHex.Magnitude() < speed) { + xa8_renderState0.x20_areaPoint = hex.GetTranslation(); + } else { + xa8_renderState0.x20_areaPoint += areaToHex.AsNormalized() * speed; } } - if (0.f == beaconTime) { - x1e0_hintSteps.pop_front(); - } - break; - } - case SAutoMapperHintStep::kHST_ZoomOut: { - x160_renderState2 = xa8_renderState0; - x104_renderState1.x18_camDist = gpTweakAutoMapper->x10_maxCamDist; - x104_renderState1.ResetInterpolation(); - x104_renderState1.x48_camEase = SAutoMapperRenderState::kE_Linear; - ResetInterpolationTimer(0.5f); - x1e0_hintSteps.pop_front(); - break; } - case SAutoMapperHintStep::kHST_ZoomIn: { - x160_renderState2 = xa8_renderState0; - x104_renderState1.x18_camDist = gpTweakAutoMapper->x8_camDist; - x104_renderState1.ResetInterpolation(); - x104_renderState1.x48_camEase = SAutoMapperRenderState::kE_Linear; - ResetInterpolationTimer(0.5f); - x1e0_hintSteps.pop_front(); - break; +} + +void CAutoMapper::Draw(const CStateManager& mgr, const CTransform4f& xf, float alpha) const { + alpha *= gpGameState->GameOptions().GetHudAlpha(); + gpRender->SetBlendMode_AlphaBlended(); + CGraphics::SetCullMode(kCM_Front); + + float alphaInterp; + if (IsFullyOutOfMiniMapState()) { + alphaInterp = 1.f; + } else if (IsInMapperState(kAMS_MiniMap)) { + alphaInterp = alpha; + } else if (x1c0_nextState == kAMS_MiniMap) { + float t = GetInterp(); + alphaInterp = alpha * t + (1.f - t); + } else if (x1bc_state == kAMS_MiniMap) { + float t = GetInterp(); + alphaInterp = alpha * (1.f - t) + t; + } else { + alphaInterp = 1.f; } - default: - break; + + float aspect = static_cast< float >(xa8_renderState0.x0_viewportSize.GetX()) / + static_cast< float >(xa8_renderState0.x0_viewportSize.GetY()); + float camAngleRad = xa8_renderState0.x1c_camAngle * (1.f / 360.f) * (2.f * M_PIF); + float yScale = + xa8_renderState0.x18_camDist / static_cast< float >(tan(M_PIF / 2.f - 0.5f * camAngleRad)); + + CTransform4f camXf = + xa8_renderState0.x8_camOrientation.BuildTransform4f(xa8_renderState0.x20_areaPoint); + + CTransform4f distScale(1.f / (yScale * aspect), 0.f, 0.f, 0.f, 0.f, 0.001f, 0.f, 0.f, 0.f, 0.f, + 1.f / yScale, 0.f); + + CTransform4f tweakScale = CTransform4f::Scale(gpTweakAutoMapper->xc4_mapPlaneScaleX, 0.f, + gpTweakAutoMapper->xc8_mapPlaneScaleZ); + + CTransform4f planeXf = xf * tweakScale * distScale * camXf.GetQuickInverse(); + + float universeInterp = 0.f; + if (x1c0_nextState == kAMS_MapScreenUniverse) { + if (x1bc_state == kAMS_MapScreenUniverse) + universeInterp = 1.f; + else + universeInterp = GetInterp(); + } else if (x1bc_state == kAMS_MapScreenUniverse) { + universeInterp = 1.f - GetInterp(); } -} -void CAutoMapper::ProcessControllerInput(const CFinalInput& input, const CStateManager& mgr) { - if (!IsRenderStateInterpolating()) { - bool inPlayerControl = IsInMapperState(kAMS_MapScreen) || IsInMapperState(kAMS_MapScreenUniverse); - if (inPlayerControl) { - if (x32c_loadingDummyWorld) { - CheckDummyWorldLoad(mgr); - } else if (static_cast< int >(x1e0_hintSteps.size()) > 0) { - UpdateHintNavigation(input.Time(), mgr); - } else if (x328_ == 0) { - ProcessMapScreenInput(input, mgr); + const CTransform4f* preXf; + bool isMapScreenUniverse = + (x1bc_state == kAMS_MapScreenUniverse || x1c0_nextState == kAMS_MapScreenUniverse); + if (isMapScreenUniverse) + preXf = &x8_mapu.GetObject()->GetMapWorldData(x9c_worldIdx).GetWorldTransform(); + else + preXf = &CTransform4f::Identity(); + + float objectScale = xa8_renderState0.x18_camDist / gpTweakAutoMapper->xc_minCamDist; + float mapAlpha = alphaInterp * (1.f - universeInterp); + + if (IsFullyOutOfMiniMapState()) { + if (universeInterp < 1.f && x24_world != NULL) { + CWorldState& worldState = gpGameState->StateForWorld(x24_world->IGetWorldAssetId()); + const CMapWorldInfo& mwInfo = *worldState.GetMapWorldInfo().GetPtr(); + const CMapWorld* mw = x24_world->IGetMapWorld(); + + float hintFlash = 0.f; + if (!x1e0_hintSteps.empty() && + x1e0_hintSteps.begin()->x0_type == SAutoMapperHintStep::kHST_ShowBeacon) { + float hintStepFloat = x1e0_hintSteps.begin()->x4_float; + if (xa0_curAreaId == mgr.GetNextAreaId() && x24_world == mgr.GetWorld()) { + float pulseTime = static_cast< float >(fmod(hintStepFloat * 8.f, 1.0)); + hintFlash = 2.f * (pulseTime < 0.5f ? pulseTime : 1.f - pulseTime); + } else { + rstl::list< SAutoMapperHintLocation >::const_iterator locIt = x1f8_hintLocations.begin(); + for (; locIt != x1f8_hintLocations.end(); ++locIt) { + if (x24_world->IGetWorldAssetId() != locIt->x8_worldId) + continue; + if (xa0_curAreaId != locIt->xc_areaId) + continue; + float pulseTime = static_cast< float >( + fmod((1.f - rstl::max_val(0.f, (hintStepFloat - 0.5f) / 0.5f)) * 4.f, 1.0)); + hintFlash = 2.f * (pulseTime < 0.5f ? pulseTime : 1.f - pulseTime); + break; + } + } } + + int curArea = xa0_curAreaId.value; + CTransform4f modelXf = planeXf * *preXf; + const CMapWorld::CMapWorldDrawParms parms( + xa8_renderState0.x34_alphaSurfaceVisited * alphaInterp, + xa8_renderState0.x38_alphaOutlineVisited * alphaInterp, + xa8_renderState0.x3c_alphaSurfaceUnvisited * alphaInterp, + xa8_renderState0.x40_alphaOutlineUnvisited * alphaInterp, mapAlpha, mgr, modelXf, camXf, + *x24_world, mwInfo, 2.f, true, x1dc_playerFlashPulse, hintFlash, objectScale); + mw->Draw(parms, curArea, curArea, xa8_renderState0.x2c_drawDepth1, + xa8_renderState0.x30_drawDepth2, true); } + } else if (IsInMapperState(kAMS_MiniMap)) { + const CMapWorld* mw = x24_world->IGetMapWorld(); + CWorldState& worldState = gpGameState->StateForWorld(x24_world->IGetWorldAssetId()); + const CMapWorldInfo& mwInfo = *worldState.GetMapWorldInfo().GetPtr(); + const CMapWorld::CMapWorldDrawParms parms( + xa8_renderState0.x34_alphaSurfaceVisited * alphaInterp, + xa8_renderState0.x38_alphaOutlineVisited * alphaInterp, + xa8_renderState0.x3c_alphaSurfaceUnvisited * alphaInterp, + xa8_renderState0.x40_alphaOutlineUnvisited * alphaInterp, mapAlpha, mgr, planeXf, camXf, + *x24_world, mwInfo, 1.f, false, 0.f, 0.f, objectScale); + mw->Draw(parms, xa0_curAreaId.value, xa4_otherAreaId.value, xa8_renderState0.x2c_drawDepth1, + xa8_renderState0.x30_drawDepth2, false); + } else { + int curArea = xa0_curAreaId.value; + const CMapWorld* mw = x24_world->IGetMapWorld(); + CWorldState& worldState = gpGameState->StateForWorld(x24_world->IGetWorldAssetId()); + const CMapWorldInfo& mwInfo = *worldState.GetMapWorldInfo().GetPtr(); + CTransform4f modelXf = planeXf * *preXf; + const CMapWorld::CMapWorldDrawParms parms( + xa8_renderState0.x34_alphaSurfaceVisited * alphaInterp, + xa8_renderState0.x38_alphaOutlineVisited * alphaInterp, + xa8_renderState0.x3c_alphaSurfaceUnvisited * alphaInterp, + xa8_renderState0.x40_alphaOutlineUnvisited * alphaInterp, mapAlpha, mgr, modelXf, camXf, + *x24_world, mwInfo, 2.f, true, 0.f, 0.f, objectScale); + mw->Draw(parms, curArea, curArea, xa8_renderState0.x2c_drawDepth1, + xa8_renderState0.x30_drawDepth2, false); } - CMatrix3f camRot = xa8_renderState0.x8_camOrientation.BuildTransform(); - if (IsInMapperState(kAMS_MapScreen)) { - CWorldState& worldState = gpGameState->StateForWorld(x24_world->IGetWorldAssetId()); - CMapWorldInfo* mwInfo = worldState.GetMapWorldInfo().GetPtr(); - CVector3f camDir = camRot.GetColumn(kDY); - CUnitVector3f unitDir(camDir.GetX(), camDir.GetY(), camDir.GetZ()); - int aid = FindClosestVisibleArea(xa8_renderState0.x20_areaPoint, - unitDir, mgr, *x24_world, *mwInfo); - if (aid != xa0_curAreaId.value) { - xa0_curAreaId = aid; - xa8_renderState0.x2c_drawDepth1 = GetMapAreaMaxDrawDepth(mgr, xa0_curAreaId.value); - xa8_renderState0.x30_drawDepth2 = GetMapAreaMaxDrawDepth(mgr, xa0_curAreaId.value); - } - } else if (IsInMapperState(kAMS_MapScreenUniverse)) { - CMapUniverse* mapu = x8_mapu.GetObject(); - uint oldWldIdx = x9c_worldIdx; - if (static_cast< int >(x1e0_hintSteps.size()) > 0) { - SAutoMapperHintStep& nextStep = *x1e0_hintSteps.begin(); - if (nextStep.x0_type == SAutoMapperHintStep::kHST_PanToWorld || - nextStep.x0_type == SAutoMapperHintStep::kHST_SwitchToWorld) { - SetCurWorldAssetId(nextStep.x4_worldId); - } else { - CVector3f camDir = camRot.GetColumn(kDY); - CUnitVector3f unitDir(camDir.GetX(), camDir.GetY(), camDir.GetZ()); - SClosestWorldResult result = FindClosestVisibleWorld( - xa8_renderState0.x20_areaPoint, unitDir, mgr); - x9c_worldIdx = result.x0_worldIdx; - } - } else { - CVector3f camDir = camRot.GetColumn(kDY); - CUnitVector3f unitDir(camDir.GetX(), camDir.GetY(), camDir.GetZ()); - SClosestWorldResult result = FindClosestVisibleWorld( - xa8_renderState0.x20_areaPoint, unitDir, mgr); - x9c_worldIdx = result.x0_worldIdx; - } + if (universeInterp > 0.f) { + const CWorld* wld = mgr.GetWorld(); + CMapWorld* mapWorld = wld->GetMapWorld(); + const CMapArea* mapArea = mapWorld->GetMapArea(mgr.GetNextAreaId().value); + CTransform4f areaXf = + const_cast< CMapArea* >(mapArea)->GetAreaPostTransform(*wld, mgr.GetNextAreaId().value); - if (x9c_worldIdx != oldWldIdx) { - CAssetId curMlvl = gpGameState->CurrentWorldAssetId(); - for (uint i = 0; static_cast< int >(i) < static_cast< int >(x14_dummyWorlds.size()); ++i) { - const CMapUniverse::CMapWorldData& mwData = mapu->GetMapWorldData(i); - if (i == x9c_worldIdx && curMlvl != mwData.GetWorldAssetId()) { - if (gpResourceFactory->CanBuild(SObjectTag('MLVL', mwData.GetWorldAssetId()))) { - CDummyWorld* dw = rs_new CDummyWorld(mwData.GetWorldAssetId()); - rstl::auto_ptr< IWorld > newWorld(dw); - x14_dummyWorlds[i] = newWorld; - } - } else { - rstl::auto_ptr< IWorld > emptyWorld; - x14_dummyWorlds[i] = emptyWorld; - } - } - if (curMlvl == mapu->GetMapWorldData(x9c_worldIdx).GetWorldAssetId()) { - x24_world = const_cast< CWorld* >(mgr.GetWorld()); - } else { - x24_world = NULL; + CAssetId curWorldId = gpGameState->CurrentWorldAssetId(); + const CMapUniverse::CMapWorldData& mwData = + x8_mapu.GetObject()->GetMapWorldDataByWorldId(curWorldId); + CTransform4f universeAreaXf = mwData.GetWorldTransform() * areaXf; + + float minMag = FLT_MAX; + int hexIdx = -1; + for (int i = 0; i < static_cast< int >(mwData.GetNumMapAreaDatas()); ++i) { + CVector3f diff = universeAreaXf.GetTranslation() - mwData.GetMapAreaData(i).GetTranslation(); + float mag = diff.Magnitude(); + if (mag < minMag) { + hexIdx = i; + minMag = mag; } } + + const CMapUniverse::CMapUniverseDrawParms uParms(universeInterp, x9c_worldIdx, + gpGameState->CurrentWorldAssetId(), hexIdx, + x1dc_playerFlashPulse, mgr, planeXf, camXf); + x8_mapu.GetObject()->Draw(uParms, CVector3f::Zero(), 0.f, 0.f); } - if (x300_textpane_instructions != NULL) { - if (x78_areaHintDesc.valid() && x78_areaHintDesc->TryCache()) { - x2fc_textpane_hint->TextSupport().SetText( - rstl::wstring(x78_areaHintDesc->GetObject()->GetString(0))); - x304_textpane_instructions1->TextSupport().SetText(rstl::wstring_l(L"")); - x300_textpane_instructions->TextSupport().SetText(rstl::wstring_l(L"")); - x308_textpane_instructions2->TextSupport().SetText(rstl::wstring_l(L"")); - } else { - x2fc_textpane_hint->TextSupport().SetText(rstl::wstring_l(L"")); + if (!IsInMapperState(kAMS_MapScreenUniverse)) { + CTransform4f mapXf = planeXf * *preXf; + if (x24_world == mgr.GetWorld()) { + float func = CMath::Clamp( + 0.f, 0.5f * (1.f + CMath::FastSinR(5.f * CGraphics::GetSecondsMod900() - (M_PIF / 2.f))), + 1.f); + float scale = rstl::min_val( + 0.6f * gpTweakAutoMapper->x10_maxCamDist / gpTweakAutoMapper->xc_minCamDist, objectScale); - const wchar_t imagePrefix[] = L"&image="; - const wchar_t imageSuffix[] = L";"; - CStringTable* strTable = gpStringTable; + CEulerAngles eulers = + CEulerAngles::FromTransform(mgr.GetCameraManager()->GetCurrentCameraTransform(mgr)); + CRelAngle angle = CRelAngle(normalize_angle(eulers.GetZ())); - rstl::wstring string; - string.reserve(0x100); - string.append(imagePrefix, -1); - { - rstl::string hexStr( - CBasics::Stringize("SI,0.6,1.0,%8.8X", gpTweakPlayerRes->x24_lStick[x2e4_lStickPos])); - string.append(CStringExtras::ConvertToUNICODE(hexStr)); - } - string.append(imageSuffix, -1); - string.append(strTable->GetString(0x2e), -1); - x300_textpane_instructions->TextSupport().SetText(string); + CVector3f playerPos = CMapArea::GetAreaPostTranslate(*x24_world, mgr.GetNextAreaId().value) + + mgr.GetPlayer()->GetTranslation(); - string.assign(imagePrefix, -1); - { - rstl::string hexStr( - CBasics::Stringize("SI,0.6,1.0,%8.8X", gpTweakPlayerRes->x4c_cStick[x2e8_rStickPos])); - string.append(CStringExtras::ConvertToUNICODE(hexStr)); - } - string.append(imageSuffix, -1); - string.append(strTable->GetString(0x2f), -1); - x304_textpane_instructions1->TextSupport().SetText(string); + CTransform4f playerXf(CMatrix3f::RotateZ(angle), playerPos); - string.assign(imagePrefix, -1); - { - rstl::string hexStr( - CBasics::Stringize("%8.8X", gpTweakPlayerRes->x74_lTrigger[x2ec_lTriggerPos])); - string.append(CStringExtras::ConvertToUNICODE(hexStr)); - } - string.append(imageSuffix, -1); - string.append(strTable->GetString(0x30), -1); - string.append(imagePrefix, -1); - { - rstl::string hexStr( - CBasics::Stringize("%8.8X", gpTweakPlayerRes->x80_rTrigger[x2f0_rTriggerPos])); - string.append(CStringExtras::ConvertToUNICODE(hexStr)); + gpRender->SetModelMatrix(mapXf * playerXf * + CTransform4f::Scale(scale * (0.25f * func + 0.75f))); + + float colorAlpha; + if (IsFullyOutOfMiniMapState()) { + colorAlpha = 1.f; + } else { + colorAlpha = xa8_renderState0.x34_alphaSurfaceVisited; } - string.append(imageSuffix, -1); - x308_textpane_instructions2->TextSupport().SetText(string); + colorAlpha *= mapAlpha; + CColor modColor = + gpTweakAutoMapper->xf0_miniMapSamusModColor.WithAlphaModulatedBy(colorAlpha); + CModelFlags flags(CModelFlags::kT_Blend, static_cast< uchar >(0), + static_cast< CModelFlags::EFlags >(CModelFlags::kF_DepthCompare | + CModelFlags::kF_DepthGreater), + modColor); + x30_miniMapSamus.GetObject()->Draw(flags); } - } - if (input.PY()) { - CSystemState& sysState = gpGameState->SystemState(); - int keyState = sysState.GetAutoMapperKeyState(); - if (keyState == 0) { - sysState.SetAutoMapperKeyState(1); - CSfxManager::SfxStart(0x5ac, 127, 64, false, CSfxManager::kMedPriority, false, - CSfxManager::kAllAreas); - } else if (keyState == 1) { - sysState.SetAutoMapperKeyState(2); - CSfxManager::SfxStart(0x5a6, 127, 64, false, CSfxManager::kMedPriority, false, - CSfxManager::kAllAreas); - } else if (keyState == 2) { - sysState.SetAutoMapperKeyState(0); - CSfxManager::SfxStart(0x5ad, 127, 64, false, CSfxManager::kMedPriority, false, - CSfxManager::kAllAreas); - } - } + if (IsInMapperState(kAMS_MapScreen)) { + CAssetId wldMlvl = x24_world->IGetWorldAssetId(); + const CMapWorld* mw = x24_world->IGetMapWorld(); + rstl::list< SAutoMapperHintLocation >::const_iterator locIt = x1f8_hintLocations.begin(); + for (; locIt != x1f8_hintLocations.end(); ++locIt) { + if (locIt->x8_worldId != wldMlvl) + continue; + const CMapArea* mapa = mw->GetMapArea(locIt->xc_areaId.value); + if (mapa == NULL) + continue; - if ((input.PZ() || input.PB()) && x328_ == 0) { - if (CanLeaveMapScreenInternal(mgr)) { - LeaveMapScreen(mgr); - } else if (NotHintNavigating()) { - BeginMapperStateTransition(kAMS_MapScreenUniverse, mgr); - x328_ = 1; + CTransform4f camRot(camXf.BuildMatrix3f(), CVector3f::Zero()); + CGraphics::SetModelMatrix( + mapXf * + CTransform4f::Translate(const_cast< CMapArea* >(mapa) + ->GetAreaPostTransform(*x24_world, locIt->xc_areaId.value) + .GetTranslation()) * + CTransform4f::Translate(mapa->GetAreaCenterPoint()) * CTransform4f::Scale(objectScale) * + camRot); + + float beaconAlpha = 0.f; + if (locIt->x0_showBeacon == 1) { + beaconAlpha = locIt->x4_beaconAlpha; + } + + if (beaconAlpha > 0.f) { + CGraphics::SetTevOp(kTS_Stage0, CGraphics::kEnvModulate); + x3c_hintBeacon.GetObject()->Load(GX_TEXMAP0, CTexture::kCM_Repeat); + gpRender->SetBlendMode_AdditiveAlpha(); + CGraphics::StreamBegin(kP_TriangleStrip); + float beaconColorAlpha; + if (IsFullyOutOfMiniMapState()) { + beaconColorAlpha = 1.f; + } else { + beaconColorAlpha = xa8_renderState0.x34_alphaSurfaceVisited; + } + CColor beaconColor( + 0xff, 0xff, 0xff, + static_cast< uchar >(beaconAlpha * beaconColorAlpha * mapAlpha * 255.f)); + CGraphics::StreamColor(beaconColor); + CGraphics::StreamTexcoord(0.f, 1.f); + CGraphics::StreamVertex(CVector3f(-4.f, -8.f, 8.f)); + CGraphics::StreamTexcoord(0.f, 0.f); + CGraphics::StreamVertex(CVector3f(-4.f, -8.f, 0.f)); + CGraphics::StreamTexcoord(1.f, 1.f); + CGraphics::StreamVertex(CVector3f(4.f, -8.f, 8.f)); + CGraphics::StreamTexcoord(1.f, 0.f); + CGraphics::StreamVertex(CVector3f(4.f, -8.f, 0.f)); + CGraphics::StreamEnd(); + } + } } } -} -void CAutoMapper::ProcessMapScreenInput(const CFinalInput& input, const CStateManager& mgr) { - CMatrix3f camRot(xa8_renderState0.x8_camOrientation.BuildTransform()); - if (x1bc_state == kAMS_MapScreen) { - if (input.PA() && x328_ == 0) { - if (HasCurrentMapUniverseWorld()) { - BeginMapperStateTransition(kAMS_MapScreenUniverse, mgr); - } - } - } else if (x1bc_state == kAMS_MapScreenUniverse && input.PA()) { - const CMapUniverse::CMapWorldData& mapuWld = x8_mapu.GetObject()->GetMapWorldData(x9c_worldIdx); - CVector3f pointLocal = mapuWld.GetWorldTransform().GetQuickInverse() * xa8_renderState0.x20_areaPoint; - if (mapuWld.GetWorldAssetId() != gpGameState->CurrentWorldAssetId()) { - x32c_loadingDummyWorld = true; - CheckDummyWorldLoad(mgr); + gpRender->SetDepthReadWrite(false, false); + gpRender->SetAmbientColor(CColor::White()); + CGraphics::DisableAllLights(); + + if (x2c_frmeInitialized != NULL) { + float frmeAlpha; + if (IsFullyOutOfMiniMapState()) { + frmeAlpha = 1.f; + } else if (x1c0_nextState != kAMS_MiniMap) { + frmeAlpha = 0.f; + if (x1c4_interpDur > 0.f) + frmeAlpha = x1c8_interpTime / x1c4_interpDur; } else { - x24_world = const_cast< CWorld* >(mgr.GetWorld()); - CWorldState& worldState = gpGameState->StateForWorld(x24_world->IGetWorldAssetId()); - CMapWorldInfo* mwInfo = worldState.GetMapWorldInfo().GetPtr(); - CVector3f camDir = camRot.GetColumn(kDY); - xa0_curAreaId.value = FindClosestVisibleArea( - pointLocal, CUnitVector3f(camDir.GetX(), camDir.GetY(), camDir.GetZ()), - mgr, *x24_world, *mwInfo); - BeginMapperStateTransition(kAMS_MapScreen, mgr); + frmeAlpha = 0.f; + if (x1c4_interpDur > 0.f) + frmeAlpha = x1c8_interpTime / x1c4_interpDur; + frmeAlpha = 1.f - frmeAlpha; } - } - x2f4_aButtonPos = 0; - if (input.PA()) { - x2f4_aButtonPos = 1; + CGraphics::SetDepthRange(0.f, 0.f); + CGuiWidgetDrawParms parms(frmeAlpha, CVector3f::Zero()); + x2c_frmeInitialized->Draw(parms); + CGraphics::SetDepthRange(0.f, 1.f / 512.f); } +} - bool inPlayerControl = false; - if (IsInMapperState(kAMS_MapScreen) || IsInMapperState(kAMS_MapScreenUniverse)) { - inPlayerControl = true; - } - if (inPlayerControl) { - x2e4_lStickPos = 0; - x2e8_rStickPos = 0; - x2ec_lTriggerPos = 0; - x2f0_rTriggerPos = 0; - ProcessMapRotateInput(input, mgr); - ProcessMapZoomInput(input, mgr); - ProcessMapPanInput(input, mgr); +CAssetId CAutoMapper::GetAreaHintDescriptionString(CAssetId mreaId) { + const CHintOptions& hintOpts = gpGameState->HintOptions(); + const rstl::vector< SHintState >& hintStates = hintOpts.GetHintStates(); + for (int i = 0; i < static_cast< int >(hintStates.size()); ++i) { + if (hintStates[i].x0_state != kHS_Displaying) + continue; + const CGameHintInfo::CGameHint& hint = GetGameHints()[i]; + int numLocs = static_cast< int >(hint.x20_locations.size()); + for (int j = 0; j < numLocs; ++j) { + const CGameHintInfo::SHintLocation& loc = hint.x20_locations[j]; + if (loc.x4_mreaId != mreaId) + continue; + rstl::list< SAutoMapperHintLocation >::const_iterator locIt = x1f8_hintLocations.begin(); + for (; locIt != x1f8_hintLocations.end(); ++locIt) { + if (locIt->xc_areaId != loc.x8_areaId) + continue; + if (locIt->x4_beaconAlpha > 0.f) + return loc.xc_stringId; + } + } } + return kInvalidAssetId; } -void CAutoMapper::ProcessMapRotateInput(const CFinalInput& input, const CStateManager& mgr) { - float up = ControlMapper::GetAnalogInput(ControlMapper::kC_MapCircleUp, input); - float down = ControlMapper::GetAnalogInput(ControlMapper::kC_MapCircleDown, input); - float left = ControlMapper::GetAnalogInput(ControlMapper::kC_MapCircleLeft, input); - float right = ControlMapper::GetAnalogInput(ControlMapper::kC_MapCircleRight, input); +void CAutoMapper::Update(float dt, const CStateManager& mgr) { + if (IsFullyOutOfMiniMapState()) { + x1d8_flashTimer = static_cast< float >(fmod(x1d8_flashTimer + dt, 0.75)); + if (x1d8_flashTimer < 0.375f) { + x1dc_playerFlashPulse = x1d8_flashTimer / 0.375f; + } else { + x1dc_playerFlashPulse = (0.75f - x1d8_flashTimer) / 0.375f; + } + } - int flags = 0; - if (up > 0.f) - flags += 2; - if (down > 0.f) - flags += 1; - if (left > 0.f) - flags += 4; - if (right > 0.f) - flags += 8; + // Initialize frame widgets when map screen frame is loaded + if (x28_frmeMapScreen.get() != NULL && x2c_frmeInitialized == NULL) { + if (x28_frmeMapScreen->TryCache()) { + x2c_frmeInitialized = x28_frmeMapScreen->GetObject(); - switch (flags) { - case 1: - x2e4_lStickPos = 1; - break; - case 2: - x2e4_lStickPos = 5; - break; - case 4: - x2e4_lStickPos = 3; - break; - case 5: - x2e4_lStickPos = 2; - break; - case 6: - x2e4_lStickPos = 4; - break; - case 8: - x2e4_lStickPos = 7; - break; - case 9: - x2e4_lStickPos = 8; - break; - case 10: - x2e4_lStickPos = 6; - break; - default: - break; - } + CGuiTextPane* leftPane = + static_cast< CGuiTextPane* >(x2c_frmeInitialized->FindWidget("textpane_left")); + leftPane->TextSupport().SetText(rstl::wstring(gpStringTable->GetString(0x2a))); - float maxMag = up; - int dirSlot = 0; - if (down > up) { - maxMag = down; - dirSlot = 1; - } - if (left > maxMag) { - maxMag = left; - dirSlot = 2; - } - if (right > maxMag) { - maxMag = right; - dirSlot = 3; - } + CGuiTextPane* yicon = + static_cast< CGuiTextPane* >(x2c_frmeInitialized->FindWidget("textpane_yicon")); + yicon->TextSupport().SetText(rstl::wstring(gpStringTable->GetString(0x2b))); - float dirs2 = 0.f; - float dirs0 = 0.f; - float dirs1 = 0.f; - float dirs3 = 0.f; - switch (dirSlot) { - case 0: - dirs0 = maxMag; - break; - case 1: - dirs1 = maxMag; - break; - case 2: - dirs2 = maxMag; - break; - case 3: - dirs3 = maxMag; - break; - default: - break; - } + x2fc_textpane_hint = + static_cast< CGuiTextPane* >(x2c_frmeInitialized->FindWidget("textpane_hint")); + x300_textpane_instructions = + static_cast< CGuiTextPane* >(x2c_frmeInitialized->FindWidget("textpane_instructions")); + x304_textpane_instructions1 = + static_cast< CGuiTextPane* >(x2c_frmeInitialized->FindWidget("textpane_instructions1")); + x308_textpane_instructions2 = + static_cast< CGuiTextPane* >(x2c_frmeInitialized->FindWidget("textpane_instructions2")); + + CGuiTextPane* mapLegend = + static_cast< CGuiTextPane* >(x2c_frmeInitialized->FindWidget("textpane_mapLegend")); + mapLegend->TextSupport().SetWordWrap(false); + mapLegend->TextSupport().SetImageBaseline(true); + mapLegend->TextSupport().SetText(rstl::wstring(gpStringTable->GetString(0x31))); - if (dirs0 > 0.f || dirs1 > 0.f || dirs2 > 0.f || dirs3 > 0.f) { - float deltaFrames = 60.f * input.Time(); - SetShouldRotatingSoundBePlaying(true); - float minCamRotateX = gpTweakAutoMapper->x14_minCamRotateX; - float maxCamRotateX = gpTweakAutoMapper->x18_maxCamRotateX; - CEulerAngles eulers = CEulerAngles::FromQuaternion(xa8_renderState0.x8_camOrientation); - CRelAngle angX(normalize_angle(eulers.GetX())); - CRelAngle angZ(normalize_angle(eulers.GetZ())); + x30c_basewidget_leftPane = x2c_frmeInitialized->FindWidget("basewidget_leftPane"); + x310_basewidget_yButtonPane = x2c_frmeInitialized->FindWidget("basewidget_yButtonPane"); + x314_basewidget_bottomPane = x2c_frmeInitialized->FindWidget("basewidget_bottomPane"); - float dt = deltaFrames * gpTweakAutoMapper->x74_rotateDegPerFrame; + x2f8_textpane_areaname = + static_cast< CGuiTextPane* >(x2c_frmeInitialized->FindWidget("textpane_areaname")); + x2f8_textpane_areaname->SetDepthTest(false); + } + } - angZ -= CRelAngle::FromDegrees(dt * dirs2); - angZ = CRelAngle(normalize_angle(angZ.AsRadians())); - angZ += CRelAngle::FromDegrees(dt * dirs3); - angZ = CRelAngle(normalize_angle(angZ.AsRadians())); + // Update frame and text panes + if (x2c_frmeInitialized != NULL) { + x2c_frmeInitialized->Update(dt); - angX -= CRelAngle::FromDegrees(dt * dirs0); - angX = CRelAngle(normalize_angle(angX.AsRadians())); - angX += CRelAngle::FromDegrees(dt * dirs1); - angX = CRelAngle(normalize_angle(angX.AsRadians())); + CGuiTextPane* right1 = + static_cast< CGuiTextPane* >(x2c_frmeInitialized->FindWidget("textpane_right1")); + const wchar_t imagePrefix[] = L"&image="; + const wchar_t imageSuffix[] = L";"; + rstl::wstring string; - float angXDeg = angX.AsDegrees(); - if (angXDeg > 180.f) - angXDeg -= 360.f; - float clampedX = CMath::Clamp(minCamRotateX, angXDeg, maxCamRotateX); - angX = CRelAngle(normalize_angle(CRelAngle::FromDegrees(clampedX).AsRadians())); + if (x1bc_state == kAMS_MapScreenUniverse || + (x1bc_state == kAMS_MapScreen && HasCurrentMapUniverseWorld())) { + string.reserve(0x100); + string.append(imagePrefix, -1); + rstl::string hexStr( + CBasics::Stringize("%8.8X", gpTweakPlayerRes->x98_aButton[x2f4_aButtonPos])); + rstl::wstring unicodeHex = CStringExtras::ConvertToUNICODE(hexStr); + string.append(unicodeHex); + string.append(imageSuffix, -1); + } else { + string = rstl::wstring_l(L""); + } + right1->TextSupport().SetText(string); - CQuaternion q = CQuaternion::ZRotation(angZ) * - CQuaternion::XRotation(angX) * - CQuaternion::YRotation(CRelAngle(0.f)); - xa8_renderState0.x8_camOrientation = q; - } else { - SetShouldRotatingSoundBePlaying(false); + CGuiTextPane* right = + static_cast< CGuiTextPane* >(x2c_frmeInitialized->FindWidget("textpane_right")); + rstl::wstring rightString; + if (x1bc_state == kAMS_MapScreenUniverse) { + rightString = rstl::wstring_l(gpStringTable->GetString(0x2d)); + } else if (x1bc_state == kAMS_MapScreen && HasCurrentMapUniverseWorld()) { + rightString = rstl::wstring_l(gpStringTable->GetString(0x2c)); + } else { + rightString = rstl::wstring_l(L""); + } + right->TextSupport().SetText(rightString); } -} - -void CAutoMapper::ProcessMapZoomInput(const CFinalInput& input, const CStateManager& mgr) { - bool zoomIn = ControlMapper::GetDigitalInput(ControlMapper::kC_MapZoomIn, input); - bool zoomOut = ControlMapper::GetDigitalInput(ControlMapper::kC_MapZoomOut, input); - int curState = x324_zoomState; - int nextZoomState = 0; - float oldDist = xa8_renderState0.x18_camDist; - switch (curState) { + // Update pane positions + float dt2 = 2.f * dt; + switch (gpGameState->SystemState().GetAutoMapperKeyState()) { case 0: - if (zoomIn) - nextZoomState = 1; - else if (zoomOut) - nextZoomState = 2; + x318_leftPanePos -= dt2; + x31c_yButtonPanePos -= dt2; + x320_bottomPanePos -= dt2; break; case 1: - if (zoomIn) - nextZoomState = 1; - else if (zoomOut) - nextZoomState = 2; + x318_leftPanePos += dt2; + x31c_yButtonPanePos -= dt2; + x320_bottomPanePos -= dt2; break; case 2: - if (zoomOut) - nextZoomState = 2; - else if (zoomIn) - nextZoomState = 1; + x318_leftPanePos += dt2; + x31c_yButtonPanePos += dt2; + x320_bottomPanePos += dt2; break; default: break; } - x324_zoomState = static_cast< EZoomState >(nextZoomState); + if (x318_leftPanePos < 0.f) + x318_leftPanePos = 0.f; + else if (x318_leftPanePos > 1.f) + x318_leftPanePos = 1.f; + if (x31c_yButtonPanePos < 0.f) + x31c_yButtonPanePos = 0.f; + else if (x31c_yButtonPanePos > 1.f) + x31c_yButtonPanePos = 1.f; + if (x320_bottomPanePos < 0.f) + x320_bottomPanePos = 0.f; + else if (x320_bottomPanePos > 1.f) + x320_bottomPanePos = 1.f; - float deltaFrames = 60.f * input.Time(); - float speedMult = x1bc_state == kAMS_MapScreen ? 1.f : 4.f; - float delta = gpTweakAutoMapper->x70_zoomUnitsPerFrame * (deltaFrames * speedMult); + if (x30c_basewidget_leftPane != NULL) { + x30c_basewidget_leftPane->LocalTransform() = + CTransform4f::Translate(CVector3f(-15.f, 0.f, 0.f) * x318_leftPanePos) * + x30c_basewidget_leftPane->GetTransform(); + x30c_basewidget_leftPane->RecalculateTransforms(); + } - if (x324_zoomState == kZS_In) { - xa8_renderState0.x18_camDist = GetClampedMapScreenCameraDistance(xa8_renderState0.x18_camDist - delta); - x2f0_rTriggerPos = 1; - x324_zoomState = kZS_In; - } else if (x324_zoomState == kZS_Out) { - xa8_renderState0.x18_camDist = GetClampedMapScreenCameraDistance(xa8_renderState0.x18_camDist + delta); - x2ec_lTriggerPos = 1; - x324_zoomState = kZS_Out; + if (x310_basewidget_yButtonPane != NULL) { + x310_basewidget_yButtonPane->LocalTransform() = + CTransform4f::Translate(CVector3f(0.f, 0.f, -3.5f) * x31c_yButtonPanePos) * + x310_basewidget_yButtonPane->GetTransform(); + x310_basewidget_yButtonPane->RecalculateTransforms(); } - if (oldDist == xa8_renderState0.x18_camDist) - SetShouldZoomingSoundBePlaying(false); - else - SetShouldZoomingSoundBePlaying(true); -} + if (x314_basewidget_bottomPane != NULL) { + x314_basewidget_bottomPane->LocalTransform() = + CTransform4f::Translate(CVector3f(0.f, 0.f, -7.f) * x320_bottomPanePos) * + x314_basewidget_bottomPane->GetTransform(); + x314_basewidget_bottomPane->RecalculateTransforms(); + } -void CAutoMapper::ProcessMapPanInput(const CFinalInput& input, const CStateManager& mgr) { - float forward = ControlMapper::GetAnalogInput(ControlMapper::kC_MapMoveForward, input); - float back = ControlMapper::GetAnalogInput(ControlMapper::kC_MapMoveBack, input); - float left = ControlMapper::GetAnalogInput(ControlMapper::kC_MapMoveLeft, input); - float right = ControlMapper::GetAnalogInput(ControlMapper::kC_MapMoveRight, input); + // Update camera and area for minimap + if (IsInMapperState(kAMS_MiniMap)) { + xa8_renderState0.x8_camOrientation = GetMiniMapCameraOrientation(mgr); + float camDist = xa8_renderState0.x18_camDist; + float desiredDist = GetDesiredMiniMapCameraDistance(mgr); + if (CMath::AbsF(camDist - desiredDist) < 3.f) { + xa8_renderState0.x18_camDist = desiredDist; + } else if (camDist < desiredDist) { + xa8_renderState0.x18_camDist = camDist + 3.f; + } else { + xa8_renderState0.x18_camDist = camDist - 3.f; + } - CMatrix3f camRot = xa8_renderState0.x8_camOrientation.BuildTransform(); - if (forward > 0.f || back > 0.f || left > 0.f || right > 0.f) { - float deltaFrames = 60.f * input.Time(); - float speed = GetFinalMapScreenCameraMoveSpeed(); - int flags = 0; - if (forward > 0.f) - flags += 1; - if (back > 0.f) - flags += 2; - if (left > 0.f) - flags += 4; - if (right > 0.f) - flags += 8; + TAreaId curAid = x24_world->IGetCurrentAreaId(); + if (curAid != xa0_curAreaId) { + x160_renderState2 = xa8_renderState0; + x104_renderState1 = xa8_renderState0; + xa4_otherAreaId = xa0_curAreaId; + xa0_curAreaId = curAid; + CVector3f areaPoint = GetAreaPointOfInterest(mgr, xa0_curAreaId.value); + x104_renderState1.x20_areaPoint = areaPoint; + x104_renderState1.x44_viewportEase = SAutoMapperRenderState::kE_None; + x104_renderState1.x48_camEase = SAutoMapperRenderState::kE_None; + x104_renderState1.x4c_pointEase = SAutoMapperRenderState::kE_InOut; + x104_renderState1.x50_depth1Ease = SAutoMapperRenderState::kE_Linear; + x104_renderState1.x54_depth2Ease = SAutoMapperRenderState::kE_Linear; + x104_renderState1.x58_alphaEase = SAutoMapperRenderState::kE_None; + x104_renderState1.x2c_drawDepth1 = GetMapAreaMiniMapDrawDepth(); + x104_renderState1.x30_drawDepth2 = GetMapAreaMiniMapDrawDepth(); + x160_renderState2.x2c_drawDepth1 = GetMapAreaMiniMapDrawDepth() - 1.f; + x160_renderState2.x30_drawDepth2 = GetMapAreaMiniMapDrawDepth() - 1.f; + ResetInterpolationTimer(gpTweakAutoMapper->x6c_hintPanTime); + } + xa8_renderState0.x34_alphaSurfaceVisited = GetMapAreaMiniMapDrawAlphaSurfaceVisited(mgr); + xa8_renderState0.x38_alphaOutlineVisited = GetMapAreaMiniMapDrawAlphaOutlineVisited(mgr); + xa8_renderState0.x3c_alphaSurfaceUnvisited = GetMapAreaMiniMapDrawAlphaSurfaceUnvisited(mgr); + xa8_renderState0.x40_alphaOutlineUnvisited = GetMapAreaMiniMapDrawAlphaOutlineUnvisited(mgr); + } else if (x1c0_nextState == kAMS_MiniMap) { + float camDist = x104_renderState1.x18_camDist; + float desiredDist = GetDesiredMiniMapCameraDistance(mgr); + if (CMath::AbsF(camDist - desiredDist) < 3.f) { + xa8_renderState0.x18_camDist = desiredDist; + } else if (camDist < desiredDist) { + x104_renderState1.x18_camDist = camDist + 3.f; + } else { + x104_renderState1.x18_camDist = camDist - 3.f; + } + } else { + if (x1bc_state != kAMS_MiniMap && x1c0_nextState != kAMS_MiniMap && x24_world != NULL) { + const CMapWorld* mapWorld = x24_world->IGetMapWorld(); + CWorldState& worldState = gpGameState->StateForWorld(x24_world->IGetWorldAssetId()); + CMapWorldInfo* mwInfo = worldState.GetMapWorldInfo().GetPtr(); + mapWorld->RecalculateWorldSphere(*mwInfo, *x24_world); + } + } - switch (flags) { - case 1: - x2e8_rStickPos = 1; - break; - case 2: - x2e8_rStickPos = 5; - break; - case 4: - x2e8_rStickPos = 3; - break; - case 5: - x2e8_rStickPos = 2; - break; - case 6: - x2e8_rStickPos = 4; - break; - case 8: - x2e8_rStickPos = 7; - break; - case 9: - x2e8_rStickPos = 8; - break; - case 10: - x2e8_rStickPos = 6; - break; - default: - break; + // Update interpolation + if (IsRenderStateInterpolating()) { + float newTime = x1c8_interpTime + dt; + x1c8_interpTime = newTime < x1c4_interpDur ? newTime : x1c4_interpDur; + SAutoMapperRenderState::InterpolateWithClamp( + x160_renderState2, xa8_renderState0, x104_renderState1, x1c8_interpTime / x1c4_interpDur); + if (x1c8_interpTime == x1c4_interpDur && x328_ == 2) { + SetupMiniMapWorld(const_cast< CStateManager& >(mgr)); } + } else if (IsInMapperStateTransition()) { + CompleteMapperStateTransition(mgr); + } - CVector3f dirVec(right - left, 0.f, forward - back); - CVector3f deltaVec = camRot * (dirVec * deltaFrames * speed); - float newX = xa8_renderState0.x20_areaPoint.GetX() + deltaVec.GetX(); - float newY = xa8_renderState0.x20_areaPoint.GetY() + deltaVec.GetY(); - float newZ = xa8_renderState0.x20_areaPoint.GetZ() + deltaVec.GetZ(); - CVector3f newPoint(newX, newY, newZ); - if (deltaVec.Magnitude() > input.Time()) { - SetShouldPanningSoundBePlaying(true); + // Update map area string + CAssetId stringId = x88_mapAreaStringId; + if (IsInMapperState(kAMS_MapScreenUniverse)) { + IWorld* dummyWorld = x14_dummyWorlds[x9c_worldIdx].get(); + if (dummyWorld != NULL && dummyWorld->ICheckWorldComplete()) { + stringId = dummyWorld->IGetStringTableAssetId(); + } else if (x24_world != NULL) { + stringId = x24_world->IGetStringTableAssetId(); + } + } else if (x24_world != NULL) { + const IGameArea* area = x24_world->IGetAreaAlways(xa0_curAreaId); + CWorldState& worldState = gpGameState->StateForWorld(x24_world->IGetWorldAssetId()); + CMapWorldInfo* mwInfo = worldState.GetMapWorldInfo().GetPtr(); + bool showName = true; + if (!mwInfo->IsMapped(xa0_curAreaId)) { + if (!mwInfo->IsAreaVisited(xa0_curAreaId)) { + showName = false; + } + } + if (showName) { + stringId = area->IGetStringTableAssetId(); } else { - SetShouldPanningSoundBePlaying(false); + stringId = kInvalidAssetId; } + } - if (x1bc_state == kAMS_MapScreen) { - xa8_renderState0.x20_areaPoint = - x24_world->IGetMapWorld()->ConstrainToWorldVolume(newPoint, camRot.GetColumn(kDY)); + if (stringId != x88_mapAreaStringId) { + x88_mapAreaStringId = stringId; + if (x88_mapAreaStringId != kInvalidAssetId) { + x8c_mapAreaString = TCachedToken< CStringTable >( + gpSimplePool->GetObj(SObjectTag('STRG', x88_mapAreaStringId))); + x8c_mapAreaString->Lock(); } else { - CVector3f localPoint = newPoint - x8_mapu.GetObject()->GetMapUniverseCenterPoint(); - if (localPoint.Magnitude() > x8_mapu.GetObject()->GetMapUniverseRadius()) { - newPoint = x8_mapu.GetObject()->GetMapUniverseCenterPoint() + - localPoint.AsNormalized() * x8_mapu.GetObject()->GetMapUniverseRadius(); + x8c_mapAreaString = rstl::optional_object< TCachedToken< CStringTable > >(); + } + } + + if (x2f8_textpane_areaname != NULL) { + if (x8c_mapAreaString) { + if (x8c_mapAreaString->IsLoaded()) { + x2f8_textpane_areaname->TextSupport().SetText( + rstl::wstring(x8c_mapAreaString->GetObject()->GetString(0))); + } + } else { + x2f8_textpane_areaname->TextSupport().SetText(rstl::wstring_l(L"")); + } + } + + // Update hint description for map screen + if (IsInMapperState(kAMS_MapScreen)) { + const IGameArea* area = x24_world->IGetAreaAlways(xa0_curAreaId); + CAssetId hintDescId = GetAreaHintDescriptionString(area->IGetAreaAssetId()); + if (hintDescId != x74_areaHintDescId) { + x74_areaHintDescId = hintDescId; + if (x74_areaHintDescId != kInvalidAssetId) { + x78_areaHintDesc = TCachedToken< CStringTable >( + gpSimplePool->GetObj(SObjectTag('STRG', x74_areaHintDescId))); + x78_areaHintDesc->Lock(); + } else { + x78_areaHintDesc = rstl::optional_object< TCachedToken< CStringTable > >(); + } + } + } + + // Update dummy worlds + for (int i = 0; i < static_cast< int >(x14_dummyWorlds.size()); ++i) { + IWorld* dummyWorld = x14_dummyWorlds[i].get(); + if (dummyWorld != NULL) { + dummyWorld->ICheckWorldComplete(); + } + } +} + +void CAutoMapper::BeginMapperStateTransition(EAutoMapperState state, const CStateManager& mgr) { + if (state == x1c0_nextState) + return; + + if ((state == kAMS_MiniMap && x1c0_nextState != kAMS_MiniMap) || + (state != kAMS_MiniMap && x1c0_nextState == kAMS_MiniMap)) { + CSfxManager::KillAll(CSfxManager::kSC_PauseScreen); + } + + x1bc_state = x1c0_nextState; + x1c0_nextState = state; + x160_renderState2 = xa8_renderState0; + x104_renderState1 = xa8_renderState0; + + if (x1bc_state == kAMS_MiniMap && state == kAMS_MapScreen) { + x104_renderState1 = BuildMapScreenWorldRenderState(mgr, xa8_renderState0.x8_camOrientation, + xa0_curAreaId.value, false); + ResetInterpolationTimer(gpTweakAutoMapper->x64_openMapScreenTime); + } else if (x1bc_state == kAMS_MapScreen && state == kAMS_MiniMap) { + xa0_curAreaId = x24_world->IGetCurrentAreaId(); + x104_renderState1 = + BuildMiniMapWorldRenderState(mgr, xa8_renderState0.x8_camOrientation, xa0_curAreaId.value); + ResetInterpolationTimer(gpTweakAutoMapper->x68_closeMapScreenTime); + { + rstl::list< SAutoMapperHintLocation >::iterator end = x1f8_hintLocations.end(); + rstl::list< SAutoMapperHintLocation >::iterator it = x1f8_hintLocations.begin(); + while (it != end) { + it = x1f8_hintLocations.erase(it); } - xa8_renderState0.x20_areaPoint = newPoint; } - } else { - SetShouldPanningSoundBePlaying(false); - float speed = gpTweakAutoMapper->xe0_camPanUnitsPerFrame * GetBaseMapScreenCameraMoveSpeed(); - if (x1bc_state == kAMS_MapScreen) { - const CMapArea* area = x24_world->IGetMapWorld()->GetMapArea(xa0_curAreaId.value); - CVector3f worldPoint = const_cast< CMapArea* >(area)->GetAreaPostTransform( - *x24_world, xa0_curAreaId.value) * area->GetAreaCenterPoint(); - CVector3f viewPoint = worldPoint - xa8_renderState0.x20_areaPoint; - if (viewPoint.Magnitude() < speed) { - xa8_renderState0.x20_areaPoint = worldPoint; - } else { - xa8_renderState0.x20_areaPoint += viewPoint.AsNormalized() * speed; + } else if (x1bc_state == kAMS_MapScreen && state == kAMS_MapScreenUniverse) { + CSfxManager::SfxStart(0x592, 127, 64, false, CSfxManager::kMedPriority, false, + CSfxManager::kAllAreas); + x104_renderState1 = BuildMapScreenUniverseRenderState(mgr, xa8_renderState0.x8_camOrientation, + xa0_curAreaId.value); + TransformRenderStatesWorldToUniverse(); + ResetInterpolationTimer(gpTweakAutoMapper->xdc_switchToFromUniverseTime); + } else if (x1bc_state == kAMS_MapScreenUniverse && state == kAMS_MapScreen) { + CSfxManager::SfxStart(0x593, 127, 64, false, CSfxManager::kMedPriority, false, + CSfxManager::kAllAreas); + x104_renderState1 = BuildMapScreenWorldRenderState(mgr, xa8_renderState0.x8_camOrientation, + xa0_curAreaId.value, x1e0_hintSteps.size()); + TransformRenderStateWorldToUniverse(x104_renderState1); + ResetInterpolationTimer(gpTweakAutoMapper->xdc_switchToFromUniverseTime); + for (int i = 0; i < static_cast< int >(x14_dummyWorlds.size()); ++i) { + if (x14_dummyWorlds[i].get() != x24_world || x24_world == mgr.GetWorld()) { + rstl::auto_ptr< IWorld > empty; + x14_dummyWorlds[i] = empty; } - } else { - CVector3f camDir = camRot.GetColumn(kDY); - SClosestWorldResult result = FindClosestVisibleWorld( - xa8_renderState0.x20_areaPoint, CUnitVector3f(camDir.GetX(), camDir.GetY(), camDir.GetZ()), mgr); - const CTransform4f& hex = x8_mapu.GetObject()->GetMapWorldData(result.x0_worldIdx).GetMapAreaData(result.x4_areaIdx); - CVector3f areaToHex = hex.GetTranslation() - xa8_renderState0.x20_areaPoint; - if (areaToHex.Magnitude() < speed) { - xa8_renderState0.x20_areaPoint = hex.GetTranslation(); - } else { - xa8_renderState0.x20_areaPoint += areaToHex.AsNormalized() * speed; + } + } else if (x1bc_state == kAMS_MapScreenUniverse && state == kAMS_MiniMap) { + x24_world = const_cast< CWorld* >(mgr.GetWorld()); + xa0_curAreaId = x24_world->IGetCurrentAreaId(); + x104_renderState1 = + BuildMiniMapWorldRenderState(mgr, xa8_renderState0.x8_camOrientation, xa0_curAreaId.value); + SetCurWorldAssetId(x24_world->IGetWorldAssetId()); + TransformRenderStateWorldToUniverse(x104_renderState1); + ResetInterpolationTimer(gpTweakAutoMapper->x68_closeMapScreenTime); + { + rstl::list< SAutoMapperHintLocation >::iterator end = x1f8_hintLocations.end(); + rstl::list< SAutoMapperHintLocation >::iterator it = x1f8_hintLocations.begin(); + while (it != end) { + it = x1f8_hintLocations.erase(it); } } + for (int i = 0; i < static_cast< int >(x14_dummyWorlds.size()); ++i) { + rstl::auto_ptr< IWorld > empty; + x14_dummyWorlds[i] = empty; + } } } -#pragma inline_max_size(400) +void CAutoMapper::CompleteMapperStateTransition(const CStateManager& mgr) { + if (x1bc_state == kAMS_MapScreenUniverse) + TransformRenderStatesUniverseToWorld(); -bool CAutoMapper::NotHintNavigating() const { return x1e0_hintSteps.empty(); } + if (x1c0_nextState == kAMS_MapScreen) { + const CMapWorld* mw = x24_world->IGetMapWorld(); + mw->IsMapAreasStreaming(); + CWorldState& worldState = gpGameState->StateForWorld(x24_world->IGetWorldAssetId()); + CMapWorldInfo* mwInfo = worldState.GetMapWorldInfo().GetPtr(); + mw->RecalculateWorldSphere(*mwInfo, *x24_world); + x1d8_flashTimer = 0.f; + x1dc_playerFlashPulse = 0.f; + } -void CAutoMapper::UnmuteAllLoopedSounds() { - CSfxManager::SfxVolume(x1cc_panningSfx, 127); - CSfxManager::SfxVolume(x1d0_rotatingSfx, 127); - CSfxManager::SfxVolume(x1d4_zoomingSfx, 127); + if (x1c0_nextState == kAMS_MiniMap) { + x28_frmeMapScreen = NULL; + x2c_frmeInitialized = NULL; + x2fc_textpane_hint = NULL; + x300_textpane_instructions = NULL; + x304_textpane_instructions1 = NULL; + x308_textpane_instructions2 = NULL; + x2f8_textpane_areaname = NULL; + x30c_basewidget_leftPane = NULL; + x310_basewidget_yButtonPane = NULL; + x314_basewidget_bottomPane = NULL; + SetResLockState(x210_lstick, false); + SetResLockState(x25c_cstick, false); + SetResLockState(x2a8_ltrigger, false); + SetResLockState(x2bc_rtrigger, false); + SetResLockState(x2d0_abutton, false); + } + + if (x1c0_nextState == kAMS_MapScreenUniverse && x328_ == 1) + LeaveMapScreen(mgr); + + x1bc_state = x1c0_nextState; } -bool CAutoMapper::HasCurrentMapUniverseWorld() { - CMapUniverse* mapu = x8_mapu.GetObject(); - CAssetId mlvlId = x24_world->IGetWorldAssetId(); - int numWorlds = mapu->GetNumMapWorldDatas(); - for (int i = 0; i < numWorlds; ++i) { - if (mapu->GetMapWorldData(i).GetWorldAssetId() == mlvlId) - return true; - } - return false; +void CAutoMapper::ResetInterpolationTimer(float duration) { + x1c4_interpDur = duration; + x1c8_interpTime = 0.f; } -CAutoMapper::SAutoMapperRenderState CAutoMapper::BuildMiniMapWorldRenderState( - const CStateManager& stateMgr, const CQuaternion& rot, int area) const { +CAutoMapper::SAutoMapperRenderState +CAutoMapper::BuildMiniMapWorldRenderState(const CStateManager& stateMgr, const CQuaternion& rot, + int area) const { CQuaternion camOrient = GetMiniMapCameraOrientation(stateMgr); CQuaternion useOrient = (CQuaternion::Dot(rot, camOrient) >= 0.f) ? camOrient : camOrient.BuildEquivalent(); @@ -1398,8 +1761,9 @@ CAutoMapper::SAutoMapperRenderState CAutoMapper::BuildMiniMapWorldRenderState( return ret; } -CAutoMapper::SAutoMapperRenderState CAutoMapper::BuildMapScreenWorldRenderState( - const CStateManager& mgr, const CQuaternion& rot, int area, bool doingHint) const { +CAutoMapper::SAutoMapperRenderState +CAutoMapper::BuildMapScreenWorldRenderState(const CStateManager& mgr, const CQuaternion& rot, + int area, bool doingHint) const { float camDist = doingHint ? gpTweakAutoMapper->x10_maxCamDist : gpTweakAutoMapper->x8_camDist; SAutoMapperRenderState ret( GetMapScreenViewportSize(), rot, camDist, gpTweakAutoMapper->x1c_camAngle, @@ -1416,8 +1780,28 @@ CAutoMapper::SAutoMapperRenderState CAutoMapper::BuildMapScreenWorldRenderState( return ret; } -CAutoMapper::SAutoMapperRenderState CAutoMapper::BuildMapScreenUniverseRenderState( - const CStateManager& mgr, const CQuaternion& rot, int area) const { +CAutoMapper::SAutoMapperRenderState::SAutoMapperRenderState(const SAutoMapperRenderState& other) +: x0_viewportSize(other.x0_viewportSize) +, x8_camOrientation(other.x8_camOrientation) +, x18_camDist(other.x18_camDist) +, x1c_camAngle(other.x1c_camAngle) +, x20_areaPoint(other.x20_areaPoint) +, x2c_drawDepth1(other.x2c_drawDepth1) +, x30_drawDepth2(other.x30_drawDepth2) +, x34_alphaSurfaceVisited(other.x34_alphaSurfaceVisited) +, x38_alphaOutlineVisited(other.x38_alphaOutlineVisited) +, x3c_alphaSurfaceUnvisited(other.x3c_alphaSurfaceUnvisited) +, x40_alphaOutlineUnvisited(other.x40_alphaOutlineUnvisited) +, x44_viewportEase(other.x44_viewportEase) +, x48_camEase(other.x48_camEase) +, x4c_pointEase(other.x4c_pointEase) +, x50_depth1Ease(other.x50_depth1Ease) +, x54_depth2Ease(other.x54_depth2Ease) +, x58_alphaEase(other.x58_alphaEase) {} + +CAutoMapper::SAutoMapperRenderState +CAutoMapper::BuildMapScreenUniverseRenderState(const CStateManager& mgr, const CQuaternion& rot, + int area) const { SAutoMapperRenderState ret( GetMapScreenViewportSize(), rot, gpTweakAutoMapper->xd0_universeCamDist, gpTweakAutoMapper->x1c_camAngle, GetAreaPointOfInterest(mgr, area), @@ -1434,9 +1818,8 @@ CAutoMapper::SAutoMapperRenderState CAutoMapper::BuildMapScreenUniverseRenderSta void CAutoMapper::SetShouldPanningSoundBePlaying(bool shouldBePlaying) { if (shouldBePlaying) { if (!x1cc_panningSfx) - x1cc_panningSfx = CSfxManager::SfxStart(0x57E, 127, 64, false, - CSfxManager::kMedPriority, true, - CSfxManager::kAllAreas); + x1cc_panningSfx = CSfxManager::SfxStart(0x57E, 127, 64, false, CSfxManager::kMedPriority, + true, CSfxManager::kAllAreas); } else { CSfxManager::SfxStop(x1cc_panningSfx); x1cc_panningSfx.Clear(); @@ -1446,9 +1829,8 @@ void CAutoMapper::SetShouldPanningSoundBePlaying(bool shouldBePlaying) { void CAutoMapper::SetShouldZoomingSoundBePlaying(bool shouldBePlaying) { if (shouldBePlaying) { if (!x1d4_zoomingSfx) - x1d4_zoomingSfx = CSfxManager::SfxStart(0x560, 127, 64, false, - CSfxManager::kMedPriority, true, - CSfxManager::kAllAreas); + x1d4_zoomingSfx = CSfxManager::SfxStart(0x560, 127, 64, false, CSfxManager::kMedPriority, + true, CSfxManager::kAllAreas); } else { CSfxManager::SfxStop(x1d4_zoomingSfx); x1d4_zoomingSfx.Clear(); @@ -1458,131 +1840,14 @@ void CAutoMapper::SetShouldZoomingSoundBePlaying(bool shouldBePlaying) { void CAutoMapper::SetShouldRotatingSoundBePlaying(bool shouldBePlaying) { if (shouldBePlaying) { if (!x1d0_rotatingSfx) - x1d0_rotatingSfx = CSfxManager::SfxStart(0x55F, 127, 64, false, - CSfxManager::kMedPriority, true, - CSfxManager::kAllAreas); + x1d0_rotatingSfx = CSfxManager::SfxStart(0x55F, 127, 64, false, CSfxManager::kMedPriority, + true, CSfxManager::kAllAreas); } else { CSfxManager::SfxStop(x1d0_rotatingSfx); x1d0_rotatingSfx.Clear(); } } -void CAutoMapper::ResetInterpolationTimer(float duration) { - x1c4_interpDur = duration; - x1c8_interpTime = 0.f; -} - -void CAutoMapper::CompleteMapperStateTransition(const CStateManager& mgr) { - if (x1bc_state == kAMS_MapScreenUniverse) - TransformRenderStatesUniverseToWorld(); - - if (x1c0_nextState == kAMS_MapScreen) { - const CMapWorld* mw = x24_world->IGetMapWorld(); - mw->IsMapAreasStreaming(); - CWorldState& worldState = gpGameState->StateForWorld(x24_world->IGetWorldAssetId()); - CMapWorldInfo* mwInfo = worldState.GetMapWorldInfo().GetPtr(); - mw->RecalculateWorldSphere(*mwInfo, *x24_world); - x1d8_flashTimer = 0.f; - x1dc_playerFlashPulse = 0.f; - } - - if (x1c0_nextState == kAMS_MiniMap) { - x28_frmeMapScreen = NULL; - x2c_frmeInitialized = NULL; - x2fc_textpane_hint = NULL; - x300_textpane_instructions = NULL; - x304_textpane_instructions1 = NULL; - x308_textpane_instructions2 = NULL; - x2f8_textpane_areaname = NULL; - x30c_basewidget_leftPane = NULL; - x310_basewidget_yButtonPane = NULL; - x314_basewidget_bottomPane = NULL; - SetResLockState(x210_lstick, false); - SetResLockState(x25c_cstick, false); - SetResLockState(x2a8_ltrigger, false); - SetResLockState(x2bc_rtrigger, false); - SetResLockState(x2d0_abutton, false); - } - - if (x1c0_nextState == kAMS_MapScreenUniverse && x328_ == 1) - LeaveMapScreen(mgr); - - x1bc_state = x1c0_nextState; -} - -void CAutoMapper::BeginMapperStateTransition(EAutoMapperState state, const CStateManager& mgr) { - if (state == x1c0_nextState) - return; - - if ((state == kAMS_MiniMap && x1c0_nextState != kAMS_MiniMap) || - (state != kAMS_MiniMap && x1c0_nextState == kAMS_MiniMap)) { - CSfxManager::KillAll(CSfxManager::kSC_PauseScreen); - } - - x1bc_state = x1c0_nextState; - x1c0_nextState = state; - x160_renderState2 = xa8_renderState0; - x104_renderState1 = xa8_renderState0; - - if (x1bc_state == kAMS_MiniMap && state == kAMS_MapScreen) { - x104_renderState1 = - BuildMapScreenWorldRenderState(mgr, xa8_renderState0.x8_camOrientation, xa0_curAreaId.value, false); - ResetInterpolationTimer(gpTweakAutoMapper->x64_openMapScreenTime); - } else if (x1bc_state == kAMS_MapScreen && state == kAMS_MiniMap) { - xa0_curAreaId = x24_world->IGetCurrentAreaId(); - x104_renderState1 = - BuildMiniMapWorldRenderState(mgr, xa8_renderState0.x8_camOrientation, xa0_curAreaId.value); - ResetInterpolationTimer(gpTweakAutoMapper->x68_closeMapScreenTime); - { - rstl::list< SAutoMapperHintLocation >::iterator end = x1f8_hintLocations.end(); - rstl::list< SAutoMapperHintLocation >::iterator it = x1f8_hintLocations.begin(); - while (it != end) { - it = x1f8_hintLocations.erase(it); - } - } - } else if (x1bc_state == kAMS_MapScreen && state == kAMS_MapScreenUniverse) { - CSfxManager::SfxStart(0x592, 127, 64, false, CSfxManager::kMedPriority, false, - CSfxManager::kAllAreas); - x104_renderState1 = - BuildMapScreenUniverseRenderState(mgr, xa8_renderState0.x8_camOrientation, xa0_curAreaId.value); - TransformRenderStatesWorldToUniverse(); - ResetInterpolationTimer(gpTweakAutoMapper->xdc_switchToFromUniverseTime); - } else if (x1bc_state == kAMS_MapScreenUniverse && state == kAMS_MapScreen) { - CSfxManager::SfxStart(0x593, 127, 64, false, CSfxManager::kMedPriority, false, - CSfxManager::kAllAreas); - x104_renderState1 = - BuildMapScreenWorldRenderState(mgr, xa8_renderState0.x8_camOrientation, xa0_curAreaId.value, - x1e0_hintSteps.size()); - TransformRenderStateWorldToUniverse(x104_renderState1); - ResetInterpolationTimer(gpTweakAutoMapper->xdc_switchToFromUniverseTime); - for (int i = 0; i < static_cast< int >(x14_dummyWorlds.size()); ++i) { - if (x14_dummyWorlds[i].get() != x24_world || x24_world == mgr.GetWorld()) { - rstl::auto_ptr< IWorld > empty; - x14_dummyWorlds[i] = empty; - } - } - } else if (x1bc_state == kAMS_MapScreenUniverse && state == kAMS_MiniMap) { - x24_world = const_cast< CWorld* >(mgr.GetWorld()); - xa0_curAreaId = x24_world->IGetCurrentAreaId(); - x104_renderState1 = - BuildMiniMapWorldRenderState(mgr, xa8_renderState0.x8_camOrientation, xa0_curAreaId.value); - SetCurWorldAssetId(x24_world->IGetWorldAssetId()); - TransformRenderStateWorldToUniverse(x104_renderState1); - ResetInterpolationTimer(gpTweakAutoMapper->x68_closeMapScreenTime); - { - rstl::list< SAutoMapperHintLocation >::iterator end = x1f8_hintLocations.end(); - rstl::list< SAutoMapperHintLocation >::iterator it = x1f8_hintLocations.begin(); - while (it != end) { - it = x1f8_hintLocations.erase(it); - } - } - for (int i = 0; i < static_cast< int >(x14_dummyWorlds.size()); ++i) { - rstl::auto_ptr< IWorld > empty; - x14_dummyWorlds[i] = empty; - } - } -} - void CAutoMapper::LeaveMapScreenState() { SetShouldPanningSoundBePlaying(false); SetShouldZoomingSoundBePlaying(false); @@ -1639,8 +1904,9 @@ int CAutoMapper::FindClosestVisibleArea(const CVector3f& point, const CUnitVecto return closestArea; } -CAutoMapper::SClosestWorldResult CAutoMapper::FindClosestVisibleWorld( - const CVector3f& point, const CUnitVector3f& camDir, const CStateManager& mgr) const { +CAutoMapper::SClosestWorldResult +CAutoMapper::FindClosestVisibleWorld(const CVector3f& point, const CUnitVector3f& camDir, + const CStateManager& mgr) const { float minDist = 29999.f; int closestWorldIdx = x9c_worldIdx; int closestAreaIdx = xa0_curAreaId.value; @@ -1719,6 +1985,17 @@ float CAutoMapper::GetMapAreaMiniMapDrawAlphaOutlineUnvisited(const CStateManage ((1.f - mapAlphaInterp) * mgr.GetPlayer()->GetGunAlpha() + mapAlphaInterp); } +float CAutoMapper::GetClampedMapScreenCameraDistance(float value) const { + if (x1bc_state == kAMS_MapScreenUniverse) { + float clamped = CMath::Clamp(gpTweakAutoMapper->xd4_minUniverseCamDist, value, + gpTweakAutoMapper->xd8_maxUniverseCamDist); + return clamped; + } + float clamped = + CMath::Clamp(gpTweakAutoMapper->xc_minCamDist, value, gpTweakAutoMapper->x10_maxCamDist); + return clamped; +} + float CAutoMapper::GetDesiredMiniMapCameraDistance(const CStateManager& mgr) const { CWorldState& worldState = gpGameState->StateForWorld(x24_world->IGetWorldAssetId()); CMapWorldInfo* mwInfo = worldState.GetMapWorldInfo().GetPtr(); @@ -1727,10 +2004,12 @@ float CAutoMapper::GetDesiredMiniMapCameraDistance(const CStateManager& mgr) con const IGameArea* area = x24_world->IGetAreaAlways(xa0_curAreaId); const CMapArea* mapa = mw->GetMapArea(xa0_curAreaId.value); bool oneMiniMapArea = gpTweakAutoMapper->x4_24_showOneMiniMapArea; - for (int i = -1; i < (oneMiniMapArea ? 0 : static_cast< int >(area->IGetNumAttachedAreas())); ++i) { + for (int i = -1; i < (oneMiniMapArea ? 0 : static_cast< int >(area->IGetNumAttachedAreas())); + ++i) { TAreaId aid = i == -1 ? xa0_curAreaId : area->IGetAttachedAreaId(i); const CMapArea* attMapa = mw->GetMapArea(aid.value); - if (attMapa->GetIsVisibleToAutoMapper(mwInfo->IsWorldVisible(aid), mwInfo->IsAreaVisible(aid))) { + if (attMapa->GetIsVisibleToAutoMapper(mwInfo->IsWorldVisible(aid), + mwInfo->IsAreaVisible(aid))) { CAABox areaAABB(attMapa->GetBoundingBox().GetTransformedAABox( const_cast< CMapArea* >(attMapa)->GetAreaPostTransform(*x24_world, aid.value))); aabb.AccumulateBounds(areaAABB.GetMinPoint()); @@ -1738,35 +2017,25 @@ float CAutoMapper::GetDesiredMiniMapCameraDistance(const CStateManager& mgr) con } } - CVector3f xfPoint = const_cast< CMapArea* >(mapa)->GetAreaPostTransform(*x24_world, xa0_curAreaId.value) * - mapa->GetAreaCenterPoint(); + CVector3f xfPoint = + const_cast< CMapArea* >(mapa)->GetAreaPostTransform(*x24_world, xa0_curAreaId.value) * + mapa->GetAreaCenterPoint(); float diffXa = xfPoint.GetX() - aabb.GetMinPoint().GetX(); float diffXb = aabb.GetMaxPoint().GetX() - xfPoint.GetX(); - float maxX = max_val(diffXa, diffXb); + float maxX = rstl::max_val(diffXa, diffXb); float diffYa = xfPoint.GetY() - aabb.GetMinPoint().GetY(); float diffYb = aabb.GetMaxPoint().GetY() - xfPoint.GetY(); - float maxY = max_val(diffYa, diffYb); + float maxY = rstl::max_val(diffYa, diffYb); float diffZa = xfPoint.GetZ() - aabb.GetMinPoint().GetZ(); float diffZb = aabb.GetMaxPoint().GetZ() - xfPoint.GetZ(); - float maxZ = max_val(diffZa, diffZb); + float maxZ = rstl::max_val(diffZa, diffZb); CVector3f extent = mapa->GetBoundingBox().GetMaxPoint() - mapa->GetBoundingBox().GetMinPoint(); CVector3f maxMargin(maxX, maxY, maxZ); return (0.5f * (0.5f * extent.Magnitude()) + 0.5f * maxMargin.Magnitude()) * gpTweakAutoMapper->xc0_miniMapCamDistScale * - static_cast< float >(tan(M_PIF / 2.f - 0.5f * 2.f * M_PIF * - (xa8_renderState0.x1c_camAngle / 360.f))); -} - -float CAutoMapper::GetClampedMapScreenCameraDistance(float value) const { - if (x1bc_state == kAMS_MapScreenUniverse) { - float clamped = CMath::Clamp(gpTweakAutoMapper->xd4_minUniverseCamDist, value, - gpTweakAutoMapper->xd8_maxUniverseCamDist); - return clamped; - } - float clamped = CMath::Clamp(gpTweakAutoMapper->xc_minCamDist, value, - gpTweakAutoMapper->x10_maxCamDist); - return clamped; + static_cast< float >( + tan(M_PIF / 2.f - 0.5f * 2.f * M_PIF * (xa8_renderState0.x1c_camAngle / 360.f))); } float CAutoMapper::GetBaseMapScreenCameraMoveSpeed() const { @@ -1781,21 +2050,16 @@ float CAutoMapper::GetFinalMapScreenCameraMoveSpeed() const { return ret; } -bool CAutoMapper::IsRenderStateInterpolating() const { - return x1c8_interpTime < x1c4_interpDur; -} - -bool CAutoMapper::IsInMapperStateTransition() const { - return x1bc_state != x1c0_nextState; -} - bool CAutoMapper::IsInMapperState(EAutoMapperState state) const { return state == x1bc_state && state == x1c0_nextState; } +bool CAutoMapper::IsInMapperStateTransition() const { return x1bc_state != x1c0_nextState; } + +bool CAutoMapper::IsRenderStateInterpolating() const { return x1c8_interpTime < x1c4_interpDur; } + void CAutoMapper::TransformRenderStatesWorldToUniverse() { - const CTransform4f& xf = - x8_mapu.GetObject()->GetMapWorldData(x9c_worldIdx).GetWorldTransform(); + const CTransform4f& xf = x8_mapu.GetObject()->GetMapWorldData(x9c_worldIdx).GetWorldTransform(); CQuaternion rot = CQuaternion::FromMatrix(xf); x160_renderState2.x8_camOrientation = x160_renderState2.x8_camOrientation * rot; x160_renderState2.x20_areaPoint = xf * x160_renderState2.x20_areaPoint; @@ -1818,11 +2082,20 @@ void CAutoMapper::TransformRenderStatesUniverseToWorld() { } void CAutoMapper::TransformRenderStateWorldToUniverse(SAutoMapperRenderState& state) { - const CTransform4f& xf = - x8_mapu.GetObject()->GetMapWorldData(x9c_worldIdx).GetWorldTransform(); + const CTransform4f& xf = x8_mapu.GetObject()->GetMapWorldData(x9c_worldIdx).GetWorldTransform(); state.x20_areaPoint = xf * x104_renderState1.x20_areaPoint; } +void CAutoMapper::SetCurWorldAssetId(int mlvlId) { + int numWorlds = x8_mapu.GetObject()->GetNumMapWorldDatas(); + for (int i = 0; i < numWorlds; ++i) { + if (mlvlId == x8_mapu.GetObject()->GetMapWorldData(i).GetWorldAssetId()) { + x9c_worldIdx = i; + return; + } + } +} + void CAutoMapper::LeaveMapScreen(const CStateManager& mgr) const { CAutoMapper* self = const_cast< CAutoMapper* >(this); if (x1c0_nextState == kAMS_MapScreenUniverse) { @@ -1853,274 +2126,3 @@ void CAutoMapper::SetupMiniMapWorld(CStateManager& mgr) { wld->GetMapWorld()->SetWhichMapAreasLoaded(*wld, wld->GetCurrentAreaId().value, 3); x328_ = 3; } - -void CAutoMapper::SetCurWorldAssetId(int mlvlId) { - int numWorlds = x8_mapu.GetObject()->GetNumMapWorldDatas(); - for (int i = 0; i < numWorlds; ++i) { - if (mlvlId == x8_mapu.GetObject()->GetMapWorldData(i).GetWorldAssetId()) { - x9c_worldIdx = i; - return; - } - } -} - -CAutoMapper::SAutoMapperRenderState::SAutoMapperRenderState( - const CVector2i& viewportSize, const CQuaternion& camOrientation, float camDist, - float camAngle, const CVector3f& areaPoint, float drawDepth1, float drawDepth2, - float alphaSurfaceVisited, float alphaOutlineVisited, float alphaSurfaceUnvisited, - float alphaOutlineUnvisited) -: x0_viewportSize(viewportSize) -, x8_camOrientation(camOrientation) -, x18_camDist(camDist) -, x1c_camAngle(camAngle) -, x20_areaPoint(areaPoint) -, x2c_drawDepth1(drawDepth1) -, x30_drawDepth2(drawDepth2) -, x34_alphaSurfaceVisited(alphaSurfaceVisited) -, x38_alphaOutlineVisited(alphaOutlineVisited) -, x3c_alphaSurfaceUnvisited(alphaSurfaceUnvisited) -, x40_alphaOutlineUnvisited(alphaOutlineUnvisited) -, x44_viewportEase(kE_None) -, x48_camEase(kE_None) -, x4c_pointEase(kE_None) -, x50_depth1Ease(kE_None) -, x54_depth2Ease(kE_None) -, x58_alphaEase(kE_None) {} - -void CAutoMapper::SAutoMapperRenderState::InterpolateWithClamp( - const SAutoMapperRenderState& a, SAutoMapperRenderState& out, - const SAutoMapperRenderState& b, float t) { - float ct = CMath::Clamp(0.f, t, 1.f); - float easeIn = CMath::Clamp(0.f, ct * ct * ct, 1.f); - float omt = 1.f - ct; - float omtCubed = omt * omt * omt; - float easeOut = CMath::Clamp(0.f, 1.f - omtCubed, 1.f); - - float easeInOut; - if (ct >= 0.5f) { - easeInOut = CMath::Clamp(0.f, 0.5f * CMath::SqrtF(2.f * ct - 1.f) + 0.5f, 1.f); - } else { - easeInOut = CMath::Clamp(0.f, 1.f - (0.5f * CMath::SqrtF(2.f * omt - 1.f) + 0.5f), 1.f); - } - - float eases[5] = {0.f, ct, easeOut, easeIn, easeInOut}; - - if (b.x44_viewportEase != kE_None) { - float easeB = eases[b.x44_viewportEase]; - float easeA = 1.f - easeB; - out.x0_viewportSize = CVector2i( - static_cast< int >(easeA * a.x0_viewportSize.GetX() + easeB * b.x0_viewportSize.GetX()), - static_cast< int >(easeA * a.x0_viewportSize.GetY() + easeB * b.x0_viewportSize.GetY())); - } - - if (b.x48_camEase != kE_None) { - float easeB = eases[b.x48_camEase]; - out.x8_camOrientation = CQuaternion::Slerp(a.x8_camOrientation, b.x8_camOrientation, easeB); - float easeA = 1.f - easeB; - out.x18_camDist = a.x18_camDist * easeA + b.x18_camDist * easeB; - out.x1c_camAngle = a.x1c_camAngle * easeA + b.x1c_camAngle * easeB; - } - - if (b.x4c_pointEase != kE_None) { - float eB = eases[b.x4c_pointEase]; - float eA = 1.f - eB; - out.x20_areaPoint = CVector3f( - a.x20_areaPoint.GetX() * eA + b.x20_areaPoint.GetX() * eB, - a.x20_areaPoint.GetY() * eA + b.x20_areaPoint.GetY() * eB, - a.x20_areaPoint.GetZ() * eA + b.x20_areaPoint.GetZ() * eB); - } - - if (b.x50_depth1Ease != kE_None) { - float eB = eases[b.x50_depth1Ease]; - out.x2c_drawDepth1 = a.x2c_drawDepth1 * (1.f - eB) + b.x2c_drawDepth1 * eB; - } - - if (b.x54_depth2Ease != kE_None) { - float eB = eases[b.x54_depth2Ease]; - out.x30_drawDepth2 = a.x30_drawDepth2 * (1.f - eB) + b.x30_drawDepth2 * eB; - } - - if (b.x58_alphaEase != kE_None) { - float eB = eases[b.x58_alphaEase]; - float eA = 1.f - eB; - out.x34_alphaSurfaceVisited = a.x34_alphaSurfaceVisited * eA + b.x34_alphaSurfaceVisited * eB; - out.x38_alphaOutlineVisited = a.x38_alphaOutlineVisited * eA + b.x38_alphaOutlineVisited * eB; - out.x3c_alphaSurfaceUnvisited = a.x3c_alphaSurfaceUnvisited * eA + b.x3c_alphaSurfaceUnvisited * eB; - out.x40_alphaOutlineUnvisited = a.x40_alphaOutlineUnvisited * eA + b.x40_alphaOutlineUnvisited * eB; - } -} - -void CAutoMapper::SAutoMapperRenderState::ResetInterpolation() { - x44_viewportEase = kE_None; - x48_camEase = kE_None; - x4c_pointEase = kE_None; - x50_depth1Ease = kE_None; - x54_depth2Ease = kE_None; - x58_alphaEase = kE_None; -} - -CAutoMapper::SAutoMapperHintLocation::SAutoMapperHintLocation( - uint showBeacon, float beaconAlpha, CAssetId worldId, TAreaId areaId) -: x0_showBeacon(showBeacon) -, x4_beaconAlpha(beaconAlpha) -, x8_worldId(worldId) -, xc_areaId(areaId) {} - -CAutoMapper::SAutoMapperRenderState::SAutoMapperRenderState( - const SAutoMapperRenderState& other) -: x0_viewportSize(other.x0_viewportSize) -, x8_camOrientation(other.x8_camOrientation) -, x18_camDist(other.x18_camDist) -, x1c_camAngle(other.x1c_camAngle) -, x20_areaPoint(other.x20_areaPoint) -, x2c_drawDepth1(other.x2c_drawDepth1) -, x30_drawDepth2(other.x30_drawDepth2) -, x34_alphaSurfaceVisited(other.x34_alphaSurfaceVisited) -, x38_alphaOutlineVisited(other.x38_alphaOutlineVisited) -, x3c_alphaSurfaceUnvisited(other.x3c_alphaSurfaceUnvisited) -, x40_alphaOutlineUnvisited(other.x40_alphaOutlineUnvisited) -, x44_viewportEase(other.x44_viewportEase) -, x48_camEase(other.x48_camEase) -, x4c_pointEase(other.x4c_pointEase) -, x50_depth1Ease(other.x50_depth1Ease) -, x54_depth2Ease(other.x54_depth2Ease) -, x58_alphaEase(other.x58_alphaEase) {} - -CAutoMapper::CAutoMapper(CStateManager& stateMgr) -: x4_loadPhase(kLP_LoadResources) -, x8_mapu(gpSimplePool->GetObj("MAPU_MapUniverse")) -, x14_dummyWorlds() -, x24_world(stateMgr.World()) -, x28_frmeMapScreen() -, x30_miniMapSamus(gpSimplePool->GetObj("CMDL_MiniMapSamus")) -, x3c_hintBeacon(gpSimplePool->GetObj("TXTR_HintBeacon")) -, x48_mapIcons() -, x74_areaHintDescId(kInvalidAssetId) -, x78_areaHintDesc() -, x88_mapAreaStringId(kInvalidAssetId) -, x8c_mapAreaString() -, x9c_worldIdx(0) -, xa0_curAreaId(x24_world->IGetCurrentAreaId()) -, xa4_otherAreaId(xa0_curAreaId) -, xa8_renderState0(BuildMiniMapWorldRenderState(stateMgr, - CQuaternion::FromMatrix(stateMgr.GetCameraManager()->GetCurrentCamera(stateMgr).GetTransform()), - xa0_curAreaId.value)) -, x104_renderState1(xa8_renderState0) -, x160_renderState2(xa8_renderState0) -, x1bc_state(kAMS_MiniMap) -, x1c0_nextState(kAMS_MiniMap) -, x1c4_interpDur(0.f) -, x1c8_interpTime(0.f) -, x1cc_panningSfx() -, x1d0_rotatingSfx() -, x1d4_zoomingSfx() -, x1d8_flashTimer(0.f) -, x1dc_playerFlashPulse(0.f) -, x1e0_hintSteps() -, x1f8_hintLocations() -, x210_lstick() -, x25c_cstick() -, x2a8_ltrigger() -, x2bc_rtrigger() -, x2d0_abutton() -, x2e4_lStickPos(0) -, x2e8_rStickPos(0) -, x2ec_lTriggerPos(0) -, x2f0_rTriggerPos(0) -, x2f4_aButtonPos(0) -, x2f8_textpane_areaname(NULL) -, x2fc_textpane_hint(NULL) -, x300_textpane_instructions(NULL) -, x304_textpane_instructions1(NULL) -, x308_textpane_instructions2(NULL) -, x30c_basewidget_leftPane(NULL) -, x310_basewidget_yButtonPane(NULL) -, x314_basewidget_bottomPane(NULL) -, x318_leftPanePos(0.f) -, x31c_yButtonPanePos(0.f) -, x320_bottomPanePos(0.f) -, x324_zoomState(kZS_None) -, x328_(0) -, x32c_loadingDummyWorld(false) { - x8_mapu.Lock(); - x30_miniMapSamus.Lock(); - x3c_hintBeacon.Lock(); - - x48_mapIcons.push_back(gpSimplePool->GetObj(SObjectTag('TXTR', gpTweakPlayerRes->x4_saveStationIcon))); - x48_mapIcons.push_back(gpSimplePool->GetObj(SObjectTag('TXTR', gpTweakPlayerRes->x8_missileStationIcon))); - x48_mapIcons.push_back(gpSimplePool->GetObj(SObjectTag('TXTR', gpTweakPlayerRes->xc_elevatorIcon))); - x48_mapIcons.push_back(gpSimplePool->GetObj(SObjectTag('TXTR', gpTweakPlayerRes->x10_minesBreakFirstTopIcon))); - x48_mapIcons.push_back(gpSimplePool->GetObj(SObjectTag('TXTR', gpTweakPlayerRes->x14_minesBreakFirstBottomIcon))); - - for (CToken* it = x48_mapIcons.begin(); it != x48_mapIcons.end(); ++it) { - it->Lock(); - } - - for (int i = 0; i < 9; ++i) { - x210_lstick.push_back(gpSimplePool->GetObj( - SObjectTag('TXTR', ((const CAssetId*)((const char*)gpTweakPlayerRes + 0x24))[i]))); - x25c_cstick.push_back(gpSimplePool->GetObj( - SObjectTag('TXTR', ((const CAssetId*)((const char*)gpTweakPlayerRes + 0x4c))[i]))); - } - - for (int i = 0; i < 2; ++i) { - x2a8_ltrigger.push_back(gpSimplePool->GetObj( - SObjectTag('TXTR', ((const CAssetId*)((const char*)gpTweakPlayerRes + 0x74))[i]))); - x2bc_rtrigger.push_back(gpSimplePool->GetObj( - SObjectTag('TXTR', ((const CAssetId*)((const char*)gpTweakPlayerRes + 0x80))[i]))); - x2d0_abutton.push_back(gpSimplePool->GetObj( - SObjectTag('TXTR', ((const CAssetId*)((const char*)gpTweakPlayerRes + 0x98))[i]))); - } -} - -template < class T > -void CAutoMapper::SetResLockState(T& list, bool lock) { - CToken* it = list.data(); - for (; it != list.data() + list.size(); ++it) { - if (lock) { - it->Lock(); - } else { - it->Unlock(); - } - } -} - -bool CAutoMapper::CheckLoadComplete() { - switch (x4_loadPhase) { - case kLP_LoadResources: { - CToken* iconIt = x48_mapIcons.data(); - CToken* iconEnd = iconIt + x48_mapIcons.size(); - for (; iconIt != iconEnd; ++iconIt) { - if (!iconIt->IsLoaded()) - return false; - } - if (x30_miniMapSamus.TryCache()) { - if (x3c_hintBeacon.TryCache()) { - x4_loadPhase = kLP_LoadUniverse; - } else { - return false; - } - } else { - return false; - } - } - case kLP_LoadUniverse: { - if (x8_mapu.TryCache()) { - int numWorlds = x8_mapu.GetObject()->GetNumMapWorldDatas(); - rstl::auto_ptr< IWorld > dummy; - rstl::vector< rstl::auto_ptr< IWorld > > worlds(numWorlds, dummy); - x14_dummyWorlds = worlds; - SetCurWorldAssetId(x24_world->IGetWorldAssetId()); - x4_loadPhase = kLP_Done; - } else { - return false; - } - } - case kLP_Done: - return true; - default: - return false; - } -} - -CAutoMapper::~CAutoMapper() { CSfxManager::KillAll(CSfxManager::kSC_PauseScreen); } diff --git a/src/MetroidPrime/CEulerAngles.cpp b/src/MetroidPrime/CEulerAngles.cpp index af591be18..0fc6dd572 100644 --- a/src/MetroidPrime/CEulerAngles.cpp +++ b/src/MetroidPrime/CEulerAngles.cpp @@ -25,4 +25,42 @@ CEulerAngles CEulerAngles::FromTransform(const CTransform4f& xf) { return CEulerAngles(roll, pitch, 0.f); } -CEulerAngles CEulerAngles::FromQuaternion(const CQuaternion& quat) {} +CEulerAngles CEulerAngles::FromQuaternion(const CQuaternion& quat) { + const CVector3f& v = quat.GetVector(); + float quatDot = v.GetX() * v.GetX() + v.GetY() * v.GetY() + v.GetZ() * v.GetZ() + + quat.GetScalar() * quat.GetScalar(); + float t0 = quatDot > 0.f ? 2.f / quatDot : 0.f; + + float t0_z_z = t0 * v.GetZ() * v.GetZ(); + float t0_y_x = t0 * v.GetY() * v.GetX(); + float t0_x_x = t0 * v.GetX() * v.GetX(); + float t0_z_w = t0 * v.GetZ() * quat.GetScalar(); + float t0_y_y = t0 * v.GetY() * v.GetY(); + + float t1 = 1.f - (t0_x_x + t0_z_z); + float t2 = t0_y_x - t0_z_w; + float t3 = t1 * t1 + t2 * t2; + + float t0_y_w = t0 * v.GetY() * quat.GetScalar(); + float t0_z_x = t0 * v.GetZ() * v.GetX(); + float t0_z_y = t0 * v.GetZ() * v.GetY(); + float t0_x_w = t0 * v.GetX() * quat.GetScalar(); + + float one_minus_yy_zz = 1.f - (t0_y_y + t0_z_z); + float zx_plus_yw = t0_z_x + t0_y_w; + float zx_minus_yw = t0_z_x - t0_y_w; + float t5 = t0_z_y + t0_x_w; + float one_minus_xx_yy = 1.f - (t0_x_x + t0_y_y); + + float t4 = sqrt(t3); + + if (!close_enough(t4, 0.f)) { + double yaw = -atan2(t2, t1); + double pitch = -atan2(zx_minus_yw, one_minus_xx_yy); + double roll = -atan2(-t5, t4); + return CEulerAngles(roll, pitch, yaw); + } + double pitch = -atan2(-zx_plus_yw, one_minus_yy_zz); + double roll = -atan2(-t5, t4); + return CEulerAngles(roll, pitch, 0.f); +} diff --git a/src/MetroidPrime/CRippleManager.cpp b/src/MetroidPrime/CRippleManager.cpp index 53421e97c..99ea875ff 100644 --- a/src/MetroidPrime/CRippleManager.cpp +++ b/src/MetroidPrime/CRippleManager.cpp @@ -14,8 +14,6 @@ CRippleManager::CRippleManager(const CRippleManager& other) , x4_ripples(other.x4_ripples) , x14_alpha(other.x14_alpha) {} -CRippleManager::~CRippleManager() {} - void CRippleManager::Init(int maxRipples) { x4_ripples.resize(maxRipples); for (AUTO(it, x4_ripples.begin()); it != x4_ripples.end(); ++it) { diff --git a/src/MetroidPrime/CStateManager.cpp b/src/MetroidPrime/CStateManager.cpp index 456ab25f2..5b1b3ba93 100644 --- a/src/MetroidPrime/CStateManager.cpp +++ b/src/MetroidPrime/CStateManager.cpp @@ -1,3 +1,5 @@ +#pragma inline_max_size(250) + #include "MetroidPrime/CStateManager.hpp" #include "MetroidPrime/CControlMapper.hpp" diff --git a/src/MetroidPrime/CWeaponMgr.cpp b/src/MetroidPrime/CWeaponMgr.cpp index dae290d51..3f188c8cc 100644 --- a/src/MetroidPrime/CWeaponMgr.cpp +++ b/src/MetroidPrime/CWeaponMgr.cpp @@ -5,7 +5,7 @@ CWeaponMgr::CWeaponMgr() {} void CWeaponMgr::Remove(TUniqueId uid) { rstl::map< TUniqueId, Vec >::iterator iter = x0_weapons.find(uid); if (iter != x0_weapons.end()) { - x0_weapons.get_inner().erase(iter); + x0_weapons.erase(iter); } } diff --git a/src/MetroidPrime/Player/CPlayer.cpp b/src/MetroidPrime/Player/CPlayer.cpp index fa9a9a638..16059bb9a 100644 --- a/src/MetroidPrime/Player/CPlayer.cpp +++ b/src/MetroidPrime/Player/CPlayer.cpp @@ -1994,10 +1994,8 @@ CVector3f CPlayer::CalculateLeftStickEdgePosition(float strafeInput, float forwa float f1 = CMath::ArcTangentR(fabsf(forwardInput) / fabsf(strafeInput)); float f4 = CMath::Limit(f1 / (M_PIF / 4.f), 1.f); return CVector3f(f31, 0.f, 0.f) + - CVector3f(f4, f4, f4) * (CVector3f(f30, f29, 0.f) - CVector3f(f31, 0.f, 0.f)); - // or: - // CVector3f d = CVector3f(f30, f29, 0.f) - CVector3f(f31, 0.f, 0.f); - // return CVector3f(f31, 0.f, 0.f) + CVector3f(f4 * d.GetX(), f4 * d.GetY(), f4 * d.GetZ()); + CVector3f::ByElementMultiply(CVector3f(f4, f4, f4), + CVector3f(f30, f29, 0.f) - CVector3f(f31, 0.f, 0.f)); } bool CPlayer::AttachActorToPlayer(TUniqueId id, bool disableGun) { @@ -2740,7 +2738,8 @@ void CPlayer::FluidFXThink(EFluidState state, CScriptWater& water, CStateManager if (mgr.GetFluidPlaneManager()->GetLastSplashDeltaTime(GetUniqueId()) >= 0.2f) { CVector3f posOffset = x50c_moveDir; if (posOffset.CanBeNormalized()) { - posOffset = posOffset.AsNormalized() * CVector3f(1.2f, 1.2f, 0.f); + posOffset = + CVector3f::ByElementMultiply(posOffset.AsNormalized(), CVector3f(1.2f, 1.2f, 0.f)); } switch (state) { case kFS_EnteredFluid: { diff --git a/src/MetroidPrime/Weapons/CIceImpact.cpp b/src/MetroidPrime/Weapons/CIceImpact.cpp index 3778dcf7a..f14b94dbd 100644 --- a/src/MetroidPrime/Weapons/CIceImpact.cpp +++ b/src/MetroidPrime/Weapons/CIceImpact.cpp @@ -3,7 +3,7 @@ CMarkerGrid::CMarkerGrid(const CAABox& bounds) : mBounds(bounds) -, mGridUnits(CVector3f(0.0625f, 0.0625f, 0.0625f) * (mBounds.GetMaxPoint() - mBounds.GetMinPoint())) +, mGridUnits((mBounds.GetMaxPoint() - mBounds.GetMinPoint()) * 0.0625f) , mGridState(0) {} uint CMarkerGrid::GetValue(const uint x, const uint y, const uint z) const {