Implement CarRace (#1165)

* Implement `CarRace`

* Improve `CarRace::Create`

* Fix arrays

* Clean up array

* Fix CI errors

* Implement `LegoRaceCar::FUN_10012e00()`

* Implement `Doors::VTable0xcc`

* Address review comments, part 1

* Address review comments, part 2

---------

Co-authored-by: jonschz <jonschz@users.noreply.github.com>
This commit is contained in:
jonschz
2024-11-23 20:33:15 +01:00
committed by GitHub
parent e56ac9ab55
commit 0aa5e010ae
21 changed files with 533 additions and 61 deletions

View File

@@ -66,11 +66,21 @@ public:
// FUNCTION: BETA10 0x100f16f0 // FUNCTION: BETA10 0x100f16f0
void SetSkeleton(RaceSkel* p_skeleton) { m_skeleton = p_skeleton; } void SetSkeleton(RaceSkel* p_skeleton) { m_skeleton = p_skeleton; }
void FUN_10017820(MxS32 p_param1, MxS16 p_param2);
// SYNTHETIC: LEGO1 0x10016c70 // SYNTHETIC: LEGO1 0x10016c70
// CarRace::`scalar deleting destructor' // CarRace::`scalar deleting destructor'
private: private:
undefined m_unk0x144[12]; // 0x144 static MxS32 g_unk0x100d5d10[];
static MxS32 g_unk0x100d5d30[];
static MxS32 g_unk0x100d5d40[];
static MxS32 g_unk0x100d5d50[];
static MxS32 g_unk0x100d5d60[];
MxS32 m_unk0x144; // 0x144
MxS32 m_unk0x148; // 0x148
MxS32 m_unk0x14c; // 0x14c
RaceSkel* m_skeleton; // 0x150 RaceSkel* m_skeleton; // 0x150
}; };

View File

@@ -27,7 +27,7 @@ public:
void ParseAction(char* p_extra) override; // vtable+0x20 void ParseAction(char* p_extra) override; // vtable+0x20
void VTable0x70(float p_float) override; // vtable+0x70 void VTable0x70(float p_float) override; // vtable+0x70
MxResult VTable0x94(LegoPathActor* p_actor, MxBool p_bool) override; // vtable+0x94 MxResult VTable0x94(LegoPathActor* p_actor, MxBool p_bool) override; // vtable+0x94
virtual double VTable0xcc(float p_float); // vtable+0xcc virtual MxFloat VTable0xcc(float p_float); // vtable+0xcc
// SYNTHETIC: LEGO1 0x1000e580 // SYNTHETIC: LEGO1 0x1000e580
// Doors::`scalar deleting destructor' // Doors::`scalar deleting destructor'

View File

@@ -65,8 +65,6 @@ public:
void FUN_10016930(MxS32 p_param1, MxS16 p_param2); void FUN_10016930(MxS32 p_param1, MxS16 p_param2);
private: private:
inline MxS32 PossiblyGetPlaceOfPlayer();
static MxS32 g_unk0x100f0c78; static MxS32 g_unk0x100f0c78;
}; };

View File

@@ -10,6 +10,7 @@ class MxDSAction;
class Vector3; class Vector3;
// VTABLE: LEGO1 0x100d4858 // VTABLE: LEGO1 0x100d4858
// VTABLE: BETA10 0x101b9388
// SIZE 0x68 // SIZE 0x68
class LegoEntity : public MxEntity { class LegoEntity : public MxEntity {
public: public:
@@ -38,6 +39,7 @@ public:
MxLong Notify(MxParam& p_param) override; // vtable+0x04 MxLong Notify(MxParam& p_param) override; // vtable+0x04
// FUNCTION: LEGO1 0x1000c2f0 // FUNCTION: LEGO1 0x1000c2f0
// FUNCTION: BETA10 0x10012730
const char* ClassName() const override // vtable+0x0c const char* ClassName() const override // vtable+0x0c
{ {
// STRING: LEGO1 0x100f0064 // STRING: LEGO1 0x100f0064
@@ -116,7 +118,10 @@ protected:
// For tokens from the extra string that look like this: // For tokens from the extra string that look like this:
// "Action:openram;\lego\scripts\Race\CarRaceR;0" // "Action:openram;\lego\scripts\Race\CarRaceR;0"
Extra::ActionType m_actionType; // 0x5c Extra::ActionType m_actionType; // 0x5c
char* m_filename; // 0x60
// variable name verified by BETA10 0x1007eddf
char* m_siFile; // 0x60
MxS32 m_targetEntityId; // 0x64 MxS32 m_targetEntityId; // 0x64
}; };

View File

@@ -82,6 +82,12 @@ public:
// FUNCTION: BETA10 0x100b0f40 // FUNCTION: BETA10 0x100b0f40
void SetLinearVel(MxFloat p_linearVel) { m_linearVel = p_linearVel; } void SetLinearVel(MxFloat p_linearVel) { m_linearVel = p_linearVel; }
// FUNCTION: BETA10 0x100c99e0
void SetDeadZone(MxS32 p_deadZone) { m_deadZone = p_deadZone; }
// FUNCTION: BETA10 0x100c7880
void SetTrackDefault(MxS32 p_trackDefault) { m_trackDefault = p_trackDefault; }
MxFloat GetLinearVel() { return m_linearVel; } MxFloat GetLinearVel() { return m_linearVel; }
MxFloat GetRotationalVel() { return m_rotationalVel; } MxFloat GetRotationalVel() { return m_rotationalVel; }
MxFloat GetMaxLinearVel() { return m_maxLinearVel; } MxFloat GetMaxLinearVel() { return m_maxLinearVel; }
@@ -91,6 +97,9 @@ public:
m_trackDefault = 0; m_trackDefault = 0;
} }
// FUNCTION: BETA10 0x100c9a10
int GetDefaultDeadZone() { return g_defdeadZone; }
// SYNTHETIC: LEGO1 0x10054c10 // SYNTHETIC: LEGO1 0x10054c10
// LegoNavController::`scalar deleting destructor' // LegoNavController::`scalar deleting destructor'

