From 1f251ff817b0e068d67503defc7bb7c3af484e11 Mon Sep 17 00:00:00 2001
From: jonschz <17198703+jonschz@users.noreply.github.com>
Date: Sun, 28 Jul 2024 20:13:18 +0200
Subject: [PATCH] Implement/match `LegoCarRaceActor::FUN_10080590` (#1070)

* Implement/match `LegoCarRaceActor::FUN_10080590`

* Add vbtable annotations

* disable formatter for assertion

* Fix BETA10 annotations

* Address review comments

---------

Co-authored-by: jonschz <jonschz@users.noreply.github.com>
---
 .../lego/legoomni/include/legocarraceactor.h  | 36 ++++++++---
 LEGO1/lego/legoomni/src/common/misc.cpp       |  2 +
 .../legoomni/src/entity/legocarraceactor.cpp  | 61 +++++++++++++++++--
 LEGO1/lego/legoomni/src/race/raceskel.cpp     |  2 +-
 LEGO1/lego/sources/geom/legounkown100db7f4.h  |  6 ++
 LEGO1/lego/sources/geom/legoweedge.h          |  2 +
 LEGO1/realtime/orientableroi.h                |  3 +
 7 files changed, 100 insertions(+), 12 deletions(-)

diff --git a/LEGO1/lego/legoomni/include/legocarraceactor.h b/LEGO1/lego/legoomni/include/legocarraceactor.h
index 6f1c72f4..e1d41dce 100644
--- a/LEGO1/lego/legoomni/include/legocarraceactor.h
+++ b/LEGO1/lego/legoomni/include/legocarraceactor.h
@@ -7,12 +7,17 @@
 // VTABLE: LEGO1 0x100da0c8 LegoAnimActor
 // VTABLE: LEGO1 0x100da0d8 LegoPathActor
 // VTABLE: LEGO1 0x100da1a8 LegoCarRaceActor
+// VTABLE: BETA10 0x101bea74 LegoRaceActor
+// VTABLE: BETA10 0x101bea78 LegoAnimActor
+// VTABLE: BETA10 0x101bea90 LegoPathActor
+// VTABLE: BETA10 0x101beb80 LegoCarRaceActor
 // SIZE 0x1a0
 class LegoCarRaceActor : public virtual LegoRaceActor {
 public:
 	LegoCarRaceActor();
 
 	// FUNCTION: LEGO1 0x10081660
+	// FUNCTION: BETA10 0x100aab10
 	const char* ClassName() const override // vtable+0x0c
 	{
 		// STRING: LEGO1 0x100f0568
@@ -20,6 +25,7 @@ public:
 	}
 
 	// FUNCTION: LEGO1 0x10081680
+	// FUNCTION: BETA10 0x100aa9e0
 	MxBool IsA(const char* p_name) const override // vtable+0x10
 	{
 		return !strcmp(p_name, LegoCarRaceActor::ClassName()) || LegoRaceActor::IsA(p_name);
@@ -38,7 +44,7 @@ public:
 		override;                   // vtable+0x98
 	MxResult VTable0x9c() override; // vtable+0x9c
 
-	virtual void FUN_10080590(float);
+	virtual void FUN_10080590(float p_float);
 
 	// FUNCTION: LEGO1 0x10012bb0
 	virtual void FUN_10012bb0(float p_unk0x14) { m_unk0x14 = p_unk0x14; }
@@ -67,12 +73,28 @@ public:
 	// LegoCarRaceActor::`scalar deleting destructor'
 
 protected:
-	float m_unk0x08;      // 0x08
-	MxU8 m_unk0x0c;       // 0x0c
-	float m_unk0x10;      // 0x10
-	float m_unk0x14;      // 0x14
-	float m_unk0x18;      // 0x18
-	undefined4 m_unk0x1c; // 0x1c
+	MxFloat m_unk0x08; // 0x08
+	MxU8 m_unk0x0c;    // 0x0c
+
+	// Could be a multiplier for the maximum speed when going straight
+	MxFloat m_unk0x10; // 0x10
+
+	// Could be the acceleration
+	MxFloat m_unk0x14; // 0x14
+
+	MxFloat m_unk0x18; // 0x18
+
+	// Could be the current timestamp for time-based movement
+	MxFloat m_unk0x1c; // 0x1c
 };
 
+// GLOBAL: LEGO1 0x100da0b0
+// LegoCarRaceActor::`vbtable'
+
+// GLOBAL: LEGO1 0x100da0a8
+// LegoCarRaceActor::`vbtable'{for `LegoAnimActor'}
+
+// GLOBAL: LEGO1 0x100da098
+// LegoCarRaceActor::`vbtable'{for `LegoRaceActor'}
+
 #endif // LEGOCARRACEACTOR_H
diff --git a/LEGO1/lego/legoomni/src/common/misc.cpp b/LEGO1/lego/legoomni/src/common/misc.cpp
index 6d879aa1..901810d0 100644
--- a/LEGO1/lego/legoomni/src/common/misc.cpp
+++ b/LEGO1/lego/legoomni/src/common/misc.cpp
@@ -67,8 +67,10 @@ LegoNavController* NavController()
 }
 
 // FUNCTION: LEGO1 0x10015790
+// FUNCTION: BETA10 0x100e49ff
 LegoPathActor* UserActor()
 {
+	assert(LegoOmni::GetInstance());
 	return LegoOmni::GetInstance()->GetUserActor();
 }
 
diff --git a/LEGO1/lego/legoomni/src/entity/legocarraceactor.cpp b/LEGO1/lego/legoomni/src/entity/legocarraceactor.cpp
index b74a652b..3dd009da 100644
--- a/LEGO1/lego/legoomni/src/entity/legocarraceactor.cpp
+++ b/LEGO1/lego/legoomni/src/entity/legocarraceactor.cpp
@@ -1,5 +1,8 @@
 #include "legocarraceactor.h"
 
+#include "geom/legounkown100db7f4.h"
+#include "legopathboundary.h"
+#include "misc.h"
 #include "mxmisc.h"
 #include "mxvariabletable.h"
 
@@ -10,6 +13,7 @@ DECOMP_SIZE_ASSERT(LegoCarRaceActor, 0x1a0)
 const char* g_fuel = "FUEL";
 
 // FUNCTION: LEGO1 0x10080350
+// FUNCTION: BETA10 0x100cd6b0
 LegoCarRaceActor::LegoCarRaceActor()
 {
 	m_unk0x08 = 1.0f;
@@ -27,9 +31,57 @@ LegoCarRaceActor::LegoCarRaceActor()
 	VariableTable()->SetVariable(g_fuel, "0.8");
 }
 
-// STUB: LEGO1 0x10080590
-void LegoCarRaceActor::FUN_10080590(float)
+// FUNCTION: LEGO1 0x10080590
+// FUNCTION: BETA10 0x100cd8cf
+void LegoCarRaceActor::FUN_10080590(float p_float)
 {
+	MxFloat maxSpeed = m_maxLinearVel;
+	Mx3DPointFloat destEdgeUnknownVector;
+	Mx3DPointFloat worldDirection = Mx3DPointFloat(m_roi->GetWorldDirection());
+
+	m_destEdge->FUN_1002ddc0(*m_boundary, destEdgeUnknownVector);
+
+	if (abs(destEdgeUnknownVector.Dot(destEdgeUnknownVector.GetData(), worldDirection.GetData())) > 0.5) {
+		maxSpeed *= m_unk0x10;
+	}
+
+	MxS32 deltaUnk0x70;
+	LegoPathActor* userActor = UserActor();
+
+	if (userActor) {
+		// All known implementations of LegoPathActor->VTable0x5c() return LegoPathActor::m_unk0x70
+		deltaUnk0x70 = m_unk0x70 - userActor->VTable0x5c();
+	}
+	else {
+		deltaUnk0x70 = 0;
+	}
+
+	if (deltaUnk0x70 > 1) {
+		if (deltaUnk0x70 > 3) {
+			deltaUnk0x70 = 3;
+		}
+
+		maxSpeed *= (m_unk0x18 * (--deltaUnk0x70) * -0.25f + 1.0f);
+	}
+	else if (deltaUnk0x70 < -1) {
+		maxSpeed *= 1.3;
+	}
+
+	MxFloat deltaSpeed = maxSpeed - m_worldSpeed;
+	MxFloat changeInSpeed = (p_float - m_unk0x1c) * m_unk0x14;
+	m_unk0x1c = p_float;
+
+	if (deltaSpeed < 0.0f) {
+		changeInSpeed = -changeInSpeed;
+	}
+
+	MxFloat newWorldSpeed = changeInSpeed + m_worldSpeed;
+
+	if (newWorldSpeed > maxSpeed) {
+		newWorldSpeed = maxSpeed;
+	}
+
+	SetWorldSpeed(newWorldSpeed);
 }
 
 // STUB: LEGO1 0x10080740
@@ -37,10 +89,11 @@ void LegoCarRaceActor::VTable0x1c()
 {
 }
 
-// STUB: LEGO1 0x10080b40
+// FUNCTION: LEGO1 0x10080b40
+// FUNCTION: BETA10 0x100cdb3c
 void LegoCarRaceActor::SwitchBoundary(LegoPathBoundary*& p_boundary, LegoUnknown100db7f4*& p_edge, float& p_unk0xe4)
 {
-	// TODO
+	LegoPathActor::SwitchBoundary(m_boundary, m_destEdge, m_unk0xe4);
 }
 
 // STUB: LEGO1 0x10080b70
diff --git a/LEGO1/lego/legoomni/src/race/raceskel.cpp b/LEGO1/lego/legoomni/src/race/raceskel.cpp
index dd7e87f3..2d5c71bf 100644
--- a/LEGO1/lego/legoomni/src/race/raceskel.cpp
+++ b/LEGO1/lego/legoomni/src/race/raceskel.cpp
@@ -1,6 +1,6 @@
 #include "raceskel.h"
 
-#include <cassert>
+#include <assert.h>
 
 DECOMP_SIZE_ASSERT(RaceSkel, 0x178)
 
diff --git a/LEGO1/lego/sources/geom/legounkown100db7f4.h b/LEGO1/lego/sources/geom/legounkown100db7f4.h
index 4e6ffead..68f4c9f2 100644
--- a/LEGO1/lego/sources/geom/legounkown100db7f4.h
+++ b/LEGO1/lego/sources/geom/legounkown100db7f4.h
@@ -5,6 +5,8 @@
 #include "legowegedge.h"
 #include "mxgeometry/mxgeometry3d.h"
 
+#include <assert.h>
+
 // VTABLE: LEGO1 0x100db7f4
 // SIZE 0x40
 struct LegoUnknown100db7f4 : public LegoEdge {
@@ -28,6 +30,10 @@ public:
 			p_point[2] = -m_unk0x28[2];
 		}
 		else {
+			// clang-format off
+			// FIXME: There is no * dereference in the original assertion
+			assert(p_f.IsEqual( *m_faceB ));
+			// clang-format on
 			p_point = m_unk0x28;
 		}
 
diff --git a/LEGO1/lego/sources/geom/legoweedge.h b/LEGO1/lego/sources/geom/legoweedge.h
index 74dc6a45..6e5f4548 100644
--- a/LEGO1/lego/sources/geom/legoweedge.h
+++ b/LEGO1/lego/sources/geom/legoweedge.h
@@ -20,6 +20,8 @@ public:
 	// FUNCTION: BETA10 0x1001cc30
 	LegoEdge** GetEdges() { return m_edges; }
 
+	// TODO: The assertion at BETA10 0x10037352 suggests that this function might take a pointer instead of a reference
+	// FUNCTION: BETA10 0x100373f0
 	LegoU32 IsEqual(LegoWEEdge& p_other) { return this == &p_other; }
 
 	void SetEdges(LegoEdge** p_edges, LegoU8 p_numEdges)
diff --git a/LEGO1/realtime/orientableroi.h b/LEGO1/realtime/orientableroi.h
index 54059f20..ad84d9ed 100644
--- a/LEGO1/realtime/orientableroi.h
+++ b/LEGO1/realtime/orientableroi.h
@@ -43,7 +43,10 @@ public:
 	const Matrix4& GetLocal2World() const { return m_local2world; }
 
 	const float* GetWorldPosition() const { return m_local2world[3]; }
+
+	// FUNCTION: BETA10 0x10011780
 	const float* GetWorldDirection() const { return m_local2world[2]; }
+
 	const float* GetWorldUp() const { return m_local2world[1]; }
 	OrientableROI* GetParentROI() const { return m_parentROI; }