From 87a9a37b33c564bae70f2a1981b6a175ebbd3bc0 Mon Sep 17 00:00:00 2001
From: Christian Semmler <mail@csemmler.com>
Date: Thu, 4 Jul 2024 17:11:20 -0700
Subject: [PATCH] Implement/match Jetski class (#1061)

---
 LEGO1/lego/legoomni/include/dunebuggy.h       |   4 +-
 LEGO1/lego/legoomni/include/jetski.h          |  10 +-
 LEGO1/lego/legoomni/include/legovariables.h   |   2 +
 LEGO1/lego/legoomni/src/actors/dunebuggy.cpp  |  18 +--
 LEGO1/lego/legoomni/src/actors/jetski.cpp     | 151 +++++++++++++++---
 .../legoomni/src/common/legovariables.cpp     |   8 +
 LEGO1/lego/legoomni/src/worlds/isle.cpp       |   2 +-
 7 files changed, 159 insertions(+), 36 deletions(-)

diff --git a/LEGO1/lego/legoomni/include/dunebuggy.h b/LEGO1/lego/legoomni/include/dunebuggy.h
index 81b7bfe5..7302e0ce 100644
--- a/LEGO1/lego/legoomni/include/dunebuggy.h
+++ b/LEGO1/lego/legoomni/include/dunebuggy.h
@@ -32,12 +32,12 @@ public:
 
 	void ActivateSceneActions();
 
+	static MxS32 GetColorOffset(const char* p_variable);
+
 	// SYNTHETIC: LEGO1 0x10067dc0
 	// DuneBuggy::`scalar deleting destructor'
 
 private:
-	static MxS32 GetDashboardOffset(const char* p_variable);
-
 	MxS16 m_dashboard; // 0x160
 	MxFloat m_fuel;    // 0x164
 	MxFloat m_time;    // 0x168
diff --git a/LEGO1/lego/legoomni/include/jetski.h b/LEGO1/lego/legoomni/include/jetski.h
index 70102eac..e81824be 100644
--- a/LEGO1/lego/legoomni/include/jetski.h
+++ b/LEGO1/lego/legoomni/include/jetski.h
@@ -26,12 +26,12 @@ public:
 	}
 
 	MxResult Create(MxDSAction& p_dsAction) override;                    // vtable+0x18
-	void VTable0x70(float p_float) override;                             // vtable+0x70
+	void VTable0x70(float p_time) override;                              // vtable+0x70
 	MxLong HandleClick() override;                                       // vtable+0xcc
 	MxLong HandleControl(LegoControlManagerNotificationParam&) override; // vtable+0xd4
 	void Exit() override;                                                // vtable+0xe4
 
-	void FUN_1007e990();
+	void ActivateSceneActions();
 
 	MxS16 GetUnknown0x160() { return m_unk0x160; }
 
@@ -39,9 +39,9 @@ public:
 	// Jetski::`scalar deleting destructor'
 
 private:
-	// TODO: Jetski fields
-	MxS16 m_unk0x160;        // 0x160
-	undefined m_unk0x162[2]; // 0x162
+	void RemoveFromWorld();
+
+	MxS16 m_unk0x160; // 0x160
 };
 
 #endif // JETSKI_H
diff --git a/LEGO1/lego/legoomni/include/legovariables.h b/LEGO1/lego/legoomni/include/legovariables.h
index 1901a254..b209776c 100644
--- a/LEGO1/lego/legoomni/include/legovariables.h
+++ b/LEGO1/lego/legoomni/include/legovariables.h
@@ -3,6 +3,8 @@
 
 #include "mxvariable.h"
 
+extern const char* g_varJETSPEED;
+extern const char* g_varJETFUEL;
 extern const char* g_varDUNESPEED;
 extern const char* g_varDUNEFUEL;
 extern const char* g_varMOTOSPEED;