View File

@@ -15,6 +15,7 @@ struct LegoUnknown100db7f4;
class LegoWEEdge; class LegoWEEdge;
extern MxLong g_unk0x100f3308; extern MxLong g_unk0x100f3308;
extern const char* g_strHIT_WALL_SOUND;
// VTABLE: LEGO1 0x100d6e28 // VTABLE: LEGO1 0x100d6e28
// SIZE 0x154 // SIZE 0x154

View File

@@ -77,6 +77,7 @@ public:
virtual MxU32 HandleSkeletonKicks(float p_param1); virtual MxU32 HandleSkeletonKicks(float p_param1);
static void FUN_10012de0(); static void FUN_10012de0();
static void FUN_10012e00();
static void FUN_10013670(); static void FUN_10013670();
// SYNTHETIC: LEGO1 0x10014240 // SYNTHETIC: LEGO1 0x10014240

View File

@@ -4,11 +4,24 @@
#include "mxmisc.h" #include "mxmisc.h"
#include "mxtimer.h" #include "mxtimer.h"
#include "roi/legoroi.h" #include "roi/legoroi.h"
#include "tgl/tglvector.h"
#include <assert.h> #include <assert.h>
DECOMP_SIZE_ASSERT(Doors, 0x1f8) DECOMP_SIZE_ASSERT(Doors, 0x1f8)
// GLOBAL: LEGO1 0x100d8e7c
// GLOBAL: BETA10 0x101b954c
MxFloat g_unk0x100d8e7c = 1000.0f;
// GLOBAL: LEGO1 0x100d8e80
// GLOBAL: BETA10 0x101b9550
MxFloat g_unk0x100d8e80 = 4000.0f;
// GLOBAL: LEGO1 0x100d8e84
// GLOBAL: BETA10 0x101b9554
MxFloat g_unk0x100d8e84 = 6000.0f;
// FUNCTION: LEGO1 0x10066100 // FUNCTION: LEGO1 0x10066100
// FUNCTION: BETA10 0x10026850 // FUNCTION: BETA10 0x10026850
MxResult Doors::VTable0x94(LegoPathActor* p_actor, MxBool p_bool) MxResult Doors::VTable0x94(LegoPathActor* p_actor, MxBool p_bool)
@@ -25,11 +38,29 @@ MxResult Doors::VTable0x94(LegoPathActor* p_actor, MxBool p_bool)
return m_unk0x1f4 < 0.001 ? SUCCESS : FAILURE; return m_unk0x1f4 < 0.001 ? SUCCESS : FAILURE;
} }
// STUB: LEGO1 0x10066190 // FUNCTION: LEGO1 0x10066190
// FUNCTION: BETA10 0x1002696b // FUNCTION: BETA10 0x1002696b
double Doors::VTable0xcc(float p_float) MxFloat Doors::VTable0xcc(float p_float)
{ {
return 0.0; MxFloat fVar1;
fVar1 = p_float - m_unk0x158;
if (fVar1 <= 0.0f) {
return 0.0f;
}
if (fVar1 <= g_unk0x100d8e7c) {
return fVar1 * 1.570796 / g_unk0x100d8e7c;
}
else if (fVar1 <= g_unk0x100d8e7c + g_unk0x100d8e80) {
return 1.570796012878418; // Pi / 2
}
else if (fVar1 <= g_unk0x100d8e84) {
return (1.0 - ((fVar1 - g_unk0x100d8e80) - g_unk0x100d8e7c) / g_unk0x100d8e7c) * 1.570796;
}
return 0.0f;
} }
// FUNCTION: LEGO1 0x10066250 // FUNCTION: LEGO1 0x10066250
@@ -71,7 +102,7 @@ void Doors::VTable0x70(float p_float)
m_unk0x1f4 = local8; m_unk0x1f4 = local8;
} }
if (m_unk0x158 + 6000.0f < p_float) { if (m_unk0x158 + g_unk0x100d8e84 < p_float) {
m_ltDoor->FUN_100a58f0(m_ltDoorLocal); m_ltDoor->FUN_100a58f0(m_ltDoorLocal);
m_rtDoor->FUN_100a58f0(m_rtDoorLocal); m_rtDoor->FUN_100a58f0(m_rtDoorLocal);
m_ltDoor->VTable0x14(); m_ltDoor->VTable0x14();

View File

@@ -12,6 +12,7 @@
#include "legocontrolmanager.h" #include "legocontrolmanager.h"
#include "legogamestate.h" #include "legogamestate.h"
#include "legoinputmanager.h" #include "legoinputmanager.h"
#include "legomain.h"
#include "legosoundmanager.h" #include "legosoundmanager.h"
#include "legoutils.h" #include "legoutils.h"
#include "misc.h" #include "misc.h"
@@ -1254,7 +1255,7 @@ void LegoCarBuild::FUN_10024ef0()
m_buildState->m_animationState = LegoVehicleBuildState::e_cutscene; m_buildState->m_animationState = LegoVehicleBuildState::e_cutscene;
FUN_10025720(FUN_10025d70()); FUN_10025720(FUN_10025d70());
m_buildState->m_unk0x4c += 1; m_buildState->m_unk0x4c += 1;
FUN_10015820(FALSE, 7); FUN_10015820(FALSE, LegoOmni::c_disableInput | LegoOmni::c_disable3d | LegoOmni::c_clearScreen);
} }
// FUNCTION: LEGO1 0x10024f30 // FUNCTION: LEGO1 0x10024f30

View File

