From 71a7498481fa56af26bfbca5d1b5911f66085a51 Mon Sep 17 00:00:00 2001 From: jonschz <17198703+jonschz@users.noreply.github.com> Date: Thu, 14 Nov 2024 21:42:38 +0100 Subject: [PATCH] Implement most of `JetskiRace`, add BETA10 annotations (#1149) * Implement most of `JetskiRace`, add BETA10 annotations * Fix declaration order issue * Clean up FUN_10012de0 * Fix regression * Address review comments * Address another review comment --------- Co-authored-by: jonschz --- LEGO1/lego/legoomni/include/jetskirace.h | 7 + LEGO1/lego/legoomni/include/legoactor.h | 2 +- LEGO1/lego/legoomni/include/legoentity.h | 1 + LEGO1/lego/legoomni/include/legorace.h | 26 ++- LEGO1/lego/legoomni/include/legoracers.h | 5 +- LEGO1/lego/legoomni/include/legostate.h | 5 + LEGO1/lego/legoomni/include/legoworld.h | 38 +++-- .../include/mxbackgroundaudiomanager.h | 8 +- .../src/audio/mxbackgroundaudiomanager.cpp | 29 ++-- LEGO1/lego/legoomni/src/common/misc.cpp | 2 + LEGO1/lego/legoomni/src/entity/legoworld.cpp | 17 +- .../legoomni/src/paths/legopathstruct.cpp | 1 + LEGO1/lego/legoomni/src/race/jetskirace.cpp | 153 ++++++++++++++++-- LEGO1/lego/legoomni/src/race/legorace.cpp | 10 +- LEGO1/lego/legoomni/src/race/legoracers.cpp | 24 +++ LEGO1/library_msvc.h | 3 + LEGO1/omni/include/mxentity.h | 2 + 17 files changed, 272 insertions(+), 61 deletions(-) diff --git a/LEGO1/lego/legoomni/include/jetskirace.h b/LEGO1/lego/legoomni/include/jetskirace.h index 77501f4f..ce1ea834 100644 --- a/LEGO1/lego/legoomni/include/jetskirace.h +++ b/LEGO1/lego/legoomni/include/jetskirace.h @@ -4,27 +4,33 @@ #include "legorace.h" // VTABLE: LEGO1 0x100d4fa8 +// VTABLE: BETA10 0x101bd5d0 // SIZE 0x2c class JetskiRaceState : public RaceState { public: // FUNCTION: LEGO1 0x1000dc40 + // FUNCTION: BETA10 0x100a8f30 const char* ClassName() const override // vtable+0x0c { // STRING: LEGO1 0x100f00ac + // STRING: BETA10 0x101f1d0c return "JetskiRaceState"; } // FUNCTION: LEGO1 0x1000dc50 + // FUNCTION: BETA10 0x100a8f60 MxBool IsA(const char* p_name) const override // vtable+0x10 { return !strcmp(p_name, JetskiRaceState::ClassName()) || RaceState::IsA(p_name); } // SYNTHETIC: LEGO1 0x1000f680 + // SYNTHETIC: BETA10 0x100a9d10 // JetskiRaceState::`scalar deleting destructor' }; // VTABLE: LEGO1 0x100d4fe8 +// VTABLE: BETA10 0x101bd268 // SIZE 0x144 class JetskiRace : public LegoRace { public: @@ -43,6 +49,7 @@ class JetskiRace : public LegoRace { } // FUNCTION: LEGO1 0x1000db00 + // FUNCTION: BETA10 0x100a8860 MxBool IsA(const char* p_name) const override // vtable+0x10 { return !strcmp(p_name, JetskiRace::ClassName()) || LegoRace::IsA(p_name); diff --git a/LEGO1/lego/legoomni/include/legoactor.h b/LEGO1/lego/legoomni/include/legoactor.h index d5641b72..75ea7e63 100644 --- a/LEGO1/lego/legoomni/include/legoactor.h +++ b/LEGO1/lego/legoomni/include/legoactor.h @@ -62,9 +62,9 @@ class LegoActor : public LegoEntity { static const char* GetActorName(MxU8 p_id); -protected: void Mute(MxBool p_muted); +protected: MxFloat m_frequencyFactor; // 0x68 LegoCacheSound* m_sound; // 0x6c MxFloat m_unk0x70; // 0x70 diff --git a/LEGO1/lego/legoomni/include/legoentity.h b/LEGO1/lego/legoomni/include/legoentity.h index 115ed813..26256c84 100644 --- a/LEGO1/lego/legoomni/include/legoentity.h +++ b/LEGO1/lego/legoomni/include/legoentity.h @@ -45,6 +45,7 @@ class LegoEntity : public MxEntity { } // FUNCTION: LEGO1 0x1000c300 + // FUNCTION: BETA10 0x100125a0 MxBool IsA(const char* p_name) const override // vtable+0x10 { return !strcmp(p_name, LegoEntity::ClassName()) || MxEntity::IsA(p_name); diff --git a/LEGO1/lego/legoomni/include/legorace.h b/LEGO1/lego/legoomni/include/legorace.h index ff463538..cad39de1 100644 --- a/LEGO1/lego/legoomni/include/legorace.h +++ b/LEGO1/lego/legoomni/include/legorace.h @@ -3,6 +3,7 @@ #include "decomp.h" #include "legogamestate.h" +#include "legoraceactor.h" #include "legoracemap.h" #include "legostate.h" #include "legoworld.h" @@ -17,6 +18,7 @@ class MxNotificationParam; class LegoPathStructNotificationParam; // VTABLE: LEGO1 0x100d5e30 +// VTABLE: BETA10 0x101be270 // SIZE 0x2c class RaceState : public LegoState { public: @@ -34,6 +36,7 @@ class RaceState : public LegoState { MxS16 GetUnknown0x02() { return m_unk0x02; } MxS16 GetHighScore() { return m_score; } + // FUNCTION: BETA10 0x100c96f0 MxResult Serialize(LegoFile* p_file) { if (p_file->IsReadMode()) { @@ -59,13 +62,16 @@ class RaceState : public LegoState { RaceState(); // FUNCTION: LEGO1 0x10016010 + // FUNCTION: BETA10 0x100a9040 const char* ClassName() const override // vtable+0x0c { // STRING: LEGO1 0x100f07d0 + // STRING: BETA10 0x101f1d20 return "RaceState"; } // FUNCTION: LEGO1 0x10016020 + // FUNCTION: BETA10 0x100a8fd0 MxBool IsA(const char* p_name) const override // vtable+0x10 { return !strcmp(p_name, RaceState::ClassName()) || LegoState::IsA(p_name); @@ -90,6 +96,7 @@ class RaceState : public LegoState { }; // VTABLE: LEGO1 0x100d5db0 +// VTABLE: BETA10 0x101be1e0 // SIZE 0x144 class LegoRace : public LegoWorld { public: @@ -113,6 +120,7 @@ class LegoRace : public LegoWorld { } // FUNCTION: LEGO1 0x10015bb0 + // FUNCTION: BETA10 0x100a88d0 MxBool IsA(const char* p_name) const override // vtable+0x10 { return !strcmp(p_name, LegoRace::ClassName()) || LegoWorld::IsA(p_name); @@ -132,8 +140,12 @@ class LegoRace : public LegoWorld { // FUNCTION: LEGO1 0x1000dab0 virtual MxLong HandleType0Notification(MxNotificationParam&) { return 0; } // vtable+0x78 - // STUB: LEGO1 0x1000dac0 - virtual void VTable0x7c(LegoRaceMap*, undefined4) {} // vtable+0x7c + // FUNCTION: LEGO1 0x1000dac0 + // FUNCTION: BETA10 0x100a87d0 + virtual void VTable0x7c(LegoRaceActor* p_map, MxU32 p_index) // vtable+0x7c + { + m_unk0x110[p_index] = (LegoRaceActor*) p_map; + } // SYNTHETIC: LEGO1 0x10015cc0 // LegoRace::`scalar deleting destructor' @@ -145,16 +157,14 @@ class LegoRace : public LegoWorld { undefined4 m_unk0x104; // 0x104 undefined4 m_unk0x108; // 0x108 undefined4 m_unk0x10c; // 0x10c - undefined4 m_unk0x110; // 0x110 - undefined4 m_unk0x114; // 0x114 - undefined4 m_unk0x118; // 0x118 + LegoRaceActor* m_unk0x110[3]; // 0x110 LegoGameState::Area m_destLocation; // 0x11c LegoPathActor* m_pathActor; // 0x120 Act1State* m_act1State; // 0x124 - undefined4 m_unk0x128; // 0x128 - undefined4 m_unk0x12c; // 0x12c + MxStillPresenter* m_unk0x128; // 0x128 + MxStillPresenter* m_unk0x12c; // 0x12c MxRect32 m_unk0x130; // 0x130 - undefined4 m_unk0x140; // 0x140 + RaceState* m_raceState; // 0x140 }; #endif // LEGORACE_H diff --git a/LEGO1/lego/legoomni/include/legoracers.h b/LEGO1/lego/legoomni/include/legoracers.h index ea8472d1..add7d795 100644 --- a/LEGO1/lego/legoomni/include/legoracers.h +++ b/LEGO1/lego/legoomni/include/legoracers.h @@ -75,6 +75,9 @@ class LegoRaceCar : public LegoCarRaceActor, public LegoRaceMap { virtual void FUN_10012ff0(float p_param); virtual MxU32 HandleSkeletonKicks(float p_param1); + static void FUN_10012de0(); + static void FUN_10013670(); + // SYNTHETIC: LEGO1 0x10014240 // LegoRaceCar::`scalar deleting destructor' @@ -108,10 +111,10 @@ class LegoRaceCar : public LegoCarRaceActor, public LegoRaceMap { static MxU32 g_srtsl6to10Index; static MxU32 g_emptySoundKeyListIndex; static MxU32 g_srtrhIndex; + static Mx3DPointFloat g_unk0x10102af0; static MxLong g_timeLastSoundPlayed; static MxS32 g_unk0x100f0b88; static MxBool g_unk0x100f0b8c; - static Mx3DPointFloat g_unk0x10102af0; }; #endif // LEGORACERS_H diff --git a/LEGO1/lego/legoomni/include/legostate.h b/LEGO1/lego/legoomni/include/legostate.h index f217573c..b8b9c47f 100644 --- a/LEGO1/lego/legoomni/include/legostate.h +++ b/LEGO1/lego/legoomni/include/legostate.h @@ -6,6 +6,7 @@ #include "mxcore.h" // VTABLE: LEGO1 0x100d46c0 +// VTABLE: BETA10 0x101b89d8 // SIZE 0x08 class LegoState : public MxCore { public: @@ -84,13 +85,16 @@ class LegoState : public MxCore { ~LegoState() override {} // FUNCTION: LEGO1 0x100060d0 + // FUNCTION: BETA10 0x10017d20 const char* ClassName() const override // vtable+0x0c { // STRING: LEGO1 0x100f01b8 + // STRING: BETA10 0x101dcdac return "LegoState"; } // FUNCTION: LEGO1 0x100060e0 + // FUNCTION: BETA10 0x100a9000 MxBool IsA(const char* p_name) const override // vtable+0x10 { return !strcmp(p_name, LegoState::ClassName()) || MxCore::IsA(p_name); @@ -103,6 +107,7 @@ class LegoState : public MxCore { virtual MxBool Reset() { return FALSE; } // vtable+0x18 // FUNCTION: LEGO1 0x10005fb0 + // FUNCTION: BETA10 0x10017af0 virtual MxResult Serialize(LegoFile* p_file) { if (p_file->IsWriteMode()) { diff --git a/LEGO1/lego/legoomni/include/legoworld.h b/LEGO1/lego/legoomni/include/legoworld.h index d248ab5b..e2aaf8f4 100644 --- a/LEGO1/lego/legoomni/include/legoworld.h +++ b/LEGO1/lego/legoomni/include/legoworld.h @@ -28,6 +28,7 @@ struct CoreSetCompare { typedef set MxCoreSet; // VTABLE: LEGO1 0x100d6280 +// VTABLE: BETA10 0x101befd8 // SIZE 0xf8 class LegoWorld : public LegoEntity { public: @@ -46,6 +47,7 @@ class LegoWorld : public LegoEntity { MxResult Tickle() override; // vtable+0x08 // FUNCTION: LEGO1 0x1001d690 + // FUNCTION: BETA10 0x10017660 const char* ClassName() const override // vtable+0x0c { // STRING: LEGO1 0x100f0058 @@ -53,6 +55,7 @@ class LegoWorld : public LegoEntity { } // FUNCTION: LEGO1 0x1001d6a0 + // FUNCTION: BETA10 0x100175f0 MxBool IsA(const char* p_name) const override // vtable+0x10 { return !strcmp(p_name, LegoWorld::ClassName()) || LegoEntity::IsA(p_name); @@ -104,7 +107,7 @@ class LegoWorld : public LegoEntity { MxS32 GetWorldId() { return m_worldId; } MxBool GetUnknown0xd0Empty() { return m_set0xd0.empty(); } list& GetROIList() { return m_roiList; } - LegoHideAnimPresenter* GetHideAnimPresenter() { return m_hideAnimPresenter; } + LegoHideAnimPresenter* GetHideAnimPresenter() { return m_hideAnim; } void SetWorldId(MxS32 p_worldId) { m_worldId = p_worldId; } @@ -112,21 +115,24 @@ class LegoWorld : public LegoEntity { // LegoWorld::`scalar deleting destructor' protected: - LegoPathControllerList m_list0x68; // 0x68 - MxPresenterList m_animPresenters; // 0x80 - LegoCameraController* m_cameraController; // 0x98 - LegoEntityList* m_entityList; // 0x9c - LegoCacheSoundList* m_cacheSoundList; // 0xa0 - MxBool m_destroyed; // 0xa4 - MxCoreSet m_set0xa8; // 0xa8 - MxPresenterList m_controlPresenters; // 0xb8 - MxCoreSet m_set0xd0; // 0xd0 - list m_roiList; // 0xe0 - MxS32 m_worldId; // 0xec - LegoHideAnimPresenter* m_hideAnimPresenter; // 0xf0 - MxS16 m_startupTicks; // 0xf4 - MxBool m_worldStarted; // 0xf6 - undefined m_unk0xf7; // 0xf7 + LegoPathControllerList m_list0x68; // 0x68 + MxPresenterList m_animPresenters; // 0x80 + LegoCameraController* m_cameraController; // 0x98 + LegoEntityList* m_entityList; // 0x9c + LegoCacheSoundList* m_cacheSoundList; // 0xa0 + MxBool m_destroyed; // 0xa4 + MxCoreSet m_set0xa8; // 0xa8 + MxPresenterList m_controlPresenters; // 0xb8 + MxCoreSet m_set0xd0; // 0xd0 + list m_roiList; // 0xe0 + MxS32 m_worldId; // 0xec + + // name verified by BETA10 0x100c7f59 + LegoHideAnimPresenter* m_hideAnim; // 0xf0 + + MxS16 m_startupTicks; // 0xf4 + MxBool m_worldStarted; // 0xf6 + undefined m_unk0xf7; // 0xf7 }; // clang-format off diff --git a/LEGO1/lego/legoomni/include/mxbackgroundaudiomanager.h b/LEGO1/lego/legoomni/include/mxbackgroundaudiomanager.h index 88bec8b1..210f646c 100644 --- a/LEGO1/lego/legoomni/include/mxbackgroundaudiomanager.h +++ b/LEGO1/lego/legoomni/include/mxbackgroundaudiomanager.h @@ -35,7 +35,7 @@ class MxBackgroundAudioManager : public MxCore { void StartAction(MxParam& p_param); void StopAction(MxParam& p_param); - MxResult PlayMusic(MxDSAction& p_action, undefined4 p_unk0x140, MxPresenter::TickleState p_tickleState); + MxResult PlayMusic(MxDSAction& p_action, undefined4 p_speed, MxPresenter::TickleState p_tickleState); void FUN_1007ee70(); void FUN_1007ef40(); @@ -48,7 +48,7 @@ class MxBackgroundAudioManager : public MxCore { void Stop(); void LowerVolume(); void RaiseVolume(); - undefined4 FUN_1007f610(MxPresenter* p_unk0x138, MxS32 p_unk0x140, MxPresenter::TickleState p_tickleState); + undefined4 FUN_1007f610(MxPresenter* p_unk0x138, MxS32 p_speed, MxPresenter::TickleState p_tickleState); // SYNTHETIC: LEGO1 0x1007ec00 // MxBackgroundAudioManager::`scalar deleting destructor' @@ -66,7 +66,9 @@ class MxBackgroundAudioManager : public MxCore { // name is inferred from context MxPresenter::TickleState m_tickleState; // 0x13c - MxS32 m_unk0x140; // 0x140 + // name inferred from parameter p_speed + MxS32 m_speed; // 0x140 + MxS32 m_targetVolume; // 0x144 MxS16 m_unk0x148; // 0x148 MxAtomId m_script; // 0x14c diff --git a/LEGO1/lego/legoomni/src/audio/mxbackgroundaudiomanager.cpp b/LEGO1/lego/legoomni/src/audio/mxbackgroundaudiomanager.cpp index 7ab16e56..98cf881b 100644 --- a/LEGO1/lego/legoomni/src/audio/mxbackgroundaudiomanager.cpp +++ b/LEGO1/lego/legoomni/src/audio/mxbackgroundaudiomanager.cpp @@ -21,7 +21,7 @@ MxBackgroundAudioManager::MxBackgroundAudioManager() m_unk0xa0 = 0; m_unk0x138 = 0; m_tickleState = MxPresenter::e_idle; - m_unk0x140 = 0; + m_speed = 0; m_targetVolume = 0; m_unk0x148 = 0; m_enabled = FALSE; @@ -128,8 +128,8 @@ void MxBackgroundAudioManager::FUN_1007ef40() volume = m_unk0x138->GetVolume(); if (volume < compare) { - if (m_unk0x140 + m_unk0x138->GetVolume() <= compare) { - compare = m_unk0x140 + m_unk0x138->GetVolume(); + if (m_speed + m_unk0x138->GetVolume() <= compare) { + compare = m_speed + m_unk0x138->GetVolume(); } m_unk0x138->SetVolume(compare); @@ -150,8 +150,8 @@ void MxBackgroundAudioManager::FUN_1007ef40() DeleteObject(*m_unk0xa0->GetAction()); } else { - if (m_unk0xa0->GetVolume() - m_unk0x140 > 0) { - volume = m_unk0xa0->GetVolume() - m_unk0x140; + if (m_unk0xa0->GetVolume() - m_speed > 0) { + volume = m_unk0xa0->GetVolume() - m_speed; } else { volume = 0; @@ -178,11 +178,11 @@ void MxBackgroundAudioManager::FadeInOrFadeOut() } if (volume < compare) { - volume = Min(volume + m_unk0x140, compare); + volume = Min(volume + m_speed, compare); m_unk0xa0->SetVolume(volume); } else if (compare < volume) { - volume = Max(volume - m_unk0x140, compare); + volume = Max(volume - m_speed, compare); m_unk0xa0->SetVolume(volume); } else { @@ -238,12 +238,15 @@ void MxBackgroundAudioManager::StopAction(MxParam& p_param) } // FUNCTION: LEGO1 0x1007f2f0 +// FUNCTION: BETA10 0x100e90fc MxResult MxBackgroundAudioManager::PlayMusic( MxDSAction& p_action, - undefined4 p_unk0x140, + undefined4 p_speed, MxPresenter::TickleState p_tickleState ) { + assert(p_speed > 0); + if (!m_enabled) { return SUCCESS; } @@ -267,7 +270,7 @@ MxResult MxBackgroundAudioManager::PlayMusic( if (result == SUCCESS) { m_tickleState = p_tickleState; - m_unk0x140 = p_unk0x140; + m_speed = p_speed; } return result; @@ -307,7 +310,7 @@ void MxBackgroundAudioManager::LowerVolume() if (m_tickleState == 0) { m_tickleState = MxPresenter::e_starting; } - m_unk0x140 = 20; + m_speed = 20; } m_unk0x148++; } @@ -321,7 +324,7 @@ void MxBackgroundAudioManager::RaiseVolume() if (m_tickleState == 0) { m_tickleState = MxPresenter::e_starting; } - m_unk0x140 = 10; + m_speed = 10; } } } @@ -342,7 +345,7 @@ void MxBackgroundAudioManager::Enable(MxBool p_enable) // FUNCTION: BETA10 0x100e95ee undefined4 MxBackgroundAudioManager::FUN_1007f610( MxPresenter* p_unk0x138, - MxS32 p_unk0x140, + MxS32 p_speed, MxPresenter::TickleState p_tickleState ) @@ -352,7 +355,7 @@ undefined4 MxBackgroundAudioManager::FUN_1007f610( ((MxCompositePresenter*) m_unk0x138)->VTable0x60(NULL); - m_unk0x140 = p_unk0x140; + m_speed = p_speed; m_tickleState = p_tickleState; return 0; } diff --git a/LEGO1/lego/legoomni/src/common/misc.cpp b/LEGO1/lego/legoomni/src/common/misc.cpp index 6c01313f..61399b00 100644 --- a/LEGO1/lego/legoomni/src/common/misc.cpp +++ b/LEGO1/lego/legoomni/src/common/misc.cpp @@ -63,8 +63,10 @@ LegoGameState* GameState() } // FUNCTION: LEGO1 0x10015770 +// FUNCTION: BETA10 0x100e4971 LegoAnimationManager* AnimationManager() { + assert(LegoOmni::GetInstance()); return LegoOmni::GetInstance()->GetAnimationManager(); } diff --git a/LEGO1/lego/legoomni/src/entity/legoworld.cpp b/LEGO1/lego/legoomni/src/entity/legoworld.cpp index 822121e6..f28134e9 100644 --- a/LEGO1/lego/legoomni/src/entity/legoworld.cpp +++ b/LEGO1/lego/legoomni/src/entity/legoworld.cpp @@ -42,15 +42,17 @@ LegoWorld::LegoWorld() : m_list0x68(TRUE) m_entityList = NULL; m_cacheSoundList = NULL; m_destroyed = FALSE; - m_hideAnimPresenter = NULL; + m_hideAnim = NULL; m_worldStarted = FALSE; NotificationManager()->Register(this); } // FUNCTION: LEGO1 0x1001d670 +// FUNCTION: BETA10 0x10017530 MxBool LegoWorld::VTable0x5c() { + // The BETA10 match could also be LegoWorld::Escape(), only the child classes might be able to tell return FALSE; } @@ -245,6 +247,7 @@ MxLong LegoWorld::Notify(MxParam& p_param) } // FUNCTION: LEGO1 0x1001f630 +// FUNCTION: BETA10 0x100d9fc2 LegoCameraController* LegoWorld::VTable0x54() { MxBool success = FALSE; @@ -413,6 +416,7 @@ MxResult LegoWorld::GetCurrPathInfo(LegoPathBoundary** p_boundaries, MxS32& p_nu } // FUNCTION: LEGO1 0x10020220 +// FUNCTION: BETA10 0x100da90b void LegoWorld::Add(MxCore* p_object) { if (p_object && !p_object->IsA("LegoWorld") && !p_object->IsA("LegoWorldPresenter")) { @@ -443,7 +447,8 @@ void LegoWorld::Add(MxCore* p_object) m_entityList->Append((LegoEntity*) p_object); } - else if (p_object->IsA("LegoLocomotionAnimPresenter") || p_object->IsA("LegoHideAnimPresenter") || p_object->IsA("LegoLoopingAnimPresenter")) { + else if (p_object->IsA("LegoLocomotionAnimPresenter") || p_object->IsA("LegoHideAnimPresenter") || + p_object->IsA("LegoLoopingAnimPresenter")) { MxPresenterListCursor cursor(&m_animPresenters); if (cursor.Find((MxPresenter*) p_object)) { @@ -454,7 +459,7 @@ void LegoWorld::Add(MxCore* p_object) m_animPresenters.Append(((MxPresenter*) p_object)); if (p_object->IsA("LegoHideAnimPresenter")) { - m_hideAnimPresenter = (LegoHideAnimPresenter*) p_object; + m_hideAnim = (LegoHideAnimPresenter*) p_object; } } else if (p_object->IsA("LegoCacheSound")) { @@ -497,7 +502,8 @@ void LegoWorld::Remove(MxCore* p_object) ((MxControlPresenter*) p_object)->VTable0x68(TRUE); } } - else if (p_object->IsA("LegoLocomotionAnimPresenter") || p_object->IsA("LegoHideAnimPresenter") || p_object->IsA("LegoLoopingAnimPresenter")) { + else if (p_object->IsA("LegoLocomotionAnimPresenter") || p_object->IsA("LegoHideAnimPresenter") || + p_object->IsA("LegoLoopingAnimPresenter")) { MxPresenterListCursor cursor(&m_animPresenters); if (cursor.Find((MxPresenter*) p_object)) { @@ -505,7 +511,7 @@ void LegoWorld::Remove(MxCore* p_object) } if (p_object->IsA("LegoHideAnimPresenter")) { - m_hideAnimPresenter = NULL; + m_hideAnim = NULL; } } else if (p_object->IsA("MxEntity")) { @@ -654,6 +660,7 @@ MxCore* LegoWorld::Find(const MxAtomId& p_atom, MxS32 p_entityId) } // FUNCTION: LEGO1 0x10021a70 +// FUNCTION: BETA10 0x100db758 void LegoWorld::Enable(MxBool p_enable) { if (p_enable && !m_set0xd0.empty()) { diff --git a/LEGO1/lego/legoomni/src/paths/legopathstruct.cpp b/LEGO1/lego/legoomni/src/paths/legopathstruct.cpp index 970402c8..e269f727 100644 --- a/LEGO1/lego/legoomni/src/paths/legopathstruct.cpp +++ b/LEGO1/lego/legoomni/src/paths/legopathstruct.cpp @@ -19,6 +19,7 @@ DECOMP_SIZE_ASSERT(LegoPathStruct, 0x14) extern MxU32 g_isleFlags; // GLOBAL: LEGO1 0x100f119c +// GLOBAL: BETA10 0x100f119c MxBool g_unk0x100f119c = FALSE; // FUNCTION: LEGO1 0x1001b700 diff --git a/LEGO1/lego/legoomni/src/race/jetskirace.cpp b/LEGO1/lego/legoomni/src/race/jetskirace.cpp index 3b39e584..ba0cd4ea 100644 --- a/LEGO1/lego/legoomni/src/race/jetskirace.cpp +++ b/LEGO1/lego/legoomni/src/race/jetskirace.cpp @@ -1,28 +1,154 @@ #include "jetskirace.h" +#include "actions/jetrace_actions.h" +#include "actions/jetski_actions.h" +#include "actions/jukebox_actions.h" +#include "dunebuggy.h" +#include "isle.h" +#include "legoanimationmanager.h" +#include "legocontrolmanager.h" +#include "legohideanimpresenter.h" +#include "legomain.h" +#include "legopathstruct.h" +#include "legoracers.h" +#include "legoracespecial.h" +#include "legoutils.h" +#include "misc.h" +#include "mxactionnotificationparam.h" +#include "mxbackgroundaudiomanager.h" +#include "mxmisc.h" +#include "mxstillpresenter.h" +#include "mxtransitionmanager.h" +#include "mxvariabletable.h" +#include "scripts.h" + +// Defined in legopathstruct.cpp +extern MxBool g_unk0x100f119c; + +// Defined in jetski.cpp +extern const char* g_varJSFRNTY5; +extern const char* g_varJSWNSHY5; + +// Defined in legoracespecial.cpp +extern const char* g_raceState; +extern const char* g_racing; + +// Defined in legopathactor.cpp +extern const char* g_strHIT_WALL_SOUND; + DECOMP_SIZE_ASSERT(JetskiRace, 0x144) -// STUB: LEGO1 0x100162c0 +// FUNCTION: LEGO1 0x100162c0 +// FUNCTION: BETA10 0x100c7e6f MxResult JetskiRace::Create(MxDSAction& p_dsAction) { - return SUCCESS; + MxResult result = LegoRace::Create(p_dsAction); + + GameState()->m_currentArea = LegoGameState::e_jetrace; + GameState()->StopArea(LegoGameState::e_undefined); + LegoGameState* gameState = GameState(); + RaceState* jetskiRaceState = (RaceState*) gameState->GetState("JetskiRaceState"); + + if (!jetskiRaceState) { + jetskiRaceState = (RaceState*) gameState->CreateState("JetskiRaceState"); + } + + m_raceState = jetskiRaceState; + + if (!jetskiRaceState) { + return FAILURE; + } + + m_raceState->m_unk0x28 = 1; + m_unk0x130.SetLeft(397); + m_unk0x130.SetTop(317); + m_unk0x130.SetRight(543); + m_unk0x130.SetBottom(333); + LegoRaceCar::FUN_10013670(); + InvokeAction( + Extra::e_start, + m_atomId, + DuneBuggy::GetColorOffset(g_varJSFRNTY5) + (DuneBuggy::GetColorOffset(g_varJSWNSHY5) * 5 + 0xf) * 2, + NULL + ); + InvokeAction(Extra::e_start, m_atomId, JetraceScript::c_JetskiDashboard, NULL); + g_unk0x100f119c = TRUE; + + return result; } -// STUB: LEGO1 0x100163b0 +// FUNCTION: LEGO1 0x100163b0 +// FUNCTION: BETA10 0x100c7f10 void JetskiRace::ReadyWorld() { + assert(m_hideAnim); + LegoWorld::ReadyWorld(); + m_hideAnim->FUN_1006db40(0); + + MxDSAction action; + action.SetAtomId(*g_jukeboxScript); + action.SetObjectId(JukeboxScript::c_JetskiRace_Music); + BackgroundAudioManager()->PlayMusic(action, 5, MxPresenter::e_repeating); + + AnimationManager()->Resume(); + + m_unk0x128 = (MxStillPresenter*) Find("MxPresenter", "JetskiLocator2"); + m_unk0x128->SetPosition(m_unk0x130.GetLeft(), m_unk0x130.GetTop()); + m_unk0x12c = (MxStillPresenter*) Find("MxPresenter", "JetskiLocator3"); + m_unk0x12c->SetPosition(m_unk0x130.GetLeft(), m_unk0x130.GetTop()); + + FUN_10015820(FALSE, LegoOmni::c_disableInput | LegoOmni::c_disable3d | LegoOmni::c_clearScreen); + + VariableTable()->SetVariable("DISTANCE", "0.036"); + + InvokeAction(Extra::e_start, *g_jetraceScript, JetraceScript::c_AirHorn_PlayWav, NULL); } -// STUB: LEGO1 0x10016520 -MxLong JetskiRace::HandleEndAction(MxEndActionNotificationParam&) +// FUNCTION: LEGO1 0x10016520 +MxLong JetskiRace::HandleEndAction(MxEndActionNotificationParam& p_param) { - return 0; + MxLong result = 0; + + if ((p_param.GetAction()) && (p_param.GetAction()->GetObjectId() == JetraceScript::c_AirHorn_PlayWav)) { + m_unk0x110[0]->Mute(FALSE); + m_unk0x110[1]->Mute(FALSE); + m_unk0x110[2]->Mute(FALSE); + + VariableTable()->SetVariable(g_raceState, g_racing); + result = 1; + } + + return result; } -// STUB: LEGO1 0x100165a0 -MxLong JetskiRace::HandleClick(LegoEventNotificationParam&) +// FUNCTION: LEGO1 0x100165a0 +MxLong JetskiRace::HandleClick(LegoEventNotificationParam& p_param) { - return 0; + MxLong result = 0; + if (((LegoControlManagerNotificationParam*) &p_param)->m_unk0x28 == 1) { + switch (((LegoControlManagerNotificationParam*) &p_param)->m_clickedObjectId) { + case JetraceScript::c_JetskiArms_Ctl: + m_act1State->m_unk0x018 = 0; + VariableTable()->SetVariable(g_raceState, ""); + VariableTable()->SetVariable(g_strHIT_WALL_SOUND, ""); + LegoRaceCar::FUN_10012de0(); + m_destLocation = LegoGameState::e_jetraceExterior; + TransitionManager()->StartTransition(MxTransitionManager::e_mosaic, 50, FALSE, FALSE); + break; + case JetraceScript::c_JetskiInfo_Ctl: + m_act1State->m_unk0x018 = 0; + VariableTable()->SetVariable(g_raceState, ""); + VariableTable()->SetVariable(g_strHIT_WALL_SOUND, ""); + LegoRaceCar::FUN_10012de0(); + m_destLocation = LegoGameState::e_infomain; + result = 1; + TransitionManager()->StartTransition(MxTransitionManager::e_mosaic, 50, FALSE, FALSE); + break; + default: + break; + } + } + return result; } // STUB: LEGO1 0x100166a0 @@ -31,8 +157,15 @@ MxLong JetskiRace::HandlePathStruct(LegoPathStructNotificationParam&) return 0; } -// STUB: LEGO1 0x10016a10 +// FUNCTION: LEGO1 0x10016a10 MxBool JetskiRace::Escape() { + AnimationManager()->FUN_10061010(FALSE); + DeleteObjects(&m_atomId, 500, 999); + m_act1State->m_unk0x018 = 0; + VariableTable()->SetVariable(g_raceState, ""); + VariableTable()->SetVariable(g_strHIT_WALL_SOUND, ""); + m_destLocation = LegoGameState::e_infomain; + LegoRaceCar::FUN_10012de0(); return TRUE; } diff --git a/LEGO1/lego/legoomni/src/race/legorace.cpp b/LEGO1/lego/legoomni/src/race/legorace.cpp index f4cad9d0..2564f7a5 100644 --- a/LEGO1/lego/legoomni/src/race/legorace.cpp +++ b/LEGO1/lego/legoomni/src/race/legorace.cpp @@ -23,10 +23,10 @@ LegoRace::LegoRace() m_unk0x104 = 0; m_unk0x108 = 0; m_unk0x10c = 0; - m_unk0x140 = 0; - m_unk0x110 = 0; - m_unk0x114 = 0; - m_unk0x118 = 0; + m_raceState = NULL; + m_unk0x110[0] = NULL; + m_unk0x110[1] = NULL; + m_unk0x110[2] = NULL; m_unk0x128 = 0; m_unk0x12c = 0; m_pathActor = 0; @@ -54,6 +54,7 @@ MxBool LegoRace::Escape() } // FUNCTION: LEGO1 0x10015ce0 +// FUNCTION: BETA10 0x100c7a71 MxResult LegoRace::Create(MxDSAction& p_dsAction) { MxResult result = LegoWorld::Create(p_dsAction); @@ -150,6 +151,7 @@ RaceState::RaceState() } // FUNCTION: LEGO1 0x10016140 +// FUNCTION: BETA10 0x100c7d9f MxResult RaceState::Serialize(LegoFile* p_file) { LegoState::Serialize(p_file); diff --git a/LEGO1/lego/legoomni/src/race/legoracers.cpp b/LEGO1/lego/legoomni/src/race/legoracers.cpp index 7a60d59c..472421ec 100644 --- a/LEGO1/lego/legoomni/src/race/legoracers.cpp +++ b/LEGO1/lego/legoomni/src/race/legoracers.cpp @@ -21,6 +21,12 @@ DECOMP_SIZE_ASSERT(EdgeReference, 0x08) DECOMP_SIZE_ASSERT(SkeletonKickPhase, 0x10) DECOMP_SIZE_ASSERT(LegoRaceCar, 0x200) +// GLOBAL: LEGO1 0x100f0bac +static undefined4 g_unk0x100f0bac = 0; + +// GLOBAL: LEGO1 0x100f0bb0 +static undefined4 g_unk0x100f0bb0 = 0; + // GLOBAL: LEGO1 0x100f0a20 // GLOBAL: BETA10 0x101f5e34 EdgeReference LegoRaceCar::g_skBMap[] = { @@ -166,6 +172,14 @@ MxLong LegoRaceCar::Notify(MxParam& p_param) return LegoRaceMap::Notify(p_param); } +// FUNCTION: LEGO1 0x10012de0 +void LegoRaceCar::FUN_10012de0() +{ + g_unk0x100f0b8c = TRUE; + g_timeLastSoundPlayed = 0; + g_unk0x100f0b88 = 0; +} + // FUNCTION: LEGO1 0x10012e60 // FUNCTION: BETA10 0x100cb191 void LegoRaceCar::SetWorldSpeed(MxFloat p_worldSpeed) @@ -513,6 +527,16 @@ MxResult LegoRaceCar::VTable0x9c() return result; } +// FUNCTION: LEGO1 0x10013670 +void LegoRaceCar::FUN_10013670() +{ + g_unk0x100f0bac = (rand() & 0xc) >> 2; + + // Inlining the `rand()` causes this function to mismatch + MxU32 uVar1 = rand(); + g_unk0x100f0bb0 = uVar1 % 0xc >> 2; +} + // FUNCTION: LEGO1 0x10014500 // FUNCTION: BETA10 0x100cd5e0 MxU32 LegoRaceCar::VTable0x6c( diff --git a/LEGO1/library_msvc.h b/LEGO1/library_msvc.h index c4a46848..f50de490 100644 --- a/LEGO1/library_msvc.h +++ b/LEGO1/library_msvc.h @@ -741,4 +741,7 @@ // LIBRARY: BETA10 0x1001d1a0 // `vector constructor iterator' +// LIBRARY: BETA10 0x100f8ad0 +// strcmp + #endif diff --git a/LEGO1/omni/include/mxentity.h b/LEGO1/omni/include/mxentity.h index cf16ad07..96348a65 100644 --- a/LEGO1/omni/include/mxentity.h +++ b/LEGO1/omni/include/mxentity.h @@ -18,6 +18,7 @@ class MxEntity : public MxCore { ~MxEntity() override {} // FUNCTION: LEGO1 0x1000c180 + // FUNCTION: BETA10 0x10012700 const char* ClassName() const override // vtable+0x0c { // STRING: LEGO1 0x100f0070 @@ -25,6 +26,7 @@ class MxEntity : public MxCore { } // FUNCTION: LEGO1 0x1000c190 + // FUNCTION: BETA10 0x10012610 MxBool IsA(const char* p_name) const override // vtable+0x10 { return !strcmp(p_name, MxEntity::ClassName()) || MxCore::IsA(p_name);