diff --git a/LEGO1/lego/legoomni/src/actors/dunebuggy.cpp b/LEGO1/lego/legoomni/src/actors/dunebuggy.cpp
index 30aff459..a4504310 100644
--- a/LEGO1/lego/legoomni/src/actors/dunebuggy.cpp
+++ b/LEGO1/lego/legoomni/src/actors/dunebuggy.cpp
@@ -102,7 +102,7 @@ MxLong DuneBuggy::HandleClick()
 	}
 
 	m_time = Timer()->GetTime();
-	m_dashboard = IsleScript::c_DuneCarSpeedMeter + GetDashboardOffset(g_varDBFRFNY4);
+	m_dashboard = IsleScript::c_DuneCarSpeedMeter + GetColorOffset(g_varDBFRFNY4);
 
 	InvokeAction(Extra::ActionType::e_start, *g_isleScript, m_dashboard, NULL);
 	InvokeAction(Extra::ActionType::e_start, *g_isleScript, IsleScript::c_DuneCarDashboard, NULL);
@@ -157,30 +157,30 @@ MxLong DuneBuggy::HandlePathStruct(LegoPathStructNotificationParam& p_param)
 }
 
 // FUNCTION: LEGO1 0x10068290
-MxS32 DuneBuggy::GetDashboardOffset(const char* p_variable)
+MxS32 DuneBuggy::GetColorOffset(const char* p_variable)
 {
-	MxS32 color = 1;
+	MxS32 offset = 1;
 	const char* colorName = VariableTable()->GetVariable(p_variable);
 
 	if (strcmpi(colorName, "lego green")) {
 		if (!strcmpi(colorName, "lego red")) {
-			color = 2;
+			offset = 2;
 		}
 		else if (!strcmpi(colorName, "lego yellow")) {
-			color = 3;
+			offset = 3;
 		}
 		else if (!strcmpi(colorName, "lego black")) {
-			color = 4;
+			offset = 4;
 		}
 		else if (!strcmpi(colorName, "lego blue")) {
-			color = 5;
+			offset = 5;
 		}
 		else if (!strcmpi(colorName, "lego white")) {
-			color = 6;
+			offset = 6;
 		}
 	}
 
-	return color;
+	return offset;
 }
 
 // FUNCTION: LEGO1 0x10068350
diff --git a/LEGO1/lego/legoomni/src/actors/jetski.cpp b/LEGO1/lego/legoomni/src/actors/jetski.cpp
index a65268ac..158df471 100644
--- a/LEGO1/lego/legoomni/src/actors/jetski.cpp
+++ b/LEGO1/lego/legoomni/src/actors/jetski.cpp
@@ -1,50 +1,163 @@
 #include "jetski.h"
 
+#include "dunebuggy.h"
+#include "isle.h"
+#include "isle_actions.h"
+#include "jukebox_actions.h"
+#include "legoanimationmanager.h"
+#include "legocontrolmanager.h"
+#include "legonavcontroller.h"
+#include "legoutils.h"
+#include "legovariables.h"
+#include "legoworld.h"
+#include "misc.h"
+#include "mxmisc.h"
+#include "mxtransitionmanager.h"
+#include "mxvariabletable.h"
+#include "scripts.h"
+
 DECOMP_SIZE_ASSERT(Jetski, 0x164)
 
+// GLOBAL: LEGO1 0x100f7ab8
+// STRING: LEGO1 0x100f3ce0
+const char* g_varJSFRNTY5 = "c_jsfrnty5";
+
+// GLOBAL: LEGO1 0x100f7abc
+// STRING: LEGO1 0x100f3ca4
+const char* g_varJSWNSHY5 = "c_jswnshy5";
+
 // FUNCTION: LEGO1 0x1007e3b0
 Jetski::Jetski()
 {
-	this->m_maxLinearVel = 25.0;
-	this->m_unk0x150 = 2.0;
-	this->m_unk0x148 = 1;
+	m_maxLinearVel = 25.0;
+	m_unk0x150 = 2.0;
+	m_unk0x148 = 1;
 }
 