@@ -41,8 +41,10 @@ MxBackgroundAudioManager* BackgroundAudioManager()
} }
// FUNCTION: LEGO1 0x10015740 // FUNCTION: LEGO1 0x10015740
// FUNCTION: BETA10 0x100e4895
LegoInputManager* InputManager() LegoInputManager* InputManager()
{ {
assert(LegoOmni::GetInstance());
return LegoOmni::GetInstance()->GetInputManager(); return LegoOmni::GetInstance()->GetInputManager();
} }
@@ -95,8 +97,10 @@ LegoWorld* CurrentWorld()
} }
// FUNCTION: LEGO1 0x100157b0 // FUNCTION: LEGO1 0x100157b0
// FUNCTION: BETA10 0x100e4a8d
LegoCharacterManager* CharacterManager() LegoCharacterManager* CharacterManager()
{ {
assert(LegoOmni::GetInstance());
return LegoOmni::GetInstance()->GetCharacterManager(); return LegoOmni::GetInstance()->GetCharacterManager();
} }
@@ -131,8 +135,10 @@ ViewLODListManager* GetViewLODListManager()
} }
// FUNCTION: LEGO1 0x10015820 // FUNCTION: LEGO1 0x10015820
// FUNCTION: BETA10 0x100e4c92
void FUN_10015820(MxBool p_disable, MxU16 p_flags) void FUN_10015820(MxBool p_disable, MxU16 p_flags)
{ {
assert(LegoOmni::GetInstance());
LegoOmni::GetInstance()->FUN_1005b4f0(p_disable, p_flags); LegoOmni::GetInstance()->FUN_1005b4f0(p_disable, p_flags);
} }

View File

