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
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 533 additions and 61 deletions

View file

@ -66,12 +66,22 @@ class CarRace : public LegoRace {
// FUNCTION: BETA10 0x100f16f0
void SetSkeleton(RaceSkel* p_skeleton) { m_skeleton = p_skeleton; }
void FUN_10017820(MxS32 p_param1, MxS16 p_param2);
// SYNTHETIC: LEGO1 0x10016c70
// CarRace::`scalar deleting destructor'
private:
undefined m_unk0x144[12]; // 0x144
RaceSkel* m_skeleton; // 0x150
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
};
#endif // CARRACE_H

View file

@ -27,7 +27,7 @@ class Doors : public LegoPathActor {
void ParseAction(char* p_extra) override; // vtable+0x20
void VTable0x70(float p_float) override; // vtable+0x70
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
// Doors::`scalar deleting destructor'

View file

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

View file

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

View file

@ -82,6 +82,12 @@ class LegoNavController : public MxCore {
// FUNCTION: BETA10 0x100b0f40
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 GetRotationalVel() { return m_rotationalVel; }
MxFloat GetMaxLinearVel() { return m_maxLinearVel; }
@ -91,6 +97,9 @@ class LegoNavController : public MxCore {
m_trackDefault = 0;
}
// FUNCTION: BETA10 0x100c9a10
int GetDefaultDeadZone() { return g_defdeadZone; }
// SYNTHETIC: LEGO1 0x10054c10
// LegoNavController::`scalar deleting destructor'

View file

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

View file

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

View file

@ -4,11 +4,24 @@
#include "mxmisc.h"
#include "mxtimer.h"
#include "roi/legoroi.h"
#include "tgl/tglvector.h"
#include <assert.h>
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: BETA10 0x10026850
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;
}
// STUB: LEGO1 0x10066190
// FUNCTION: LEGO1 0x10066190
// 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
@ -71,7 +102,7 @@ void Doors::VTable0x70(float p_float)
m_unk0x1f4 = local8;
}
if (m_unk0x158 + 6000.0f < p_float) {
if (m_unk0x158 + g_unk0x100d8e84 < p_float) {
m_ltDoor->FUN_100a58f0(m_ltDoorLocal);
m_rtDoor->FUN_100a58f0(m_rtDoorLocal);
m_ltDoor->VTable0x14();

View file

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

View file

@ -41,8 +41,10 @@ MxBackgroundAudioManager* BackgroundAudioManager()
}
// FUNCTION: LEGO1 0x10015740
// FUNCTION: BETA10 0x100e4895
LegoInputManager* InputManager()
{
assert(LegoOmni::GetInstance());
return LegoOmni::GetInstance()->GetInputManager();
}
@ -95,8 +97,10 @@ LegoWorld* CurrentWorld()
}
// FUNCTION: LEGO1 0x100157b0
// FUNCTION: BETA10 0x100e4a8d
LegoCharacterManager* CharacterManager()
{
assert(LegoOmni::GetInstance());
return LegoOmni::GetInstance()->GetCharacterManager();
}
@ -131,8 +135,10 @@ ViewLODListManager* GetViewLODListManager()
}
// FUNCTION: LEGO1 0x10015820
// FUNCTION: BETA10 0x100e4c92
void FUN_10015820(MxBool p_disable, MxU16 p_flags)
{
assert(LegoOmni::GetInstance());
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: BETA10 0x1003d6f2
void LegoActor::Mute(MxBool p_muted)
{
if (m_sound != NULL) {

View file

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

View file

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

View file

@ -1,9 +1,73 @@
#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 "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)
// 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
CarRace::CarRace()
{
@ -11,50 +75,345 @@ CarRace::CarRace()
this->m_unk0x130 = MxRect32(0x16c, 0x154, 0x1ec, 0x15e);
}
// STUB: LEGO1 0x10016ce0
// FUNCTION: LEGO1 0x10016ce0
// FUNCTION: BETA10 0x100c8364
MxResult CarRace::Create(MxDSAction& p_dsAction)
{
// TODO
return SUCCESS;
MxResult result = LegoRace::Create(p_dsAction);
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()
{
// 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
MxLong CarRace::HandleEndAction(MxEndActionNotificationParam&)
// FUNCTION: LEGO1 0x10016f60
// FUNCTION: BETA10 0x100c85eb
MxLong CarRace::HandleEndAction(MxEndActionNotificationParam& p_param)
{
// TODO
return 0;
MxLong result = 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
MxLong CarRace::HandlePathStruct(LegoPathStructNotificationParam&)
// FUNCTION: LEGO1 0x100170e0
// FUNCTION: BETA10 0x100c87ac
MxLong CarRace::HandlePathStruct(LegoPathStructNotificationParam& p_param)
{
// TODO
return 0;
MxLong result = 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
MxLong CarRace::HandleClick(LegoEventNotificationParam&)
// FUNCTION: LEGO1 0x10017650
MxLong CarRace::HandleClick(LegoEventNotificationParam& p_param)
{
// TODO
return 0;
LegoControlManagerNotificationParam* param = (LegoControlManagerNotificationParam*) &p_param;
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&)
{
// TODO
return 0;
if (m_raceState->m_unk0x28 == 2) {
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()
{
// TODO
return FALSE;
InvokeAction(Extra::e_stop, *g_carraceScript, CarraceScript::c_irtx08ra_PlayWav, NULL);
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;
}
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: BETA10 0x100c8085
MxLong JetskiRace::HandlePathStruct(LegoPathStructNotificationParam& p_param)
@ -190,7 +178,17 @@ MxLong JetskiRace::HandlePathStruct(LegoPathStructNotificationParam& p_param)
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_strHIT_WALL_SOUND, "");
@ -198,10 +196,10 @@ MxLong JetskiRace::HandlePathStruct(LegoPathStructNotificationParam& p_param)
m_raceState->m_unk0x28 = 2;
RaceState::Entry* raceStateEntry = m_raceState->GetState(GameState()->GetActorId());
raceStateEntry->m_unk0x02 = sVar6;
raceStateEntry->m_unk0x02 = position;
if (raceStateEntry->m_score < sVar6) {
raceStateEntry->m_score = sVar6;
if (raceStateEntry->m_score < (MxS16) position) {
raceStateEntry->m_score = position;
}
m_destLocation = LegoGameState::e_jetrace2;
@ -215,13 +213,14 @@ MxLong JetskiRace::HandlePathStruct(LegoPathStructNotificationParam& p_param)
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);
FUN_10016930(11, paramData);
m_unk0x108 = paramData;
if (m_unk0x108 == 0x14) {
@ -232,13 +231,14 @@ MxLong JetskiRace::HandlePathStruct(LegoPathStructNotificationParam& p_param)
((LegoPathActor*) p_param.GetSender())->SetMaxLinearVel(0.1);
}
}
break;
case 12:
if (paramData <= m_unk0x10c || paramData >= m_unk0x10c + 5) {
break;
}
FUN_10016930(0xc, paramData);
FUN_10016930(12, paramData);
m_unk0x10c = paramData;
@ -250,6 +250,7 @@ MxLong JetskiRace::HandlePathStruct(LegoPathStructNotificationParam& p_param)
((LegoPathActor*) p_param.GetSender())->SetMaxLinearVel(0.1);
}
}
break;
}
}

View file

@ -180,6 +180,18 @@ void LegoRaceCar::FUN_10012de0()
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: BETA10 0x100cb191
void LegoRaceCar::SetWorldSpeed(MxFloat p_worldSpeed)

View file

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

View file

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

View file

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

View file

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

View file

@ -19,3 +19,14 @@ targets:
source-root: LEGO1
hash:
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