-// STUB: LEGO1 0x1007e630
+// FUNCTION: LEGO1 0x1007e630
 MxResult Jetski::Create(MxDSAction& p_dsAction)
 {
-	// TODO
-	return SUCCESS;
+	MxResult result = IslePathActor::Create(p_dsAction);
+	m_world = CurrentWorld();
+
+	if (m_world) {
+		m_world->Add(this);
+	}
+
+	VariableTable()->SetVariable(g_varJETFUEL, "0.8");
+	return result;
 }
 
-// STUB: LEGO1 0x1007e680
-void Jetski::VTable0x70(float p_float)
+// FUNCTION: LEGO1 0x1007e680
+void Jetski::VTable0x70(float p_time)
 {
-	// TODO
+	IslePathActor::VTable0x70(p_time);
+
+	char buf[200];
+	float speed = abs(m_worldSpeed);
+	float maxLinearVel = NavController()->GetMaxLinearVel();
+
+	sprintf(buf, "%g", speed / maxLinearVel);
+	VariableTable()->SetVariable(g_varJETSPEED, buf);
 }
 
-// STUB: LEGO1 0x1007e6f0
+// FUNCTION: LEGO1 0x1007e6f0
 void Jetski::Exit()
 {
-	// TODO
+	SpawnPlayer(LegoGameState::e_unk45, FALSE, c_spawnBit1 | c_playMusic | c_spawnBit3);
+	IslePathActor::Exit();
+	GameState()->m_currentArea = LegoGameState::e_jetski;
+	RemoveFromWorld();
+	EnableAnimations(TRUE);
+	SetIsWorldActive(TRUE);
+	ControlManager()->Unregister(this);
 }
 
-// STUB: LEGO1 0x1007e750
+// FUNCTION: LEGO1 0x1007e750
+// FUNCTION: BETA10 0x10037621
 MxLong Jetski::HandleClick()
 {
-	// TODO
-	return 0;
+	if (!FUN_1003ef60()) {
+		return 1;
+	}
+
+	FUN_10015820(TRUE, 0);
+
+	((Isle*) CurrentWorld())->SetDestLocation(LegoGameState::Area::e_jetski);
+	TransitionManager()->StartTransition(MxTransitionManager::e_mosaic, 50, FALSE, TRUE);
+
+	if (GameState()->GetActorId() != UserActor()->GetActorId()) {
+		((IslePathActor*) UserActor())->Exit();
+	}
+
+	// TODO: Match
+	m_unk0x160 = ((DuneBuggy::GetColorOffset(g_varJSWNSHY5) * 5 + 15) * 2);
+	m_unk0x160 += DuneBuggy::GetColorOffset(g_varJSFRNTY5);
+
+	InvokeAction(Extra::ActionType::e_start, *g_isleScript, m_unk0x160, NULL);
+	InvokeAction(Extra::ActionType::e_start, *g_isleScript, IsleScript::c_JetskiDashboard, NULL);
+	GetCurrentAction().SetObjectId(-1);
+
+	AnimationManager()->FUN_1005f6d0(FALSE);
+	AnimationManager()->FUN_10064670(NULL);
+	Enter();
+	ControlManager()->Register(this);
+	return 1;
 }
 
-// STUB: LEGO1 0x1007e8e0
+// FUNCTION: LEGO1 0x1007e880
+void Jetski::RemoveFromWorld()
+{
+	RemoveFromCurrentWorld(*g_isleScript, m_unk0x160);
+	RemoveFromCurrentWorld(*g_isleScript, IsleScript::c_JetskiArms_Ctl);
+	RemoveFromCurrentWorld(*g_isleScript, IsleScript::c_JetskiInfo_Ctl);
+	RemoveFromCurrentWorld(*g_isleScript, IsleScript::c_JetskiSpeedMeter);
+	RemoveFromCurrentWorld(*g_isleScript, IsleScript::c_JetskiFuelMeter);
+}
+
+// FUNCTION: LEGO1 0x1007e8e0
 MxLong Jetski::HandleControl(LegoControlManagerNotificationParam& p_param)
 {
-	// TODO
+	if (p_param.GetUnknown0x28() == 1 && CurrentWorld()->IsA("Isle")) {
+		switch (p_param.GetClickedObjectId()) {
+		case IsleScript::c_JetskiArms_Ctl:
+			Exit();
+			((IslePathActor*) UserActor())
+				->SpawnPlayer(LegoGameState::e_jetraceExterior, TRUE, c_spawnBit1 | c_playMusic | c_spawnBit3);
+			GameState()->m_currentArea = LegoGameState::e_unk66;
+			return 1;
+		case IsleScript::c_JetskiInfo_Ctl:
+			((Isle*) CurrentWorld())->SetDestLocation(LegoGameState::e_infomain);
+			TransitionManager()->StartTransition(MxTransitionManager::e_mosaic, 50, FALSE, FALSE);
+			Exit();
+			return 1;
+		}
+	}
+
 	return 0;
 }
 