@@ -140,6 +140,7 @@ void LegoActor::SetROI(LegoROI* p_roi, MxBool p_bool1, MxBool p_bool2)
} }
// FUNCTION: LEGO1 0x1002d6e0 // FUNCTION: LEGO1 0x1002d6e0
// FUNCTION: BETA10 0x1003d6f2
void LegoActor::Mute(MxBool p_muted) void LegoActor::Mute(MxBool p_muted)
{ {
if (m_sound != NULL) { if (m_sound != NULL) {

View File

@@ -28,7 +28,7 @@ void LegoEntity::Init()
m_worldSpeed = 0; m_worldSpeed = 0;
m_roi = NULL; m_roi = NULL;
m_cameraFlag = FALSE; m_cameraFlag = FALSE;
m_filename = NULL; m_siFile = NULL;
m_unk0x10 = 0; m_unk0x10 = 0;
m_flags = 0; m_flags = 0;
m_actionType = Extra::ActionType::e_unknown; m_actionType = Extra::ActionType::e_unknown;
@@ -37,6 +37,7 @@ void LegoEntity::Init()
} }
// FUNCTION: LEGO1 0x10010650 // FUNCTION: LEGO1 0x10010650
// FUNCTION: BETA10 0x1007e39a
void LegoEntity::ResetWorldTransform(MxBool p_cameraFlag) void LegoEntity::ResetWorldTransform(MxBool p_cameraFlag)
{ {
LegoWorld* world = CurrentWorld(); LegoWorld* world = CurrentWorld();
@@ -66,6 +67,7 @@ void LegoEntity::ResetWorldTransform(MxBool p_cameraFlag)
} }
// FUNCTION: LEGO1 0x10010790 // FUNCTION: LEGO1 0x10010790
// FUNCTION: BETA10 0x1007e4f6
void LegoEntity::SetWorldTransform(const Vector3& p_location, const Vector3& p_direction, const Vector3& p_up) void LegoEntity::SetWorldTransform(const Vector3& p_location, const Vector3& p_direction, const Vector3& p_up)
{ {
LegoWorld* world = CurrentWorld(); LegoWorld* world = CurrentWorld();
@@ -78,6 +80,7 @@ void LegoEntity::SetWorldTransform(const Vector3& p_location, const Vector3& p_d
} }
// FUNCTION: LEGO1 0x100107e0 // FUNCTION: LEGO1 0x100107e0
// FUNCTION: BETA10 0x1007e572
MxResult LegoEntity::Create(MxDSAction& p_dsAction) MxResult LegoEntity::Create(MxDSAction& p_dsAction)
{ {
m_entityId = p_dsAction.GetObjectId(); m_entityId = p_dsAction.GetObjectId();
@@ -87,6 +90,7 @@ MxResult LegoEntity::Create(MxDSAction& p_dsAction)
} }
// FUNCTION: LEGO1 0x10010810 // FUNCTION: LEGO1 0x10010810
// FUNCTION: BETA10 0x1007e5b9
void LegoEntity::Destroy(MxBool p_fromDestructor) void LegoEntity::Destroy(MxBool p_fromDestructor)
{ {
if (m_roi) { if (m_roi) {
@@ -103,11 +107,12 @@ void LegoEntity::Destroy(MxBool p_fromDestructor)
} }
} }
delete[] m_filename; delete[] m_siFile;
Init(); Init();
} }
// FUNCTION: LEGO1 0x10010880 // FUNCTION: LEGO1 0x10010880
// FUNCTION: BETA10 0x1007e6e1
void LegoEntity::SetWorld() void LegoEntity::SetWorld()
{ {
LegoWorld* world = CurrentWorld(); LegoWorld* world = CurrentWorld();
@@ -118,6 +123,7 @@ void LegoEntity::SetWorld()
} }
// FUNCTION: LEGO1 0x100108a0 // FUNCTION: LEGO1 0x100108a0
// FUNCTION: BETA10 0x1007e724
void LegoEntity::SetROI(LegoROI* p_roi, MxBool p_bool1, MxBool p_bool2) void LegoEntity::SetROI(LegoROI* p_roi, MxBool p_bool1, MxBool p_bool2)
{ {
m_roi = p_roi; m_roi = p_roi;
@@ -225,6 +231,7 @@ Mx3DPointFloat LegoEntity::GetWorldPosition()
} }
// FUNCTION: LEGO1 0x10010e10 // FUNCTION: LEGO1 0x10010e10
// FUNCTION: BETA10 0x1007ec97
void LegoEntity::ParseAction(char* p_extra) void LegoEntity::ParseAction(char* p_extra)
{ {
char copy[1024]; char copy[1024];
@@ -232,16 +239,22 @@ void LegoEntity::ParseAction(char* p_extra)
strcpy(copy, p_extra); strcpy(copy, p_extra);
if (KeyValueStringParse(actionValue, g_strACTION, copy)) { if (KeyValueStringParse(actionValue, g_strACTION, copy)) {
m_actionType = MatchActionString(strtok(actionValue, g_parseExtraTokens)); char* token = strtok(actionValue, g_parseExtraTokens);
assert(token);
m_actionType = MatchActionString(token);
if (m_actionType != Extra::ActionType::e_exit) { if (m_actionType != Extra::ActionType::e_exit) {
char* token = strtok(NULL, g_parseExtraTokens); token = strtok(NULL, g_parseExtraTokens);
assert(token);
m_filename = new char[strlen(token) + 1]; m_siFile = new char[strlen(token) + 1];
strcpy(m_filename, token); assert(m_siFile);
strcpy(m_siFile, token);
if (m_actionType != Extra::ActionType::e_run) { if (m_actionType != Extra::ActionType::e_run) {
m_targetEntityId = atoi(strtok(NULL, g_parseExtraTokens)); token = strtok(NULL, g_parseExtraTokens);
assert(token);
m_targetEntityId = atoi(token);
} }
} }
} }
@@ -458,7 +471,7 @@ MxLong LegoEntity::Notify(MxParam& p_param)
} }
if (m_actionType != Extra::e_unknown) { if (m_actionType != Extra::e_unknown) {
InvokeAction(m_actionType, MxAtomId(m_filename, e_lowerCase2), m_targetEntityId, this); InvokeAction(m_actionType, MxAtomId(m_siFile, e_lowerCase2), m_targetEntityId, this);
} }
else { else {
switch (GameState()->GetActorId()) { switch (GameState()->GetActorId()) {

View File

@@ -15,6 +15,7 @@ MxAtomId* g_jetskiScript = NULL;
MxAtomId* g_racecarScript = NULL; MxAtomId* g_racecarScript = NULL;
// GLOBAL: LEGO1 0x100f452c // GLOBAL: LEGO1 0x100f452c
// GLOBAL: BETA10 0x10211514
MxAtomId* g_carraceScript = NULL; MxAtomId* g_carraceScript = NULL;
// GLOBAL: LEGO1 0x100f4530 // GLOBAL: LEGO1 0x100f4530

View File

@@ -1,9 +1,73 @@
#include "carrace.h" #include "carrace.h"
#include "actions/carrace_actions.h"
#include "actions/jukebox_actions.h"
#include "dunebuggy.h"
#include "isle.h"
#include "legoanimationmanager.h"
#include "legobackgroundcolor.h"
#include "legocontrolmanager.h"
#include "legohideanimpresenter.h"
#include "legomain.h"
#include "legonavcontroller.h"
#include "legopathstruct.h"
#include "legoracers.h"
#include "legoutils.h"
#include "misc.h"
#include "mxactionnotificationparam.h" #include "mxactionnotificationparam.h"
#include "mxbackgroundaudiomanager.h"
#include "mxmisc.h"
#include "mxnotificationmanager.h"
#include "mxstillpresenter.h"
#include "mxtransitionmanager.h"
#include "mxvariabletable.h"
#include "scripts.h"
DECOMP_SIZE_ASSERT(CarRace, 0x154) DECOMP_SIZE_ASSERT(CarRace, 0x154)
// GLOBAL: LEGO1 0x100d5d10
MxS32 CarRace::g_unk0x100d5d10[] = {
CarraceScript::c_srt001sl_RunAnim,
CarraceScript::c_srt002sl_RunAnim,
CarraceScript::c_srt003sl_RunAnim,
CarraceScript::c_srt004sl_RunAnim,
CarraceScript::c_srt005sl_RunAnim,
CarraceScript::c_srt001rh_RunAnim,
CarraceScript::c_srt002rh_RunAnim,
CarraceScript::c_srt003rh_RunAnim
};
// GLOBAL: LEGO1 0x100d5d30
MxS32 CarRace::g_unk0x100d5d30[] = {
CarraceScript::c_srt011sl_RunAnim,
CarraceScript::c_srt012sl_RunAnim,
CarraceScript::c_srt013sl_RunAnim,
CarraceScript::c_srt014sl_RunAnim
};
// GLOBAL: LEGO1 0x100d5d40
MxS32 CarRace::g_unk0x100d5d40[] =
{CarraceScript::c_srt015sl_RunAnim, CarraceScript::c_srt016sl_RunAnim, CarraceScript::c_srt017sl_RunAnim};
// GLOBAL: LEGO1 0x100d5d50
MxS32 CarRace::g_unk0x100d5d50[] =
{CarraceScript::c_srt007rh_RunAnim, CarraceScript::c_srt008rh_RunAnim, CarraceScript::c_srt009rh_RunAnim};
// GLOBAL: LEGO1 0x100d5d60
MxS32 CarRace::g_unk0x100d5d60[] =
{CarraceScript::c_srt010rh_RunAnim, CarraceScript::c_srt011rh_RunAnim, CarraceScript::c_srt012rh_RunAnim};
// GLOBAL: LEGO1 0x100f0c70
// STRING: LEGO1 0x100f0c48
LegoChar* g_strCRCFRNTY6 = "C_RCFRNTY6";
// GLOBAL: LEGO1 0x100f0c74
// STRING: LEGO1 0x100f0c3c
LegoChar* g_strCRCEDGEY0 = "C_RCEDGEY0";
// GLOBAL: LEGO1 0x100f0c7c
static MxS32 g_unk0x100f0c7c = 2;
// FUNCTION: LEGO1 0x10016a90 // FUNCTION: LEGO1 0x10016a90
CarRace::CarRace() CarRace::CarRace()
{ {
@@ -11,50 +75,345 @@ CarRace::CarRace()
this->m_unk0x130 = MxRect32(0x16c, 0x154, 0x1ec, 0x15e); this->m_unk0x130 = MxRect32(0x16c, 0x154, 0x1ec, 0x15e);
} }
// STUB: LEGO1 0x10016ce0 // FUNCTION: LEGO1 0x10016ce0
// FUNCTION: BETA10 0x100c8364
MxResult CarRace::Create(MxDSAction& p_dsAction) MxResult CarRace::Create(MxDSAction& p_dsAction)
{ {
// TODO MxResult result = LegoRace::Create(p_dsAction);
return SUCCESS;
NavController()->SetDeadZone(1);
NavController()->SetTrackDefault(0);
GameState()->m_currentArea = LegoGameState::e_carrace;
GameState()->StopArea(LegoGameState::e_undefined);
LegoGameState* state = GameState();
RaceState* raceState = (RaceState*) state->GetState("CarRaceState");
if (!raceState) {
raceState = (RaceState*) state->CreateState("CarRaceState");
}
m_raceState = raceState;
m_act1State->m_unk0x018 = 6;
m_unk0x144 = -1;
m_unk0x148 = -1;
m_unk0x14c = -1;
LegoRaceCar::FUN_10012e00();
MxS32 streamId =
DuneBuggy::GetColorOffset(g_strCRCEDGEY0) + (DuneBuggy::GetColorOffset(g_strCRCFRNTY6) * 5 + 15) * 2;
InvokeAction(Extra::e_start, m_atomId, streamId, NULL);
InvokeAction(Extra::e_start, m_atomId, CarraceScript::c_RaceCarDashboard, NULL);
return result;
} }
// STUB: LEGO1 0x10016dd0 // FUNCTION: LEGO1 0x10016dd0
// FUNCTION: BETA10 0x100c8490
void CarRace::ReadyWorld() void CarRace::ReadyWorld()
{ {
// TODO assert(m_hideAnim);
LegoWorld::ReadyWorld();
m_hideAnim->FUN_1006db40(0);
MxDSAction action;
action.SetAtomId(*g_jukeboxScript);
action.SetObjectId(JukeboxScript::c_RaceTrackRoad_Music);
BackgroundAudioManager()->PlayMusic(action, 5, MxPresenter::e_repeating);
AnimationManager()->Resume();
FUN_10015820(FALSE, LegoOmni::c_disableInput | LegoOmni::c_disable3d | LegoOmni::c_clearScreen);
m_unk0x144 = g_unk0x100d5d10[rand() & 7];
AnimationManager()->FUN_10060dc0(m_unk0x144, NULL, TRUE, FALSE, NULL, FALSE, TRUE, FALSE, TRUE);
m_unk0x128 = (MxStillPresenter*) Find("MxPresenter", "CarLocator2");
m_unk0x128->SetPosition(m_unk0x130.GetLeft(), m_unk0x130.GetTop());
m_unk0x12c = (MxStillPresenter*) Find("MxPresenter", "CarLocator3");
m_unk0x12c->SetPosition(m_unk0x130.GetLeft(), m_unk0x130.GetTop());
VariableTable()->SetVariable("DISTANCE", "0.036");
} }
// STUB: LEGO1 0x10016f60 // FUNCTION: LEGO1 0x10016f60
MxLong CarRace::HandleEndAction(MxEndActionNotificationParam&) // FUNCTION: BETA10 0x100c85eb
MxLong CarRace::HandleEndAction(MxEndActionNotificationParam& p_param)
{ {
// TODO MxLong result = 0;
return 0;
if (p_param.GetAction()) {
MxDSAction* action = p_param.GetAction();
MxU32 objectId = action->GetObjectId();
if (m_unk0x144 == objectId) {
InvokeAction(Extra::e_start, *g_carraceScript, CarraceScript::c_irtx08ra_PlayWav, NULL);
result = 1;
}
else if (objectId == CarraceScript::c_irtx08ra_PlayWav && m_destLocation == LegoGameState::e_undefined) {
m_unk0x110[0]->Mute(FALSE);
m_unk0x110[1]->Mute(FALSE);
m_unk0x110[2]->Mute(FALSE);
VariableTable()->SetVariable(g_raceState, g_racing);
result = 1;
}
else if (m_unk0x148 == objectId) {
AnimationManager()->FUN_10060dc0(m_unk0x14c, NULL, TRUE, FALSE, NULL, FALSE, TRUE, FALSE, TRUE);
}
else if (m_unk0x14c == objectId) {
NotificationManager()->Send(this, MxNotificationParam());
}
}
return result;
} }
// STUB: LEGO1 0x100170e0 // FUNCTION: LEGO1 0x100170e0
MxLong CarRace::HandlePathStruct(LegoPathStructNotificationParam&) // FUNCTION: BETA10 0x100c87ac
MxLong CarRace::HandlePathStruct(LegoPathStructNotificationParam& p_param)
{ {
// TODO MxLong result = 0;
return 0;
if (p_param.GetTrigger() == 68) {
MxEntity* sender = (MxEntity*) p_param.GetSender();
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.036 + 0.928 * (m_unk0xf8 * 20.0 + m_unk0x104) / (g_unk0x100f0c7c * 20.0));
VariableTable()->SetVariable("DISTANCE", buffer);
if (m_unk0x104 == 0x14) {
m_unk0x104 = 0;
m_unk0xf8++;
if (g_unk0x100f0c7c == m_unk0xf8) {
VariableTable()->SetVariable(g_raceState, "");
m_unk0x110[0]->Mute(TRUE);
m_unk0x110[1]->Mute(TRUE);
m_unk0x110[2]->Mute(TRUE);
m_unk0x110[0]->SetMaxLinearVel(-1.0);
m_unk0x110[1]->SetMaxLinearVel(-1.0);
m_unk0x110[2]->SetMaxLinearVel(-1.0);
RemoveActor(m_unk0x110[1]);
m_unk0x110[1]->ClearMaps();
RemoveActor(m_unk0x110[2]);
m_unk0x110[2]->ClearMaps();
MxS32 position;
if (m_unk0xfc < m_unk0xf8 && m_unk0x100 < m_unk0xf8) {
position = 3;
m_unk0x148 = g_unk0x100d5d40[rand() % 3];
m_unk0x14c = g_unk0x100d5d60[rand() % 3];
}
else if (m_unk0xfc < m_unk0xf8 || m_unk0x100 < m_unk0xf8) {
position = 2;
if (m_unk0xfc == g_unk0x100f0c7c) {
m_unk0x148 = g_unk0x100d5d30[rand() % 4];
m_unk0x14c = g_unk0x100d5d60[rand() % 3];
}
else {
m_unk0x148 = g_unk0x100d5d50[rand() % 3];
m_unk0x14c = g_unk0x100d5d40[rand() % 3];
}
}
else {
position = 1;
m_unk0x148 = g_unk0x100d5d30[rand() % 4];
m_unk0x14c = g_unk0x100d5d50[rand() % 3];
}
InputManager()->DisableInputProcessing();
InputManager()->SetUnknown336(TRUE);
VariableTable()->SetVariable(g_strHIT_WALL_SOUND, "");
NavController()->SetDeadZone(NavController()->GetDefaultDeadZone());
NavController()->SetTrackDefault(1);
LegoRaceCar::FUN_10012de0();
m_raceState->m_unk0x28 = 2;
RaceState::Entry* raceState = m_raceState->GetState(GameState()->GetActorId());
raceState->m_unk0x02 = position;
if (raceState->m_score < (MxS16) position) {
raceState->m_score = position;
}
AnimationManager()->FUN_10060dc0(m_unk0x148, NULL, TRUE, FALSE, NULL, FALSE, TRUE, FALSE, TRUE);
}
result = 1;
}
break;
case 11:
if (paramData <= m_unk0x108 || paramData >= m_unk0x108 + 5) {
break;
}
FUN_10017820(11, paramData);
m_unk0x108 = paramData;
if (m_unk0x108 == 0x14) {
m_unk0x108 = 0;
m_unk0xfc++;
if (g_unk0x100f0c7c == m_unk0xfc) {
m_unk0x110[1]->SetMaxLinearVel(-1.0);
RemoveActor(m_unk0x110[1]);
m_unk0x110[1]->ClearMaps();
m_unk0x110[1]->GetROI()->SetVisibility(FALSE);
LegoROI* roi = FindROI("rcblack");
if (roi) {
roi->SetVisibility(FALSE);
}
}
}
break;
case 12:
if (paramData <= m_unk0x10c || paramData >= m_unk0x10c + 5) {
break;
}
FUN_10017820(12, paramData);
m_unk0x10c = paramData;
if (m_unk0x10c == 0x14) {
m_unk0x10c = 0;
m_unk0x100++;
if (g_unk0x100f0c7c == m_unk0x100) {
m_unk0x110[2]->SetMaxLinearVel(-1.0);
RemoveActor(m_unk0x110[2]);
m_unk0x110[2]->ClearMaps();
m_unk0x110[2]->GetROI()->SetVisibility(FALSE);
LegoROI* roi = FindROI("rcgreen");
if (roi) {
roi->SetVisibility(FALSE);
}
}
}
break;
}
}
return result;
} }
// STUB: LEGO1 0x10017650 // FUNCTION: LEGO1 0x10017650
MxLong CarRace::HandleClick(LegoEventNotificationParam&) MxLong CarRace::HandleClick(LegoEventNotificationParam& p_param)
{ {
// TODO LegoControlManagerNotificationParam* param = (LegoControlManagerNotificationParam*) &p_param;
return 0;
if (param->m_unk0x28 == 1) {
switch (param->m_clickedObjectId) {
case 3:
InvokeAction(Extra::e_stop, *g_carraceScript, CarraceScript::c_irtx08ra_PlayWav, NULL);
m_act1State->m_unk0x018 = 0;
VariableTable()->SetVariable(g_raceState, "");
VariableTable()->SetVariable(g_strHIT_WALL_SOUND, "");
NavController()->SetDeadZone(NavController()->GetDefaultDeadZone());
NavController()->SetTrackDefault(1);
LegoRaceCar::FUN_10012de0();
m_destLocation = LegoGameState::e_infomain;
TransitionManager()->StartTransition(MxTransitionManager::e_mosaic, 50, FALSE, FALSE);
GameState()->GetBackgroundColor()->SetValue("reset");
break;
case 98:
InvokeAction(Extra::e_stop, *g_carraceScript, CarraceScript::c_irtx08ra_PlayWav, NULL);
m_act1State->m_unk0x018 = 0;
VariableTable()->SetVariable(g_raceState, "");
VariableTable()->SetVariable(g_strHIT_WALL_SOUND, "");
NavController()->SetDeadZone(NavController()->GetDefaultDeadZone());
NavController()->SetTrackDefault(1);
LegoRaceCar::FUN_10012de0();
m_destLocation = LegoGameState::e_carraceExterior;
TransitionManager()->StartTransition(MxTransitionManager::e_mosaic, 50, FALSE, FALSE);
GameState()->GetBackgroundColor()->SetValue("reset");
break;
default:
break;
}
}
return 1;
} }
// STUB: LEGO1 0x100177e0 // FUNCTION: LEGO1 0x100177e0
// FUNCTION: BETA10 0x100c8f59
MxLong CarRace::HandleType0Notification(MxNotificationParam&) MxLong CarRace::HandleType0Notification(MxNotificationParam&)
{ {
// TODO if (m_raceState->m_unk0x28 == 2) {
return 0; m_destLocation = LegoGameState::e_unk21;
TransitionManager()->StartTransition(MxTransitionManager::e_mosaic, 50, FALSE, FALSE);
}
return 1;
} }
// STUB: LEGO1 0x10017900 // FUNCTION: LEGO1 0x10017820
void CarRace::FUN_10017820(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_unk0x100f0c7c * 20.0);
y = m_unk0x130.GetTop() + 0.5 +
(m_unk0x130.GetBottom() - m_unk0x130.GetTop() + 1) * (local4 * 20.0 + p_param2) / (g_unk0x100f0c7c * 20.0);
presenter->SetPosition(x, y);
}
}
// FUNCTION: LEGO1 0x10017900
MxBool CarRace::Escape() MxBool CarRace::Escape()
{ {
// TODO InvokeAction(Extra::e_stop, *g_carraceScript, CarraceScript::c_irtx08ra_PlayWav, NULL);
return FALSE;
AnimationManager()->FUN_10061010(FALSE);
DeleteObjects(&m_atomId, 500, 999);
m_act1State->m_unk0x018 = 0;
VariableTable()->SetVariable(g_strHIT_WALL_SOUND, "");
VariableTable()->SetVariable(g_raceState, "");
NavController()->SetDeadZone(NavController()->GetDefaultDeadZone());
NavController()->SetTrackDefault(1);
LegoRaceCar::FUN_10012de0();
GameState()->GetBackgroundColor()->SetValue("reset");
m_destLocation = LegoGameState::e_infomain;
return TRUE;
} }

