isle/LEGO1/lego/legoomni/src/race/legoracespecial.cpp
2026-01-05 12:29:42 -07:00

552 lines
12 KiB
C++

#include "legoracespecial.h"
#include "geom/legoorientededge.h"
#include "legonavcontroller.h"
#include "legopathboundary.h"
#include "legopathcontroller.h"
#include "misc.h"
#include "mxmisc.h"
#include "mxvariabletable.h"
#include <vec.h>
// File name verified by BETA10 0x100cedf7
DECOMP_SIZE_ASSERT(LegoCarRaceActor, 0x1a0)
DECOMP_SIZE_ASSERT(LegoJetskiRaceActor, 0x1a8)
// GLOBAL: LEGO1 0x100f0c68
// STRING: LEGO1 0x100f0c5c
// GLOBAL: BETA10 0x101f5b04
// STRING: BETA10 0x101f5b14
const char* g_raceState = "RACE_STATE";
// GLOBAL: LEGO1 0x100f7af0
// STRING: LEGO1 0x100f7ae4
const char* g_fuel = "FUEL";
// GLOBAL: LEGO1 0x100f0c6c
// STRING: LEGO1 0x100f0c54
// GLOBAL: BETA10 0x101f5b08
// STRING: BETA10 0x101f5b20
const char* g_racing = "RACING";
// GLOBAL: LEGO1 0x100f7aec
MxFloat LegoCarRaceActor::g_unk0x100f7aec = 8.0f;
// GLOBAL: LEGO1 0x100da044
// GLOBAL: BETA10 0x101be9fc
MxFloat g_unk0x100da044 = 8.0f;
// FUNCTION: LEGO1 0x10080350
// FUNCTION: BETA10 0x100cd6b0
LegoCarRaceActor::LegoCarRaceActor()
{
m_unk0x08 = 1.0f;
m_lastPathStruct = 0.0f;
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_unk0x140 = 0.1f;
m_unk0x150 = -5.0f;
m_unk0x148 = 1;
VariableTable()->SetVariable(g_fuel, "0.8");
}
// FUNCTION: LEGO1 0x10080590
// FUNCTION: BETA10 0x100cd8cf
void LegoCarRaceActor::FUN_10080590(float p_time)
{
MxFloat maxSpeed = m_maxLinearVel;
Mx3DPointFloat destEdgeUnknownVector;
Mx3DPointFloat worldDirection = Mx3DPointFloat(m_roi->GetWorldDirection());
m_destEdge->GetFaceNormal(*m_boundary, destEdgeUnknownVector);
if (abs(destEdgeUnknownVector.Dot(destEdgeUnknownVector.GetData(), worldDirection.GetData())) > 0.5) {
maxSpeed *= m_unk0x10;
}
MxS32 deltaUnk0x70;
LegoPathActor* userActor = UserActor();
if (userActor) {
// All known implementations of LegoPathActor->GetLastPathStruct() return LegoPathActor::m_lastPathStruct
deltaUnk0x70 = m_lastPathStruct - userActor->GetLastPathStruct();
}
else {
deltaUnk0x70 = 0;
}
if (deltaUnk0x70 > 1) {
if (deltaUnk0x70 > 3) {
deltaUnk0x70 = 3;
}
maxSpeed *= (m_unk0x18 * (--deltaUnk0x70) * -0.25f + 1.0f);
}
else if (deltaUnk0x70 < -1) {
maxSpeed *= 1.3;
}
MxFloat deltaSpeed = maxSpeed - m_worldSpeed;
MxFloat changeInSpeed = (p_time - m_unk0x1c) * m_unk0x14;
m_unk0x1c = p_time;
if (deltaSpeed < 0.0f) {
changeInSpeed = -changeInSpeed;
}
MxFloat newWorldSpeed = changeInSpeed + m_worldSpeed;
if (newWorldSpeed > maxSpeed) {
newWorldSpeed = maxSpeed;
}
SetWorldSpeed(newWorldSpeed);
}
// FUNCTION: LEGO1 0x10080740
// FUNCTION: BETA10 0x100cece0
MxS32 LegoCarRaceActor::VTable0x1c(LegoPathBoundary* p_boundary, LegoEdge* p_edge)
{
Mx3DPointFloat pointUnknown;
Mx3DPointFloat destEdgeUnknownVector;
Mx3DPointFloat crossProduct;
if (m_actorState == c_one) {
m_boundary = NULL;
// Not sure where the upper bound of 11 comes from, the underlying array has a size of 16
for (MxS32 i = 0; i < 11; i += 2) {
if (LegoPathController::GetControlEdgeA(i + 1) == m_destEdge) {
m_boundary = LegoPathController::GetControlBoundaryA(i + 1);
break;
}
}
assert(m_boundary);
m_actorState = c_initial;
m_unk0x7c = 0;
if (m_userNavFlag) {
NavController()->SetLinearVel(m_worldSpeed);
return 0;
}
else {
return 1;
}
}
else {
for (MxS32 i = 0; i < 11; i += 2) {
if (LegoPathController::GetControlEdgeA(i) == p_edge) {
m_actorState = c_one;
if (m_worldSpeed < g_unk0x100f7aec) {
m_worldSpeed = g_unk0x100f7aec;
}
m_destEdge = LegoPathController::GetControlEdgeA(i + 1);
m_boundary = LegoPathController::GetControlBoundaryA(i + 1);
break;
}
}
if (m_actorState == c_one) {
if (m_userNavFlag) {
m_unk0xe4 = 0.5f;
}
// variable names verified by BETA10
Vector3* v1 = m_destEdge->CCWVertex(*m_boundary);
Vector3* v2 = m_destEdge->CWVertex(*m_boundary);
assert(v1 && v2);
LERP3(pointUnknown, *v1, *v2, m_unk0xe4);
m_destEdge->GetFaceNormal(*m_boundary, destEdgeUnknownVector);
crossProduct.EqualsCross(*m_boundary->GetUp(), destEdgeUnknownVector);
crossProduct.Unitize();
Mx3DPointFloat worldDirection(Vector3(m_roi->GetWorldDirection()));
if (!m_userNavFlag) {
worldDirection *= -1.0f;
}
worldDirection *= 5.0f;
crossProduct *= 5.0f;
MxResult callResult =
VTable0x80(Vector3(m_roi->GetWorldPosition()), worldDirection, pointUnknown, crossProduct);
if (callResult) {
m_unk0x7c = 0;
return 0;
}
else {
m_unk0x7c = 0;
#ifdef BETA10
assert(0);
#endif
return 0; // BETA10 returns -1 here
}
}
else {
// This `for` loop does not exist in BETA10
for (MxS32 i = 0; i < 10; i++) {
if (LegoPathController::GetControlEdgeB(i) == p_edge &&
LegoPathController::GetControlBoundaryB(i) == m_boundary) {
return 0;
}
}
return 1;
}
}
}
// FUNCTION: LEGO1 0x10080b40
// FUNCTION: BETA10 0x100cdb3c
void LegoCarRaceActor::SwitchBoundary(LegoPathBoundary*& p_boundary, LegoOrientedEdge*& p_edge, float& p_unk0xe4)
{
LegoPathActor::SwitchBoundary(m_boundary, m_destEdge, m_unk0xe4);
}
// FUNCTION: LEGO1 0x10080b70
// FUNCTION: BETA10 0x100cdbae
void LegoCarRaceActor::Animate(float p_time)
{
// m_animState is not an MxBool, there are places where it is set to 2 or higher
if (m_animState == 0) {
const char* value = VariableTable()->GetVariable(g_raceState);
if (strcmpi(value, g_racing) == 0) {
m_animState = 1;
m_lastTime = p_time - 1.0f;
m_unk0x1c = p_time;
}
}
if (m_animState == 1) {
LegoAnimActor::Animate(p_time);
}
}
// FUNCTION: LEGO1 0x10080be0
// FUNCTION: BETA10 0x100cdc54
MxResult LegoCarRaceActor::VTable0x9c()
{
LegoOrientedEdge* d = m_destEdge;
if (VTable0x1c(m_boundary, m_destEdge)) {
LegoPathBoundary* b = m_boundary;
SwitchBoundary(m_boundary, m_destEdge, m_unk0xe4);
assert(m_boundary && m_destEdge);
// variable names verified by BETA10
Vector3* v1 = m_destEdge->CWVertex(*m_boundary);
Vector3* v2 = m_destEdge->CCWVertex(*m_boundary);
assert(v1 && v2);
Mx3DPointFloat point1;
LERP3(point1, *v1, *v2, m_unk0xe4);
Mx3DPointFloat point2;
Mx3DPointFloat point3;
Mx3DPointFloat point4;
Mx3DPointFloat point5;
d->GetFaceNormal(*b, point2);
m_destEdge->GetFaceNormal(*m_boundary, point3);
point4.EqualsCross(point2, *m_boundary->GetUp());
point5.EqualsCross(*m_boundary->GetUp(), point3);
point4.Unitize();
point5.Unitize();
point4 *= 5.0f;
point5 *= 5.0f;
MxResult res = VTable0x80(m_roi->GetWorldPosition(), point4, point1, point5);
#ifdef BETA10
if (res) {
assert(0);
return -1;
}
#endif
m_unk0x7c = 0;
}
return SUCCESS;
}
// FUNCTION: LEGO1 0x10080ef0
// FUNCTION: BETA10 0x100a8990
LegoJetskiRaceActor::LegoJetskiRaceActor()
{
m_unk0x10 = 0.95f;
m_unk0x14 = 0.04f;
m_unk0x18 = 0.5f;
m_unk0x150 = 1.5f;
}
// FUNCTION: LEGO1 0x10081120
// FUNCTION: BETA10 0x100ce19f
MxS32 LegoJetskiRaceActor::VTable0x1c(LegoPathBoundary* p_boundary, LegoEdge* p_edge)
{
// These are almost certainly not the correct names, but they produce the correct BETA10 stack
Mx3DPointFloat a;
Mx3DPointFloat bbb;
Mx3DPointFloat c;
// These names are verified by an assertion below
Vector3* v1 = NULL;
Vector3* v2 = NULL;
if (m_actorState == c_one) {
if (m_destEdge == LegoPathController::GetControlEdgeA(13)) {
m_boundary = (LegoPathBoundary*) m_destEdge->OtherFace(LegoPathController::GetControlBoundaryA(13));
}
else if (m_destEdge == LegoPathController::GetControlEdgeA(15)) {
m_boundary = (LegoPathBoundary*) m_destEdge->OtherFace(LegoPathController::GetControlBoundaryA(15));
}
m_actorState = c_initial;
m_unk0x7c = 0;
if (m_userNavFlag) {
NavController()->SetLinearVel(m_worldSpeed);
return 0;
}
else {
return 1;
}
}
else {
if (p_edge == LegoPathController::GetControlEdgeA(12)) {
m_actorState = c_one;
if (m_worldSpeed < g_unk0x100da044) {
m_worldSpeed = g_unk0x100da044;
}
m_destEdge = LegoPathController::GetControlEdgeA(13);
m_boundary = LegoPathController::GetControlBoundaryA(13);
}
else if (p_edge == LegoPathController::GetControlEdgeA(14)) {
m_actorState = c_one;
if (m_worldSpeed < g_unk0x100da044) {
m_worldSpeed = g_unk0x100da044;
}
m_destEdge = LegoPathController::GetControlEdgeA(15);
m_boundary = LegoPathController::GetControlBoundaryA(15);
}
if (m_actorState == c_one) {
if (m_userNavFlag) {
m_unk0xe4 = 0.5f;
}
v1 = m_destEdge->CCWVertex(*m_boundary);
v2 = m_destEdge->CWVertex(*m_boundary);
assert(v1 && v2);
LERP3(a, *v1, *v2, m_unk0xe4);
m_destEdge->GetFaceNormal(*m_boundary, bbb);
c.EqualsCross(bbb, *m_boundary->GetUp());
c.Unitize();
Mx3DPointFloat worldDirection(m_roi->GetWorldDirection());
if (!m_userNavFlag) {
worldDirection *= -1.0f;
}
if (VTable0x80(m_roi->GetWorldPosition(), worldDirection, a, c)) {
#ifndef BETA10
m_unk0x7c = 0;
return 0;
#else
assert(0);
return -1;
#endif
}
m_unk0x7c = 0;
return 0;
}
else {
return 1;
}
}
}
// FUNCTION: LEGO1 0x10081550
void LegoJetskiRaceActor::Animate(float p_time)
{
if (m_animState == 0) {
const LegoChar* raceState = VariableTable()->GetVariable(g_raceState);
if (!stricmp(raceState, g_racing)) {
m_animState = 1;
m_lastTime = p_time - 1.0f;
m_unk0x1c = p_time;
}
else if (!m_userNavFlag) {
LegoAnimActor::Animate(m_lastTime + 1.0f);
}
}
if (m_animState == 1) {
LegoAnimActor::Animate(p_time);
}
}
// FUNCTION: LEGO1 0x10081840
// FUNCTION: BETA10 0x100cf680
inline MxU32 LegoCarRaceActor::VTable0x6c(
LegoPathBoundary* p_boundary,
Vector3& p_v1,
Vector3& p_v2,
float p_f1,
float p_f2,
Vector3& p_v3
)
{
// STRING: LEGO1 0x100f7af4
const char* str_rcdor = "rcdor";
LegoAnimPresenterSet& presenters = p_boundary->GetPresenters();
for (LegoAnimPresenterSet::iterator itap = presenters.begin(); itap != presenters.end(); itap++) {
if ((*itap)->VTable0x94(p_v1, p_v2, p_f1, p_f2, p_v3)) {
return 1;
}
}
LegoPathActorSet& plpas = p_boundary->GetActors();
LegoPathActorSet lpas(plpas);
for (LegoPathActorSet::iterator itpa = lpas.begin(); itpa != lpas.end(); itpa++) {
if (plpas.end() != plpas.find(*itpa)) {
LegoPathActor* actor = *itpa;
if (actor != this) {
LegoROI* roi = actor->GetROI();
if (roi != NULL && (roi->GetVisibility() || actor->GetCameraFlag())) {
if (strncmp(roi->GetName(), str_rcdor, 5) == 0) {
const CompoundObject* co = roi->GetComp(); // name verified by BETA10 0x100cf8ba
if (co) {
assert(co->size() == 2);
LegoROI* firstROI = (LegoROI*) co->front();
if (firstROI
->Intersect(p_v1, p_v2, p_f1, p_f2, p_v3, m_collideBox && actor->GetCollideBox())) {
HitActor(actor, TRUE);
if (actor->HitActor(this, FALSE) < 0) {
return 0;
}
else {
return 2;
}
}
LegoROI* lastROI = (LegoROI*) co->back();
if (lastROI
->Intersect(p_v1, p_v2, p_f1, p_f2, p_v3, m_collideBox && actor->GetCollideBox())) {
HitActor(actor, TRUE);
if (actor->HitActor(this, FALSE) < 0) {
return 0;
}
else {
return 2;
}
}
}
}
else {
if (roi->Intersect(p_v1, p_v2, p_f1, p_f2, p_v3, m_collideBox && actor->GetCollideBox())) {
HitActor(actor, TRUE);
if (actor->HitActor(this, FALSE) < 0) {
return 0;
}
else {
return 2;
}
}
}
}
}
}
}
return 0;
}
// FUNCTION: LEGO1 0x10081fd0
inline MxU32 LegoJetskiRaceActor::VTable0x6c(
LegoPathBoundary* p_boundary,
Vector3& p_v1,
Vector3& p_v2,
float p_f1,
float p_f2,
Vector3& p_v3
)
{
LegoAnimPresenterSet& presenters = p_boundary->GetPresenters();
for (LegoAnimPresenterSet::iterator itap = presenters.begin(); itap != presenters.end(); itap++) {
if ((*itap)->VTable0x94(p_v1, p_v2, p_f1, p_f2, p_v3)) {
return 1;
}
}
LegoPathActorSet& plpas = p_boundary->GetActors();
LegoPathActorSet lpas(plpas);
for (LegoPathActorSet::iterator itpa = lpas.begin(); itpa != lpas.end(); itpa++) {
if (plpas.find(*itpa) != plpas.end()) {
LegoPathActor* actor = *itpa;
if (this != actor) {
LegoROI* roi = actor->GetROI();
if (roi != NULL && (roi->GetVisibility() || actor->GetCameraFlag())) {
if (roi->Intersect(p_v1, p_v2, p_f1, p_f2, p_v3, m_collideBox && actor->GetCollideBox())) {
HitActor(actor, TRUE);
if (actor->HitActor(this, FALSE) < 0) {
return 0;
}
else {
return 2;
}
}
}
}
}
}
return 0;
}