-// STUB: LEGO1 0x1007e990
-void Jetski::FUN_1007e990()
+// FUNCTION: LEGO1 0x1007e990
+void Jetski::ActivateSceneActions()
 {
-	// TODO
+	PlayMusic(JukeboxScript::c_JetskiRace_Music);
+
+	Act1State* act1state = (Act1State*) GameState()->GetState("Act1State");
+	if (!act1state->m_unk0x018) {
+		if (act1state->m_unk0x022) {
+			PlayCamAnim(this, FALSE, 68, TRUE);
+		}
+		else {
+			act1state->m_unk0x022 = TRUE;
+
+			LegoPathActor* user = UserActor();
+			if (user != NULL) {
+				MxMatrix mat(user->GetROI()->GetLocal2World());
+				mat.TranslateBy(mat[2][0] * 2.5, mat[2][1] + 0.6, mat[2][2] * 2.5);
+
+				AnimationManager()
+					->FUN_10060dc0(IsleScript::c_sjs007in_RunAnim, &mat, TRUE, FALSE, NULL, FALSE, TRUE, TRUE, TRUE);
+			}
+		}
+	}
 }
diff --git a/LEGO1/lego/legoomni/src/common/legovariables.cpp b/LEGO1/lego/legoomni/src/common/legovariables.cpp
index fcb4b975..54569743 100644
--- a/LEGO1/lego/legoomni/src/common/legovariables.cpp
+++ b/LEGO1/lego/legoomni/src/common/legovariables.cpp
@@ -16,6 +16,14 @@ DECOMP_SIZE_ASSERT(CursorVariable, 0x24)
 DECOMP_SIZE_ASSERT(WhoAmIVariable, 0x24)
 DECOMP_SIZE_ASSERT(CustomizeAnimFileVariable, 0x24)
 
+// GLOBAL: LEGO1 0x100f7ab0
+// STRING: LEGO1 0x100f09c0
+const char* g_varJETSPEED = "jetSPEED";
+
+// GLOBAL: LEGO1 0x100f7ab4
+// STRING: LEGO1 0x100f7aa8
+const char* g_varJETFUEL = "jetFUEL";
+
 // GLOBAL: LEGO1 0x100f7658
 // STRING: LEGO1 0x100f764c
 const char* g_varDUNESPEED = "duneSPEED";
diff --git a/LEGO1/lego/legoomni/src/worlds/isle.cpp b/LEGO1/lego/legoomni/src/worlds/isle.cpp
index d7cabb7b..fa9f9c5f 100644
--- a/LEGO1/lego/legoomni/src/worlds/isle.cpp
+++ b/LEGO1/lego/legoomni/src/worlds/isle.cpp
@@ -1044,7 +1044,7 @@ MxLong Isle::HandleTransitionEnd()
 		FUN_10032d30((IsleScript::Script) m_jetski->GetUnknown0x160(), JukeboxScript::c_MusicTheme1, NULL, TRUE);
 
 		if (!m_act1state->m_unk0x01f) {
-			m_jetski->FUN_1007e990();
+			m_jetski->ActivateSceneActions();
 		}
 		break;
 	default: