diff --git a/LEGO1/lego/legoomni/include/act3.h b/LEGO1/lego/legoomni/include/act3.h index ffba305bb..b07131368 100644 --- a/LEGO1/lego/legoomni/include/act3.h +++ b/LEGO1/lego/legoomni/include/act3.h @@ -143,15 +143,15 @@ class Act3 : public LegoWorld { MxResult ShootPizza(LegoPathController* p_controller, Vector3& p_location, Vector3& p_direction, Vector3& p_up); MxResult ShootDonut(LegoPathController* p_controller, Vector3& p_location, Vector3& p_direction, Vector3& p_up); void TriggerHitSound(undefined4 p_param1); - MxResult FUN_10073360(Act3Ammo& p_ammo, const Vector3& p_param2); - MxResult FUN_10073390(Act3Ammo& p_ammo, const Vector3& p_param2); + MxResult HitBrickster(Act3Ammo& p_ammo, const Vector3& p_param2); + MxResult HitCop(Act3Ammo& p_ammo, const Vector3& p_param2); void SetBrickster(Act3Brickster* p_brickster); void AddCop(Act3Cop* p_cop); - void FUN_10073400(); - void FUN_10073430(); + void TransitionToGoodEnding(); + void TransitionToBadEnding(); void GoodEnding(const Matrix4& p_destination); void BadEnding(const Matrix4& p_destination); - void FUN_10073a60(); + void DisableHelicopterDot(); // BETA indicates that the following classes access certain members directly. friend class Act3Ammo; @@ -171,27 +171,27 @@ class Act3 : public LegoWorld { const MxQuaternionTransformer& p_quatTransform ); - Act3State* m_state; // 0xf8 - Act3Ammo m_pizzas[MAX_PIZZAS]; // 0xfc - Act3Ammo m_donuts[MAX_DONUTS]; // 0x217c - undefined m_unk0x41fc; // 0x41fc - Act3Cop* m_cop1; // 0x4200 - Act3Cop* m_cop2; // 0x4204 - Act3Brickster* m_brickster; // 0x4208 - Helicopter* m_copter; // 0x420c - Act3Shark* m_shark; // 0x4210 - MxFloat m_time; // 0x4214 - MxU8 m_pizzaHitSound; // 0x4218 - MxU8 m_pizzaMissSound; // 0x4219 - MxU8 m_copDonutSound; // 0x421a - MxU8 m_donutMissSound; // 0x421b - MxU8 m_islanderSound; // 0x421c - MxU8 m_bricksterDonutSound; // 0x421d - undefined m_unk0x421e; // 0x421e - Act3List m_unk0x4220; // 0x4220 - MxPresenter* m_helicopterDots[15]; // 0x4230 - Act3Script::Script m_unk0x426c; // 0x426c - LegoGameState::Area m_destLocation; // 0x4270 + Act3State* m_state; // 0xf8 + Act3Ammo m_pizzas[MAX_PIZZAS]; // 0xfc + Act3Ammo m_donuts[MAX_DONUTS]; // 0x217c + undefined m_unk0x41fc; // 0x41fc + Act3Cop* m_cop1; // 0x4200 + Act3Cop* m_cop2; // 0x4204 + Act3Brickster* m_brickster; // 0x4208 + Helicopter* m_copter; // 0x420c + Act3Shark* m_shark; // 0x4210 + MxFloat m_time; // 0x4214 + MxU8 m_pizzaHitSound; // 0x4218 + MxU8 m_pizzaMissSound; // 0x4219 + MxU8 m_copDonutSound; // 0x421a + MxU8 m_donutMissSound; // 0x421b + MxU8 m_islanderSound; // 0x421c + MxU8 m_bricksterDonutSound; // 0x421d + undefined m_helicopterDotCount; // 0x421e + Act3List m_soundList; // 0x4220 + MxPresenter* m_helicopterDots[15]; // 0x4230 + Act3Script::Script m_explanationAnimation; // 0x426c + LegoGameState::Area m_destLocation; // 0x4270 }; // TEMPLATE: LEGO1 0x10071f10 diff --git a/LEGO1/lego/legoomni/include/legoact2.h b/LEGO1/lego/legoomni/include/legoact2.h index d27f92d8f..ae360f5a7 100644 --- a/LEGO1/lego/legoomni/include/legoact2.h +++ b/LEGO1/lego/legoomni/include/legoact2.h @@ -77,19 +77,19 @@ class LegoAct2 : public LegoWorld { MxBool Escape() override; // vtable+0x64 void Enable(MxBool p_enable) override; // vtable+0x68 - void SetUnknown0x1138(Act2Actor* p_unk0x1138) { m_unk0x1138 = p_unk0x1138; } + void SetAmbulanceActor(Act2Actor* p_ambulanceActor) { m_ambulanceActor = p_ambulanceActor; } void SetDestLocation(LegoGameState::Area p_destLocation) { m_destLocation = p_destLocation; } - MxResult CreateBrick(); - void FUN_100517b0(); + MxResult CreateDroppingBrick(); + void CreateBrick(); MxResult BadEnding(); MxResult StartAction( Act2mainScript::Script p_objectId, - MxBool p_param2, - MxBool p_param3, + MxBool p_isAnimation, + MxBool p_ignoreCurrentAction, Mx3DPointFloat* p_location, Mx3DPointFloat* p_direction, - Mx3DPointFloat* p_param6 + Mx3DPointFloat* p_up ); // SYNTHETIC: LEGO1 0x1004fe20 @@ -117,7 +117,7 @@ class LegoAct2 : public LegoWorld { MxLong HandleTransitionEnd(); MxLong HandlePathStruct(LegoPathStructNotificationParam& p_param); void PlayMusic(JukeboxScript::Script p_objectId); - void FUN_10051900(); + void DisableAnimations(); void HideMaPaInfo(); void InitBricks(); void UninitBricks(); @@ -145,7 +145,7 @@ class LegoAct2 : public LegoWorld { undefined4 m_unk0x112c; // 0x112c undefined4 m_unk0x1130; // 0x1130 undefined4 m_unk0x1134; // 0x1134 - Act2Actor* m_unk0x1138; // 0x1138 + Act2Actor* m_ambulanceActor; // 0x1138 undefined m_unk0x113c; // 0x113c Act2mainScript::Script m_currentAction; // 0x1140 Act2mainScript::Script m_infomanDirecting; // 0x1144 diff --git a/LEGO1/lego/legoomni/include/legogamestate.h b/LEGO1/lego/legoomni/include/legogamestate.h index ce8c1cd94..d75f52b08 100644 --- a/LEGO1/lego/legoomni/include/legogamestate.h +++ b/LEGO1/lego/legoomni/include/legogamestate.h @@ -114,21 +114,21 @@ class LegoGameState { e_jetskibuild, e_racecarbuild, e_helicopterSpawn, - e_unk41, - e_unk42, + e_helicopterLanded, + e_helicopterTakenOff, e_dunebuggySpawn, e_racecarSpawn, e_jetskiSpawn, e_act2main, e_act3script, - e_unk48, - e_unk49, - e_unk50, + e_helicopterLandedAct3, + e_helicopterTakenOffAct3, + e_pepperSpawnAct2, e_unk51, e_towTrackHookedUp, e_jukeboxw, e_jukeboxExterior, - e_unk55, + e_helicopterExited, e_histbook, e_bike, e_dunecar, diff --git a/LEGO1/lego/legoomni/include/legopathactor.h b/LEGO1/lego/legoomni/include/legopathactor.h index 491e067c6..dac4f341d 100644 --- a/LEGO1/lego/legoomni/include/legopathactor.h +++ b/LEGO1/lego/legoomni/include/legopathactor.h @@ -222,9 +222,6 @@ class LegoPathActor : public LegoActor { MxFloat m_linearRotationRatio; // 0x150 }; -// FUNCTION: LEGO1 0x1002edd0 -// LegoPathActor::CheckIntersectionBothFaces - // TEMPLATE: LEGO1 0x10018b70 // List::~List diff --git a/LEGO1/lego/legoomni/include/legopathstruct.h b/LEGO1/lego/legoomni/include/legopathstruct.h index 162438f54..4174be963 100644 --- a/LEGO1/lego/legoomni/include/legopathstruct.h +++ b/LEGO1/lego/legoomni/include/legopathstruct.h @@ -73,13 +73,13 @@ class LegoPathStruct : public LegoPathStructBase { public: enum Trigger { c_camAnim = 'C', - c_d = 'D', - c_e = 'E', - c_g = 'G', - c_h = 'H', + c_waypoint = 'D', + c_deleteAction = 'E', + c_nothing = 'G', + c_hideAnim = 'H', c_music = 'M', - c_s = 'S', - c_w = 'W' + c_specialMissionWaypointAndAction = 'S', + c_missionFinalWaypoint = 'W' }; // FUNCTION: LEGO1 0x100473a0 @@ -94,8 +94,8 @@ class LegoPathStruct : public LegoPathStructBase { void SetAtomId(const MxAtomId& p_atomId) { m_atomId = p_atomId; } private: - MxBool HandleTrigger(LegoPathActor* p_actor, MxBool p_direction, MxU32 p_data, MxBool p_bool); - void FUN_1001bc40(const char* p_name, MxU32 p_data, MxBool p_bool); + MxBool HandleTrigger(LegoPathActor* p_actor, MxBool p_direction, MxU32 p_data, MxBool p_invertDirection); + void HandleAction(const char* p_name, MxU32 p_data, MxBool p_start); void PlayMusic(MxBool p_direction, MxU32 p_data); LegoWorld* m_world; // 0x0c diff --git a/LEGO1/lego/legoomni/include/legoracespecial.h b/LEGO1/lego/legoomni/include/legoracespecial.h index a6e252b07..5c343ed29 100644 --- a/LEGO1/lego/legoomni/include/legoracespecial.h +++ b/LEGO1/lego/legoomni/include/legoracespecial.h @@ -53,27 +53,33 @@ class LegoCarRaceActor : public virtual LegoRaceActor { // LegoCarRaceActor vtable - virtual void FUN_10080590(float p_time); // vtable+0x00 + virtual void UpdateWorldSpeed(float p_time); // vtable+0x00 // FUNCTION: LEGO1 0x10012bb0 - virtual void FUN_10012bb0(float p_unk0x14) { m_unk0x14 = p_unk0x14; } // vtable+0x04 + virtual void SetAcceleration(float p_acceleration) { m_acceleration = p_acceleration; } // vtable+0x04 // FUNCTION: LEGO1 0x10012bc0 - virtual float FUN_10012bc0() { return m_unk0x14; } // vtable+0x08 + virtual float GetAcceleration() { return m_acceleration; } // vtable+0x08 // FUNCTION: LEGO1 0x10012bd0 - virtual void FUN_10012bd0(float p_unk0x10) { m_unk0x10 = p_unk0x10; } // vtable+0x0c + virtual void SetCurveSpeedFactor(float p_curveSpeedFactor) + { + m_curveSpeedFactor = p_curveSpeedFactor; + } // vtable+0x0c // FUNCTION: LEGO1 0x10012be0 - virtual float FUN_10012be0() { return m_unk0x10; } // vtable+0x10 + virtual float GetCurveSpeedFactor() { return m_curveSpeedFactor; } // vtable+0x10 // FUNCTION: LEGO1 0x10012bf0 - virtual void FUN_10012bf0(float p_unk0x18) { m_unk0x18 = p_unk0x18; } // vtable+0x14 + virtual void SetRubberBandFactor(float p_rubberBandFactor) + { + m_rubberBandFactor = p_rubberBandFactor; + } // vtable+0x14 // FUNCTION: LEGO1 0x10012c00 - virtual float FUN_10012c00() { return m_unk0x18; } // vtable+0x18 + virtual float GetRubberBandFactor() { return m_rubberBandFactor; } // vtable+0x18 - virtual MxS32 VTable0x1c(LegoPathBoundary* p_boundary, LegoEdge* p_edge); // vtable+0x1c + virtual MxS32 HandleJump(LegoPathBoundary* p_boundary, LegoEdge* p_edge); // vtable+0x1c // SYNTHETIC: LEGO1 0x10012c30 // LegoCarRaceActor::`vbase destructor' @@ -88,18 +94,18 @@ class LegoCarRaceActor : public virtual LegoRaceActor { MxFloat m_unk0x08; // 0x08 MxU8 m_animState; // 0x0c - // Could be a multiplier for the maximum speed when going straight - MxFloat m_unk0x10; // 0x10 + // A multiplier for the maximum speed when going around a curve + MxFloat m_curveSpeedFactor; // 0x10 // Could be the acceleration - MxFloat m_unk0x14; // 0x14 + MxFloat m_acceleration; // 0x14 - MxFloat m_unk0x18; // 0x18 + MxFloat m_rubberBandFactor; // 0x18 // Could be the current timestamp for time-based movement - MxFloat m_unk0x1c; // 0x1c + MxFloat m_lastAcceleration; // 0x1c - static MxFloat g_unk0x100f7aec; + static MxFloat g_maxSpeed; }; // VTABLE: LEGO1 0x100da208 LegoCarRaceActor @@ -139,7 +145,7 @@ class LegoJetskiRaceActor : public virtual LegoCarRaceActor { Vector3& p_intersectionPoint ) override; // vtable+0x6c void Animate(float p_time) override; // vtable+0x70 - MxS32 VTable0x1c(LegoPathBoundary* p_boundary, LegoEdge* p_edge) override; // vtable+0x1c + MxS32 HandleJump(LegoPathBoundary* p_boundary, LegoEdge* p_edge) override; // vtable+0x1c // SYNTHETIC: LEGO1 0x10013a80 // LegoJetskiRaceActor::`vbase destructor' diff --git a/LEGO1/lego/legoomni/include/legoutils.h b/LEGO1/lego/legoomni/include/legoutils.h index 1b8d72540..8f6e9d818 100644 --- a/LEGO1/lego/legoomni/include/legoutils.h +++ b/LEGO1/lego/legoomni/include/legoutils.h @@ -65,7 +65,7 @@ void CalculateViewFromAnimation(LegoAnimPresenter* p_presenter); Extra::ActionType MatchActionString(const char*); void InvokeAction(Extra::ActionType p_actionId, const MxAtomId& p_pAtom, MxS32 p_streamId, LegoEntity* p_sender); void SetCameraControllerFromIsle(); -void ConvertHSVToRGB(float p_h, float p_s, float p_v, float* p_rOut, float* p_bOut, float* p_gOut); +void ConvertHSVToRGB(float p_h, float p_s, float p_v, float* p_rOut, float* p_gOut, float* p_bOut); void PlayCamAnim(LegoPathActor* p_actor, MxBool p_unused, MxU32 p_location, MxBool p_bool); void ResetViewVelocity(); MxBool RemoveFromCurrentWorld(const MxAtomId& p_atomId, MxS32 p_id); diff --git a/LEGO1/lego/legoomni/src/actors/act2actor.cpp b/LEGO1/lego/legoomni/src/actors/act2actor.cpp index 5af883d49..8fb630eea 100644 --- a/LEGO1/lego/legoomni/src/actors/act2actor.cpp +++ b/LEGO1/lego/legoomni/src/actors/act2actor.cpp @@ -287,7 +287,7 @@ void Act2Actor::Animate(float p_time) } SetWorldSpeed(0.0f); - ((LegoAct2*) CurrentWorld())->FUN_100517b0(); + ((LegoAct2*) CurrentWorld())->CreateBrick(); return; } #endif @@ -355,7 +355,7 @@ void Act2Actor::Animate(float p_time) m_state = e_createdBrick; m_createBrickTime = p_time; - if (((LegoAct2*) CurrentWorld())->CreateBrick() == SUCCESS) { + if (((LegoAct2*) CurrentWorld())->CreateDroppingBrick() == SUCCESS) { PlayNextVoiceOver(VoiceOver::e_behind); } #ifndef BETA10 diff --git a/LEGO1/lego/legoomni/src/actors/act3actors.cpp b/LEGO1/lego/legoomni/src/actors/act3actors.cpp index bb5330045..d5c8dd62a 100644 --- a/LEGO1/lego/legoomni/src/actors/act3actors.cpp +++ b/LEGO1/lego/legoomni/src/actors/act3actors.cpp @@ -649,7 +649,7 @@ void Act3Brickster::Animate(float p_time) assert(m_shootAnim && m_bInfo); if (m_unk0x50 < p_time) { - ((Act3*) m_world)->FUN_10073a60(); + ((Act3*) m_world)->DisableHelicopterDot(); m_unk0x58 = 0; assert(SoundManager()->GetCacheSoundManager()); SoundManager()->GetCacheSoundManager()->Play("thpt", NULL, FALSE); diff --git a/LEGO1/lego/legoomni/src/actors/act3ammo.cpp b/LEGO1/lego/legoomni/src/actors/act3ammo.cpp index 8630fb8bc..56972beb1 100644 --- a/LEGO1/lego/legoomni/src/actors/act3ammo.cpp +++ b/LEGO1/lego/legoomni/src/actors/act3ammo.cpp @@ -483,10 +483,10 @@ void Act3Ammo::Animate(float p_time) if (!annihilated) { if (IsPizza()) { - m_world->FUN_10073360(*this, position); + m_world->HitBrickster(*this, position); } else { - m_world->FUN_10073390(*this, position); + m_world->HitCop(*this, position); } m_worldSpeed = -1.0f; diff --git a/LEGO1/lego/legoomni/src/actors/ambulance.cpp b/LEGO1/lego/legoomni/src/actors/ambulance.cpp index 0d2b89cca..40bb3142c 100644 --- a/LEGO1/lego/legoomni/src/actors/ambulance.cpp +++ b/LEGO1/lego/legoomni/src/actors/ambulance.cpp @@ -318,7 +318,7 @@ MxLong Ambulance::HandlePathStruct(LegoPathStructNotificationParam& p_param) PlayAction(IsleScript::c_Avo915In_PlayWav); } } - else if (p_param.GetTrigger() == LegoPathStruct::c_s && p_param.GetData() == 0x131 && m_atBeachTask == 0) { + else if (p_param.GetTrigger() == LegoPathStruct::c_specialMissionWaypointAndAction && p_param.GetData() == 0x131 && m_atBeachTask == 0) { m_atBeachTask = 1; m_taskState = Ambulance::e_waiting; diff --git a/LEGO1/lego/legoomni/src/actors/helicopter.cpp b/LEGO1/lego/legoomni/src/actors/helicopter.cpp index a69430eb7..740e92fd6 100644 --- a/LEGO1/lego/legoomni/src/actors/helicopter.cpp +++ b/LEGO1/lego/legoomni/src/actors/helicopter.cpp @@ -95,7 +95,7 @@ void Helicopter::Exit() if (UserActor() && UserActor()->IsA("IslePathActor")) { ((IslePathActor*) UserActor()) ->SpawnPlayer( - LegoGameState::e_unk55, + LegoGameState::e_helicopterExited, TRUE, IslePathActor::c_spawnBit1 | IslePathActor::c_playMusic | IslePathActor::c_spawnBit3 ); @@ -143,7 +143,7 @@ MxLong Helicopter::HandleClick() m_script = *g_isleScript; AnimationManager()->FUN_10064670(NULL); SpawnPlayer( - LegoGameState::e_unk41, + LegoGameState::e_helicopterLanded, TRUE, IslePathActor::c_spawnBit1 | IslePathActor::c_playMusic | IslePathActor::c_spawnBit3 ); @@ -320,14 +320,14 @@ MxLong Helicopter::HandleEndAnim(LegoEndAnimNotificationParam& p_param) assert(act1State); act1State->m_state = Act1State::e_helicopter; SpawnPlayer( - LegoGameState::e_unk42, + LegoGameState::e_helicopterTakenOff, TRUE, IslePathActor::c_spawnBit1 | IslePathActor::c_playMusic | IslePathActor::c_spawnBit3 ); } else { SpawnPlayer( - LegoGameState::e_unk49, + LegoGameState::e_helicopterTakenOffAct3, TRUE, IslePathActor::c_spawnBit1 | IslePathActor::c_playMusic | IslePathActor::c_spawnBit3 ); @@ -361,14 +361,14 @@ MxLong Helicopter::HandleEndAnim(LegoEndAnimNotificationParam& p_param) assert(act1State); act1State->m_state = Act1State::e_none; SpawnPlayer( - LegoGameState::e_unk41, + LegoGameState::e_helicopterLanded, TRUE, IslePathActor::c_spawnBit1 | IslePathActor::c_playMusic | IslePathActor::c_spawnBit3 ); } else { SpawnPlayer( - LegoGameState::e_unk48, + LegoGameState::e_helicopterLandedAct3, TRUE, IslePathActor::c_spawnBit1 | IslePathActor::c_playMusic | IslePathActor::c_spawnBit3 ); @@ -430,10 +430,10 @@ void Helicopter::Animate(float p_time) } else { if (m_state->m_unk0x08 == 4) { - ((Act3*) m_world)->FUN_10073400(); + ((Act3*) m_world)->TransitionToGoodEnding(); } else { - ((Act3*) m_world)->FUN_10073430(); + ((Act3*) m_world)->TransitionToBadEnding(); } SetActorState(c_disabled); diff --git a/LEGO1/lego/legoomni/src/actors/islepathactor.cpp b/LEGO1/lego/legoomni/src/actors/islepathactor.cpp index df7c36349..53349c7b4 100644 --- a/LEGO1/lego/legoomni/src/actors/islepathactor.cpp +++ b/LEGO1/lego/legoomni/src/actors/islepathactor.cpp @@ -367,7 +367,7 @@ void IslePathActor::RegisterSpawnLocations() JukeboxScript::c_noneJukebox ); g_spawnLocations[17] = SpawnLocation( - LegoGameState::e_unk41, + LegoGameState::e_helicopterLanded, g_isleScript, 0, "edg02_51", @@ -415,7 +415,7 @@ void IslePathActor::RegisterSpawnLocations() JukeboxScript::c_noneJukebox ); g_spawnLocations[21] = SpawnLocation( - LegoGameState::e_unk42, + LegoGameState::e_helicopterTakenOff, g_isleScript, 0, "inv_05", @@ -427,7 +427,7 @@ void IslePathActor::RegisterSpawnLocations() JukeboxScript::c_noneJukebox ); g_spawnLocations[22] = SpawnLocation( - LegoGameState::e_unk48, + LegoGameState::e_helicopterLandedAct3, g_act3Script, 0, "edg02_51", @@ -439,7 +439,7 @@ void IslePathActor::RegisterSpawnLocations() JukeboxScript::c_noneJukebox ); g_spawnLocations[23] = SpawnLocation( - LegoGameState::e_unk49, + LegoGameState::e_helicopterTakenOffAct3, g_act3Script, 0, "inv_05", @@ -451,7 +451,7 @@ void IslePathActor::RegisterSpawnLocations() JukeboxScript::c_noneJukebox ); g_spawnLocations[24] = SpawnLocation( - LegoGameState::e_unk50, + LegoGameState::e_pepperSpawnAct2, g_act2mainScript, 0, "EDG02_51", @@ -499,7 +499,7 @@ void IslePathActor::RegisterSpawnLocations() JukeboxScript::c_noneJukebox ); g_spawnLocations[28] = SpawnLocation( - LegoGameState::e_unk55, + LegoGameState::e_helicopterExited, g_isleScript, 0, "edg02_50", diff --git a/LEGO1/lego/legoomni/src/actors/pizza.cpp b/LEGO1/lego/legoomni/src/actors/pizza.cpp index 291969cf2..11b822eec 100644 --- a/LEGO1/lego/legoomni/src/actors/pizza.cpp +++ b/LEGO1/lego/legoomni/src/actors/pizza.cpp @@ -281,7 +281,7 @@ MxLong Pizza::HandlePathStruct(LegoPathStructNotificationParam& p_param) if (m_state->m_state == PizzaMissionState::e_delivering) { MxLong time = Timer()->GetTime() - m_mission->m_startTime; - if (p_param.GetTrigger() == LegoPathStruct::c_s && p_param.GetData() == 0x12e && + if (p_param.GetTrigger() == LegoPathStruct::c_specialMissionWaypointAndAction && p_param.GetData() == 0x12e && GameState()->GetActorId() == LegoActor::c_pepper) { m_state->m_state = PizzaMissionState::e_arrivedAtDestination; m_state->SetPlayedAction(SndanimScript::c_TRS302_OpenJailDoor); @@ -303,7 +303,7 @@ MxLong Pizza::HandlePathStruct(LegoPathStructNotificationParam& p_param) (p_param.GetData() == 0x33 && GameState()->GetActorId() == LegoActor::c_papa) || ((p_param.GetData() == 0x08 || p_param.GetData() == 0x09) && GameState()->GetActorId() == LegoActor::c_nick) || (p_param.GetData() == 0x0b && GameState()->GetActorId() == LegoActor::c_laura) - )) || (p_param.GetTrigger() == LegoPathStruct::c_w && p_param.GetData() == 0x169 && GameState()->GetActorId() == LegoActor::c_nick)) { + )) || (p_param.GetTrigger() == LegoPathStruct::c_missionFinalWaypoint && p_param.GetData() == 0x169 && GameState()->GetActorId() == LegoActor::c_nick)) { IsleScript::Script action; if (time < m_mission->GetRedFinishTime()) { @@ -353,7 +353,7 @@ MxLong Pizza::HandlePathStruct(LegoPathStructNotificationParam& p_param) MxTrace("Pizza mission: ending\n"); } - else if (p_param.GetTrigger() == LegoPathStruct::c_w) { + else if (p_param.GetTrigger() == LegoPathStruct::c_missionFinalWaypoint) { if (p_param.GetData() == 0x15e && GameState()->GetActorId() == LegoActor::c_pepper) { if (!m_playedLocationAnimation) { m_playedLocationAnimation = TRUE; diff --git a/LEGO1/lego/legoomni/src/actors/towtrack.cpp b/LEGO1/lego/legoomni/src/actors/towtrack.cpp index e1cf01040..301c9d448 100644 --- a/LEGO1/lego/legoomni/src/actors/towtrack.cpp +++ b/LEGO1/lego/legoomni/src/actors/towtrack.cpp @@ -302,7 +302,7 @@ MxLong TowTrack::HandlePathStruct(LegoPathStructNotificationParam& p_param) if (m_state->m_state == TowTrackMissionState::e_hookedUp && ((p_param.GetTrigger() == LegoPathStruct::c_camAnim && (p_param.GetData() == 9 || p_param.GetData() == 8)) || - (p_param.GetTrigger() == LegoPathStruct::c_w && p_param.GetData() == 0x169))) { + (p_param.GetTrigger() == LegoPathStruct::c_missionFinalWaypoint && p_param.GetData() == 0x169))) { m_state->m_state = TowTrackMissionState::e_none; MxLong time = Timer()->GetTime() - m_state->m_startTime; @@ -329,7 +329,7 @@ MxLong TowTrack::HandlePathStruct(LegoPathStructNotificationParam& p_param) Leave(); PlayFinalAnimation(IsleScript::c_wrt060bm_RunAnim); } - else if (p_param.GetTrigger() == LegoPathStruct::c_w && m_state->m_state == TowTrackMissionState::e_started) { + else if (p_param.GetTrigger() == LegoPathStruct::c_missionFinalWaypoint && m_state->m_state == TowTrackMissionState::e_started) { if (p_param.GetData() == 0x15f) { if (m_treeBlockageTriggered == 0) { m_treeBlockageTriggered = 1; diff --git a/LEGO1/lego/legoomni/src/build/legocarbuild.cpp b/LEGO1/lego/legoomni/src/build/legocarbuild.cpp index 92d39bb4f..08dc3769d 100644 --- a/LEGO1/lego/legoomni/src/build/legocarbuild.cpp +++ b/LEGO1/lego/legoomni/src/build/legocarbuild.cpp @@ -1684,8 +1684,7 @@ void LegoCarBuild::HandleEndAnim() MxBool LegoCarBuild::Escape() { BackgroundAudioManager()->Init(); - MxS32 targetEntityId = GetBuildMovieId(m_carId); - InvokeAction(Extra::ActionType::e_stop, *g_jukeboxScript, targetEntityId, NULL); + InvokeAction(Extra::ActionType::e_stop, *g_jukeboxScript, GetBuildMovieId(m_carId), NULL); DeleteObjects(&m_atomId, 500, 999); m_buildState->m_animationState = LegoVehicleBuildState::e_none; diff --git a/LEGO1/lego/legoomni/src/common/legoobjectfactory.cpp b/LEGO1/lego/legoomni/src/common/legoobjectfactory.cpp index b8514020c..4f854b960 100644 --- a/LEGO1/lego/legoomni/src/common/legoobjectfactory.cpp +++ b/LEGO1/lego/legoomni/src/common/legoobjectfactory.cpp @@ -389,7 +389,7 @@ MxCore* LegoObjectFactory::Create(const char* p_name) } else if (m_idAct2Actor == atom) { Act2Actor* actor = new Act2Actor(); - ((LegoAct2*) CurrentWorld())->SetUnknown0x1138(actor); + ((LegoAct2*) CurrentWorld())->SetAmbulanceActor(actor); object = actor; } else if (m_idAct2Brick == atom) { diff --git a/LEGO1/lego/legoomni/src/common/legoutils.cpp b/LEGO1/lego/legoomni/src/common/legoutils.cpp index f8ef1baba..111e2713e 100644 --- a/LEGO1/lego/legoomni/src/common/legoutils.cpp +++ b/LEGO1/lego/legoomni/src/common/legoutils.cpp @@ -96,11 +96,13 @@ void RotateY(LegoROI* p_roi, MxFloat p_angle) } // FUNCTION: LEGO1 0x1003de80 +// FUNCTION: BETA10 0x100d3684 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(); + float distance = DISTSQRD3(p_sphere1.Center(), p_sphere2.Center()); + return sqrt(distance) < p_sphere1.Radius() + p_sphere2.Radius(); } // FUNCTION: LEGO1 0x1003ded0 @@ -392,7 +394,7 @@ void SetCameraControllerFromIsle() } // FUNCTION: LEGO1 0x1003eae0 -void ConvertHSVToRGB(float p_h, float p_s, float p_v, float* p_rOut, float* p_bOut, float* p_gOut) +void ConvertHSVToRGB(float p_h, float p_s, float p_v, float* p_rOut, float* p_gOut, float* p_bOut) { double calc; double p; @@ -410,8 +412,8 @@ void ConvertHSVToRGB(float p_h, float p_s, float p_v, float* p_rOut, float* p_bO calc = (p_v + 1.0) * sDbl; } if (calc <= 0.0) { - *p_gOut = 0.0f; *p_bOut = 0.0f; + *p_gOut = 0.0f; *p_rOut = 0.0f; return; } @@ -423,38 +425,38 @@ void ConvertHSVToRGB(float p_h, float p_s, float p_v, float* p_rOut, float* p_bO switch (hueIndex) { case 0: *p_rOut = calc; - *p_bOut = v12; - *p_gOut = p; + *p_gOut = v12; + *p_bOut = p; break; case 1: *p_rOut = v13; - *p_bOut = calc; - *p_gOut = p; + *p_gOut = calc; + *p_bOut = p; break; case 2: *p_rOut = p; - *p_bOut = calc; - *p_gOut = v12; + *p_gOut = calc; + *p_bOut = v12; break; case 3: *p_rOut = p; - *p_bOut = v13; - *p_gOut = calc; + *p_gOut = v13; + *p_bOut = calc; break; case 4: *p_rOut = v12; - *p_bOut = p; - *p_gOut = calc; + *p_gOut = p; + *p_bOut = calc; break; case 5: *p_rOut = calc; - *p_bOut = p; - *p_gOut = v13; + *p_gOut = p; + *p_bOut = v13; break; case 6: *p_rOut = calc; - *p_bOut = p; - *p_gOut = v13; + *p_gOut = p; + *p_bOut = v13; break; default: return; diff --git a/LEGO1/lego/legoomni/src/entity/legocameracontroller.cpp b/LEGO1/lego/legoomni/src/entity/legocameracontroller.cpp index cac7d66ac..710fb902b 100644 --- a/LEGO1/lego/legoomni/src/entity/legocameracontroller.cpp +++ b/LEGO1/lego/legoomni/src/entity/legocameracontroller.cpp @@ -190,6 +190,7 @@ void LegoCameraController::TransformPointOfView(const Matrix4& p_transform, MxU3 } // FUNCTION: LEGO1 0x10012740 +// FUNCTION: BETA10 0x10069c35 Mx3DPointFloat LegoCameraController::GetWorldUp() { if (m_lego3DView && m_lego3DView->GetPointOfView()) { @@ -203,6 +204,7 @@ Mx3DPointFloat LegoCameraController::GetWorldUp() } // FUNCTION: LEGO1 0x100127f0 +// FUNCTION: BETA10 0x10069cea Mx3DPointFloat LegoCameraController::GetWorldLocation() { if (m_lego3DView && m_lego3DView->GetPointOfView()) { @@ -216,6 +218,7 @@ Mx3DPointFloat LegoCameraController::GetWorldLocation() } // FUNCTION: LEGO1 0x100128a0 +// FUNCTION: BETA10 0x10069daa Mx3DPointFloat LegoCameraController::GetWorldDirection() { if (m_lego3DView && m_lego3DView->GetPointOfView()) { diff --git a/LEGO1/lego/legoomni/src/entity/legoentity.cpp b/LEGO1/lego/legoomni/src/entity/legoentity.cpp index c6ae434f1..1f9df08b1 100644 --- a/LEGO1/lego/legoomni/src/entity/legoentity.cpp +++ b/LEGO1/lego/legoomni/src/entity/legoentity.cpp @@ -197,16 +197,18 @@ void LegoEntity::SetLocation( } // FUNCTION: LEGO1 0x10010c30 +// FUNCTION: BETA10 0x1007ea5a void LegoEntity::TransformPointOfView() { LegoWorld* world = CurrentWorld(); - if (m_cameraFlag && world && world->GetCameraController() && m_roi) { + if (GetCameraFlag() && world && world->GetCameraController() && m_roi) { world->GetCameraController()->TransformPointOfView(m_roi->GetLocal2World(), 1); } } // FUNCTION: LEGO1 0x10010c60 +// FUNCTION: BETA10 0x1007ead0 Mx3DPointFloat LegoEntity::GetWorldDirection() { if (m_roi != NULL) { @@ -218,6 +220,7 @@ Mx3DPointFloat LegoEntity::GetWorldDirection() } // FUNCTION: LEGO1 0x10010cf0 +// FUNCTION: BETA10 0x1007eb47 Mx3DPointFloat LegoEntity::GetWorldUp() { if (m_roi != NULL) { diff --git a/LEGO1/lego/legoomni/src/paths/legopathactor.cpp b/LEGO1/lego/legoomni/src/paths/legopathactor.cpp index a91a51be2..c53485db4 100644 --- a/LEGO1/lego/legoomni/src/paths/legopathactor.cpp +++ b/LEGO1/lego/legoomni/src/paths/legopathactor.cpp @@ -189,23 +189,31 @@ MxResult LegoPathActor::SetTransformAndDestinationFromPoints( assert(p_destEdge); Vector3* v3 = p_destEdge->CWVertex(*p_boundary); + // LINE: LEGO1 0x1002de35 Vector3* v4 = p_destEdge->CCWVertex(*p_boundary); assert(v3 && v4); Mx3DPointFloat end, destNormal, endDirection; + // LINE: LEGO1 0x1002de8f end = *v4; end -= *v3; end *= p_destScale; + // LINE: LEGO1 0x1002deae end += *v3; + // LINE: LEGO1 0x1002deba m_boundary = p_boundary; + // LINE: LEGO1 0x1002dece m_destEdge = p_destEdge; + // LINE: LEGO1 0x1002ded4 m_destScale = p_destScale; m_traveledDistance = 0; m_transformTime = p_time; m_actorTime = p_time; + // TODO: this one fails to inline + // LINE: LEGO1 0x1002deed p_destEdge->GetFaceNormal(*p_boundary, destNormal); MxMatrix matrix; @@ -516,6 +524,95 @@ MxU32 LegoPathActor::CheckPresenterAndActorIntersections( return 0; } +#ifdef BETA10 +// FUNCTION: BETA10 0x100af35e +MxS32 LegoPathActor::CheckIntersections(Vector3& p_rayOrigin, Vector3& p_rayEnd, Vector3& p_intersectionPoint) +{ + assert(m_boundary && m_roi); + + Mx3DPointFloat rayDirection(p_rayEnd); + rayDirection -= p_rayOrigin; + + float len = rayDirection.LenSquared(); + + if (len <= 0.001) { + return 0; + } + + len = sqrt((double) len); + rayDirection /= len; + + float radius = m_roi->GetWorldBoundingSphere().Radius(); + LegoPathBoundary* b = m_boundary; + LegoOrientedEdge* local14 = *m_boundary->GetEdges(); + LegoOrientedEdge* local18 = NULL; + + while (1) { + assert(b); + + MxU32 result = + CheckPresenterAndActorIntersections(b, p_rayOrigin, rayDirection, len, radius, p_intersectionPoint); + + if (result != 0) { + return result; + } + + if (local18 == NULL) { + local18 = (LegoOrientedEdge*) local14->GetCounterclockwiseEdge(*m_boundary); + b = (LegoPathBoundary*) local14->OtherFace(m_boundary); + } + else { + b = NULL; + } + + while (!b) { + if (local18 == local14) { + return 0; + } + + b = (LegoPathBoundary*) local18->OtherFace(m_boundary); + local18 = (LegoOrientedEdge*) local18->GetCounterclockwiseEdge(*m_boundary); + } + } + + return 0; +} +#else +// FUNCTION: LEGO1 0x1002ebe0 +MxS32 LegoPathActor::CheckIntersections(Vector3& p_rayOrigin, Vector3& p_rayEnd, Vector3& p_intersectionPoint) +{ + assert(m_boundary && m_roi); + + Mx3DPointFloat rayDirection(p_rayEnd); + rayDirection -= p_rayOrigin; + + float len = rayDirection.LenSquared(); + + if (len <= 0.001) { + return 0; + } + + len = sqrt((double) len); + rayDirection /= len; + + float radius = m_roi->GetWorldBoundingSphere().Radius(); + list boundaries; + // This function is inlined once. The recursion calls into the actual function. + // Matching `CheckIntersectionBothFaces` will likely match `CheckIntersections` as well. + return CheckIntersectionBothFaces( + boundaries, + m_boundary, + p_rayOrigin, + rayDirection, + len, + radius, + p_intersectionPoint, + 0 + ); +} +#endif + +// FUNCTION: LEGO1 0x1002edd0 inline MxU32 LegoPathActor::CheckIntersectionBothFaces( list& p_checkedBoundaries, LegoPathBoundary* p_boundary, @@ -549,17 +646,22 @@ inline MxU32 LegoPathActor::CheckIntersectionBothFaces( LegoS32 numEdges = p_boundary->GetNumEdges(); for (MxS32 i = 0; i < numEdges; i++) { LegoOrientedEdge* edge = p_boundary->GetEdges()[i]; + // LINE: LEGO1 0x1002ee8c LegoPathBoundary* boundary = (LegoPathBoundary*) edge->OtherFace(p_boundary); + // LINE: LEGO1 0x1002ee9f if (boundary != NULL) { list::const_iterator it; + // LINE: LEGO1 0x1002eead for (it = p_checkedBoundaries.begin(); !(it == p_checkedBoundaries.end()); it++) { + // LINE: LEGO1 0x1002eeb3 if ((*it) == boundary) { break; } } + // LINE: LEGO1 0x1002eec4 if (it == p_checkedBoundaries.end()) { result = CheckIntersectionBothFaces( p_checkedBoundaries, @@ -582,39 +684,6 @@ inline MxU32 LegoPathActor::CheckIntersectionBothFaces( return 0; } -// FUNCTION: LEGO1 0x1002ebe0 -// FUNCTION: BETA10 0x100af35e -MxS32 LegoPathActor::CheckIntersections(Vector3& p_rayOrigin, Vector3& p_rayEnd, Vector3& p_intersectionPoint) -{ - assert(m_boundary && m_roi); - - Mx3DPointFloat rayDirection(p_rayEnd); - rayDirection -= p_rayOrigin; - - float len = rayDirection.LenSquared(); - - if (len <= 0.001) { - return 0; - } - - len = sqrt((double) len); - rayDirection /= len; - - float radius = m_roi->GetWorldBoundingSphere().Radius(); - list boundaries; - - return CheckIntersectionBothFaces( - boundaries, - m_boundary, - p_rayOrigin, - rayDirection, - len, - radius, - p_intersectionPoint, - 0 - ); -} - // FUNCTION: LEGO1 0x1002f020 // FUNCTION: BETA10 0x100af54a void LegoPathActor::ParseAction(char* p_extra) diff --git a/LEGO1/lego/legoomni/src/paths/legopathstruct.cpp b/LEGO1/lego/legoomni/src/paths/legopathstruct.cpp index 4f40ace4d..b81723eb7 100644 --- a/LEGO1/lego/legoomni/src/paths/legopathstruct.cpp +++ b/LEGO1/lego/legoomni/src/paths/legopathstruct.cpp @@ -19,24 +19,24 @@ DECOMP_SIZE_ASSERT(LegoPathStruct, 0x14) extern MxU32 g_isleFlags; // GLOBAL: LEGO1 0x100f119c -MxBool g_unk0x100f119c = FALSE; +MxBool g_triggerHandlingIgnoreDirection = FALSE; // FUNCTION: LEGO1 0x1001b700 void LegoPathStruct::HandleTrigger(LegoPathActor* p_actor, MxBool p_direction, MxU32 p_data) { - if (!HandleTrigger(p_actor, p_direction, p_data, FALSE) && g_unk0x100f119c) { + if (!HandleTrigger(p_actor, p_direction, p_data, FALSE) && g_triggerHandlingIgnoreDirection) { HandleTrigger(p_actor, p_direction, p_data, TRUE); } } // FUNCTION: LEGO1 0x1001b740 // FUNCTION: BETA10 0x100c26c5 -MxBool LegoPathStruct::HandleTrigger(LegoPathActor* p_actor, MxBool p_direction, MxU32 p_data, MxBool p_bool) +MxBool LegoPathStruct::HandleTrigger(LegoPathActor* p_actor, MxBool p_direction, MxU32 p_data, MxBool p_invertDirection) { MxBool triggered = FALSE; - MxBool bool2 = p_bool ? !p_direction : p_direction; + MxBool actualDirection = p_invertDirection ? !p_direction : p_direction; - MxU32 flags = bool2 ? c_bit5 : c_bit6; + MxU32 flags = actualDirection ? c_bit5 : c_bit6; flags |= p_actor->GetCameraFlag() ? c_bit1 : (c_bit2 | c_bit3 | c_bit4); if ((m_flags & flags & (c_bit5 | c_bit6 | c_bit7)) && (m_flags & flags & (c_bit1 | c_bit2 | c_bit3 | c_bit4))) { @@ -45,10 +45,10 @@ MxBool LegoPathStruct::HandleTrigger(LegoPathActor* p_actor, MxBool p_direction, switch (m_name[2]) { case c_camAnim: if (g_isleFlags & Isle::c_playCamAnims) { - PlayCamAnim(p_actor, bool2, p_data, TRUE); + PlayCamAnim(p_actor, actualDirection, p_data, TRUE); } break; - case c_d: { + case c_waypoint: { p_actor->SetLastPathStruct(p_data); LegoPathStructNotificationParam param(c_notificationPathStruct, p_actor, m_name[2], p_data); @@ -60,12 +60,12 @@ MxBool LegoPathStruct::HandleTrigger(LegoPathActor* p_actor, MxBool p_direction, } break; } - case c_e: - FUN_1001bc40(m_name, p_data, !(p_bool == FALSE)); + case c_deleteAction: + HandleAction(m_name, p_data, !(p_invertDirection == FALSE)); break; - case c_g: + case c_nothing: break; - case c_h: { + case c_hideAnim: { LegoHideAnimPresenter* presenter = m_world->GetHideAnimPresenter(); if (presenter != NULL) { presenter->ApplyVisibility(p_data * 100); @@ -77,7 +77,7 @@ MxBool LegoPathStruct::HandleTrigger(LegoPathActor* p_actor, MxBool p_direction, PlayMusic(p_direction, p_data); } break; - case c_s: { + case c_specialMissionWaypointAndAction: { LegoWorld* world = CurrentWorld(); if (world != NULL) { LegoPathStructNotificationParam param(c_notificationPathStruct, p_actor, m_name[2], p_data); @@ -87,10 +87,10 @@ MxBool LegoPathStruct::HandleTrigger(LegoPathActor* p_actor, MxBool p_direction, } } - FUN_1001bc40(m_name, p_data, p_bool == FALSE); + HandleAction(m_name, p_data, p_invertDirection == FALSE); break; } - case c_w: { + case c_missionFinalWaypoint: { LegoWorld* world = CurrentWorld(); if (world != NULL) { LegoPathStructNotificationParam param(c_notificationPathStruct, p_actor, m_name[2], p_data); @@ -106,13 +106,13 @@ MxBool LegoPathStruct::HandleTrigger(LegoPathActor* p_actor, MxBool p_direction, // FUNCTION: LEGO1 0x1001bc40 // FUNCTION: BETA10 0x100c2a6c -void LegoPathStruct::FUN_1001bc40(const char* p_name, MxU32 p_data, MxBool p_bool) +void LegoPathStruct::HandleAction(const char* p_name, MxU32 p_data, MxBool p_start) { MxDSAction action; action.SetObjectId(p_data); action.SetAtomId(m_atomId); - if (p_bool) { + if (p_start) { action.SetUnknown24(-1); Start(&action); } diff --git a/LEGO1/lego/legoomni/src/race/carrace.cpp b/LEGO1/lego/legoomni/src/race/carrace.cpp index 969c6c11a..e9cd854c1 100644 --- a/LEGO1/lego/legoomni/src/race/carrace.cpp +++ b/LEGO1/lego/legoomni/src/race/carrace.cpp @@ -191,7 +191,7 @@ MxLong CarRace::HandlePathStruct(LegoPathStructNotificationParam& p_param) { MxLong result = 0; - if (p_param.GetTrigger() == LegoPathStruct::c_d) { + if (p_param.GetTrigger() == LegoPathStruct::c_waypoint) { MxEntity* sender = (MxEntity*) p_param.GetSender(); MxS32 paramData = p_param.GetData(); diff --git a/LEGO1/lego/legoomni/src/race/jetskirace.cpp b/LEGO1/lego/legoomni/src/race/jetskirace.cpp index f16ee5a07..80aa89809 100644 --- a/LEGO1/lego/legoomni/src/race/jetskirace.cpp +++ b/LEGO1/lego/legoomni/src/race/jetskirace.cpp @@ -25,7 +25,7 @@ #include // Defined in legopathstruct.cpp -extern MxBool g_unk0x100f119c; +extern MxBool g_triggerHandlingIgnoreDirection; // Defined in jetski.cpp extern const char* g_varJSFRNTY5; @@ -72,7 +72,7 @@ MxResult JetskiRace::Create(MxDSAction& p_dsAction) InvokeAction(Extra::e_start, m_atomId, raceCarDashboardStreamId, NULL); InvokeAction(Extra::e_start, m_atomId, JetraceScript::c_JetskiDashboard, NULL); - g_unk0x100f119c = TRUE; + g_triggerHandlingIgnoreDirection = TRUE; return result; } @@ -160,7 +160,7 @@ MxLong JetskiRace::HandlePathStruct(LegoPathStructNotificationParam& p_param) MxLong result = 0; MxEntity* sender = (MxEntity*) p_param.GetSender(); - if (p_param.GetTrigger() == LegoPathStruct::c_d) { + if (p_param.GetTrigger() == LegoPathStruct::c_waypoint) { MxS32 paramData = p_param.GetData(); switch (sender->GetEntityId()) { diff --git a/LEGO1/lego/legoomni/src/race/legorace.cpp b/LEGO1/lego/legoomni/src/race/legorace.cpp index 5f8690956..a3076dbcb 100644 --- a/LEGO1/lego/legoomni/src/race/legorace.cpp +++ b/LEGO1/lego/legoomni/src/race/legorace.cpp @@ -12,7 +12,7 @@ DECOMP_SIZE_ASSERT(RaceState::Entry, 0x06) DECOMP_SIZE_ASSERT(RaceState, 0x2c) // Defined in legopathstruct.cpp -extern MxBool g_unk0x100f119c; +extern MxBool g_triggerHandlingIgnoreDirection; // FUNCTION: LEGO1 0x10015aa0 LegoRace::LegoRace() @@ -56,7 +56,7 @@ MxResult LegoRace::Create(MxDSAction& p_dsAction) // FUNCTION: BETA10 0x100c7ab5 LegoRace::~LegoRace() { - g_unk0x100f119c = FALSE; + g_triggerHandlingIgnoreDirection = FALSE; if (m_pathActor) { SetUserActor(m_pathActor); NavController()->ResetMaxLinearVel(m_pathActor->GetMaxLinearVel()); diff --git a/LEGO1/lego/legoomni/src/race/legoracers.cpp b/LEGO1/lego/legoomni/src/race/legoracers.cpp index 8f7476352..148d9c2fa 100644 --- a/LEGO1/lego/legoomni/src/race/legoracers.cpp +++ b/LEGO1/lego/legoomni/src/race/legoracers.cpp @@ -418,7 +418,7 @@ void LegoRaceCar::Animate(float p_time) UpdateMapLocatorPosition(); if (!m_userNavFlag) { - FUN_10080590(p_time); + UpdateWorldSpeed(p_time); return; } @@ -620,7 +620,7 @@ void LegoJetski::Animate(float p_time) UpdateMapLocatorPosition(); if (!m_userNavFlag) { - FUN_10080590(p_time); + UpdateWorldSpeed(p_time); return; } diff --git a/LEGO1/lego/legoomni/src/race/legoracespecial.cpp b/LEGO1/lego/legoomni/src/race/legoracespecial.cpp index 0269bf378..1ce42e687 100644 --- a/LEGO1/lego/legoomni/src/race/legoracespecial.cpp +++ b/LEGO1/lego/legoomni/src/race/legoracespecial.cpp @@ -33,11 +33,11 @@ const char* g_fuel = "FUEL"; const char* g_racing = "RACING"; // GLOBAL: LEGO1 0x100f7aec -MxFloat LegoCarRaceActor::g_unk0x100f7aec = 8.0f; +MxFloat LegoCarRaceActor::g_maxSpeed = 8.0f; // GLOBAL: LEGO1 0x100da044 // GLOBAL: BETA10 0x101be9fc -MxFloat g_unk0x100da044 = 8.0f; +MxFloat g_maxWorldSpeed = 8.0f; // FUNCTION: LEGO1 0x10080350 // FUNCTION: BETA10 0x100cd6b0 @@ -48,10 +48,10 @@ LegoCarRaceActor::LegoCarRaceActor() m_animState = 0; m_maxLinearVel = 0.0f; m_frequencyFactor = 1.0f; - m_unk0x1c = 0; - m_unk0x10 = 0.65f; - m_unk0x14 = 0.03f; - m_unk0x18 = 0.6f; + m_lastAcceleration = 0; + m_curveSpeedFactor = 0.65f; + m_acceleration = 0.03f; + m_rubberBandFactor = 0.6f; m_wallHitDirectionFactor = 0.1f; m_linearRotationRatio = -5.0f; m_canRotate = 1; @@ -60,43 +60,43 @@ LegoCarRaceActor::LegoCarRaceActor() // FUNCTION: LEGO1 0x10080590 // FUNCTION: BETA10 0x100cd8cf -void LegoCarRaceActor::FUN_10080590(float p_time) +void LegoCarRaceActor::UpdateWorldSpeed(float p_time) { MxFloat maxSpeed = m_maxLinearVel; - Mx3DPointFloat destEdgeUnknownVector; + Mx3DPointFloat edgeNormal; Mx3DPointFloat worldDirection = Mx3DPointFloat(m_roi->GetWorldDirection()); - m_destEdge->GetFaceNormal(*m_boundary, destEdgeUnknownVector); + m_destEdge->GetFaceNormal(*m_boundary, edgeNormal); - if (abs(destEdgeUnknownVector.Dot(destEdgeUnknownVector.GetData(), worldDirection.GetData())) > 0.5) { - maxSpeed *= m_unk0x10; + if (abs(edgeNormal.Dot(edgeNormal.GetData(), worldDirection.GetData())) > 0.5) { + maxSpeed *= m_curveSpeedFactor; } - MxS32 deltaUnk0x70; + MxS32 deltaPathStructs; LegoPathActor* userActor = UserActor(); if (userActor) { // All known implementations of LegoPathActor->GetLastPathStruct() return LegoPathActor::m_lastPathStruct - deltaUnk0x70 = m_lastPathStruct - userActor->GetLastPathStruct(); + deltaPathStructs = m_lastPathStruct - userActor->GetLastPathStruct(); } else { - deltaUnk0x70 = 0; + deltaPathStructs = 0; } - if (deltaUnk0x70 > 1) { - if (deltaUnk0x70 > 3) { - deltaUnk0x70 = 3; + if (deltaPathStructs > 1) { + if (deltaPathStructs > 3) { + deltaPathStructs = 3; } - maxSpeed *= (m_unk0x18 * (--deltaUnk0x70) * -0.25f + 1.0f); + maxSpeed *= (m_rubberBandFactor * (--deltaPathStructs) * -0.25f + 1.0f); } - else if (deltaUnk0x70 < -1) { + else if (deltaPathStructs < -1) { maxSpeed *= 1.3; } MxFloat deltaSpeed = maxSpeed - m_worldSpeed; - MxFloat changeInSpeed = (p_time - m_unk0x1c) * m_unk0x14; - m_unk0x1c = p_time; + MxFloat changeInSpeed = (p_time - m_lastAcceleration) * m_acceleration; + m_lastAcceleration = p_time; if (deltaSpeed < 0.0f) { changeInSpeed = -changeInSpeed; @@ -113,16 +113,17 @@ void LegoCarRaceActor::FUN_10080590(float p_time) // FUNCTION: LEGO1 0x10080740 // FUNCTION: BETA10 0x100cece0 -MxS32 LegoCarRaceActor::VTable0x1c(LegoPathBoundary* p_boundary, LegoEdge* p_edge) +MxS32 LegoCarRaceActor::HandleJump(LegoPathBoundary* p_boundary, LegoEdge* p_edge) { - Mx3DPointFloat pointUnknown; + Mx3DPointFloat targetPosition; Mx3DPointFloat destEdgeUnknownVector; - Mx3DPointFloat crossProduct; + Mx3DPointFloat targetDirection; if (m_actorState == c_ready) { m_boundary = NULL; - // Not sure where the upper bound of 11 comes from, the underlying array has a size of 16 + // The first 12 elements are used for the car race, the other 4 for jetski + // As it increments by 2, counting to 10 or 11 is the same. for (MxS32 i = 0; i < 11; i += 2) { if (LegoPathController::GetControlEdgeA(i + 1) == m_destEdge) { m_boundary = LegoPathController::GetControlBoundaryA(i + 1); @@ -148,8 +149,8 @@ MxS32 LegoCarRaceActor::VTable0x1c(LegoPathBoundary* p_boundary, LegoEdge* p_edg if (LegoPathController::GetControlEdgeA(i) == p_edge) { m_actorState = c_ready; - if (m_worldSpeed < g_unk0x100f7aec) { - m_worldSpeed = g_unk0x100f7aec; + if (m_worldSpeed < g_maxSpeed) { + m_worldSpeed = g_maxSpeed; } m_destEdge = LegoPathController::GetControlEdgeA(i + 1); @@ -168,12 +169,12 @@ MxS32 LegoCarRaceActor::VTable0x1c(LegoPathBoundary* p_boundary, LegoEdge* p_edg Vector3* v2 = m_destEdge->CWVertex(*m_boundary); assert(v1 && v2); - LERP3(pointUnknown, *v1, *v2, m_destScale); + LERP3(targetPosition, *v1, *v2, m_destScale); m_destEdge->GetFaceNormal(*m_boundary, destEdgeUnknownVector); - crossProduct.EqualsCross(*m_boundary->GetUp(), destEdgeUnknownVector); - crossProduct.Unitize(); + targetDirection.EqualsCross(*m_boundary->GetUp(), destEdgeUnknownVector); + targetDirection.Unitize(); Mx3DPointFloat worldDirection(Vector3(m_roi->GetWorldDirection())); @@ -182,10 +183,10 @@ MxS32 LegoCarRaceActor::VTable0x1c(LegoPathBoundary* p_boundary, LegoEdge* p_edg } worldDirection *= 5.0f; - crossProduct *= 5.0f; + targetDirection *= 5.0f; MxResult callResult = - SetSpline(Vector3(m_roi->GetWorldPosition()), worldDirection, pointUnknown, crossProduct); + SetSpline(Vector3(m_roi->GetWorldPosition()), worldDirection, targetPosition, targetDirection); if (callResult) { m_traveledDistance = 0; @@ -231,7 +232,7 @@ void LegoCarRaceActor::Animate(float p_time) if (SDL_strcasecmp(value, g_racing) == 0) { m_animState = 1; m_transformTime = p_time - 1.0f; - m_unk0x1c = p_time; + m_lastAcceleration = p_time; } } @@ -246,7 +247,7 @@ MxResult LegoCarRaceActor::CalculateSpline() { LegoOrientedEdge* d = m_destEdge; - if (VTable0x1c(m_boundary, m_destEdge)) { + if (HandleJump(m_boundary, m_destEdge)) { LegoPathBoundary* b = m_boundary; SwitchBoundary(m_boundary, m_destEdge, m_destScale); @@ -257,27 +258,27 @@ MxResult LegoCarRaceActor::CalculateSpline() Vector3* v2 = m_destEdge->CCWVertex(*m_boundary); assert(v1 && v2); - Mx3DPointFloat point1; - LERP3(point1, *v1, *v2, m_destScale); + Mx3DPointFloat end; + LERP3(end, *v1, *v2, m_destScale); - Mx3DPointFloat point2; - Mx3DPointFloat point3; - Mx3DPointFloat point4; - Mx3DPointFloat point5; + Mx3DPointFloat startEdgeNormal; + Mx3DPointFloat endEdgeNormal; + Mx3DPointFloat startDirection; + Mx3DPointFloat endDirection; - d->GetFaceNormal(*b, point2); - m_destEdge->GetFaceNormal(*m_boundary, point3); + d->GetFaceNormal(*b, startEdgeNormal); + m_destEdge->GetFaceNormal(*m_boundary, endEdgeNormal); - point4.EqualsCross(point2, *m_boundary->GetUp()); - point5.EqualsCross(*m_boundary->GetUp(), point3); + startDirection.EqualsCross(startEdgeNormal, *m_boundary->GetUp()); + endDirection.EqualsCross(*m_boundary->GetUp(), endEdgeNormal); - point4.Unitize(); - point5.Unitize(); + startDirection.Unitize(); + endDirection.Unitize(); - point4 *= 5.0f; - point5 *= 5.0f; + startDirection *= 5.0f; + endDirection *= 5.0f; - MxResult res = SetSpline(m_roi->GetWorldPosition(), point4, point1, point5); + MxResult res = SetSpline(m_roi->GetWorldPosition(), startDirection, end, endDirection); #ifdef BETA10 if (res) { @@ -296,15 +297,15 @@ MxResult LegoCarRaceActor::CalculateSpline() // FUNCTION: BETA10 0x100a8990 LegoJetskiRaceActor::LegoJetskiRaceActor() { - m_unk0x10 = 0.95f; - m_unk0x14 = 0.04f; - m_unk0x18 = 0.5f; + m_curveSpeedFactor = 0.95f; + m_acceleration = 0.04f; + m_rubberBandFactor = 0.5f; m_linearRotationRatio = 1.5f; } // FUNCTION: LEGO1 0x10081120 // FUNCTION: BETA10 0x100ce19f -MxS32 LegoJetskiRaceActor::VTable0x1c(LegoPathBoundary* p_boundary, LegoEdge* p_edge) +MxS32 LegoJetskiRaceActor::HandleJump(LegoPathBoundary* p_boundary, LegoEdge* p_edge) { // These are almost certainly not the correct names, but they produce the correct BETA10 stack Mx3DPointFloat a; @@ -338,8 +339,8 @@ MxS32 LegoJetskiRaceActor::VTable0x1c(LegoPathBoundary* p_boundary, LegoEdge* p_ if (p_edge == LegoPathController::GetControlEdgeA(12)) { m_actorState = c_ready; - if (m_worldSpeed < g_unk0x100da044) { - m_worldSpeed = g_unk0x100da044; + if (m_worldSpeed < g_maxWorldSpeed) { + m_worldSpeed = g_maxWorldSpeed; } m_destEdge = LegoPathController::GetControlEdgeA(13); @@ -348,8 +349,8 @@ MxS32 LegoJetskiRaceActor::VTable0x1c(LegoPathBoundary* p_boundary, LegoEdge* p_ else if (p_edge == LegoPathController::GetControlEdgeA(14)) { m_actorState = c_ready; - if (m_worldSpeed < g_unk0x100da044) { - m_worldSpeed = g_unk0x100da044; + if (m_worldSpeed < g_maxWorldSpeed) { + m_worldSpeed = g_maxWorldSpeed; } m_destEdge = LegoPathController::GetControlEdgeA(15); @@ -404,7 +405,7 @@ void LegoJetskiRaceActor::Animate(float p_time) if (!SDL_strcasecmp(raceState, g_racing)) { m_animState = 1; m_transformTime = p_time - 1.0f; - m_unk0x1c = p_time; + m_lastAcceleration = p_time; } else if (!m_userNavFlag) { LegoAnimActor::Animate(m_transformTime + 1.0f); diff --git a/LEGO1/lego/legoomni/src/worlds/act3.cpp b/LEGO1/lego/legoomni/src/worlds/act3.cpp index e8fc3df66..68203193a 100644 --- a/LEGO1/lego/legoomni/src/worlds/act3.cpp +++ b/LEGO1/lego/legoomni/src/worlds/act3.cpp @@ -103,10 +103,10 @@ Act3Script::Script g_bricksterDonutSounds[] = { }; // GLOBAL: LEGO1 0x100f7814 -MxU8 g_unk0x100f7814 = 0; +MxU8 g_copSelector = 0; // GLOBAL: LEGO1 0x100d95e8 -Act3Script::Script g_unk0x100d95e8[] = +Act3Script::Script g_explanationAnimations[] = {Act3Script::c_tlp053in_RunAnim, Act3Script::c_tlp064la_RunAnim, Act3Script::c_tlp068in_RunAnim}; // FUNCTION: LEGO1 0x10071d40 @@ -247,7 +247,7 @@ Act3::Act3() m_copter = NULL; m_shark = NULL; m_time = -1; - m_unk0x421e = 0; + m_helicopterDotCount = 0; memset(m_helicopterDots, 0, sizeof(m_helicopterDots)); @@ -327,7 +327,7 @@ MxResult Act3::ShootPizza(LegoPathController* p_controller, Vector3& p_location, for (nextPizza = 0; nextPizza < (MxS32) sizeOfArray(m_pizzas); nextPizza++) { if (!m_pizzas[nextPizza].IsValid()) { LegoPathBoundary* boundary = NULL; - MxU32 local18 = TRUE; + MxU32 noBoundaryIntersected = TRUE; m_pizzas[nextPizza].Create(this, TRUE, nextPizza); @@ -335,26 +335,26 @@ MxResult Act3::ShootPizza(LegoPathController* p_controller, Vector3& p_location, return FAILURE; } - MxFloat unk0x19c = *m_pizzas[nextPizza].GetApexParameter(); + MxFloat apexParameter = *m_pizzas[nextPizza].GetApexParameter(); if (p_controller->FindIntersectionBoundary( p_location, p_direction, m_pizzas[nextPizza].GetCoefficients(), boundary, - unk0x19c + apexParameter ) == SUCCESS) { Mx3DPointFloat direction; direction = p_direction; - direction *= unk0x19c; + direction *= apexParameter; direction += p_location; assert(m_brickster && m_brickster->GetROI()); direction -= m_brickster->GetROI()->GetLocal2World()[3]; - local18 = FALSE; - if (m_pizzas[nextPizza].Shoot(p_controller, boundary, unk0x19c) == SUCCESS) { + noBoundaryIntersected = FALSE; + if (m_pizzas[nextPizza].Shoot(p_controller, boundary, apexParameter) == SUCCESS) { p_controller->PlaceActor(&m_pizzas[nextPizza]); boundary->AddActor(&m_pizzas[nextPizza]); m_pizzas[nextPizza].SetWorldSpeed(10.0f); @@ -362,7 +362,7 @@ MxResult Act3::ShootPizza(LegoPathController* p_controller, Vector3& p_location, } } - if (local18 && m_pizzas[nextPizza].Shoot(p_controller, unk0x19c) == SUCCESS) { + if (noBoundaryIntersected && m_pizzas[nextPizza].Shoot(p_controller, apexParameter) == SUCCESS) { p_controller->PlaceActor(&m_pizzas[nextPizza]); m_pizzas[nextPizza].SetWorldSpeed(10.0f); return SUCCESS; @@ -390,22 +390,22 @@ MxResult Act3::ShootDonut(LegoPathController* p_controller, Vector3& p_location, return FAILURE; } - MxFloat unk0x19c = *m_donuts[nextDonut].GetApexParameter(); + MxFloat apexParameter = *m_donuts[nextDonut].GetApexParameter(); if (p_controller->FindIntersectionBoundary( p_location, p_direction, m_donuts[nextDonut].GetCoefficients(), boundary, - unk0x19c + apexParameter ) == SUCCESS) { - if (m_donuts[nextDonut].Shoot(p_controller, boundary, unk0x19c) == SUCCESS) { + if (m_donuts[nextDonut].Shoot(p_controller, boundary, apexParameter) == SUCCESS) { p_controller->PlaceActor(&m_donuts[nextDonut]); boundary->AddActor(&m_donuts[nextDonut]); m_donuts[nextDonut].SetWorldSpeed(10.0f); return SUCCESS; } } - else if (m_donuts[nextDonut].Shoot(p_controller, unk0x19c) == SUCCESS) { + else if (m_donuts[nextDonut].Shoot(p_controller, apexParameter) == SUCCESS) { p_controller->PlaceActor(&m_donuts[nextDonut]); m_donuts[nextDonut].SetWorldSpeed(10.0f); return SUCCESS; @@ -469,14 +469,14 @@ void Act3::TriggerHitSound(undefined4 p_param1) m_bricksterDonutSound = 0; } - m_unk0x4220.Insert(g_bricksterDonutSounds[m_bricksterDonutSound++], Act3ListElement::e_replaceAction); + m_soundList.Insert(g_bricksterDonutSounds[m_bricksterDonutSound++], Act3ListElement::e_replaceAction); return; } default: return; } - m_unk0x4220.Insert(objectId, Act3ListElement::e_onlyIfEmpty); + m_soundList.Insert(objectId, Act3ListElement::e_onlyIfEmpty); } // FUNCTION: LEGO1 0x10072c30 @@ -599,24 +599,24 @@ MxLong Act3::Notify(MxParam& p_param) MxS32 length; LegoBuildingInfo* info = BuildingManager()->GetInfoArray(length); - m_unk0x421e = 0; + m_helicopterDotCount = 0; while (--length >= 0) { if (info[length].m_counter < 0 && info[length].m_boundary != NULL && info[length].m_entity != NULL) { - m_unk0x421e++; + m_helicopterDotCount++; } } length = 0; - m_unk0x421e--; + m_helicopterDotCount--; char buf[80]; do { sprintf(buf, "HelicopterDotOn%d_Bitmap", length + 1); m_helicopterDots[length] = (MxPresenter*) Find("MxPresenter", buf); - if (m_unk0x421e > length) { + if (m_helicopterDotCount > length) { m_helicopterDots[length]->Enable(TRUE); } else { @@ -627,7 +627,7 @@ MxLong Act3::Notify(MxParam& p_param) } while (length < (MxS32) sizeOfArray(m_helicopterDots)); } else { - m_unk0x4220.RemoveByObjectIdOrFirst(param.GetAction()->GetObjectId()); + m_soundList.RemoveByObjectIdOrFirst(param.GetAction()->GetObjectId()); } } break; @@ -647,7 +647,7 @@ MxLong Act3::Notify(MxParam& p_param) case c_notificationEndAnim: if (m_state->m_state == Act3State::e_ready) { assert(m_copter && m_brickster && m_cop1 && m_cop2); - m_unk0x4220.RemoveByObjectIdOrFirst(0); + m_soundList.RemoveByObjectIdOrFirst(0); m_state->m_state = Act3State::e_initial; Disable(TRUE, LegoOmni::c_disableInput | LegoOmni::c_disable3d | LegoOmni::c_clearScreen); m_copter->HandleClick(); @@ -683,9 +683,18 @@ void Act3::ReadyWorld() AnimationManager()->FUN_1005f6d0(FALSE); VideoManager()->Get3DManager()->SetFrustrum(90.0f, 0.1f, 125.0f); - m_unk0x426c = g_unk0x100d95e8[SDL_rand(3)]; - AnimationManager() - ->FUN_10060dc0(m_unk0x426c, NULL, TRUE, LegoAnimationManager::e_unk0, NULL, TRUE, FALSE, FALSE, FALSE); + m_explanationAnimation = g_explanationAnimations[SDL_rand(3)]; + AnimationManager()->FUN_10060dc0( + m_explanationAnimation, + NULL, + TRUE, + LegoAnimationManager::e_unk0, + NULL, + TRUE, + FALSE, + FALSE, + FALSE + ); m_state->m_state = Act3State::e_ready; } @@ -698,11 +707,11 @@ MxResult Act3::Tickle() return SUCCESS; } - if (m_unk0x426c != (Act3Script::Script) 0) { - if (AnimationManager()->FUN_10064ee0(m_unk0x426c)) { + if (m_explanationAnimation != (Act3Script::Script) 0) { + if (AnimationManager()->FUN_10064ee0(m_explanationAnimation)) { Disable(FALSE, LegoOmni::c_disableInput | LegoOmni::c_disable3d | LegoOmni::c_clearScreen); TickleManager()->UnregisterClient(this); - m_unk0x426c = (Act3Script::Script) 0; + m_explanationAnimation = (Act3Script::Script) 0; } } @@ -711,7 +720,7 @@ MxResult Act3::Tickle() // FUNCTION: LEGO1 0x10073360 // FUNCTION: BETA10 0x100169d5 -MxResult Act3::FUN_10073360(Act3Ammo& p_ammo, const Vector3& p_param2) +MxResult Act3::HitBrickster(Act3Ammo& p_ammo, const Vector3& p_param2) { assert(m_brickster); m_brickster->FUN_100417a0(p_ammo, p_param2); @@ -721,11 +730,11 @@ MxResult Act3::FUN_10073360(Act3Ammo& p_ammo, const Vector3& p_param2) // FUNCTION: LEGO1 0x10073390 // FUNCTION: BETA10 0x10016a40 -MxResult Act3::FUN_10073390(Act3Ammo& p_ammo, const Vector3& p_param2) +MxResult Act3::HitCop(Act3Ammo& p_ammo, const Vector3& p_param2) { assert(m_cop1 && m_cop2); - if (!(g_unk0x100f7814 & 1)) { + if (!(g_copSelector & 1)) { m_cop1->FUN_10040350(p_ammo, p_param2); } else { @@ -733,7 +742,7 @@ MxResult Act3::FUN_10073390(Act3Ammo& p_ammo, const Vector3& p_param2) } TriggerHitSound(3); - g_unk0x100f7814++; + g_copSelector++; return SUCCESS; } @@ -757,7 +766,7 @@ void Act3::SetBrickster(Act3Brickster* p_brickster) } // FUNCTION: LEGO1 0x10073400 -void Act3::FUN_10073400() +void Act3::TransitionToGoodEnding() { m_state->m_state = Act3State::e_goodEnding; m_destLocation = LegoGameState::e_infomain; @@ -765,7 +774,7 @@ void Act3::FUN_10073400() } // FUNCTION: LEGO1 0x10073430 -void Act3::FUN_10073430() +void Act3::TransitionToBadEnding() { m_state->m_state = Act3State::e_badEnding; m_destLocation = LegoGameState::e_infomain; @@ -783,7 +792,7 @@ void Act3::GoodEnding(const Matrix4& p_destination) m_brickster->SetActorState(LegoPathActor::c_disabled); #ifndef BETA10 - m_unk0x4220.Clear(); + m_soundList.Clear(); m_copter->FUN_10004640(p_destination); DebugPrintf("In Good Ending..."); @@ -868,7 +877,7 @@ void Act3::BadEnding(const Matrix4& p_destination) m_cop2->SetActorState(LegoPathActor::c_disabled); m_brickster->SetActorState(LegoPathActor::c_disabled); - m_unk0x4220.Clear(); + m_soundList.Clear(); m_copter->FUN_10004670(p_destination); DebugPrintf("In Bad Ending..."); @@ -884,10 +893,10 @@ void Act3::BadEnding(const Matrix4& p_destination) } // FUNCTION: LEGO1 0x10073a60 -void Act3::FUN_10073a60() +void Act3::DisableHelicopterDot() { - m_unk0x421e--; - m_helicopterDots[m_unk0x421e]->Enable(FALSE); + m_helicopterDotCount--; + m_helicopterDots[m_helicopterDotCount]->Enable(FALSE); } // FUNCTION: LEGO1 0x10073a90 diff --git a/LEGO1/lego/legoomni/src/worlds/legoact2.cpp b/LEGO1/lego/legoomni/src/worlds/legoact2.cpp index 1d9693142..267995e21 100644 --- a/LEGO1/lego/legoomni/src/worlds/legoact2.cpp +++ b/LEGO1/lego/legoomni/src/worlds/legoact2.cpp @@ -52,7 +52,7 @@ MxS32 g_animationsBricksterIsLoose[] = { const LegoChar* g_charactersBricksterIsLoose[] = {"bd", "pg", "rd", "sy", "ro", "cl"}; // GLOBAL: LEGO1 0x100f4428 -MxS32 g_unk0x100f4428[] = { +MxS32 g_animationsAfterChase[] = { Act2mainScript::c_snsx07pa_RunAnim, Act2mainScript::c_snsx12ni_RunAnim, Act2mainScript::c_snsx15la_RunAnim, @@ -68,7 +68,7 @@ MxS32 g_unk0x100f4428[] = { }; // GLOBAL: LEGO1 0x100f4458 -const LegoChar* g_unk0x100f4458[] = {"papa", "nick", "laura", "cl", "pg", "rd", "sy"}; +const LegoChar* g_charactersAfterChase[] = {"papa", "nick", "laura", "cl", "pg", "rd", "sy"}; // FUNCTION: LEGO1 0x1004fce0 // FUNCTION: BETA10 0x1003a5a0 @@ -82,7 +82,7 @@ LegoAct2::LegoAct2() m_unk0x1130 = 0; m_nextBrick = 0; m_removedBricks = 0; - m_unk0x1138 = NULL; + m_ambulanceActor = NULL; m_currentAction = (Act2mainScript::Script) 0; m_infomanDirecting = (Act2mainScript::Script) 0; m_destLocation = LegoGameState::e_undefined; @@ -100,7 +100,7 @@ LegoAct2::~LegoAct2() TickleManager()->UnregisterClient(this); } - FUN_10051900(); + DisableAnimations(); InputManager()->UnRegister(this); if (UserActor()) { Remove(UserActor()); @@ -246,7 +246,7 @@ MxResult LegoAct2::Tickle() else { m_state = LegoAct2::e_goingToHide; m_timeSinceLastStage = 0; - m_unk0x1138->GoingToHide(); + m_ambulanceActor->GoingToHide(); } } @@ -303,30 +303,30 @@ MxLong LegoAct2::Notify(MxParam& p_param) LegoEntity* entity = (LegoEntity*) param.GetSender(); - Mx3DPointFloat local20(entity->GetROI()->GetWorldPosition()); - Mx3DPointFloat locale8(m_pepper->GetWorldPosition()); - Mx3DPointFloat locala4(locale8); + Mx3DPointFloat pepperToBrick(entity->GetROI()->GetWorldPosition()); + Mx3DPointFloat pepperPosition(m_pepper->GetWorldPosition()); + Mx3DPointFloat position(pepperPosition); - local20 -= locale8; + pepperToBrick -= pepperPosition; MxMatrix local2world(m_pepper->GetLocal2World()); - Vector3 local30(local2world[0]); - Vector3 localac(local2world[1]); - Vector3 local28(local2world[2]); + Vector3 right(local2world[0]); + Vector3 up(local2world[1]); + Vector3 dir(local2world[2]); - local28 = local20; - local28.Unitize(); + dir = pepperToBrick; + dir.Unitize(); - Mx3DPointFloat local90(local28); - local90 *= 1.25f; - locala4 += local90; - locala4[1] += 0.25; - local30.EqualsCross(localac, local28); - local30.Unitize(); + Mx3DPointFloat positionOffset(dir); + positionOffset *= 1.25f; + position += positionOffset; + position[1] += 0.25; + right.EqualsCross(up, dir); + right.Unitize(); - Mx3DPointFloat locald4(local2world[2]); + Mx3DPointFloat direction(local2world[2]); Mx3DPointFloat localc0(local2world[1]); - StartAction(Act2mainScript::c_tns051in_RunAnim, TRUE, TRUE, &locala4, &locald4, NULL); + StartAction(Act2mainScript::c_tns051in_RunAnim, TRUE, TRUE, &position, &direction, NULL); m_state = LegoAct2::e_allPiecesCollected; m_timeSinceLastStage = 0; @@ -414,8 +414,8 @@ MxLong LegoAct2::HandleEndAction(MxEndActionNotificationParam& p_param) ((LegoPathActor*) m_pepper->GetEntity())->SetActorState(LegoPathActor::c_disabled); AnimationManager()->EnableCamAnims(TRUE); AnimationManager()->FUN_1005f6d0(TRUE); - AnimationManager()->FUN_100604f0(g_unk0x100f4428, sizeOfArray(g_unk0x100f4428)); - AnimationManager()->FUN_10060480(g_unk0x100f4458, sizeOfArray(g_unk0x100f4458)); + AnimationManager()->FUN_100604f0(g_animationsAfterChase, sizeOfArray(g_animationsAfterChase)); + AnimationManager()->FUN_10060480(g_charactersAfterChase, sizeOfArray(g_charactersAfterChase)); break; case LegoAct2::e_distributeRemainingBricks: { LegoROI* roi; @@ -452,7 +452,7 @@ MxLong LegoAct2::HandleEndAction(MxEndActionNotificationParam& p_param) m_bricks[i].Remove(); } - FUN_10051900(); + DisableAnimations(); m_destLocation = LegoGameState::e_copterbuild; TransitionManager()->StartTransition(MxTransitionManager::e_mosaic, 50, FALSE, FALSE); break; @@ -488,7 +488,7 @@ void LegoAct2::ReadyWorld() m_pepper = FindROI("pepper"); IslePathActor* pepper = (IslePathActor*) m_pepper->GetEntity(); pepper->SpawnPlayer( - LegoGameState::e_unk50, + LegoGameState::e_pepperSpawnAct2, TRUE, IslePathActor::c_spawnBit1 | IslePathActor::c_playMusic | IslePathActor::c_spawnBit3 ); @@ -513,7 +513,7 @@ void LegoAct2::ReadyWorld() PlaceActor(actor, "EDG00_149", 0, 0.5f, 2, 0.5f); PlayMusic(JukeboxScript::c_Jail_Music); - FUN_10051900(); + DisableAnimations(); VideoManager()->Get3DManager()->SetFrustrum(90.0f, 0.1f, 250.f); m_gameState->m_enabled = TRUE; } @@ -573,7 +573,7 @@ void LegoAct2::Enable(MxBool p_enable) m_transformOnDisable = m_pepper->GetLocal2World(); m_boundaryOnDisable = ((LegoPathActor*) m_pepper->GetEntity())->GetBoundary(); - FUN_10051900(); + DisableAnimations(); BackgroundAudioManager()->Stop(); UninitBricks(); DeleteObjects(&m_atomId, Act2mainScript::c_VOhead0_PlayWav, Act2mainScript::c_VOhide_PlayWav); @@ -599,7 +599,7 @@ MxLong LegoAct2::HandlePathStruct(LegoPathStructNotificationParam& p_param) LegoPathActor* actor = (LegoPathActor*) m_pepper->GetEntity(); actor->SetActorState(LegoPathActor::c_disabled); actor->SetWorldSpeed(0.0f); - FUN_10051900(); + DisableAnimations(); if (m_timeSinceLastStage < 90000) { StartAction(Act2mainScript::c_tra031ni_RunAnim, TRUE, TRUE, NULL, NULL, NULL); @@ -631,7 +631,7 @@ MxLong LegoAct2::HandlePathStruct(LegoPathStructNotificationParam& p_param) m_currentAction = Act2mainScript::c_VOhide_PlayWav; } - m_unk0x1138->Hide(); + m_ambulanceActor->Hide(); m_state = LegoAct2::e_hidden; m_timeSinceLastStage = 0; @@ -644,7 +644,7 @@ MxLong LegoAct2::HandlePathStruct(LegoPathStructNotificationParam& p_param) MxMatrix local2world = m_ambulance->GetLocal2World(); MxMatrix local2world2 = local2world; - LegoPathBoundary* boundary = m_unk0x1138->GetBoundary(); + LegoPathBoundary* boundary = m_ambulanceActor->GetBoundary(); local2world[3][1] += 1.5; local2world2[3][1] -= 0.1; @@ -656,7 +656,7 @@ MxLong LegoAct2::HandlePathStruct(LegoPathStructNotificationParam& p_param) // FUNCTION: LEGO1 0x100516b0 // FUNCTION: BETA10 0x1003bcbc -MxResult LegoAct2::CreateBrick() +MxResult LegoAct2::CreateDroppingBrick() { if (m_nextBrick > 4) { return FAILURE; @@ -668,7 +668,7 @@ MxResult LegoAct2::CreateBrick() MxMatrix local2world = m_ambulance->GetLocal2World(); MxMatrix local2world2 = local2world; - LegoPathBoundary* boundary = m_unk0x1138->GetBoundary(); + LegoPathBoundary* boundary = m_ambulanceActor->GetBoundary(); local2world[3][1] += 1.3; local2world2[3][1] -= 0.1; @@ -680,7 +680,7 @@ MxResult LegoAct2::CreateBrick() } // FUNCTION: LEGO1 0x100517b0 -void LegoAct2::FUN_100517b0() +void LegoAct2::CreateBrick() { Act2Brick& brick = m_bricks[m_nextBrick]; brick.Create(m_nextBrick); @@ -708,7 +708,7 @@ void LegoAct2::PlayMusic(JukeboxScript::Script p_objectId) // FUNCTION: LEGO1 0x10051900 // FUNCTION: BETA10 0x1003bed1 -void LegoAct2::FUN_10051900() +void LegoAct2::DisableAnimations() { if (AnimationManager()) { AnimationManager()->Suspend(); @@ -931,7 +931,7 @@ MxResult LegoAct2::BadEnding() m_bricks[i].Remove(); } - LegoPathActor* actor = m_unk0x1138; + LegoPathActor* actor = m_ambulanceActor; actor->SetActorState(LegoPathActor::c_disabled); m_gameState->SetState(LegoAct2State::c_badEnding); @@ -1096,7 +1096,7 @@ MxResult LegoAct2::StartAction( MxBool p_ignoreCurrentAction, Mx3DPointFloat* p_location, Mx3DPointFloat* p_direction, - Mx3DPointFloat* p_param6 + Mx3DPointFloat* p_up ) { if (m_currentAction == (Act2mainScript::Script) 0 || p_ignoreCurrentAction) { @@ -1140,19 +1140,19 @@ MxResult LegoAct2::StartAction( oneVectorNotNull = TRUE; } - if (p_param6) { - matrix[1][0] = (*p_param6)[0]; - matrix[1][1] = (*p_param6)[1]; - matrix[1][2] = (*p_param6)[2]; + if (p_up) { + matrix[1][0] = (*p_up)[0]; + matrix[1][1] = (*p_up)[1]; + matrix[1][2] = (*p_up)[2]; oneVectorNotNull = TRUE; } - Vector3 firstColumn(matrix[0]); - Vector3 secondColumn(matrix[1]); - Vector3 thirdColumn(matrix[2]); + Vector3 right(matrix[0]); + Vector3 up(matrix[1]); + Vector3 dir(matrix[2]); - firstColumn.EqualsCross(secondColumn, thirdColumn); - firstColumn.Unitize(); + right.EqualsCross(up, dir); + right.Unitize(); MxMatrix* pmatrix = NULL; @@ -1202,28 +1202,28 @@ MxResult LegoAct2::StartAction( // FUNCTION: BETA10 0x10014aa8 MxResult LegoAct2::InitializeShooting() { - LegoPathActor* actor = m_unk0x1138; + LegoPathActor* actor = m_ambulanceActor; LegoLocomotionAnimPresenter* ap; PlaceActor(actor, "EDG01_27", 2, 0.5f, 0, 0.5f); ap = (LegoLocomotionAnimPresenter*) Find("LegoAnimPresenter", "Ambul_Anim0"); assert(ap); - ap->CreateROIAndBuildMap(m_unk0x1138, 0.0f); + ap->CreateROIAndBuildMap(m_ambulanceActor, 0.0f); ap = (LegoLocomotionAnimPresenter*) Find("LegoAnimPresenter", "Ambul_Anim2"); assert(ap); - ap->CreateROIAndBuildMap(m_unk0x1138, 6.0f); + ap->CreateROIAndBuildMap(m_ambulanceActor, 6.0f); ap = (LegoLocomotionAnimPresenter*) Find("LegoAnimPresenter", "Ambul_Anim3"); assert(ap); - ap->CreateROIAndBuildMap(m_unk0x1138, 3.0f); + ap->CreateROIAndBuildMap(m_ambulanceActor, 3.0f); ap = (LegoLocomotionAnimPresenter*) Find("LegoAnimPresenter", "BrShoot"); assert(ap); - ap->CreateROIAndBuildMap(m_unk0x1138, -1.0f); + ap->CreateROIAndBuildMap(m_ambulanceActor, -1.0f); actor->SetWorldSpeed(0.0f); - m_unk0x1138->InitializeNextShot(); + m_ambulanceActor->InitializeNextShot(); return SUCCESS; } diff --git a/LEGO1/lego/sources/3dmanager/lego3dview.h b/LEGO1/lego/sources/3dmanager/lego3dview.h index 4441b85ea..8303c51c2 100644 --- a/LEGO1/lego/sources/3dmanager/lego3dview.h +++ b/LEGO1/lego/sources/3dmanager/lego3dview.h @@ -52,6 +52,7 @@ inline ViewManager* Lego3DView::GetViewManager() return m_pViewManager; } +// FUNCTION: BETA10 0x1006aae0 inline ViewROI* Lego3DView::GetPointOfView() { return m_pPointOfView; diff --git a/LEGO1/lego/sources/geom/legoedge.cpp b/LEGO1/lego/sources/geom/legoedge.cpp index d1a810d68..2bcea209a 100644 --- a/LEGO1/lego/sources/geom/legoedge.cpp +++ b/LEGO1/lego/sources/geom/legoedge.cpp @@ -6,6 +6,7 @@ DECOMP_SIZE_ASSERT(LegoEdge, 0x24) // FUNCTION: LEGO1 0x1009a470 +// FUNCTION: BETA10 0x10182250 LegoEdge::LegoEdge() { m_faceA = NULL; @@ -19,36 +20,65 @@ LegoEdge::LegoEdge() } // FUNCTION: LEGO1 0x1009a4c0 +// FUNCTION: BETA10 0x101822c2 LegoEdge::~LegoEdge() { } +// FUNCTION: BETA10 0x101822e1 +LegoResult LegoEdge::SetCounterclockwiseEdge(LegoWEEdge& p_face, LegoEdge* p_edge) +{ + // unreferenced in BETA10, not in LEGO1 + if (&p_face == m_faceA) { + m_ccwA = p_edge; + return SUCCESS; + } + if (&p_face == m_faceB) { + m_ccwB = p_edge; + return SUCCESS; + } + return FAILURE; +} + +// FUNCTION: BETA10 0x1018233c +LegoResult LegoEdge::SetClockwiseEdge(LegoWEEdge& p_face, LegoEdge* p_edge) +{ + // unreferenced in BETA10, not in LEGO1 + if (&p_face == m_faceA) { + m_cwA = p_edge; + return SUCCESS; + } + if (&p_face == m_faceB) { + m_cwB = p_edge; + return SUCCESS; + } + return FAILURE; +} + // FUNCTION: LEGO1 0x1009a4d0 +// FUNCTION: BETA10 0x10182397 LegoEdge* LegoEdge::GetClockwiseEdge(LegoWEEdge& p_face) { if (&p_face == m_faceA) { return m_cwA; } - else if (&p_face == m_faceB) { + if (&p_face == m_faceB) { return m_cwB; } - else { - return NULL; - } + return NULL; } // FUNCTION: LEGO1 0x1009a4f0 +// FUNCTION: BETA10 0x101823e5 LegoEdge* LegoEdge::GetCounterclockwiseEdge(LegoWEEdge& p_face) { if (&p_face == m_faceA) { return m_ccwA; } - else if (&p_face == m_faceB) { + if (&p_face == m_faceB) { return m_ccwB; } - else { - return NULL; - } + return NULL; } // FUNCTION: LEGO1 0x1009a510 @@ -58,10 +88,8 @@ Vector3* LegoEdge::CWVertex(LegoWEEdge& p_face) if (m_faceA == &p_face) { return m_pointB; } - else { - assert(m_faceB == &p_face); - return m_pointA; - } + assert(m_faceB == &p_face); + return m_pointA; } // FUNCTION: LEGO1 0x1009a530 @@ -71,8 +99,6 @@ Vector3* LegoEdge::CCWVertex(LegoWEEdge& p_face) if (m_faceB == &p_face) { return m_pointB; } - else { - assert(m_faceA == &p_face); - return m_pointA; - } + assert(m_faceA == &p_face); + return m_pointA; } diff --git a/LEGO1/lego/sources/geom/legoedge.h b/LEGO1/lego/sources/geom/legoedge.h index 9a14eef4f..31cdfddf1 100644 --- a/LEGO1/lego/sources/geom/legoedge.h +++ b/LEGO1/lego/sources/geom/legoedge.h @@ -7,11 +7,14 @@ class LegoWEEdge; class Vector3; // VTABLE: LEGO1 0x100db7b8 +// VTABLE: BETA10 0x101c3728 // SIZE 0x24 struct LegoEdge { LegoEdge(); virtual ~LegoEdge(); // vtable+0x00 + LegoResult SetCounterclockwiseEdge(LegoWEEdge& p_face, LegoEdge* p_edge); + LegoResult SetClockwiseEdge(LegoWEEdge& p_face, LegoEdge* p_edge); LegoEdge* GetClockwiseEdge(LegoWEEdge& p_face); LegoEdge* GetCounterclockwiseEdge(LegoWEEdge& p_face); Vector3* CWVertex(LegoWEEdge& p_face); @@ -32,6 +35,7 @@ struct LegoEdge { Vector3* GetPointB() { return m_pointB; } // SYNTHETIC: LEGO1 0x1009a4a0 + // SYNTHETIC: BETA10 0x10182b30 // LegoEdge::`scalar deleting destructor' LegoWEEdge* m_faceA; // 0x04 diff --git a/LEGO1/lego/sources/geom/legoorientededge.h b/LEGO1/lego/sources/geom/legoorientededge.h index bb6135636..05cd8009f 100644 --- a/LEGO1/lego/sources/geom/legoorientededge.h +++ b/LEGO1/lego/sources/geom/legoorientededge.h @@ -8,6 +8,7 @@ #include // VTABLE: LEGO1 0x100db7f4 +// VTABLE: BETA10 0x101c3794 // SIZE 0x40 struct LegoOrientedEdge : public LegoEdge { public: @@ -99,8 +100,12 @@ struct LegoOrientedEdge : public LegoEdge { inline LegoU32 FUN_10048c40(const Vector3& p_position); // SYNTHETIC: LEGO1 0x1009a6c0 + // SYNTHETIC: BETA10 0x101840f0 // LegoOrientedEdge::`scalar deleting destructor' + // SYNTHETIC: BETA10 0x100bd390 + // LegoOrientedEdge::~LegoOrientedEdge + LegoU16 m_flags; // 0x24 Mx3DPointFloat m_dir; // 0x28 float m_length; // 0x3c diff --git a/LEGO1/library_msvc.h b/LEGO1/library_msvc.h index fd185a534..9e38a1d91 100644 --- a/LEGO1/library_msvc.h +++ b/LEGO1/library_msvc.h @@ -873,6 +873,12 @@ // LIBRARY: BETA10 0x100fa0e0 // atof +// LIBRARY: BETA10 0x1005a500 +// sqrt + +// LIBRARY: BETA10 0x1005a530 +// sqrtf + // LIBRARY: BETA10 0x1005a9c0 // fabs diff --git a/LEGO1/realtime/orientableroi.cpp b/LEGO1/realtime/orientableroi.cpp index 3c45e90fe..db46a206e 100644 --- a/LEGO1/realtime/orientableroi.cpp +++ b/LEGO1/realtime/orientableroi.cpp @@ -29,6 +29,7 @@ void OrientableROI::WrappedSetLocal2WorldWithWorldDataUpdate(const Matrix4& p_lo } // FUNCTION: LEGO1 0x100a46b0 +// FUNCTION: BETA10 0x1016528f void OrientableROI::UpdateTransformationRelativeToParent(const Matrix4& p_transform) { MxMatrix mat; diff --git a/docs/README.md b/docs/README.md index 0ea419d76..de0f2ffeb 100644 --- a/docs/README.md +++ b/docs/README.md @@ -18,6 +18,10 @@ Kaitai Struct allows you to define binary formats in a YAML-based `.ksy` file, w | [`savegame.ksy`](/docs/savegame.ksy) | `.GS` | Main game save data (game state, progress, customizations) | | [`players.ksy`](/docs/players.ksy) | `.gsi` | Player profile save data (usernames) | | [`history.ksy`](/docs/history.ksy) | `.gsi` | Score history and high scores | +| [`animation.ksy`](/docs/animation.ksy) | `.ani` | Animation data (keyframes, actor references, camera animation) | +| [`wdb.ksy`](/docs/wdb.ksy) | `.wdb` | World database (textures, parts, models, ROI hierarchies, LODs) | +| [`dta.ksy`](/docs/dta.ksy) | `.dta` | Animation data (world animation info, model placement) | +| [`tex.ksy`](/docs/tex.ksy) | `.tex` | Texture data (named textures with palette and pixel data) | ## Using the Tools @@ -38,6 +42,18 @@ ksv samples/Players.gsi players.ksy # View a History.gsi file ksv samples/History.gsi history.ksy + +# View an animation file +ksv samples/pns065rd.ani animation.ksy + +# View the world database (from game installation) +ksv /path/to/lego/data/world.wdb wdb.ksy + +# View an animation data file +ksv samples/BLDRINF.DTA dta.ksy + +# View a texture data file +ksv samples/Dbfrfn.tex tex.ksy ``` ### Kaitai Struct Dump (ksdump) @@ -53,11 +69,28 @@ ksdump --format json samples/Players.gsi players.ksy # Dump History.gsi to YAML ksdump --format yaml samples/History.gsi history.ksy + +# Dump an animation file to JSON +ksdump --format json samples/pns065rd.ani animation.ksy + +# Dump world database to YAML (from game installation) +ksdump --format yaml /path/to/lego/data/world.wdb wdb.ksy + +# Dump an animation data file to JSON +ksdump --format json samples/BLDRINF.DTA dta.ksy + +# Dump a texture data file to JSON +ksdump --format json samples/Dbfrfn.tex tex.ksy ``` ## Sample Files -The [`samples/`](/docs/samples/) directory contains example save files for testing: +The [`samples/`](/docs/samples/) directory contains example files for testing: - `G0.GS`, `G1.GS`, `G2.GS` - Sample main game save files (slots 0, 1, 2) - `Players.gsi` - Sample player profile data - `History.gsi` - Sample score history data +- `pns065rd.ani` - Sample animation file +- `BLDRINF.DTA` - Sample animation data file +- `Dbfrfn.tex` - Sample texture data file (dune buggy front fender) + +Note: The world database (`world.wdb`) can be found in your LEGO Island installation at `lego/data/world.wdb`. diff --git a/docs/animation.ksy b/docs/animation.ksy new file mode 100644 index 000000000..6f5829048 --- /dev/null +++ b/docs/animation.ksy @@ -0,0 +1,310 @@ +meta: + id: animation + title: Animation File + application: LEGO Island + file-extension: ani + license: CC0-1.0 + endian: le +doc: | + Animation file format for LEGO Island (1997). Contains skeletal animation + data including actor references, keyframes for translation/rotation/scale, + morph visibility keys, and optional camera animation data. + + Animation files are embedded within SI (Interleaf) files and + parsed by LegoAnimPresenter. The format consists of a header with bounding + information, an actor list, animation duration, optional camera animation, + and a hierarchical tree of animation nodes. + +seq: + - id: magic + type: s4 + doc: | + Magic number identifying the file format. Must be 0x11 (17 decimal). + - id: bounding_radius + type: f4 + doc: | + Radius of the bounding sphere encompassing the entire animation. + Used for visibility culling and collision detection. + - id: center_x + type: f4 + doc: X coordinate of the bounding sphere center point. + - id: center_y + type: f4 + doc: Y coordinate of the bounding sphere center point. + - id: center_z + type: f4 + doc: Z coordinate of the bounding sphere center point. + - id: has_camera_anim + type: s4 + doc: | + Flag indicating whether camera animation data follows the actor list. + If non-zero, a camera_anim structure is present after the duration field. + - id: unused + type: s4 + doc: | + Unused field. Read by the parser but not used for anything. + - id: num_actors + type: u4 + doc: Number of actor entries in the actor list. + - id: actors + type: actor_entry + repeat: expr + repeat-expr: num_actors + doc: | + List of actors referenced by this animation. Each entry contains + the actor name and type, which determines how the actor ROI is + managed during animation playback. + - id: duration + type: s4 + doc: Total duration of the animation in milliseconds. + - id: camera_anim + type: camera_anim + if: has_camera_anim != 0 + doc: | + Camera animation data including position, target, and rotation keys. + Only present if has_camera_anim is non-zero. + - id: root_node + type: tree_node + doc: | + Root node of the animation tree. The tree structure mirrors the + skeletal hierarchy of the animated model, with each node containing + keyframe data for its corresponding bone/part. + +types: + actor_entry: + doc: | + An actor reference in the animation. The name identifies which ROI + (Realtime Object Instance) to animate, and the type determines + how the actor is managed by the character manager. + seq: + - id: name_length + type: u4 + doc: Length of the actor name in bytes. + - id: name + type: str + size: name_length + encoding: ASCII + if: name_length > 0 + doc: Actor name used to look up the ROI in the scene. + - id: actor_type + type: u4 + enum: actor_type + if: name_length > 0 + doc: | + Determines how the actor ROI is created and managed. + See actor_type enum for possible values. + + camera_anim: + doc: | + Camera animation data (LegoAnimScene). Contains keyframes for camera + position, look-at target position, and roll rotation around the + view axis. + seq: + - id: num_translation_keys + type: u2 + doc: Number of camera position keyframes. + - id: translation_keys + type: translation_key + repeat: expr + repeat-expr: num_translation_keys + doc: Camera position keyframes. + - id: num_target_keys + type: u2 + doc: Number of look-at target position keyframes. + - id: target_keys + type: translation_key + repeat: expr + repeat-expr: num_target_keys + doc: Look-at target position keyframes. + - id: num_rotation_keys + type: u2 + doc: Number of camera roll rotation keyframes. + - id: rotation_keys + type: rotation_z_key + repeat: expr + repeat-expr: num_rotation_keys + doc: Camera roll rotation keyframes (rotation around view axis). + + tree_node: + doc: | + A node in the animation tree hierarchy. Each node contains animation + data for one part of the model and references to child nodes. + seq: + - id: data + type: node_data + doc: Animation keyframe data for this node. + - id: num_children + type: u4 + doc: Number of child nodes. + - id: children + type: tree_node + repeat: expr + repeat-expr: num_children + doc: Child nodes in the animation hierarchy. + + node_data: + doc: | + Animation data for a single node (LegoAnimNodeData). Contains the + node name and arrays of keyframes for translation, rotation, scale, + and morph (visibility) animations. + seq: + - id: name_length + type: u4 + doc: Length of the node name in bytes. + - id: name + type: str + size: name_length + encoding: ASCII + if: name_length > 0 + doc: | + Node name used to match this animation data to a ROI in the scene. + Names starting with '*' indicate special handling (actor name + substitution). Names starting with '-' are ignored. + - id: num_translation_keys + type: u2 + doc: Number of translation keyframes. + - id: translation_keys + type: translation_key + repeat: expr + repeat-expr: num_translation_keys + doc: Translation (position) keyframes. + - id: num_rotation_keys + type: u2 + doc: Number of rotation keyframes. + - id: rotation_keys + type: rotation_key + repeat: expr + repeat-expr: num_rotation_keys + doc: Rotation keyframes (quaternion format). + - id: num_scale_keys + type: u2 + doc: Number of scale keyframes. + - id: scale_keys + type: scale_key + repeat: expr + repeat-expr: num_scale_keys + doc: Scale keyframes. + - id: num_morph_keys + type: u2 + doc: Number of morph (visibility) keyframes. + - id: morph_keys + type: morph_key + repeat: expr + repeat-expr: num_morph_keys + doc: Morph keyframes controlling visibility. + + anim_key: + doc: | + Base animation key containing time and flags. The time and flags + are packed into a single 32-bit value: bits 0-23 contain the time + in milliseconds, and bits 24-31 contain flags. + seq: + - id: time_and_flags + type: s4 + doc: | + Packed time and flags value. + - Bits 0-23: Time in milliseconds (mask with 0xFFFFFF) + - Bits 24-31: Flags (shift right by 24) + instances: + time: + value: time_and_flags & 0xFFFFFF + doc: Keyframe time in milliseconds. + flags: + value: (time_and_flags >> 24) & 0xFF + doc: | + Keyframe flags: + - 0x01 (active): Key has meaningful data + - 0x02 (negate_rotation): Negate quaternion for interpolation + - 0x04 (skip_interpolation): Use this key's value without blending + + translation_key: + doc: | + Translation keyframe containing position offset (LegoTranslationKey). + The translation is applied relative to the parent node's transform. + seq: + - id: key + type: anim_key + doc: Base key with time and flags. + - id: x + type: f4 + doc: X component of translation. + - id: y + type: f4 + doc: Y component of translation. + - id: z + type: f4 + doc: Z component of translation. + + rotation_key: + doc: | + Rotation keyframe containing a quaternion (LegoRotationKey). + The quaternion is stored as (angle, x, y, z) where angle is the + scalar/w component and (x, y, z) is the vector part. + seq: + - id: key + type: anim_key + doc: Base key with time and flags. + - id: angle + type: f4 + doc: | + Quaternion scalar component (w). A value of 1.0 with x=y=z=0 + represents no rotation (identity quaternion). + - id: x + type: f4 + doc: Quaternion x component. + - id: y + type: f4 + doc: Quaternion y component. + - id: z + type: f4 + doc: Quaternion z component. + + scale_key: + doc: | + Scale keyframe containing scale factors (LegoScaleKey). + Scale is applied relative to the local origin of the node. + seq: + - id: key + type: anim_key + doc: Base key with time and flags. + - id: x + type: f4 + doc: X scale factor (1.0 = no scaling). + - id: y + type: f4 + doc: Y scale factor (1.0 = no scaling). + - id: z + type: f4 + doc: Z scale factor (1.0 = no scaling). + + morph_key: + doc: | + Morph/visibility keyframe (LegoMorphKey). Controls whether the + node's ROI is visible at a given time. + seq: + - id: key + type: anim_key + doc: Base key with time and flags. + - id: visible + type: u1 + doc: Visibility flag. Non-zero means visible. + + rotation_z_key: + doc: | + Z-axis rotation keyframe (LegoRotationZKey). Used for camera roll + animation where only rotation around the view axis is needed. + seq: + - id: key + type: anim_key + doc: Base key with time and flags. + - id: z + type: f4 + doc: Rotation angle around the Z axis in radians. + +enums: + actor_type: + 2: managed_lego_actor + 3: managed_invisible_roi_trimmed + 4: managed_invisible_roi + 5: scene_roi_1 + 6: scene_roi_2 diff --git a/docs/dta.ksy b/docs/dta.ksy new file mode 100644 index 000000000..2590df71e --- /dev/null +++ b/docs/dta.ksy @@ -0,0 +1,135 @@ +meta: + id: dta + title: Animation Data File + application: LEGO Island + file-extension: dta + license: CC0-1.0 + endian: le + +doc: | + Animation data file format for LEGO Island (1997). Contains animation + information for world objects including their positions, orientations, + and associated models. + + DTA files are located at `/lego/data/inf.dta` where + is the world name (e.g., "isle", "act1", "act2m", etc.). They are + loaded by LegoAnimationManager::LoadWorldInfo() to populate animation + information for the current world. + + File structure: + 1. Header - version (must be 3) and animation count + 2. AnimInfo entries - animation references with nested model placement data + +seq: + - id: version + type: u4 + doc: | + File format version. Must be 3 for valid files. + The game rejects files with mismatched versions. + - id: num_anims + type: u2 + doc: Number of animation info entries in this file. + - id: anims + type: anim_info + repeat: expr + repeat-expr: num_anims + doc: Animation information entries. + +types: + anim_info: + doc: | + Animation information for a single animation (AnimInfo struct). + Contains metadata about the animation and a list of models involved. + Parsed by LegoAnimationManager::ReadAnimInfo(). + seq: + - id: name_length + type: u1 + doc: Length of the animation name in bytes. + - id: name + type: str + size: name_length + encoding: ASCII + doc: | + Animation name identifier. The last two characters are used + to look up a character index via GetCharacterIndex(). + - id: object_id + type: u4 + doc: Object ID used to identify this animation in the game. + - id: location + type: s2 + doc: | + Location index referencing a LegoLocation. A value of -1 + indicates no specific location is assigned. + - id: unk_0x0a + type: u1 + doc: Boolean flag (MxBool). Purpose unknown. + - id: unk_0x0b + type: u1 + doc: Unknown byte field. + - id: unk_0x0c + type: u1 + doc: Unknown byte field. + - id: unk_0x0d + type: u1 + doc: Unknown byte field. + - id: unk_0x10 + type: f4 + repeat: expr + repeat-expr: 4 + doc: Array of 4 unknown float values (16 bytes total). + - id: model_count + type: u1 + doc: Number of model entries that follow. + - id: models + type: model_info + repeat: expr + repeat-expr: model_count + doc: Model information for each model in this animation. + + model_info: + doc: | + Model information defining position and orientation for a single + model within an animation (ModelInfo struct). Used to place characters + and objects in the world during animation playback. + Parsed by LegoAnimationManager::ReadModelInfo(). + seq: + - id: name_length + type: u1 + doc: Length of the model name in bytes. + - id: name + type: str + size: name_length + encoding: ASCII + doc: | + Model name used to look up the character or vehicle. + Examples: "caprc01" (race car), "irt001d1" (character). + - id: unk_0x04 + type: u1 + doc: Unknown byte field. + - id: position + type: vertex3 + doc: World position (X, Y, Z) of the model. + - id: direction + type: vertex3 + doc: Forward direction vector of the model. + - id: up + type: vertex3 + doc: Up direction vector of the model. + - id: unk_0x2c + type: u1 + doc: | + Boolean flag. When non-zero, this model is considered a vehicle + and tracked in the animation's vehicle list (m_unk0x2a). + + vertex3: + doc: A 3D point or vector with X, Y, Z components. + seq: + - id: x + type: f4 + doc: X component. + - id: y + type: f4 + doc: Y component. + - id: z + type: f4 + doc: Z component. diff --git a/docs/samples/BLDRINF.DTA b/docs/samples/BLDRINF.DTA new file mode 100755 index 000000000..d12752b10 Binary files /dev/null and b/docs/samples/BLDRINF.DTA differ diff --git a/docs/samples/Dbfrfn.tex b/docs/samples/Dbfrfn.tex new file mode 100755 index 000000000..dcda861dd Binary files /dev/null and b/docs/samples/Dbfrfn.tex differ diff --git a/docs/samples/pns065rd.ani b/docs/samples/pns065rd.ani new file mode 100644 index 000000000..7a9128831 Binary files /dev/null and b/docs/samples/pns065rd.ani differ diff --git a/docs/savegame.ksy b/docs/savegame.ksy index f08d5c229..45e16c254 100644 --- a/docs/savegame.ksy +++ b/docs/savegame.ksy @@ -101,8 +101,10 @@ types: if: not is_end_marker doc: | Variable value. For colors this is a color name like "lego red". - For backgroundcolor this is "set R G B". - For lightposition this is a number "1" or "2". + For backgroundcolor and tempBackgroundColor this is "set H S V" where + H, S, V are HSV color values scaled 0-100 (not RGB). The game internally + converts to RGB using ConvertHSVToRGB(). + For lightposition this is a number "0" through "5" (6 sun positions). instances: is_end_marker: value: name == "END_OF_VARIABLES" @@ -153,88 +155,127 @@ types: doc: Margaret Patricia "Maggie" Post - id: bu type: standard_character_entry + doc: Buck Pounds - id: ml type: standard_character_entry + doc: Ed Mail - id: nu type: standard_character_entry + doc: Nubby Stevens - id: na type: standard_character_entry doc: Nancy Nubbins - id: cl type: standard_character_entry + doc: Dr. Clickitt - id: en type: standard_character_entry + doc: Enter - id: re type: standard_character_entry + doc: Return - id: ro type: standard_character_entry + doc: Captain D. Rom - id: d1 type: standard_character_entry + doc: Bill Ding (Race Car) - id: d2 type: standard_character_entry + doc: Bill Ding (Helicopter) - id: d3 type: standard_character_entry + doc: Bill Ding (Dune Buggy) - id: d4 type: standard_character_entry + doc: Bill Ding (Jetski) - id: l1 type: standard_character_entry + doc: The Flying Legandos #1 - id: l2 type: standard_character_entry + doc: The Flying Legandos #2 - id: l3 type: standard_character_entry + doc: The Flying Legandos #3 - id: l4 type: standard_character_entry + doc: The Flying Legandos #4 - id: l5 type: standard_character_entry + doc: The Flying Legandos #5 - id: l6 type: standard_character_entry + doc: The Flying Legandos #6 - id: b1 type: standard_character_entry + doc: The Legobobs #1 - id: b2 type: standard_character_entry + doc: The Legobobs #2 - id: b3 type: standard_character_entry + doc: The Legobobs #3 - id: b4 type: standard_character_entry + doc: The Legobobs #4 - id: cm type: standard_character_entry + doc: Brazilian Carmen - id: gd type: standard_character_entry + doc: Gideon Worse - id: rd type: standard_character_entry + doc: Red Greenbase - id: pg type: standard_character_entry doc: Polly Gone - id: bd type: standard_character_entry + doc: Bradford Brickford - id: sy type: standard_character_entry + doc: Shiney Doris - id: gn type: standard_character_entry + doc: Glen Funberg - id: df type: standard_character_entry + doc: Dorothy Funberg - id: bs type: standard_character_entry + doc: Brian Shrimp - id: lt type: standard_character_entry + doc: Luke Tepid - id: st type: standard_character_entry + doc: Shorty Tails - id: bm type: standard_character_entry + doc: Bumpy Kindergreen - id: jk type: standard_character_entry + doc: Jack O'Trades - id: ghost type: ghost_character_entry + doc: Ghost #1 - id: ghost01 type: ghost_character_entry + doc: Ghost #2 - id: ghost02 type: ghost_character_entry + doc: Ghost #3 - id: ghost03 type: ghost_character_entry + doc: Ghost #4 - id: ghost04 type: ghost_character_entry + doc: Ghost #5 - id: ghost05 type: ghost_character_entry + doc: Ghost #6 - id: hg type: standard_character_entry - id: pntgy diff --git a/docs/tex.ksy b/docs/tex.ksy new file mode 100644 index 000000000..0622c30f1 --- /dev/null +++ b/docs/tex.ksy @@ -0,0 +1,91 @@ +meta: + id: tex + title: Texture Data File + application: LEGO Island + file-extension: tex + license: CC0-1.0 + endian: le + +doc: | + Texture data format for LEGO Island (1997). Contains one or more named + textures with 8-bit indexed color image data. + + Texture data is embedded in SI (Interleaf) container files and parsed by + LegoTexturePresenter::Read(). Each texture consists of a length-prefixed + name followed by image data with a color palette and pixel indices. + + The image format is shared with the world database (world.wdb) texture + data, using the same LegoImage and LegoPaletteEntry serialization. + + File structure: + 1. Texture count + 2. Named texture entries - name + palette + pixel data + +seq: + - id: num_textures + type: u4 + doc: Number of textures in this file. + - id: textures + type: named_texture + repeat: expr + repeat-expr: num_textures + doc: Array of named textures. + +types: + named_texture: + doc: | + A named texture with 8-bit indexed color image data. + seq: + - id: name_length + type: u4 + doc: Length of the texture name buffer in bytes. + - id: name + type: str + size: name_length + encoding: ASCII + terminator: 0 + doc: | + Texture name (e.g., "dbfrfn.gif"). The name is a null-terminated + C string within the allocated buffer. Bytes after the null + terminator are unused padding and consumed but not included + in the string value. + - id: image + type: image + doc: The texture image data. + + image: + doc: | + An 8-bit indexed color image with palette. Parsed by LegoImage::Read(). + seq: + - id: width + type: u4 + doc: Image width in pixels. + - id: height + type: u4 + doc: Image height in pixels. + - id: palette_size + type: u4 + doc: Number of entries in the color palette (max 256). + - id: palette + type: palette_entry + repeat: expr + repeat-expr: palette_size + doc: Color palette entries. + - id: pixels + size: width * height + doc: | + Pixel data as palette indices. Each byte is an index into + the palette array. + + palette_entry: + doc: RGB color palette entry. Parsed by LegoPaletteEntry::Read(). + seq: + - id: red + type: u1 + doc: Red component (0-255). + - id: green + type: u1 + doc: Green component (0-255). + - id: blue + type: u1 + doc: Blue component (0-255). diff --git a/docs/wdb.ksy b/docs/wdb.ksy new file mode 100644 index 000000000..b29d7fc97 --- /dev/null +++ b/docs/wdb.ksy @@ -0,0 +1,819 @@ +meta: + id: wdb + title: World Database File + application: LEGO Island + file-extension: wdb + license: CC0-1.0 + endian: le + +doc: | + World Database file format for LEGO Island (1997). Contains world geometry + data including textures, parts (ROI definitions), and models with their + transforms and LOD (Level of Detail) information. + + The file is located at `/lego/data/world.wdb` on either + the hard drive or CD-ROM. + + File structure: + 1. World headers - list of worlds with part/model references + 2. Global textures - shared texture data (read once) + 3. Global parts - shared part definitions (read once) + 4. Part data blobs - at offsets specified in headers + 5. Model data blobs - at offsets specified in headers + +seq: + - id: num_worlds + type: s4 + doc: Number of world entries in this file. + - id: worlds + type: world_entry + repeat: expr + repeat-expr: num_worlds + doc: | + World entries containing references to parts and models. + Each world represents a distinct game area (e.g., "Act1", "Act2", "Act3"). + - id: global_textures_size + type: u4 + doc: Size in bytes of the global textures block. + - id: global_textures + type: texture_list + size: global_textures_size + doc: | + Global textures shared across all worlds. These are loaded once + when the first world is loaded and cached for subsequent worlds. + - id: global_parts_size + type: u4 + doc: Size in bytes of the global parts block. + - id: global_parts + type: part_list + size: global_parts_size + doc: | + Global parts (ROI definitions) shared across all worlds. + Like textures, these are loaded once and cached. + +types: + world_entry: + doc: | + A world entry containing references to parts and models. + Parts define reusable geometry, while models are placed instances + with specific transforms. + seq: + - id: name_length + type: s4 + doc: Length of the world name in bytes. + - id: name + type: str + size: name_length + encoding: ASCII + doc: | + World name used to identify this world (e.g., "Act1", "Act2", "Act3"). + - id: num_parts + type: s4 + doc: Number of part references in this world. + - id: parts + type: part_reference + repeat: expr + repeat-expr: num_parts + doc: References to part data stored elsewhere in the file. + - id: num_models + type: s4 + doc: Number of model entries in this world. + - id: models + type: model_entry + repeat: expr + repeat-expr: num_models + doc: | + Model entries with transform data. Each model references + geometry and specifies its position, orientation, and visibility. + + part_reference: + doc: | + Reference to part data stored at an offset in the file. + The actual part data contains ROI definitions with textures and LODs. + seq: + - id: name_length + type: u4 + doc: Length of the ROI name in bytes. + - id: name + type: str + size: name_length + encoding: ASCII + doc: ROI (Realtime Object Instance) name identifying this part. + - id: data_length + type: u4 + doc: Length of the part data in bytes. + - id: data_offset + type: u4 + doc: Absolute file offset to the part data. + instances: + data: + io: _root._io + pos: data_offset + size: data_length + type: part_data + doc: The actual part data at the specified offset. + + model_entry: + doc: | + A model entry defining a placed instance in the world. + Contains transform data (location, direction, up vector) and + a reference to the model geometry. + seq: + - id: name_length + type: u4 + doc: Length of the model name in bytes. + - id: name + type: str + size: name_length + encoding: ASCII + doc: | + Model name. Names starting with "isle" have quality variants + (isle_lo, isle, isle_hi). Names starting with "haus" have + special loading rules. + - id: data_length + type: u4 + doc: Length of the model data in bytes. + - id: data_offset + type: u4 + doc: Absolute file offset to the model data. + - id: presenter_name_length + type: u4 + doc: Length of the presenter class name in bytes. + - id: presenter_name + type: str + size: presenter_name_length + encoding: ASCII + doc: | + Presenter class name determining how the model is created. + Common values: "LegoActorPresenter", "LegoEntityPresenter". + - id: location + type: vertex3 + doc: World position of the model (X, Y, Z). + - id: direction + type: vertex3 + doc: Forward direction vector of the model. + - id: up + type: vertex3 + doc: Up direction vector of the model. + - id: visible + type: u1 + doc: Visibility flag. Non-zero means the model is initially visible. + instances: + data: + io: _root._io + pos: data_offset + size: data_length + type: model_data + doc: The model data (textures, animation, ROI) at the specified offset. + + texture_list: + doc: | + A list of named textures. Each texture includes palette and pixel data. + seq: + - id: num_textures + type: u4 + doc: Number of textures in this list. + - id: textures + type: named_texture + repeat: expr + repeat-expr: num_textures + doc: Array of named textures. + + named_texture: + doc: | + A named texture with 8-bit indexed color image data. + seq: + - id: name_length + type: u4 + doc: Length of the texture name in bytes. + - id: name + type: str + size: name_length + encoding: ASCII + doc: Texture name used for lookup. + - id: image + type: image + doc: The texture image data. + + image: + doc: | + An 8-bit indexed color image with palette. + seq: + - id: width + type: u4 + doc: Image width in pixels. + - id: height + type: u4 + doc: Image height in pixels. + - id: palette_size + type: u4 + doc: Number of entries in the color palette (max 256). + - id: palette + type: palette_entry + repeat: expr + repeat-expr: palette_size + doc: Color palette entries. + - id: pixels + size: width * height + doc: | + Pixel data as palette indices. Each byte is an index into + the palette array. + + palette_entry: + doc: RGB color palette entry. + seq: + - id: red + type: u1 + doc: Red component (0-255). + - id: green + type: u1 + doc: Green component (0-255). + - id: blue + type: u1 + doc: Blue component (0-255). + + part_list: + doc: | + A list of named parts (ROI definitions). Parts can be shared + across multiple models and worlds. + seq: + - id: texture_info_offset + type: u4 + doc: Offset within this block to texture information. + - id: num_rois + type: u4 + doc: Number of ROI definitions. + - id: rois + type: named_part + repeat: expr + repeat-expr: num_rois + doc: Array of named part definitions. + + named_part: + doc: | + A named part containing LOD (Level of Detail) definitions. + seq: + - id: name_length + type: u4 + doc: Length of the ROI name in bytes. + - id: name + type: str + size: name_length + encoding: ASCII + doc: ROI name for lookup. + - id: num_lods + type: u4 + doc: Number of LOD levels for this part. + - id: next_roi_offset + type: u4 + doc: Offset to the next ROI definition (for skipping LOD data). + - id: lods + type: lod + repeat: expr + repeat-expr: num_lods + doc: LOD definitions from highest to lowest detail. + + part_data: + doc: | + Part data blob containing textures and ROI definitions. + This is the format used for part data at offsets in the file. + seq: + - id: texture_info_offset + type: u4 + doc: Offset within this block to texture information. + - id: num_rois + type: u4 + doc: Number of ROI definitions in this part. + - id: rois + type: named_part + repeat: expr + repeat-expr: num_rois + doc: ROI definitions for this part. + + model_data: + doc: | + Model data blob containing version info, textures, animation data, + and ROI hierarchy. This is the format used for model data at offsets. + Parsed by LegoModelPresenter::CreateROI. + seq: + - id: version + type: u4 + doc: Format version. Must be 19 (MODEL_VERSION). + - id: texture_info_offset + type: u4 + doc: Offset within this blob to texture information. + - id: num_rois + type: u4 + doc: Number of ROIs (typically 1 for models). + - id: anim + type: model_anim + doc: Animation data for this model. + - id: roi + type: roi + doc: The root ROI containing the model geometry. + + model_anim: + doc: | + Animation data embedded in model data. This is a simplified form + of LegoAnim without camera/scene animation (p_parseScene=FALSE). + seq: + - id: num_actors + type: u4 + doc: Number of actor entries. + - id: actors + type: anim_actor_entry + repeat: expr + repeat-expr: num_actors + doc: Actor entries for this animation. + - id: duration + type: s4 + doc: Animation duration in milliseconds. + - id: root_node + type: anim_tree_node + doc: Root node of the animation tree. + + anim_actor_entry: + doc: | + An actor reference in the animation. The name identifies which ROI + (Realtime Object Instance) to animate, and the type determines + how the actor is managed by the character manager. + seq: + - id: name_length + type: u4 + doc: Length of the actor name in bytes. + - id: name + type: str + size: name_length + encoding: ASCII + if: name_length > 0 + doc: Actor name used to look up the ROI in the scene. + - id: actor_type + type: u4 + enum: actor_type + if: name_length > 0 + doc: | + Determines how the actor ROI is created and managed. + See actor_type enum for possible values. + + anim_tree_node: + doc: Node in the animation tree hierarchy. + seq: + - id: data + type: anim_node_data + doc: Animation keyframe data for this node. + - id: num_children + type: u4 + doc: Number of child nodes. + - id: children + type: anim_tree_node + repeat: expr + repeat-expr: num_children + doc: Child nodes. + + anim_node_data: + doc: Animation keyframe data for a single node. + seq: + - id: name_length + type: u4 + doc: Length of node name. + - id: name + type: str + size: name_length + encoding: ASCII + if: name_length > 0 + doc: Node name for matching to ROI. + - id: num_translation_keys + type: u2 + doc: Number of translation keyframes. + - id: translation_keys + type: translation_key + repeat: expr + repeat-expr: num_translation_keys + doc: Translation keyframes. + - id: num_rotation_keys + type: u2 + doc: Number of rotation keyframes. + - id: rotation_keys + type: rotation_key + repeat: expr + repeat-expr: num_rotation_keys + doc: Rotation keyframes (quaternion format). + - id: num_scale_keys + type: u2 + doc: Number of scale keyframes. + - id: scale_keys + type: scale_key + repeat: expr + repeat-expr: num_scale_keys + doc: Scale keyframes. + - id: num_morph_keys + type: u2 + doc: Number of morph (visibility) keyframes. + - id: morph_keys + type: morph_key + repeat: expr + repeat-expr: num_morph_keys + doc: Morph keyframes. + + anim_key: + doc: | + Base animation key containing time and flags. The time and flags + are packed into a single 32-bit value: bits 0-23 contain the time + in milliseconds, and bits 24-31 contain flags. + seq: + - id: time_and_flags + type: s4 + doc: | + Packed time and flags value. + - Bits 0-23: Time in milliseconds (mask with 0xFFFFFF) + - Bits 24-31: Flags (shift right by 24) + instances: + time: + value: time_and_flags & 0xFFFFFF + doc: Keyframe time in milliseconds. + flags: + value: (time_and_flags >> 24) & 0xFF + doc: | + Keyframe flags: + - 0x01 (active): Key has meaningful data + - 0x02 (negate_rotation): Negate quaternion for interpolation + - 0x04 (skip_interpolation): Use this key's value without blending + + translation_key: + doc: | + Translation keyframe containing position offset (LegoTranslationKey). + The translation is applied relative to the parent node's transform. + seq: + - id: key + type: anim_key + doc: Base key with time and flags. + - id: x + type: f4 + doc: X component of translation. + - id: y + type: f4 + doc: Y component of translation. + - id: z + type: f4 + doc: Z component of translation. + + rotation_key: + doc: | + Rotation keyframe containing a quaternion (LegoRotationKey). + The quaternion is stored as (angle, x, y, z) where angle is the + scalar/w component and (x, y, z) is the vector part. + seq: + - id: key + type: anim_key + doc: Base key with time and flags. + - id: angle + type: f4 + doc: | + Quaternion scalar component (w). A value of 1.0 with x=y=z=0 + represents no rotation (identity quaternion). + - id: x + type: f4 + doc: Quaternion x component. + - id: y + type: f4 + doc: Quaternion y component. + - id: z + type: f4 + doc: Quaternion z component. + + scale_key: + doc: | + Scale keyframe containing scale factors (LegoScaleKey). + Scale is applied relative to the local origin of the node. + seq: + - id: key + type: anim_key + doc: Base key with time and flags. + - id: x + type: f4 + doc: X scale factor (1.0 = no scaling). + - id: y + type: f4 + doc: Y scale factor (1.0 = no scaling). + - id: z + type: f4 + doc: Z scale factor (1.0 = no scaling). + + morph_key: + doc: | + Morph/visibility keyframe (LegoMorphKey). Controls whether the + node's ROI is visible at a given time. + seq: + - id: key + type: anim_key + doc: Base key with time and flags. + - id: visible + type: u1 + doc: Visibility flag. Non-zero means visible. + + roi: + doc: | + ROI (Realtime Object Instance) defining a piece of geometry. + ROIs form a hierarchy with parent-child relationships. + seq: + - id: name_length + type: u4 + doc: Length of the ROI name in bytes. + - id: name + type: str + size: name_length + encoding: ASCII + doc: ROI name used for lookup and animation binding. + - id: bounding_sphere + type: sphere + doc: Bounding sphere for visibility culling. + - id: bounding_box + type: box + doc: Axis-aligned bounding box. + - id: texture_name_length + type: u4 + doc: Length of texture/material name (0 if none). + - id: texture_name + type: str + size: texture_name_length + encoding: ASCII + if: texture_name_length > 0 + doc: | + Texture or material name. Names starting with "t_" reference + textures; other names are color aliases (e.g., "lego red"). + - id: shared_lod_list + type: u1 + doc: | + If non-zero, LODs are shared with another ROI and not stored here. + The ROI name (minus trailing digits) is used to look up shared LODs. + - id: num_lods + type: u4 + if: shared_lod_list == 0 + doc: Number of LOD levels (only if not using shared LODs). + - id: next_roi_offset + type: u4 + if: shared_lod_list == 0 and num_lods > 0 + doc: Offset to continue reading after LOD data. + - id: lods + type: lod + repeat: expr + repeat-expr: num_lods + if: shared_lod_list == 0 and num_lods > 0 + doc: LOD definitions from highest to lowest detail. + - id: num_children + type: u4 + doc: Number of child ROIs in this hierarchy. + - id: children + type: roi + repeat: expr + repeat-expr: num_children + doc: Child ROIs forming a hierarchy. + + sphere: + doc: Bounding sphere defined by center point and radius. + seq: + - id: center + type: vertex3 + doc: Center point of the sphere. + - id: radius + type: f4 + doc: Radius of the sphere. + + box: + doc: Axis-aligned bounding box defined by min and max corners. + seq: + - id: min + type: vertex3 + doc: Minimum corner (smallest X, Y, Z values). + - id: max + type: vertex3 + doc: Maximum corner (largest X, Y, Z values). + + vertex3: + doc: A 3D point or vector with X, Y, Z components. + seq: + - id: x + type: f4 + doc: X component. + - id: y + type: f4 + doc: Y component. + - id: z + type: f4 + doc: Z component. + + lod: + doc: | + Level of Detail definition containing mesh data. + LODs are ordered from highest to lowest detail. + seq: + - id: flags + type: u4 + doc: | + LOD flags. Bit 0 (0x01) indicates this is an "extra" LOD. + Other bits control visibility and rendering behavior. + - id: num_meshes + type: u4 + doc: Number of meshes in this LOD. + - id: vertex_normal_counts + type: u4 + if: num_meshes > 0 + doc: | + Packed vertex and normal counts. + Lower 16 bits: vertex count + Upper 15 bits (shifted right by 1): normal count + - id: num_texture_vertices + type: s4 + if: num_meshes > 0 + doc: Number of texture coordinate pairs. + - id: vertices + type: vertex3 + repeat: expr + repeat-expr: vertex_count + if: num_meshes > 0 and vertex_count > 0 + doc: Vertex positions shared across meshes. + - id: normals + type: vertex3 + repeat: expr + repeat-expr: normal_count + if: num_meshes > 0 and normal_count > 0 + doc: Normal vectors shared across meshes. + - id: texture_vertices + type: texture_vertex + repeat: expr + repeat-expr: num_texture_vertices + if: num_meshes > 0 and num_texture_vertices > 0 + doc: Texture coordinates (UV pairs). + - id: meshes + type: mesh + repeat: expr + repeat-expr: num_meshes + if: num_meshes > 0 + doc: Mesh definitions using the shared vertex/normal/UV data. + instances: + vertex_count: + value: '(num_meshes > 0) ? (vertex_normal_counts & 0xFFFF) : 0' + doc: Number of vertices (lower 16 bits of packed value). + normal_count: + value: '(num_meshes > 0) ? ((vertex_normal_counts >> 17) & 0x7FFF) : 0' + doc: Number of normals (upper 15 bits, shifted right by 1). + + texture_vertex: + doc: Texture coordinate pair (UV). + seq: + - id: u + type: f4 + doc: U coordinate (horizontal, 0.0-1.0). + - id: v + type: f4 + doc: V coordinate (vertical, 0.0-1.0). + + mesh: + doc: | + A mesh within an LOD, containing polygons and material properties. + seq: + - id: num_polygons + type: u2 + doc: Number of triangular polygons. + - id: num_vertices + type: u2 + doc: Number of vertices used by this mesh. + - id: polygon_indices + type: polygon_indices + repeat: expr + repeat-expr: num_polygons + doc: Vertex indices for each triangle. + - id: num_texture_indices + type: u4 + doc: | + Total number of texture indices. Should equal num_polygons * 3 + if textured, or 0 if untextured. + - id: texture_indices + type: texture_indices + repeat: expr + repeat-expr: num_polygons + if: num_texture_indices > 0 + doc: | + Texture coordinate indices for each triangle. Unlike polygon_indices, + these are simple U32 indices into the LOD's texture_vertices array, + not packed values. Each index directly references a UV coordinate pair. + - id: properties + type: mesh_properties + doc: Material and rendering properties. + + polygon_indices: + doc: | + Three packed indices forming a triangle. Each 32-bit value contains + vertex index, normal index, and a "create vertex" flag used by + Direct3D Retained Mode mesh building. + + Bit layout of each packed value: + - Bits 0-15 (16 bits): When create flag is set, this is the index into + the LOD's vertices array. When create flag is clear, this is the index + into the mesh's built vertex buffer (referencing a previously created vertex). + - Bits 16-30 (15 bits): Index into the LOD's normals array + - Bit 31: Create vertex flag. When set (1), a new mesh vertex is created + combining position, normal, and texture UV. When clear (0), the value + in bits 0-15 references an existing mesh vertex by index. + + The mesh builder creates a vertex buffer where each unique position+normal+UV + combination gets an entry. Texture indices (in texture_indices) are only + consumed when the create flag is set. + seq: + - id: a + type: u4 + doc: First packed vertex/normal index with create flag. + - id: b + type: u4 + doc: Second packed vertex/normal index with create flag. + - id: c + type: u4 + doc: Third packed vertex/normal index with create flag. + + texture_indices: + doc: | + Three texture coordinate indices forming a triangle. Unlike polygon_indices, + these are simple U32 values that directly index into the LOD's texture_vertices + array. Each value is only used when the corresponding polygon_indices entry + has its create flag (bit 31) set. + seq: + - id: a + type: u4 + doc: First texture vertex index. + - id: b + type: u4 + doc: Second texture vertex index. + - id: c + type: u4 + doc: Third texture vertex index. + + mesh_properties: + doc: | + Material and rendering properties for a mesh. + seq: + - id: color + type: color_rgb + doc: Base color of the mesh. + - id: alpha + type: f4 + doc: Transparency (0.0 = fully transparent, 1.0 = opaque). + - id: shading + type: u1 + enum: shading_mode + doc: Shading mode for rendering this mesh. + - id: unknown_0x0d + type: u1 + doc: Unknown flag. When > 0, special material is applied. + - id: unknown_0x20 + type: u1 + doc: Unknown field. + - id: use_alias + type: u1 + doc: | + If non-zero, texture_name and material_name are looked up + as aliases rather than literal names. + - id: texture_name_length + type: u4 + doc: Length of texture name (0 if no texture). + - id: texture_name + type: str + size: texture_name_length + encoding: ASCII + if: texture_name_length > 0 + doc: Texture name for this mesh. + - id: material_name_length + type: u4 + doc: Length of material/color name (0 if none). + - id: material_name + type: str + size: material_name_length + encoding: ASCII + if: material_name_length > 0 + doc: | + Material or color alias name (e.g., "lego red", "lego blue"). + + color_rgb: + doc: RGB color with 8-bit components. + seq: + - id: red + type: u1 + doc: Red component (0-255). + - id: green + type: u1 + doc: Green component (0-255). + - id: blue + type: u1 + doc: Blue component (0-255). + +enums: + shading_mode: + 0: flat + 1: gouraud + 2: wireframe + + actor_type: + 2: managed_lego_actor + 3: managed_invisible_roi_trimmed + 4: managed_invisible_roi + 5: scene_roi_1 + 6: scene_roi_2