View File

@@ -152,18 +152,6 @@ MxLong JetskiRace::HandleClick(LegoEventNotificationParam& p_param)
return result; return result;
} }
inline MxS32 JetskiRace::PossiblyGetPlaceOfPlayer()
{
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: LEGO1 0x100166a0
// FUNCTION: BETA10 0x100c8085 // FUNCTION: BETA10 0x100c8085
MxLong JetskiRace::HandlePathStruct(LegoPathStructNotificationParam& p_param) MxLong JetskiRace::HandlePathStruct(LegoPathStructNotificationParam& p_param)
@@ -190,7 +178,17 @@ MxLong JetskiRace::HandlePathStruct(LegoPathStructNotificationParam& p_param)
m_unk0xf8++; m_unk0xf8++;
if (g_unk0x100f0c78 == m_unk0xf8) { if (g_unk0x100f0c78 == m_unk0xf8) {
MxS16 sVar6 = PossiblyGetPlaceOfPlayer(); MxS32 position;
if (m_unk0xfc < m_unk0xf8 && m_unk0x100 < m_unk0xf8) {
position = 3;
}
else if (m_unk0xfc < m_unk0xf8 || m_unk0x100 < m_unk0xf8) {
position = 2;
}
else {
position = 1;
}
VariableTable()->SetVariable(g_raceState, ""); VariableTable()->SetVariable(g_raceState, "");
VariableTable()->SetVariable(g_strHIT_WALL_SOUND, ""); VariableTable()->SetVariable(g_strHIT_WALL_SOUND, "");
@@ -198,10 +196,10 @@ MxLong JetskiRace::HandlePathStruct(LegoPathStructNotificationParam& p_param)
m_raceState->m_unk0x28 = 2; m_raceState->m_unk0x28 = 2;
RaceState::Entry* raceStateEntry = m_raceState->GetState(GameState()->GetActorId()); RaceState::Entry* raceStateEntry = m_raceState->GetState(GameState()->GetActorId());
raceStateEntry->m_unk0x02 = sVar6; raceStateEntry->m_unk0x02 = position;
if (raceStateEntry->m_score < sVar6) { if (raceStateEntry->m_score < (MxS16) position) {
raceStateEntry->m_score = sVar6; raceStateEntry->m_score = position;
} }
m_destLocation = LegoGameState::e_jetrace2; m_destLocation = LegoGameState::e_jetrace2;
@@ -215,13 +213,14 @@ MxLong JetskiRace::HandlePathStruct(LegoPathStructNotificationParam& p_param)
m_hideAnim->FUN_1006db40(m_unk0xf8 * 200 + 100); m_hideAnim->FUN_1006db40(m_unk0xf8 * 200 + 100);
result = 1; result = 1;
} }
break; break;
case 11: case 11:
if (paramData <= m_unk0x108 || paramData >= m_unk0x108 + 5) { if (paramData <= m_unk0x108 || paramData >= m_unk0x108 + 5) {
break; break;
} }
FUN_10016930(0xb, paramData); FUN_10016930(11, paramData);
m_unk0x108 = paramData; m_unk0x108 = paramData;
if (m_unk0x108 == 0x14) { if (m_unk0x108 == 0x14) {
@@ -232,13 +231,14 @@ MxLong JetskiRace::HandlePathStruct(LegoPathStructNotificationParam& p_param)
((LegoPathActor*) p_param.GetSender())->SetMaxLinearVel(0.1); ((LegoPathActor*) p_param.GetSender())->SetMaxLinearVel(0.1);
} }
} }
break; break;
case 12: case 12:
if (paramData <= m_unk0x10c || paramData >= m_unk0x10c + 5) { if (paramData <= m_unk0x10c || paramData >= m_unk0x10c + 5) {
break; break;
} }
FUN_10016930(0xc, paramData); FUN_10016930(12, paramData);
m_unk0x10c = paramData; m_unk0x10c = paramData;
@@ -250,6 +250,7 @@ MxLong JetskiRace::HandlePathStruct(LegoPathStructNotificationParam& p_param)
((LegoPathActor*) p_param.GetSender())->SetMaxLinearVel(0.1); ((LegoPathActor*) p_param.GetSender())->SetMaxLinearVel(0.1);
} }
} }
break; break;
} }
} }

View File

@@ -180,6 +180,18 @@ void LegoRaceCar::FUN_10012de0()
g_unk0x100f0b88 = 0; g_unk0x100f0b88 = 0;
} }
// FUNCTION: LEGO1 0x10012e00
// FUNCTION: BETA10 0x100cb129
void LegoRaceCar::FUN_10012e00()
{
// Note the (likely unintentional) order of operations: `%` is executed before `/`,
// so the division is performed at runtime.
g_srtsl18to29Index = rand() % sizeof(g_srtsl18to29) / sizeof(g_srtsl18to29[0]);
g_srtsl6to10Index = rand() % sizeof(g_srtsl6to10) / sizeof(g_srtsl6to10[0]);
g_emptySoundKeyListIndex = rand() % sizeof(g_emptySoundKeyList) / sizeof(g_emptySoundKeyList[0]);
g_srtrhIndex = rand() % sizeof(g_srtrh) / sizeof(g_srtrh[0]);
}
// FUNCTION: LEGO1 0x10012e60 // FUNCTION: LEGO1 0x10012e60
// FUNCTION: BETA10 0x100cb191 // FUNCTION: BETA10 0x100cb191
void LegoRaceCar::SetWorldSpeed(MxFloat p_worldSpeed) void LegoRaceCar::SetWorldSpeed(MxFloat p_worldSpeed)

View File

@@ -16,6 +16,8 @@ DECOMP_SIZE_ASSERT(LegoCarRaceActor, 0x1a0)
// GLOBAL: LEGO1 0x100f0c68 // GLOBAL: LEGO1 0x100f0c68
// STRING: LEGO1 0x100f0c5c // STRING: LEGO1 0x100f0c5c
// GLOBAL: BETA10 0x101f5b04
// STRING: BETA10 0x101f5b14
const char* g_raceState = "RACE_STATE"; const char* g_raceState = "RACE_STATE";
// GLOBAL: LEGO1 0x100f7af0 // GLOBAL: LEGO1 0x100f7af0
@@ -24,6 +26,8 @@ const char* g_fuel = "FUEL";
// GLOBAL: LEGO1 0x100f0c6c // GLOBAL: LEGO1 0x100f0c6c
// STRING: LEGO1 0x100f0c54 // STRING: LEGO1 0x100f0c54
// GLOBAL: BETA10 0x101f5b08
// STRING: BETA10 0x101f5b20
const char* g_racing = "RACING"; const char* g_racing = "RACING";
// GLOBAL: LEGO1 0x100f7aec // GLOBAL: LEGO1 0x100f7aec

View File

@@ -744,4 +744,7 @@
// LIBRARY: BETA10 0x100f8ad0 // LIBRARY: BETA10 0x100f8ad0
// strcmp // strcmp
// LIBRARY: BETA10 0x100f9610
// rand
#endif #endif

