From fe8e56ba470b74fc84ac4738ecdce676ac8c3bb8 Mon Sep 17 00:00:00 2001 From: jonschz <17198703+jonschz@users.noreply.github.com> Date: Sat, 16 Nov 2024 20:24:03 +0100 Subject: [PATCH] Finish `JetskiRace` (#1158) * Implement `JetskiRace::HandlePathStruct` * Implement `JetskiRace::FUN_10016930` * Cleanup, add BETA10 * Match HandlePathStruct * Address review comments * Address another review comment --------- Co-authored-by: jonschz Co-authored-by: Christian Semmler --- LEGO1/lego/legoomni/include/isle.h | 2 + LEGO1/lego/legoomni/include/jetskirace.h | 11 ++ LEGO1/lego/legoomni/include/legopathstruct.h | 3 + LEGO1/lego/legoomni/include/legorace.h | 14 +- LEGO1/lego/legoomni/src/race/jetskirace.cpp | 136 ++++++++++++++++++- LEGO1/lego/legoomni/src/worlds/isle.cpp | 7 +- 6 files changed, 159 insertions(+), 14 deletions(-) diff --git a/LEGO1/lego/legoomni/include/isle.h b/LEGO1/lego/legoomni/include/isle.h index f75d3273..9783e4ec 100644 --- a/LEGO1/lego/legoomni/include/isle.h +++ b/LEGO1/lego/legoomni/include/isle.h @@ -102,6 +102,7 @@ class Act1State : public LegoState { }; // VTABLE: LEGO1 0x100d6fb8 +// VTABLE: BETA10 0x101b9cc8 // SIZE 0x140 class Isle : public LegoWorld { public: @@ -116,6 +117,7 @@ class Isle : public LegoWorld { MxLong Notify(MxParam& p_param) override; // vtable+0x04 // FUNCTION: LEGO1 0x10030910 + // FUNCTION: BETA10 0x10035d70 const char* ClassName() const override // vtable+0x0c { // STRING: LEGO1 0x100f0458 diff --git a/LEGO1/lego/legoomni/include/jetskirace.h b/LEGO1/lego/legoomni/include/jetskirace.h index ce1ea834..b666868f 100644 --- a/LEGO1/lego/legoomni/include/jetskirace.h +++ b/LEGO1/lego/legoomni/include/jetskirace.h @@ -61,9 +61,20 @@ class JetskiRace : public LegoRace { MxLong HandleClick(LegoEventNotificationParam&) override; // vtable+0x6c MxLong HandlePathStruct(LegoPathStructNotificationParam&) override; // vtable+0x70 MxLong HandleEndAction(MxEndActionNotificationParam&) override; // vtable+0x74 + + void FUN_10016930(MxS32 p_param1, MxS16 p_param2); + +private: + inline MxS32 PossiblyGetPlaceOfPlayer(); + + static MxS32 g_unk0x100f0c78; }; // SYNTHETIC: LEGO1 0x1000f530 +// SYNTHETIC: BETA10 0x100a9b70 // JetskiRace::`scalar deleting destructor' +// SYNTHETIC: BETA10 0x100aa150 +// JetskiRace::~JetskiRace + #endif // JETSKIRACE_H diff --git a/LEGO1/lego/legoomni/include/legopathstruct.h b/LEGO1/lego/legoomni/include/legopathstruct.h index 4412c5f1..162438f5 100644 --- a/LEGO1/lego/legoomni/include/legopathstruct.h +++ b/LEGO1/lego/legoomni/include/legopathstruct.h @@ -28,7 +28,10 @@ class LegoPathStructNotificationParam : public MxNotificationParam { return new LegoPathStructNotificationParam(m_type, m_sender, m_trigger, m_data); } // vtable+0x04 + // FUNCTION: BETA10 0x10024270 MxU8 GetTrigger() { return m_trigger; } + + // FUNCTION: BETA10 0x100242a0 MxS16 GetData() { return m_data; } protected: diff --git a/LEGO1/lego/legoomni/include/legorace.h b/LEGO1/lego/legoomni/include/legorace.h index cad39de1..22fe734b 100644 --- a/LEGO1/lego/legoomni/include/legorace.h +++ b/LEGO1/lego/legoomni/include/legorace.h @@ -81,8 +81,6 @@ class RaceState : public LegoState { Entry* GetState(MxU8 p_id); - undefined4 GetUnknown0x28() { return m_unk0x28; } - // SYNTHETIC: LEGO1 0x1000f6f0 // RaceState::~RaceState @@ -151,12 +149,12 @@ class LegoRace : public LegoWorld { // LegoRace::`scalar deleting destructor' protected: - undefined4 m_unk0xf8; // 0xf8 - undefined4 m_unk0xfc; // 0xfc - undefined4 m_unk0x100; // 0x100 - undefined4 m_unk0x104; // 0x104 - undefined4 m_unk0x108; // 0x108 - undefined4 m_unk0x10c; // 0x10c + MxS32 m_unk0xf8; // 0xf8 + MxS32 m_unk0xfc; // 0xfc + MxS32 m_unk0x100; // 0x100 + MxS32 m_unk0x104; // 0x104 + MxS32 m_unk0x108; // 0x108 + MxS32 m_unk0x10c; // 0x10c LegoRaceActor* m_unk0x110[3]; // 0x110 LegoGameState::Area m_destLocation; // 0x11c LegoPathActor* m_pathActor; // 0x120 diff --git a/LEGO1/lego/legoomni/src/race/jetskirace.cpp b/LEGO1/lego/legoomni/src/race/jetskirace.cpp index ba0cd4ea..e2af449d 100644 --- a/LEGO1/lego/legoomni/src/race/jetskirace.cpp +++ b/LEGO1/lego/legoomni/src/race/jetskirace.cpp @@ -38,6 +38,9 @@ extern const char* g_strHIT_WALL_SOUND; DECOMP_SIZE_ASSERT(JetskiRace, 0x144) +// GLOBAL: LEGO1 0x100f0c78 +MxS32 JetskiRace::g_unk0x100f0c78 = 2; + // FUNCTION: LEGO1 0x100162c0 // FUNCTION: BETA10 0x100c7e6f MxResult JetskiRace::Create(MxDSAction& p_dsAction) @@ -125,6 +128,7 @@ MxLong JetskiRace::HandleEndAction(MxEndActionNotificationParam& p_param) MxLong JetskiRace::HandleClick(LegoEventNotificationParam& p_param) { MxLong result = 0; + if (((LegoControlManagerNotificationParam*) &p_param)->m_unk0x28 == 1) { switch (((LegoControlManagerNotificationParam*) &p_param)->m_clickedObjectId) { case JetraceScript::c_JetskiArms_Ctl: @@ -148,13 +152,139 @@ MxLong JetskiRace::HandleClick(LegoEventNotificationParam& p_param) break; } } + return result; } -// STUB: LEGO1 0x100166a0 -MxLong JetskiRace::HandlePathStruct(LegoPathStructNotificationParam&) +inline MxS32 JetskiRace::PossiblyGetPlaceOfPlayer() { - return 0; + if (m_unk0xfc < m_unk0xf8 && m_unk0x100 < m_unk0xf8) { + return 3; + } + else if (m_unk0xfc < m_unk0xf8 || m_unk0x100 < m_unk0xf8) { + return 2; + } + + return 1; +} + +// FUNCTION: LEGO1 0x100166a0 +// FUNCTION: BETA10 0x100c8085 +MxLong JetskiRace::HandlePathStruct(LegoPathStructNotificationParam& p_param) +{ + MxLong result = 0; + MxEntity* sender = (MxEntity*) p_param.GetSender(); + + if (p_param.GetTrigger() == 68) { + MxS32 paramData = p_param.GetData(); + + switch (sender->GetEntityId()) { + case 10: + if (paramData <= m_unk0x104 || paramData >= m_unk0x104 + 5) { + break; + } + + m_unk0x104 = paramData; + LegoChar buffer[20]; + sprintf(buffer, "%g", 0.032 + 0.936 * (m_unk0xf8 * 20.0 + m_unk0x104) / (g_unk0x100f0c78 * 20.0)); + VariableTable()->SetVariable("DISTANCE", buffer); + + if (m_unk0x104 == 0x14) { + m_unk0x104 = 0; + m_unk0xf8++; + + if (g_unk0x100f0c78 == m_unk0xf8) { + MxS16 sVar6 = PossiblyGetPlaceOfPlayer(); + + VariableTable()->SetVariable(g_raceState, ""); + VariableTable()->SetVariable(g_strHIT_WALL_SOUND, ""); + LegoRaceCar::FUN_10012de0(); + m_raceState->m_unk0x28 = 2; + + RaceState::Entry* raceStateEntry = m_raceState->GetState(GameState()->GetActorId()); + raceStateEntry->m_unk0x02 = sVar6; + + if (raceStateEntry->m_score < sVar6) { + raceStateEntry->m_score = sVar6; + } + + m_destLocation = LegoGameState::e_jetrace2; + + TransitionManager()->StartTransition(MxTransitionManager::e_mosaic, 50, FALSE, FALSE); + } + + result = 1; + } + else if (m_unk0x104 == 0xf) { + m_hideAnim->FUN_1006db40(m_unk0xf8 * 200 + 100); + result = 1; + } + break; + case 11: + if (paramData <= m_unk0x108 || paramData >= m_unk0x108 + 5) { + break; + } + + FUN_10016930(0xb, paramData); + m_unk0x108 = paramData; + + if (m_unk0x108 == 0x14) { + m_unk0x108 = 0; + m_unk0xfc++; + + if (g_unk0x100f0c78 == m_unk0xfc) { + ((LegoPathActor*) p_param.GetSender())->SetMaxLinearVel(0.1); + } + } + break; + case 12: + if (paramData <= m_unk0x10c || paramData >= m_unk0x10c + 5) { + break; + } + + FUN_10016930(0xc, paramData); + + m_unk0x10c = paramData; + + if (m_unk0x10c == 0x14) { + m_unk0x10c = 0; + m_unk0x100++; + + if (g_unk0x100f0c78 == m_unk0x100) { + ((LegoPathActor*) p_param.GetSender())->SetMaxLinearVel(0.1); + } + } + break; + } + } + + return result; +} + +// FUNCTION: LEGO1 0x10016930 +void JetskiRace::FUN_10016930(MxS32 p_param1, MxS16 p_param2) +{ + MxS32 local4; + MxStillPresenter* presenter; + MxS32 x, y; + + if (p_param1 == 11) { + presenter = m_unk0x128; + local4 = m_unk0xfc; + } + else if (p_param1 == 12) { + presenter = m_unk0x12c; + local4 = m_unk0x100; + } + + if (presenter) { + x = m_unk0x130.GetLeft() + 0.5 + + (m_unk0x130.GetRight() - m_unk0x130.GetLeft() + 1) * (local4 * 20.0 + p_param2) / (g_unk0x100f0c78 * 20.0); + y = m_unk0x130.GetTop() + 0.5 + + (m_unk0x130.GetBottom() - m_unk0x130.GetTop() + 1) * (local4 * 20.0 + p_param2) / (g_unk0x100f0c78 * 20.0); + + presenter->SetPosition(x, y); + } } // FUNCTION: LEGO1 0x10016a10 diff --git a/LEGO1/lego/legoomni/src/worlds/isle.cpp b/LEGO1/lego/legoomni/src/worlds/isle.cpp index 2e76e1a3..190ad2ef 100644 --- a/LEGO1/lego/legoomni/src/worlds/isle.cpp +++ b/LEGO1/lego/legoomni/src/worlds/isle.cpp @@ -529,6 +529,7 @@ MxLong Isle::HandlePathStruct(LegoPathStructNotificationParam& p_param) } // FUNCTION: LEGO1 0x10031820 +// FUNCTION: BETA10 0x10034158 void Isle::Enable(MxBool p_enable) { if (m_set0xd0.empty() == p_enable) { @@ -595,7 +596,7 @@ void Isle::Enable(MxBool p_enable) SetIsWorldActive(FALSE); break; case LegoGameState::e_jetrace2: - if (((JetskiRaceState*) GameState()->GetState("JetskiRaceState"))->GetUnknown0x28() == 2) { + if (((JetskiRaceState*) GameState()->GetState("JetskiRaceState"))->m_unk0x28 == 2) { m_act1state->m_unk0x018 = 5; } @@ -738,7 +739,7 @@ void Isle::Enable(MxBool p_enable) ); JetskiRaceState* raceState = (JetskiRaceState*) GameState()->GetState("JetskiRaceState"); - if (raceState->GetUnknown0x28() == 2) { + if (raceState->m_unk0x28 == 2) { IsleScript::Script script = IsleScript::c_noneIsle; switch (raceState->GetState(GameState()->GetActorId())->GetUnknown0x02()) { @@ -771,7 +772,7 @@ void Isle::Enable(MxBool p_enable) ); CarRaceState* raceState = (CarRaceState*) GameState()->GetState("CarRaceState"); - if (raceState->GetUnknown0x28() == 2) { + if (raceState->m_unk0x28 == 2) { IsleScript::Script script = IsleScript::c_noneIsle; switch (raceState->GetState(GameState()->GetActorId())->GetUnknown0x02()) {