mirror of
https://github.com/isledecomp/isle-portable.git
synced 2024-12-19 04:12:26 -05:00
1b46859cf6
* Implement `RaceSkel`, add BETA10 annotations * fix formatting * Fix order * Address some review comments --------- Co-authored-by: jonschz <jonschz@users.noreply.github.com>
535 lines
13 KiB
C++
535 lines
13 KiB
C++
#include "legoracers.h"
|
|
|
|
#include "anim/legoanim.h"
|
|
#include "carrace.h"
|
|
#include "define.h"
|
|
#include "legocachesoundmanager.h"
|
|
#include "legocameracontroller.h"
|
|
#include "legonavcontroller.h"
|
|
#include "legorace.h"
|
|
#include "legosoundmanager.h"
|
|
#include "misc.h"
|
|
#include "mxdebug.h"
|
|
#include "mxmisc.h"
|
|
#include "mxnotificationmanager.h"
|
|
#include "mxtimer.h"
|
|
#include "mxutilities.h"
|
|
#include "mxvariabletable.h"
|
|
#include "raceskel.h"
|
|
|
|
DECOMP_SIZE_ASSERT(EdgeReference, 0x08)
|
|
DECOMP_SIZE_ASSERT(SkeletonKickPhase, 0x10)
|
|
DECOMP_SIZE_ASSERT(LegoRaceCar, 0x200)
|
|
|
|
// GLOBAL: LEGO1 0x100f0a20
|
|
// GLOBAL: BETA10 0x101f5e34
|
|
EdgeReference LegoRaceCar::g_skBMap[] = {
|
|
{// STRING: LEGO1 0x100f0a10
|
|
"EDG03_772",
|
|
NULL
|
|
},
|
|
{// STRING: LEGO1 0x100f0a04
|
|
"EDG03_773",
|
|
NULL
|
|
},
|
|
{// STRING: LEGO1 0x100f09f8
|
|
"EDG03_774",
|
|
NULL
|
|
},
|
|
{// STRING: LEGO1 0x100f09ec
|
|
"EDG03_775",
|
|
NULL
|
|
},
|
|
{// STRING: LEGO1 0x100f09e0
|
|
"EDG03_776",
|
|
NULL
|
|
},
|
|
{// STRING: LEGO1 0x100f09d4
|
|
"EDG03_777",
|
|
NULL
|
|
}
|
|
};
|
|
|
|
// GLOBAL: LEGO1 0x100f0a50
|
|
// GLOBAL: BETA10 0x101f5e60
|
|
const SkeletonKickPhase LegoRaceCar::g_skeletonKickPhases[] = {
|
|
{&LegoRaceCar::g_skBMap[0], 0.1, 0.2, LEGORACECAR_KICK2},
|
|
{&LegoRaceCar::g_skBMap[1], 0.2, 0.3, LEGORACECAR_KICK2},
|
|
{&LegoRaceCar::g_skBMap[2], 0.3, 0.4, LEGORACECAR_KICK2},
|
|
{&LegoRaceCar::g_skBMap[2], 0.6, 0.7, LEGORACECAR_KICK1},
|
|
{&LegoRaceCar::g_skBMap[1], 0.7, 0.8, LEGORACECAR_KICK1},
|
|
{&LegoRaceCar::g_skBMap[0], 0.8, 0.9, LEGORACECAR_KICK1},
|
|
{&LegoRaceCar::g_skBMap[3], 0.1, 0.2, LEGORACECAR_KICK1},
|
|
{&LegoRaceCar::g_skBMap[4], 0.2, 0.3, LEGORACECAR_KICK1},
|
|
{&LegoRaceCar::g_skBMap[5], 0.3, 0.4, LEGORACECAR_KICK1},
|
|
{&LegoRaceCar::g_skBMap[5], 0.6, 0.7, LEGORACECAR_KICK2},
|
|
{&LegoRaceCar::g_skBMap[4], 0.7, 0.8, LEGORACECAR_KICK2},
|
|
{&LegoRaceCar::g_skBMap[3], 0.8, 0.9, LEGORACECAR_KICK2},
|
|
};
|
|
|
|
// GLOBAL: LEGO1 0x100f0b10
|
|
// STRING: LEGO1 0x100f09cc
|
|
const char* LegoRaceCar::g_strSpeed = "SPEED";
|
|
|
|
// GLOBAL: LEGO1 0x100f0b18
|
|
// GLOBAL: BETA10 0x101f5f28
|
|
const char* LegoRaceCar::g_srtsl18to29[] = {
|
|
"srt018sl",
|
|
"srt019sl",
|
|
"srt020sl",
|
|
"srt021sl",
|
|
"srt022sl",
|
|
"srt023sl",
|
|
"srt024sl",
|
|
"srt025sl",
|
|
"srt026sl",
|
|
"srt027sl",
|
|
"srt028sl",
|
|
"srt029sl"
|
|
};
|
|
|
|
// GLOBAL: LEGO1 0x100f0b48
|
|
// GLOBAL: BETA10 0x101f5f58
|
|
const char* LegoRaceCar::g_srtsl6to10[] = {"srt006sl", "srt007sl", "srt008sl", "srt009sl", "srt010sl"};
|
|
|
|
// GLOBAL: LEGO1 0x100f0b5c
|
|
// GLOBAL: BETA10 0x101f5f6c
|
|
const char* LegoRaceCar::g_emptySoundKeyList[] = {NULL};
|
|
|
|
// GLOBAL: LEGO1 0x100f0b60
|
|
// GLOBAL: BETA10 0x101f5f70
|
|
const char* LegoRaceCar::g_srtrh[] = {"srt004rh", "srt005rh", "srt006rh"};
|
|
|
|
// GLOBAL: LEGO1 0x100f0b6c
|
|
// STRING: LEGO1 0x100f08c4
|
|
const char* LegoRaceCar::g_srt001ra = "srt001ra";
|
|
|
|
// GLOBAL: LEGO1 0x100f0b70
|
|
// STRING: LEGO1 0x100f08bc
|
|
const char* LegoRaceCar::g_soundSkel3 = "skel3";
|
|
|
|
// GLOBAL: LEGO1 0x100f0b74
|
|
// GLOBAL: BETA10 0x101f5f80
|
|
MxU32 LegoRaceCar::g_srtsl18to29Index = 0;
|
|
|
|
// GLOBAL: LEGO1 0x100f0b78
|
|
// GLOBAL: BETA10 0x101f5f84
|
|
MxU32 LegoRaceCar::g_srtsl6to10Index = 0;
|
|
|
|
// GLOBAL: LEGO1 0x100f0b7c
|
|
// GLOBAL: BETA10 0x101f5f88
|
|
MxU32 LegoRaceCar::g_emptySoundKeyListIndex = 0;
|
|
|
|
// GLOBAL: LEGO1 0x100f0b80
|
|
// GLOBAL: BETA10 0x101f5f8c
|
|
MxU32 LegoRaceCar::g_srtrhIndex = 0;
|
|
|
|
// GLOBAL: LEGO1 0x100f0b84
|
|
// GLOBAL: BETA10 0x101f5f90
|
|
MxLong LegoRaceCar::g_timeLastSoundPlayed = 0;
|
|
|
|
// GLOBAL: LEGO1 0x100f0b88
|
|
// GLOBAL: BETA10 0x101f5f94
|
|
MxS32 LegoRaceCar::g_unk0x100f0b88 = 0;
|
|
|
|
// GLOBAL: LEGO1 0x100f0b8c
|
|
// GLOBAL: BETA10 0x101f5f98
|
|
MxBool LegoRaceCar::g_unk0x100f0b8c = TRUE;
|
|
|
|
// Initialized at LEGO1 0x10012db0
|
|
// GLOBAL: LEGO1 0x10102af0
|
|
// GLOBAL: BETA10 0x102114c0
|
|
Mx3DPointFloat LegoRaceCar::g_unk0x10102af0 = Mx3DPointFloat(0.0f, 2.0f, 0.0f);
|
|
|
|
// FUNCTION: LEGO1 0x10012950
|
|
LegoRaceCar::LegoRaceCar()
|
|
{
|
|
m_userState = 0;
|
|
m_skelKick1Anim = 0;
|
|
m_skelKick2Anim = 0;
|
|
m_unk0x5c.Clear();
|
|
m_unk0x58 = 0;
|
|
m_kick1B = 0;
|
|
m_kick2B = 0;
|
|
NotificationManager()->Register(this);
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x10012c80
|
|
LegoRaceCar::~LegoRaceCar()
|
|
{
|
|
NotificationManager()->Unregister(this);
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x10012d90
|
|
MxLong LegoRaceCar::Notify(MxParam& p_param)
|
|
{
|
|
return LegoRaceMap::Notify(p_param);
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x10012e60
|
|
// FUNCTION: BETA10 0x100cb191
|
|
void LegoRaceCar::SetWorldSpeed(MxFloat p_worldSpeed)
|
|
{
|
|
if (!m_userNavFlag) {
|
|
if (!LegoCarRaceActor::m_unk0x0c) {
|
|
m_maxLinearVel = p_worldSpeed;
|
|
}
|
|
LegoAnimActor::SetWorldSpeed(p_worldSpeed);
|
|
}
|
|
else {
|
|
LegoEntity::SetWorldSpeed(p_worldSpeed);
|
|
}
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x10012ea0
|
|
// FUNCTION: BETA10 0x100cb220
|
|
void LegoRaceCar::SetMaxLinearVelocity(float p_maxLinearVelocity)
|
|
{
|
|
if (p_maxLinearVelocity < 0) {
|
|
LegoCarRaceActor::m_unk0x0c = 2;
|
|
m_maxLinearVel = 0;
|
|
SetWorldSpeed(0);
|
|
}
|
|
else {
|
|
m_maxLinearVel = p_maxLinearVelocity;
|
|
}
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x10012ef0
|
|
// FUNCTION: BETA10 0x100cb2aa
|
|
void LegoRaceCar::ParseAction(char* p_extra)
|
|
{
|
|
char buffer[256];
|
|
|
|
LegoAnimActor::ParseAction(p_extra);
|
|
LegoRaceMap::ParseAction(p_extra);
|
|
LegoRace* currentWorld = (LegoRace*) CurrentWorld();
|
|
|
|
if (KeyValueStringParse(buffer, g_strCOMP, p_extra) && currentWorld) {
|
|
currentWorld->VTable0x7c(this, atoi(buffer));
|
|
}
|
|
|
|
if (m_userNavFlag) {
|
|
MxS32 i;
|
|
|
|
for (i = 0; i < m_animMaps.size(); i++) {
|
|
// It appears that the implementation in BETA10 does not use this variable
|
|
LegoAnimActorStruct* animMap = m_animMaps[i];
|
|
|
|
if (animMap->m_unk0x00 == -1.0f) {
|
|
m_skelKick1Anim = animMap;
|
|
}
|
|
else if (animMap->m_unk0x00 == -2.0f) {
|
|
m_skelKick2Anim = animMap;
|
|
}
|
|
}
|
|
|
|
assert(m_skelKick1Anim && m_skelKick2Anim);
|
|
|
|
// STRING: LEGO1 0x100f0bc4
|
|
const char* edge0344 = "EDG03_44";
|
|
m_kick1B = currentWorld->FindPathBoundary(edge0344);
|
|
assert(m_kick1B);
|
|
|
|
// STRING: LEGO1 0x100f0bb8
|
|
const char* edge0354 = "EDG03_54";
|
|
m_kick2B = currentWorld->FindPathBoundary(edge0354);
|
|
assert(m_kick2B);
|
|
|
|
for (i = 0; i < sizeOfArray(g_skBMap); i++) {
|
|
assert(g_skBMap[i].m_name);
|
|
g_skBMap[i].m_b = currentWorld->FindPathBoundary(g_skBMap[i].m_name);
|
|
assert(g_skBMap[i].m_b);
|
|
}
|
|
}
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x10012ff0
|
|
// FUNCTION: BETA10 0x100cb60e
|
|
void LegoRaceCar::FUN_10012ff0(float p_param)
|
|
{
|
|
LegoAnimActorStruct* a; // called `a` in BETA10
|
|
float deltaTime;
|
|
|
|
if (m_userState == LEGORACECAR_KICK1) {
|
|
a = m_skelKick1Anim;
|
|
}
|
|
else {
|
|
assert(m_userState == LEGORACECAR_KICK2);
|
|
a = m_skelKick2Anim;
|
|
}
|
|
|
|
assert(a && a->GetAnimTreePtr() && a->GetAnimTreePtr()->GetCamAnim());
|
|
|
|
if (a->GetAnimTreePtr()) {
|
|
deltaTime = p_param - m_unk0x58;
|
|
|
|
if (a->GetDuration() <= deltaTime || deltaTime < 0.0) {
|
|
if (m_userState == LEGORACECAR_KICK1) {
|
|
LegoUnknown100db7f4** edges = m_kick1B->GetEdges();
|
|
m_destEdge = edges[2];
|
|
m_boundary = m_kick1B;
|
|
}
|
|
else {
|
|
LegoUnknown100db7f4** edges = m_kick1B->GetEdges();
|
|
m_destEdge = edges[1];
|
|
m_boundary = m_kick2B;
|
|
}
|
|
|
|
m_userState = LEGORACECAR_UNKNOWN_0;
|
|
}
|
|
else if (a->GetAnimTreePtr()->GetCamAnim()) {
|
|
MxMatrix transformationMatrix;
|
|
|
|
LegoWorld* r = CurrentWorld(); // called `r` in BETA10
|
|
assert(r);
|
|
|
|
transformationMatrix.SetIdentity();
|
|
|
|
// Possible bug in the original code: The first argument is not initialized
|
|
a->GetAnimTreePtr()->GetCamAnim()->FUN_1009f490(deltaTime, transformationMatrix);
|
|
|
|
if (r->GetCamera()) {
|
|
r->GetCamera()->FUN_100123e0(transformationMatrix, 0);
|
|
}
|
|
|
|
m_roi->FUN_100a58f0(transformationMatrix);
|
|
}
|
|
}
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x10013130
|
|
// FUNCTION: BETA10 0x100cce50
|
|
MxU32 LegoRaceCar::HandleSkeletonKicks(float p_param1)
|
|
{
|
|
const SkeletonKickPhase* current = g_skeletonKickPhases;
|
|
|
|
CarRace* r = (CarRace*) CurrentWorld(); // called `r` in BETA10
|
|
assert(r);
|
|
|
|
RaceSkel* s = r->GetSkeleton(); // called `s` in BETA10
|
|
assert(s);
|
|
|
|
float skeletonCurAnimPosition;
|
|
float skeletonCurAnimDuration;
|
|
|
|
s->GetCurrentAnimData(&skeletonCurAnimPosition, &skeletonCurAnimDuration);
|
|
|
|
float skeletonCurAnimPhase = skeletonCurAnimPosition / skeletonCurAnimDuration;
|
|
|
|
for (MxS32 i = 0; i < sizeOfArray(g_skeletonKickPhases); i++) {
|
|
if (m_boundary == current->m_edgeRef->m_b && current->m_lower <= skeletonCurAnimPhase &&
|
|
skeletonCurAnimPhase <= current->m_upper) {
|
|
m_userState = current->m_userState;
|
|
}
|
|
current = ¤t[1];
|
|
}
|
|
|
|
if (m_userState != LEGORACECAR_KICK1 && m_userState != LEGORACECAR_KICK2) {
|
|
MxTrace(
|
|
// STRING: BETA10 0x101f64c8
|
|
"Got kicked in boundary %s %d %g:%g %g\n",
|
|
m_boundary->GetName(),
|
|
skeletonCurAnimPosition,
|
|
skeletonCurAnimDuration,
|
|
skeletonCurAnimPhase
|
|
);
|
|
return FALSE;
|
|
}
|
|
|
|
m_unk0x58 = p_param1;
|
|
SoundManager()->GetCacheSoundManager()->Play(g_soundSkel3, NULL, FALSE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x100131f0
|
|
// FUNCTION: BETA10 0x100cb88a
|
|
void LegoRaceCar::VTable0x70(float p_float)
|
|
{
|
|
if (m_userNavFlag && (m_userState == LEGORACECAR_KICK1 || m_userState == LEGORACECAR_KICK2)) {
|
|
FUN_10012ff0(p_float);
|
|
return;
|
|
}
|
|
|
|
LegoCarRaceActor::VTable0x70(p_float);
|
|
|
|
if (m_userNavFlag && m_userState == LEGORACECAR_UNKNOWN_1) {
|
|
if (HandleSkeletonKicks(p_float)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (LegoCarRaceActor::m_unk0x0c == 1) {
|
|
FUN_1005d4b0();
|
|
|
|
if (!m_userNavFlag) {
|
|
FUN_10080590(p_float);
|
|
return;
|
|
}
|
|
|
|
float absoluteSpeed = abs(m_worldSpeed);
|
|
float maximumSpeed = NavController()->GetMaxLinearVel();
|
|
char buffer[200];
|
|
|
|
sprintf(buffer, "%g", absoluteSpeed / maximumSpeed);
|
|
|
|
VariableTable()->SetVariable(g_strSpeed, buffer);
|
|
|
|
if (m_sound) {
|
|
// pitches up the engine sound based on the velocity
|
|
if (absoluteSpeed > 0.83 * maximumSpeed) {
|
|
m_frequencyFactor = 1.9f;
|
|
}
|
|
else {
|
|
// this value seems to simulate RPM based on the gear
|
|
MxS32 gearRpmFactor = (MxS32) (6.0 * absoluteSpeed) % 100;
|
|
m_frequencyFactor = gearRpmFactor / 80.0 + 0.7;
|
|
}
|
|
}
|
|
|
|
if (absoluteSpeed != 0.0f) {
|
|
g_unk0x100f0b88 = p_float;
|
|
g_unk0x100f0b8c = FALSE;
|
|
}
|
|
|
|
if (p_float - g_unk0x100f0b88 > 5000.0f && !g_unk0x100f0b8c) {
|
|
SoundManager()->GetCacheSoundManager()->Play(g_srt001ra, NULL, 0);
|
|
g_unk0x100f0b8c = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x100133c0
|
|
// FUNCTION: BETA10 0x100cbb84
|
|
MxResult LegoRaceCar::VTable0x94(LegoPathActor* p_actor, MxBool p_bool)
|
|
{
|
|
// Note: Code duplication with LegoRaceActor::VTable0x94
|
|
if (!p_actor->GetUserNavFlag()) {
|
|
if (p_actor->GetState()) {
|
|
return FAILURE;
|
|
}
|
|
|
|
if (p_bool) {
|
|
LegoROI* roi = p_actor->GetROI(); // name verified by BETA10 0x100cbbf5
|
|
assert(roi);
|
|
MxMatrix matr;
|
|
matr = roi->GetLocal2World();
|
|
|
|
Vector3(matr[3]).Add(g_unk0x10102af0);
|
|
roi->FUN_100a58f0(matr);
|
|
|
|
p_actor->SetState(2);
|
|
}
|
|
|
|
if (m_userNavFlag) {
|
|
MxBool actorIsStuds = strcmpi(p_actor->GetROI()->GetName(), "studs") == 0;
|
|
MxBool actorIsRhoda = strcmpi(p_actor->GetROI()->GetName(), "rhoda") == 0;
|
|
MxLong time = Timer()->GetTime();
|
|
|
|
const char* soundKey = NULL;
|
|
MxLong timeElapsed = time - g_timeLastSoundPlayed;
|
|
|
|
if (timeElapsed > 3000) {
|
|
if (p_bool) {
|
|
if (actorIsStuds) {
|
|
soundKey = g_srtsl18to29[g_srtsl18to29Index++];
|
|
if (g_srtsl18to29Index >= sizeOfArray(g_srtsl18to29)) {
|
|
g_srtsl18to29Index = 0;
|
|
}
|
|
}
|
|
else if (actorIsRhoda) {
|
|
soundKey = g_emptySoundKeyList[g_emptySoundKeyListIndex++];
|
|
if (g_emptySoundKeyListIndex >= sizeOfArray(g_emptySoundKeyList)) {
|
|
g_emptySoundKeyListIndex = 0;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (actorIsStuds) {
|
|
soundKey = g_srtsl6to10[g_srtsl6to10Index++];
|
|
if (g_srtsl6to10Index >= sizeOfArray(g_srtsl6to10)) {
|
|
g_srtsl6to10Index = 0;
|
|
}
|
|
}
|
|
else if (actorIsRhoda) {
|
|
soundKey = g_srtrh[g_srtrhIndex++];
|
|
if (g_srtrhIndex >= sizeOfArray(g_srtrh)) {
|
|
g_srtrhIndex = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (soundKey) {
|
|
SoundManager()->GetCacheSoundManager()->Play(soundKey, NULL, FALSE);
|
|
g_timeLastSoundPlayed = g_unk0x100f3308 = time;
|
|
}
|
|
}
|
|
|
|
if (p_bool && m_worldSpeed != 0) {
|
|
return SUCCESS;
|
|
}
|
|
|
|
return FAILURE;
|
|
}
|
|
}
|
|
return SUCCESS;
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x10013600
|
|
// FUNCTION: BETA10 0x100cbe60
|
|
MxResult LegoRaceCar::VTable0x9c()
|
|
{
|
|
MxResult result;
|
|
|
|
if (m_userNavFlag) {
|
|
result = LegoCarRaceActor::VTable0x9c();
|
|
|
|
if (m_boundary) {
|
|
MxS32 bVar2 = 0;
|
|
|
|
for (MxS32 i = 0; i < sizeOfArray(g_skBMap); i++) {
|
|
assert(g_skBMap[i].m_b);
|
|
if (m_boundary == g_skBMap[i].m_b) {
|
|
bVar2 = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (m_userState == LEGORACECAR_UNKNOWN_1) {
|
|
if (!bVar2) {
|
|
m_userState = LEGORACECAR_UNKNOWN_0;
|
|
}
|
|
}
|
|
else {
|
|
m_userState = LEGORACECAR_UNKNOWN_1;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
result = LegoCarRaceActor::VTable0x9c();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x10014500
|
|
// FUNCTION: BETA10 0x100cd5e0
|
|
MxU32 LegoRaceCar::VTable0x6c(
|
|
LegoPathBoundary* p_boundary,
|
|
Vector3& p_v1,
|
|
Vector3& p_v2,
|
|
float p_f1,
|
|
float p_f2,
|
|
Vector3& p_v3
|
|
)
|
|
{
|
|
return LegoCarRaceActor::VTable0x6c(p_boundary, p_v1, p_v2, p_f1, p_f2, p_v3);
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x10014560
|
|
// FUNCTION: BETA10 0x100cd660
|
|
void LegoRaceCar::SwitchBoundary(LegoPathBoundary*& p_boundary, LegoUnknown100db7f4*& p_edge, float& p_unk0xe4)
|
|
{
|
|
LegoCarRaceActor::SwitchBoundary(p_boundary, p_edge, p_unk0xe4);
|
|
}
|