Merge remote-tracking branch 'isle/master'

This commit is contained in:
Christian Semmler 2026-03-14 16:48:49 -07:00
commit 203f932ec2
44 changed files with 1899 additions and 338 deletions

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -222,9 +222,6 @@ class LegoPathActor : public LegoActor {
MxFloat m_linearRotationRatio; // 0x150
};
// FUNCTION: LEGO1 0x1002edd0
// LegoPathActor::CheckIntersectionBothFaces
// TEMPLATE: LEGO1 0x10018b70
// List<LegoBoundaryEdge>::~List<LegoBoundaryEdge>

View File

@ -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

View File

@ -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'

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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",

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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) {

View File

@ -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;

View File

@ -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()) {

View File

@ -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) {

View File

@ -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<LegoPathBoundary*> 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<LegoPathBoundary*>& 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<LegoPathBoundary*>::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<LegoPathBoundary*> 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)

View File

@ -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);
}

View File

@ -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();

View File

@ -25,7 +25,7 @@
#include <stdio.h>
// 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()) {

View File

@ -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());

View File

@ -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;
}

View File

@ -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);

View File

@ -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

View File

@ -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;
}

View File

@ -52,6 +52,7 @@ inline ViewManager* Lego3DView::GetViewManager()
return m_pViewManager;
}
// FUNCTION: BETA10 0x1006aae0
inline ViewROI* Lego3DView::GetPointOfView()
{
return m_pPointOfView;

View File

@ -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;
}

View File

@ -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

View File

@ -8,6 +8,7 @@
#include <assert.h>
// 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

View File

@ -873,6 +873,12 @@
// LIBRARY: BETA10 0x100fa0e0
// atof
// LIBRARY: BETA10 0x1005a500
// sqrt
// LIBRARY: BETA10 0x1005a530
// sqrtf
// LIBRARY: BETA10 0x1005a9c0
// fabs

View File

@ -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;

View File

@ -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`.

310
docs/animation.ksy Normal file
View File

@ -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

135
docs/dta.ksy Normal file
View File

@ -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 `<install_path>/lego/data/<world>inf.dta` where
<world> 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.

BIN
docs/samples/BLDRINF.DTA Executable file

Binary file not shown.

BIN
docs/samples/Dbfrfn.tex Executable file

Binary file not shown.

BIN
docs/samples/pns065rd.ani Normal file

Binary file not shown.

View File

@ -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

91
docs/tex.ksy Normal file
View File

@ -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).

819
docs/wdb.ksy Normal file
View File

@ -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 `<install_path>/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