From 6cda0d95c749decd4a0bca326f71005063375bbd Mon Sep 17 00:00:00 2001 From: jonschz <17198703+jonschz@users.noreply.github.com> Date: Mon, 11 Nov 2024 15:44:03 +0100 Subject: [PATCH] Complete `LegoCarBuild` (#1144) * Complete `LegoCarBuild` * Fix match error * Address review comments * Fix regression * Fix minor sign comparison issue --------- Co-authored-by: jonschz --- LEGO1/lego/legoomni/include/legocarbuild.h | 3 + .../legoomni/include/legocarbuildpresenter.h | 5 +- LEGO1/lego/legoomni/include/legoutils.h | 2 + .../src/audio/mxbackgroundaudiomanager.cpp | 1 + .../lego/legoomni/src/build/legocarbuild.cpp | 115 +++++++++++++++++- .../src/build/legocarbuildpresenter.cpp | 34 +++++- LEGO1/lego/legoomni/src/common/legoutils.cpp | 8 ++ LEGO1/lego/legoomni/src/main/scripts.cpp | 1 + LEGO1/lego/sources/misc/legoutil.h | 3 + 9 files changed, 163 insertions(+), 9 deletions(-) diff --git a/LEGO1/lego/legoomni/include/legocarbuild.h b/LEGO1/lego/legoomni/include/legocarbuild.h index 6337aafd..945d7d06 100644 --- a/LEGO1/lego/legoomni/include/legocarbuild.h +++ b/LEGO1/lego/legoomni/include/legocarbuild.h @@ -126,9 +126,12 @@ class LegoCarBuild : public LegoWorld { ); // vtable+0x80 MxS16 GetPlacedPartCount(); + void SetPlacedPartCount(MxU8 p_placedPartCount); void InitPresenters(); + void FUN_10022f00(); void FUN_10022f30(); void FUN_10023130(MxLong p_x, MxLong p_y); + void FUN_100236d0(); undefined4 FUN_10024250(LegoEventNotificationParam* p_param); void FUN_100243a0(); undefined4 FUN_10024480(MxActionNotificationParam* p_param); diff --git a/LEGO1/lego/legoomni/include/legocarbuildpresenter.h b/LEGO1/lego/legoomni/include/legocarbuildpresenter.h index 2e61b1e5..deb55c03 100644 --- a/LEGO1/lego/legoomni/include/legocarbuildpresenter.h +++ b/LEGO1/lego/legoomni/include/legocarbuildpresenter.h @@ -70,6 +70,7 @@ class LegoCarBuildAnimPresenter : public LegoAnimPresenter { void FUN_10079680(LegoChar* p_param); LegoAnimNodeData* FindNodeDataByName(LegoTreeNode* p_treeNode, const LegoChar* p_name); LegoTreeNode* FindNodeByName(LegoTreeNode* p_treeNode, const LegoChar* p_name); + void FUN_10079790(const LegoChar* p_name); void RotateAroundYAxis(MxFloat p_angle); MxBool FUN_10079c30(const LegoChar* p_name); MxBool PartIsPlaced(const LegoChar* p_name); @@ -77,7 +78,7 @@ class LegoCarBuildAnimPresenter : public LegoAnimPresenter { MxBool StringEqualsPlatform(const LegoChar* p_string); MxBool StringEqualsShelf(const LegoChar* p_string); MxBool StringEndsOnY(const LegoChar* p_string); - MxBool StringEndsOnZero(const LegoChar* p_string); + MxBool StringDoesNotEndOnZero(const LegoChar* p_string); const LegoChar* GetWiredNameByPartName(const LegoChar* p_name); void SetPartObjectIdByName(const LegoChar* p_name, MxS16 p_objectId); @@ -130,7 +131,7 @@ class LegoCarBuildAnimPresenter : public LegoAnimPresenter { MxFloat m_unk0x130; // 0x130 MxFloat m_unk0x134; // 0x134 MxFloat m_unk0x138; // 0x138 - MxLong m_unk0x13c; // 0x13c + MxULong m_unk0x13c; // 0x13c LegoEntity* m_unk0x140; // 0x140 MxS32 m_unk0x144; // 0x144 MxS32 m_unk0x148; // 0x148 diff --git a/LEGO1/lego/legoomni/include/legoutils.h b/LEGO1/lego/legoomni/include/legoutils.h index 604e860c..ceb64416 100644 --- a/LEGO1/lego/legoomni/include/legoutils.h +++ b/LEGO1/lego/legoomni/include/legoutils.h @@ -28,6 +28,7 @@ enum Cursor { e_cursorNone }; +class BoundingSphere; class MxAtomId; class LegoEntity; class LegoFile; @@ -43,6 +44,7 @@ LegoEntity* PickEntity(MxLong, MxLong); LegoROI* PickROI(MxLong, MxLong); LegoROI* PickParentROI(MxLong p_a, MxLong p_b); void FUN_1003dde0(LegoROI* p_param1, MxFloat p_param2); +MxBool SpheresIntersect(const BoundingSphere& p_sphere1, const BoundingSphere& p_sphere2); MxBool FUN_1003ded0(MxFloat p_param1[2], MxFloat p_param2[3], MxFloat p_param3[3]); MxBool TransformWorldToScreen(const MxFloat p_world[3], MxFloat p_screen[4]); MxS16 CountTotalTreeNodes(LegoTreeNode* p_node); diff --git a/LEGO1/lego/legoomni/src/audio/mxbackgroundaudiomanager.cpp b/LEGO1/lego/legoomni/src/audio/mxbackgroundaudiomanager.cpp index 4a921511..7ab16e56 100644 --- a/LEGO1/lego/legoomni/src/audio/mxbackgroundaudiomanager.cpp +++ b/LEGO1/lego/legoomni/src/audio/mxbackgroundaudiomanager.cpp @@ -358,6 +358,7 @@ undefined4 MxBackgroundAudioManager::FUN_1007f610( } // FUNCTION: LEGO1 0x1007f650 +// FUNCTION: BETA10 0x100e9663 void MxBackgroundAudioManager::Init() { this->m_unk0xa0 = 0; diff --git a/LEGO1/lego/legoomni/src/build/legocarbuild.cpp b/LEGO1/lego/legoomni/src/build/legocarbuild.cpp index 825b37e4..0f6306fb 100644 --- a/LEGO1/lego/legoomni/src/build/legocarbuild.cpp +++ b/LEGO1/lego/legoomni/src/build/legocarbuild.cpp @@ -196,7 +196,7 @@ MxResult LegoCarBuild::Create(MxDSAction& p_dsAction) else if (m_atomId == *g_racecarScript) { buildStateClassName = "LegoRaceCarBuildState"; GameState()->m_currentArea = LegoGameState::e_racecarbuild; - m_carId = Helicopter_Actor; + m_carId = RaceCar_Actor; } LegoGameState* gameState = GameState(); @@ -235,6 +235,14 @@ MxS16 LegoCarBuild::GetPlacedPartCount() } } +// FUNCTION: LEGO1 0x10022cf0 +void LegoCarBuild::SetPlacedPartCount(MxU8 p_placedPartCount) +{ + if (m_buildState) { + m_buildState->m_placedPartCount = p_placedPartCount; + } +} + // FUNCTION: LEGO1 0x10022d10 // FUNCTION: BETA10 0x1006b27a void LegoCarBuild::InitPresenters() @@ -281,6 +289,16 @@ void LegoCarBuild::InitPresenters() } } +// FUNCTION: LEGO1 0x10022f00 +void LegoCarBuild::FUN_10022f00() +{ + if (m_unk0x110) { + VTable0x6c(); + m_unk0x258->SetUnknown0xbc(0); + m_unk0x100 = 5; + } +} + // FUNCTION: LEGO1 0x10022f30 // FUNCTION: BETA10 0x1006b835 void LegoCarBuild::FUN_10022f30() @@ -458,6 +476,55 @@ void LegoCarBuild::VTable0x80(MxFloat p_param1[2], MxFloat p_param2[2], MxFloat p_param4[1] = p_param3; } +// FUNCTION: LEGO1 0x100236d0 +// FUNCTION: BETA10 0x1006c076 +void LegoCarBuild::FUN_100236d0() +{ + MxS32 pLVar2; + + FUN_10024f70(FALSE); + FUN_100250e0(FALSE); + m_unk0x258->FUN_10079790(m_unk0x110->GetName()); + m_unk0x258->SetUnknown0xbc(1); + m_unk0x110 = NULL; + m_unk0x100 = 0; + + if (m_unk0x258->AllPartsPlaced()) { + // Note the code duplication with LEGO1 0x10025ee0 + switch (m_carId) { + case 1: + pLVar2 = 0x2f; + break; + case 2: + pLVar2 = 0x31; + break; + case 3: + pLVar2 = 0x33; + break; + case 4: + pLVar2 = 0x35; + } + + BackgroundAudioManager()->Init(); + InvokeAction(Extra::e_stop, *g_jukeboxScript, pLVar2, NULL); + + if (m_numAnimsRun > 0) { + DeleteObjects(&m_atomId, 500, 510); + } + + if (GameState()->GetCurrentAct() == LegoGameState::e_act2) { + FUN_100243a0(); + } + else { + m_buildState->m_unk0x4d = TRUE; + InvokeAction(Extra::e_start, m_atomId, m_carId, NULL); + NotificationManager()->Send(this, MxNotificationParam()); + m_buildState->m_animationState = LegoVehicleBuildState::e_unknown4; + m_buildState->m_placedPartCount = 0; + } + } +} + #define LEGOCARBUILD_TICKLE_CASE(subtract, start, end, str) \ if (start < dTime && dTime < end) { \ FUN_10025db0(str, dTime - subtract); \ @@ -812,11 +879,49 @@ undefined4 LegoCarBuild::FUN_100244e0(MxLong p_x, MxLong p_y) return 1; } -// STUB: LEGO1 0x100246e0 +// FUNCTION: LEGO1 0x100246e0 undefined4 LegoCarBuild::FUN_100246e0(MxLong p_x, MxLong p_y) { - // TODO - return 0; + switch (m_unk0x100) { + case 3: + FUN_10022f30(); + return 1; + case 4: + FUN_10022f00(); + return 1; + case 6: + if (m_unk0x258->PartIsPlaced(m_unk0x110->GetName())) { + if (SpheresIntersect(m_unk0x114, m_unk0x110->GetWorldBoundingSphere())) { + FUN_10024f70(FALSE); + FUN_100250e0(FALSE); + m_unk0x100 = 0; + m_unk0x110 = NULL; + m_PlaceBrick_Sound->Enable(FALSE); + m_PlaceBrick_Sound->Enable(TRUE); + m_unk0x258->SetUnknown0xbc(1); + return 1; + } + } + + if (m_unk0x258->FUN_10079c30(m_unk0x110->GetName())) { + if (SpheresIntersect(m_unk0x114, m_unk0x110->GetWorldBoundingSphere())) { + m_PlaceBrick_Sound->Enable(FALSE); + m_PlaceBrick_Sound->Enable(TRUE); + FUN_100236d0(); + return 1; + } + + VTable0x6c(); + m_unk0x100 = 5; + return 1; + } + + VTable0x6c(); + m_unk0x100 = 5; + return 1; + default: + return 0; + } } // FUNCTION: LEGO1 0x10024850 @@ -1207,7 +1312,7 @@ void LegoCarBuild::TogglePresentersEnabled() // FUNCTION: BETA10 0x1006e124 void LegoCarBuild::FUN_100250e0(MxBool p_enabled) { - if (m_unk0x258->StringEndsOnZero(m_unk0x110->GetName()) && m_Decals_Ctl) { + if (m_unk0x258->StringDoesNotEndOnZero(m_unk0x110->GetName()) && m_Decals_Ctl) { if (strnicmp(m_unk0x110->GetName(), "JSFRNT", strlen("JSFRNT")) == 0) { m_Decal_Bitmap->Enable(p_enabled); m_Decals_Ctl->Enable(p_enabled); diff --git a/LEGO1/lego/legoomni/src/build/legocarbuildpresenter.cpp b/LEGO1/lego/legoomni/src/build/legocarbuildpresenter.cpp index 546aac67..7e0486ee 100644 --- a/LEGO1/lego/legoomni/src/build/legocarbuildpresenter.cpp +++ b/LEGO1/lego/legoomni/src/build/legocarbuildpresenter.cpp @@ -9,6 +9,7 @@ #include "legovideomanager.h" #include "legoworld.h" #include "misc.h" +#include "misc/legoutil.h" #include "mxautolock.h" #include "mxcompositepresenter.h" #include "mxmisc.h" @@ -507,6 +508,35 @@ LegoTreeNode* LegoCarBuildAnimPresenter::FindNodeByName(LegoTreeNode* p_treeNode return NULL; } +// FUNCTION: LEGO1 0x10079790 +// FUNCTION: BETA10 0x100720a3 +void LegoCarBuildAnimPresenter::FUN_10079790(const LegoChar* p_name) +{ + MxS16 i; + LegoChar buffer[40]; + + if (strcmpi(m_parts[m_placedPartCount].m_name, p_name) != 0) { + for (i = m_placedPartCount + 1; i < m_numberOfParts; i++) { + if (stricmp(m_parts[i].m_name, p_name) == 0) { + break; + } + } + + strcpy(buffer, m_parts[m_placedPartCount].m_name); + strcpy(m_parts[m_placedPartCount].m_name, m_parts[i].m_name); + strcpy(m_parts[i].m_name, buffer); + Swap(m_parts[m_placedPartCount].m_objectId, m_parts[i].m_objectId); + } + FUN_10079050(m_placedPartCount); + m_placedPartCount++; + + ((LegoCarBuild*) m_currentWorld)->SetPlacedPartCount(m_placedPartCount); + + if (m_placedPartCount < m_numberOfParts) { + FUN_10079680(m_parts[m_placedPartCount].m_wiredName); + } +} + // FUNCTION: LEGO1 0x10079920 // FUNCTION: BETA10 0x1007225d void LegoCarBuildAnimPresenter::RotateAroundYAxis(MxFloat p_angle) @@ -622,9 +652,9 @@ MxBool LegoCarBuildAnimPresenter::StringEndsOnY(const LegoChar* p_string) // FUNCTION: LEGO1 0x10079d30 // FUNCTION: BETA10 0x1007280e -MxBool LegoCarBuildAnimPresenter::StringEndsOnZero(const LegoChar* p_string) +MxBool LegoCarBuildAnimPresenter::StringDoesNotEndOnZero(const LegoChar* p_string) { - return (p_string[strlen(p_string) - 2] != '0'); + return (p_string[strlen(p_string) - 1] != '0'); } // FUNCTION: LEGO1 0x10079d60 diff --git a/LEGO1/lego/legoomni/src/common/legoutils.cpp b/LEGO1/lego/legoomni/src/common/legoutils.cpp index d38ff896..c5e658a6 100644 --- a/LEGO1/lego/legoomni/src/common/legoutils.cpp +++ b/LEGO1/lego/legoomni/src/common/legoutils.cpp @@ -72,6 +72,14 @@ void FUN_1003dde0(LegoROI* p_param1, MxFloat p_param2) // TODO } +// FUNCTION: LEGO1 0x1003de80 +MxBool SpheresIntersect(const BoundingSphere& p_sphere1, const BoundingSphere& p_sphere2) +{ + // This doesn't look clean, but it matches. + // p_sphere1.Center().GetData() doesn't work out + return sqrt(DISTSQRD3(&p_sphere1.Center()[0], &p_sphere2.Center()[0])) < p_sphere1.Radius() + p_sphere2.Radius(); +} + // FUNCTION: LEGO1 0x1003ded0 // FUNCTION: BETA10 0x100d3802 MxBool FUN_1003ded0(MxFloat p_param1[2], MxFloat p_param2[3], MxFloat p_param3[3]) diff --git a/LEGO1/lego/legoomni/src/main/scripts.cpp b/LEGO1/lego/legoomni/src/main/scripts.cpp index b5d7fe54..b5b8f9fe 100644 --- a/LEGO1/lego/legoomni/src/main/scripts.cpp +++ b/LEGO1/lego/legoomni/src/main/scripts.cpp @@ -64,6 +64,7 @@ MxAtomId* g_act2mainScript = NULL; MxAtomId* g_act3Script = NULL; // GLOBAL: LEGO1 0x100f456c +// GLOBAL: BETA10 0x102114e0 MxAtomId* g_jukeboxScript = NULL; // GLOBAL: LEGO1 0x100f4570 diff --git a/LEGO1/lego/sources/misc/legoutil.h b/LEGO1/lego/sources/misc/legoutil.h index 92bb84c2..fc42d462 100644 --- a/LEGO1/lego/sources/misc/legoutil.h +++ b/LEGO1/lego/sources/misc/legoutil.h @@ -39,6 +39,9 @@ inline void Swap(T& p_t1, T& p_t2) p_t2 = t; } +// TEMPLATE: BETA10 0x10073c20 +// Swap + template inline T DToR(T p_d) {