View File

@@ -47,6 +47,7 @@ public:
return new MxActionNotificationParam(m_type, m_sender, m_action, m_realloc); return new MxActionNotificationParam(m_type, m_sender, m_action, m_realloc);
} // vtable+0x04 } // vtable+0x04
// FUNCTION: BETA10 0x10017970
MxDSAction* GetAction() { return m_action; } MxDSAction* GetAction() { return m_action; }
protected: protected:

View File

@@ -8,6 +8,7 @@
#include "mxtypes.h" #include "mxtypes.h"
// VTABLE: LEGO1 0x100d5390 // VTABLE: LEGO1 0x100d5390
// VTABLE: BETA10 0x101b93e8
// SIZE 0x10 // SIZE 0x10
class MxEntity : public MxCore { class MxEntity : public MxCore {
public: public:
@@ -33,6 +34,7 @@ public:
} }
// FUNCTION: LEGO1 0x10001070 // FUNCTION: LEGO1 0x10001070
// FUNCTION: BETA10 0x1000f3a0
virtual MxResult Create(MxS32 p_entityId, const MxAtomId& p_atomId) virtual MxResult Create(MxS32 p_entityId, const MxAtomId& p_atomId)
{ {
m_entityId = p_entityId; m_entityId = p_entityId;
@@ -47,7 +49,9 @@ public:
return SUCCESS; return SUCCESS;
} }
// FUNCTION: BETA10 0x10031c60
MxS32 GetEntityId() { return m_entityId; } MxS32 GetEntityId() { return m_entityId; }
MxAtomId& GetAtomId() { return m_atomId; } MxAtomId& GetAtomId() { return m_atomId; }
void SetEntityId(MxS32 p_entityId) { m_entityId = p_entityId; } void SetEntityId(MxS32 p_entityId) { m_entityId = p_entityId; }

View File

@@ -19,3 +19,14 @@ targets:
source-root: LEGO1 source-root: LEGO1
hash: hash:
sha256: d91435a40fa31f405fba33b03bd3bd40dcd4ca36ccf8ef6162c6c5ca0d7190e7 sha256: d91435a40fa31f405fba33b03bd3bd40dcd4ca36ccf8ef6162c6c5ca0d7190e7
ghidra:
ignore-types:
# these classes have been changed by hand to account for changes between LEGO1 and BETA10
- LegoCarBuild
- LegoCarBuildAnimPresenter
- LegoRace
- LegoWorld
ignore-functions:
# strcpy, strlen, ... (arguments are imported incorrectly)
- 0x100fa200
- 0x100f9780