mirror of
https://github.com/isledecomp/isle.git
synced 2026-02-27 22:47:37 +00:00
* BETA matches for LegoEdge and related * Some progress on CheckIntersections * Cleanup * Fix duplicate offset --------- Co-authored-by: jonschz <jonschz@users.noreply.github.com>
886 lines
20 KiB
C++
886 lines
20 KiB
C++
#include "legopathactor.h"
|
|
|
|
#include "define.h"
|
|
#include "geom/legoorientededge.h"
|
|
#include "legocachesoundmanager.h"
|
|
#include "legocameracontroller.h"
|
|
#include "legonamedplane.h"
|
|
#include "legonavcontroller.h"
|
|
#include "legopathboundary.h"
|
|
#include "legopathedgecontainer.h"
|
|
#include "legosoundmanager.h"
|
|
#include "legoworld.h"
|
|
#include "misc.h"
|
|
#include "mxmisc.h"
|
|
#include "mxtimer.h"
|
|
#include "mxutilities.h"
|
|
#include "mxvariabletable.h"
|
|
|
|
#include <mxdebug.h>
|
|
#include <vec.h>
|
|
|
|
DECOMP_SIZE_ASSERT(LegoPathActor, 0x154)
|
|
DECOMP_SIZE_ASSERT(LegoPathEdgeContainer, 0x3c)
|
|
|
|
#ifndef M_PI
|
|
#define M_PI 3.1416
|
|
#endif
|
|
#ifdef DTOR
|
|
#undef DTOR
|
|
#endif
|
|
#define DTOR(angle) ((angle) * M_PI / 180.)
|
|
|
|
// GLOBAL: LEGO1 0x100f3304
|
|
// STRING: LEGO1 0x100f32f4
|
|
const char* g_strHIT_WALL_SOUND = "HIT_WALL_SOUND";
|
|
|
|
// GLOBAL: LEGO1 0x100f3308
|
|
// GLOBAL: BETA10 0x101f1e1c
|
|
MxLong g_timeLastHitSoundPlayed = 0;
|
|
|
|
// FUNCTION: LEGO1 0x1002d700
|
|
// FUNCTION: BETA10 0x100ae6e0
|
|
LegoPathActor::LegoPathActor()
|
|
{
|
|
m_boundary = NULL;
|
|
m_actorTime = 0;
|
|
m_transformTime = 0;
|
|
m_traveledDistance = 0;
|
|
m_userNavFlag = FALSE;
|
|
m_actorState = c_initial;
|
|
m_grec = NULL;
|
|
m_pathController = NULL;
|
|
m_collideBox = FALSE;
|
|
m_canRotate = 0;
|
|
m_lastRotationAngle = 0;
|
|
m_wallHitDirectionFactor = 0.0099999999f;
|
|
m_wallHitDampening = 0.8f;
|
|
m_linearRotationRatio = 2.0f;
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x1002d820
|
|
// FUNCTION: BETA10 0x100ae80e
|
|
LegoPathActor::~LegoPathActor()
|
|
{
|
|
if (m_grec) {
|
|
delete m_grec;
|
|
}
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x1002d8d0
|
|
// FUNCTION: BETA10 0x100ae8cd
|
|
MxResult LegoPathActor::SetSpline(
|
|
const Vector3& p_start,
|
|
Vector3& p_tangentAtStart,
|
|
Vector3& p_end,
|
|
Vector3& p_tangentAtEnd
|
|
)
|
|
{
|
|
Mx3DPointFloat length, tangentAtStart, tangentAtEnd;
|
|
|
|
length = p_end;
|
|
length -= p_start;
|
|
m_BADuration = length.LenSquared();
|
|
|
|
if (m_BADuration > 0.0f) {
|
|
m_BADuration = sqrtf(m_BADuration);
|
|
tangentAtStart = p_tangentAtStart;
|
|
tangentAtEnd = p_tangentAtEnd;
|
|
m_spline.SetSpline(p_start, tangentAtStart, p_end, tangentAtEnd);
|
|
m_BADuration /= 0.001;
|
|
return SUCCESS;
|
|
}
|
|
else {
|
|
MxTrace("Warning: m_BADuration = %g, roi = %s\n", m_BADuration, m_roi->GetName());
|
|
return FAILURE;
|
|
}
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x1002d9c0
|
|
// FUNCTION: BETA10 0x100ae9da
|
|
MxResult LegoPathActor::SetTransformAndDestinationFromEdge(
|
|
LegoPathBoundary* p_boundary,
|
|
float p_time,
|
|
LegoEdge& p_srcEdge,
|
|
float p_srcScale,
|
|
LegoOrientedEdge& p_destEdge,
|
|
float p_destScale
|
|
)
|
|
{
|
|
Vector3* v1 = p_srcEdge.CWVertex(*p_boundary);
|
|
Vector3* v2 = p_srcEdge.CCWVertex(*p_boundary);
|
|
Vector3* v3 = p_destEdge.CWVertex(*p_boundary);
|
|
Vector3* v4 = p_destEdge.CCWVertex(*p_boundary);
|
|
|
|
Mx3DPointFloat start, end, destNormal, startDirection, endDirection;
|
|
|
|
start = *v2;
|
|
start -= *v1;
|
|
start *= p_srcScale;
|
|
start += *v1;
|
|
|
|
end = *v4;
|
|
end -= *v3;
|
|
end *= p_destScale;
|
|
end += *v3;
|
|
|
|
m_boundary = p_boundary;
|
|
m_destEdge = &p_destEdge;
|
|
m_destScale = p_destScale;
|
|
m_traveledDistance = 0;
|
|
m_transformTime = p_time;
|
|
m_actorTime = p_time;
|
|
p_destEdge.GetFaceNormal(*p_boundary, destNormal);
|
|
|
|
startDirection = end;
|
|
startDirection -= start;
|
|
startDirection.Unitize();
|
|
|
|
MxMatrix matrix;
|
|
Vector3 pos(matrix[3]);
|
|
Vector3 dir(matrix[2]);
|
|
Vector3 up(matrix[1]);
|
|
Vector3 right(matrix[0]);
|
|
|
|
matrix.SetIdentity();
|
|
pos = start;
|
|
dir = startDirection;
|
|
up = *m_boundary->GetUp();
|
|
|
|
if (!m_cameraFlag || !m_userNavFlag) {
|
|
dir *= -1.0f;
|
|
}
|
|
|
|
right.EqualsCross(up, dir);
|
|
m_roi->UpdateTransformationRelativeToParent(matrix);
|
|
|
|
if (!m_cameraFlag || !m_userNavFlag) {
|
|
endDirection.EqualsCross(*p_boundary->GetUp(), destNormal);
|
|
endDirection.Unitize();
|
|
|
|
if (SetSpline(start, startDirection, end, endDirection) == SUCCESS) {
|
|
m_boundary->AddActor(this);
|
|
}
|
|
else {
|
|
return FAILURE;
|
|
}
|
|
}
|
|
else {
|
|
m_boundary->AddActor(this);
|
|
TransformPointOfView();
|
|
}
|
|
|
|
m_local2World = m_roi->GetLocal2World();
|
|
return SUCCESS;
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x1002de10
|
|
// FUNCTION: BETA10 0x100aee61
|
|
MxResult LegoPathActor::SetTransformAndDestinationFromPoints(
|
|
LegoPathBoundary* p_boundary,
|
|
float p_time,
|
|
Vector3& p_start,
|
|
Vector3& p_direction,
|
|
LegoOrientedEdge* p_destEdge,
|
|
float p_destScale
|
|
)
|
|
{
|
|
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;
|
|
Vector3 pos(matrix[3]);
|
|
Vector3 dir(matrix[2]);
|
|
Vector3 up(matrix[1]);
|
|
Vector3 right(matrix[0]);
|
|
|
|
matrix.SetIdentity();
|
|
pos = p_start;
|
|
dir = p_direction;
|
|
up = *m_boundary->GetUp();
|
|
|
|
if (!m_cameraFlag || !m_userNavFlag) {
|
|
dir *= -1.0f;
|
|
}
|
|
|
|
right.EqualsCross(up, dir);
|
|
m_roi->UpdateTransformationRelativeToParent(matrix);
|
|
|
|
if (m_cameraFlag && m_userNavFlag) {
|
|
m_boundary->AddActor(this);
|
|
TransformPointOfView();
|
|
}
|
|
else {
|
|
endDirection.EqualsCross(*p_boundary->GetUp(), destNormal);
|
|
endDirection.Unitize();
|
|
|
|
if (SetSpline(p_start, p_direction, end, endDirection) != SUCCESS) {
|
|
MxTrace("Warning: m_BADuration = %g, roi = %s\n", m_BADuration, m_roi->GetName());
|
|
return FAILURE;
|
|
}
|
|
|
|
m_boundary->AddActor(this);
|
|
}
|
|
|
|
m_local2World = m_roi->GetLocal2World();
|
|
return SUCCESS;
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x1002e100
|
|
// FUNCTION: BETA10 0x100b0520
|
|
MxS32 LegoPathActor::CalculateTransform(float p_time, Matrix4& p_transform)
|
|
{
|
|
if (m_userNavFlag && m_actorState == c_initial) {
|
|
m_transformTime = p_time;
|
|
|
|
Mx3DPointFloat newDir, newPos, intersectionPoint, pos, dir;
|
|
dir = Vector3(m_roi->GetWorldDirection());
|
|
pos = Vector3(m_roi->GetWorldPosition());
|
|
|
|
LegoNavController* nav = NavController();
|
|
assert(nav);
|
|
|
|
m_worldSpeed = nav->GetLinearVel();
|
|
|
|
if (nav->CalculateNewPosDir(pos, dir, newPos, newDir, m_boundary->GetUp())) {
|
|
Mx3DPointFloat newPosCopy;
|
|
newPosCopy = newPos;
|
|
MxS32 result = 0;
|
|
|
|
m_finishedTravel =
|
|
m_boundary
|
|
->Intersect(m_roi->GetWorldBoundingSphere().Radius(), pos, newPos, intersectionPoint, m_destEdge);
|
|
if (m_finishedTravel == -1) {
|
|
MxTrace("Intersect returned -1\n");
|
|
return -1;
|
|
}
|
|
else {
|
|
if (m_finishedTravel != FALSE) {
|
|
newPos = intersectionPoint;
|
|
}
|
|
}
|
|
|
|
result = CheckIntersections(pos, newPos, intersectionPoint);
|
|
|
|
if (result > 0) {
|
|
newPos = pos;
|
|
m_finishedTravel = FALSE;
|
|
result = 0;
|
|
}
|
|
else {
|
|
m_boundary->CheckAndCallPathTriggers(pos, newPos, this);
|
|
}
|
|
|
|
LegoPathBoundary* oldBoundary = m_boundary;
|
|
|
|
if (m_finishedTravel != FALSE) {
|
|
CalculateSpline();
|
|
|
|
if (m_boundary == oldBoundary) {
|
|
MxLong time = Timer()->GetTime();
|
|
|
|
if (time - g_timeLastHitSoundPlayed > 1000) {
|
|
g_timeLastHitSoundPlayed = time;
|
|
const char* var = VariableTable()->GetVariable(g_strHIT_WALL_SOUND);
|
|
|
|
if (var && var[0] != 0) {
|
|
SoundManager()->GetCacheSoundManager()->Play(var, NULL, FALSE);
|
|
}
|
|
}
|
|
|
|
m_worldSpeed *= m_wallHitDampening;
|
|
nav->SetLinearVel(m_worldSpeed);
|
|
Mx3DPointFloat newPosDelta(newPos);
|
|
newPosDelta -= newPosCopy;
|
|
|
|
if (newPosDelta.Unitize() == 0) {
|
|
float f = sqrt(newDir.LenSquared()) * m_wallHitDirectionFactor;
|
|
newPosDelta *= f;
|
|
newDir += newPosDelta;
|
|
}
|
|
}
|
|
}
|
|
|
|
p_transform.SetIdentity();
|
|
|
|
Vector3 right(p_transform[0]);
|
|
Vector3 up(p_transform[1]);
|
|
Vector3 dir(p_transform[2]);
|
|
Vector3 pos(p_transform[3]);
|
|
|
|
dir = newDir;
|
|
up = *m_boundary->GetUp();
|
|
right.EqualsCross(up, dir);
|
|
|
|
MxS32 res = right.Unitize();
|
|
assert(res == 0);
|
|
|
|
dir.EqualsCross(right, up);
|
|
pos = newPos;
|
|
return result;
|
|
}
|
|
else {
|
|
return -1;
|
|
}
|
|
}
|
|
else if (p_time >= 0 && m_worldSpeed > 0) {
|
|
float endTime = (m_BADuration - m_traveledDistance) / m_worldSpeed + m_transformTime;
|
|
|
|
if (endTime < p_time) {
|
|
m_traveledDistance = m_BADuration;
|
|
m_finishedTravel = TRUE;
|
|
}
|
|
else {
|
|
endTime = p_time;
|
|
m_traveledDistance += (endTime - m_transformTime) * m_worldSpeed;
|
|
m_finishedTravel = FALSE;
|
|
}
|
|
|
|
m_actorTime += (endTime - m_transformTime) * m_worldSpeed;
|
|
m_transformTime = endTime;
|
|
p_transform.SetIdentity();
|
|
|
|
LegoResult r;
|
|
if (m_userNavFlag) {
|
|
r = m_spline.Evaluate(m_traveledDistance / m_BADuration, p_transform, *m_boundary->GetUp(), FALSE);
|
|
}
|
|
else {
|
|
r = m_spline.Evaluate(m_traveledDistance / m_BADuration, p_transform, *m_boundary->GetUp(), TRUE);
|
|
}
|
|
|
|
assert(r == 0); // SUCCESS
|
|
|
|
Vector3 end(p_transform[3]);
|
|
Vector3 origin(m_local2World[3]);
|
|
Mx3DPointFloat p1;
|
|
|
|
if (CheckIntersections(origin, end, p1) > 0) {
|
|
m_transformTime = p_time;
|
|
return 1;
|
|
}
|
|
else {
|
|
m_boundary->CheckAndCallPathTriggers(origin, end, this);
|
|
origin = end;
|
|
}
|
|
|
|
if (m_finishedTravel != FALSE) {
|
|
CalculateSpline();
|
|
}
|
|
}
|
|
else {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x1002e740
|
|
// FUNCTION: BETA10 0x100b0f70
|
|
void LegoPathActor::ApplyTransform(Matrix4& p_transform)
|
|
{
|
|
if (m_userNavFlag) {
|
|
m_roi->WrappedSetLocal2WorldWithWorldDataUpdate(p_transform);
|
|
TransformPointOfView();
|
|
}
|
|
else {
|
|
m_roi->WrappedSetLocal2WorldWithWorldDataUpdate(p_transform);
|
|
m_roi->WrappedUpdateWorldData();
|
|
|
|
if (m_cameraFlag) {
|
|
TransformPointOfView();
|
|
}
|
|
}
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x1002e790
|
|
// FUNCTION: BETA10 0x100af208
|
|
void LegoPathActor::Animate(float p_time)
|
|
{
|
|
MxMatrix transform;
|
|
MxU32 applyTransform = FALSE;
|
|
|
|
while (m_transformTime < p_time) {
|
|
if (m_actorState != c_initial && !StepState(p_time, transform)) {
|
|
return;
|
|
}
|
|
|
|
if (CalculateTransform(p_time, transform) != 0) {
|
|
break;
|
|
}
|
|
|
|
m_local2World = transform;
|
|
applyTransform = TRUE;
|
|
|
|
if (m_finishedTravel != FALSE) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (m_userNavFlag && m_canRotate) {
|
|
LegoNavController* nav = NavController();
|
|
float vel =
|
|
(nav->GetLinearVel() > 0)
|
|
? -(nav->GetRotationalVel() / (nav->GetMaxLinearVel() * m_linearRotationRatio) * nav->GetLinearVel())
|
|
: 0;
|
|
|
|
if ((MxS32) vel != m_lastRotationAngle) {
|
|
m_lastRotationAngle = vel;
|
|
LegoWorld* world = CurrentWorld();
|
|
|
|
if (world) {
|
|
world->GetCameraController()->RotateZ(DTOR(m_lastRotationAngle));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (applyTransform) {
|
|
ApplyTransform(transform);
|
|
}
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x1002e8b0
|
|
// FUNCTION: BETA10 0x100af2f7
|
|
void LegoPathActor::SwitchBoundary(LegoPathBoundary*& p_boundary, LegoOrientedEdge*& p_edge, float& p_scale)
|
|
{
|
|
assert(m_boundary);
|
|
m_boundary->SwitchBoundary(this, p_boundary, p_edge, p_scale);
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x1002e8d0
|
|
// FUNCTION: BETA10 0x100b1010
|
|
MxU32 LegoPathActor::CheckPresenterAndActorIntersections(
|
|
LegoPathBoundary* p_boundary,
|
|
Vector3& p_rayOrigin,
|
|
Vector3& p_rayDirection,
|
|
float p_rayLength,
|
|
float p_radius,
|
|
Vector3& p_intersectionPoint
|
|
)
|
|
{
|
|
LegoAnimPresenterSet& presenters = p_boundary->GetPresenters();
|
|
|
|
for (LegoAnimPresenterSet::iterator itap = presenters.begin(); itap != presenters.end(); itap++) {
|
|
if ((*itap)->Intersect(p_rayOrigin, p_rayDirection, p_rayLength, p_radius, p_intersectionPoint)) {
|
|
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 && !(actor->GetActorState() & LegoPathActor::c_noCollide)) {
|
|
LegoROI* roi = actor->GetROI();
|
|
|
|
if (roi != NULL && (roi->GetVisibility() || actor->GetCameraFlag())) {
|
|
if (roi->Intersect(
|
|
p_rayOrigin,
|
|
p_rayDirection,
|
|
p_rayLength,
|
|
p_radius,
|
|
p_intersectionPoint,
|
|
m_collideBox && actor->m_collideBox
|
|
)) {
|
|
HitActor(actor, TRUE);
|
|
actor->HitActor(this, FALSE);
|
|
return 2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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,
|
|
Vector3& p_rayOrigin,
|
|
Vector3& p_rayDirection,
|
|
float p_rayLength,
|
|
float p_radius,
|
|
Vector3& p_intersectionPoint,
|
|
MxS32 p_depth
|
|
)
|
|
{
|
|
MxU32 result = CheckPresenterAndActorIntersections(
|
|
p_boundary,
|
|
p_rayOrigin,
|
|
p_rayDirection,
|
|
p_rayLength,
|
|
p_radius,
|
|
p_intersectionPoint
|
|
);
|
|
|
|
if (result != 0) {
|
|
return result;
|
|
}
|
|
|
|
p_checkedBoundaries.push_back(p_boundary);
|
|
|
|
if (p_depth >= 2) {
|
|
return 0;
|
|
}
|
|
|
|
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,
|
|
boundary,
|
|
p_rayOrigin,
|
|
p_rayDirection,
|
|
p_rayLength,
|
|
p_radius,
|
|
p_intersectionPoint,
|
|
p_depth + 1
|
|
);
|
|
|
|
if (result != 0) {
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x1002f020
|
|
// FUNCTION: BETA10 0x100af54a
|
|
void LegoPathActor::ParseAction(char* p_extra)
|
|
{
|
|
LegoActor::ParseAction(p_extra);
|
|
|
|
char value[256];
|
|
value[0] = '\0';
|
|
|
|
if (KeyValueStringParse(value, g_strPERMIT_NAVIGATE, p_extra)) {
|
|
SetUserNavFlag(TRUE);
|
|
NavController()->ResetMaxLinearVel(m_worldSpeed);
|
|
SetUserActor(this);
|
|
}
|
|
|
|
char* token;
|
|
if (KeyValueStringParse(value, g_strPATH, p_extra)) {
|
|
char name[12];
|
|
|
|
token = strtok(value, g_parseExtraTokens);
|
|
strcpy(name, token);
|
|
|
|
token = strtok(NULL, g_parseExtraTokens);
|
|
MxS32 src = atoi(token);
|
|
|
|
token = strtok(NULL, g_parseExtraTokens);
|
|
float srcScale = atof(token);
|
|
|
|
token = strtok(NULL, g_parseExtraTokens);
|
|
MxS32 dest = atoi(token);
|
|
|
|
token = strtok(NULL, g_parseExtraTokens);
|
|
float destScale = atof(token);
|
|
|
|
LegoWorld* world = CurrentWorld();
|
|
if (world != NULL) {
|
|
world->PlaceActor(this, name, src, srcScale, dest, destScale);
|
|
}
|
|
}
|
|
|
|
if (KeyValueStringParse(value, g_strCOLLIDEBOX, p_extra)) {
|
|
token = strtok(value, g_parseExtraTokens);
|
|
m_collideBox = atoi(token);
|
|
}
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x1002f1b0
|
|
// FUNCTION: BETA10 0x100af899
|
|
MxResult LegoPathActor::CalculateSpline()
|
|
{
|
|
Mx3DPointFloat targetPosition;
|
|
Mx3DPointFloat endDirection;
|
|
MxU32 noPath1 = TRUE;
|
|
MxU32 noPath2 = TRUE;
|
|
|
|
if (m_grec != NULL) {
|
|
if (m_grec->HasPath()) {
|
|
noPath1 = FALSE;
|
|
noPath2 = FALSE;
|
|
|
|
Mx3DPointFloat vec;
|
|
switch (m_pathController
|
|
->GetNextPathEdge(*m_grec, targetPosition, endDirection, m_destScale, m_destEdge, m_boundary)) {
|
|
case 0:
|
|
case 1:
|
|
break;
|
|
default:
|
|
assert(0);
|
|
return FAILURE;
|
|
}
|
|
}
|
|
else {
|
|
delete m_grec;
|
|
m_grec = NULL;
|
|
}
|
|
}
|
|
|
|
if (noPath1 != FALSE) {
|
|
SwitchBoundary(m_boundary, m_destEdge, m_destScale);
|
|
}
|
|
|
|
if (noPath2 != FALSE) {
|
|
Mx3DPointFloat normal;
|
|
|
|
assert(m_boundary && m_destEdge);
|
|
|
|
Vector3* cw = m_destEdge->CWVertex(*m_boundary);
|
|
Vector3* ccw = m_destEdge->CCWVertex(*m_boundary);
|
|
|
|
assert(cw && ccw);
|
|
|
|
LERP3(targetPosition, *cw, *ccw, m_destScale);
|
|
|
|
m_destEdge->GetFaceNormal(*m_boundary, normal);
|
|
endDirection.EqualsCross(*m_boundary->GetUp(), normal);
|
|
endDirection.Unitize();
|
|
}
|
|
|
|
Vector3 rightRef(m_local2World[0]);
|
|
Vector3 upRef(m_local2World[1]);
|
|
Vector3 dirRef(m_local2World[2]);
|
|
|
|
upRef = *m_boundary->GetUp();
|
|
|
|
rightRef.EqualsCross(upRef, dirRef);
|
|
rightRef.Unitize();
|
|
|
|
dirRef.EqualsCross(rightRef, upRef);
|
|
dirRef.Unitize();
|
|
|
|
Mx3DPointFloat start(m_local2World[3]);
|
|
Mx3DPointFloat direction(m_local2World[2]);
|
|
Mx3DPointFloat startToTarget(targetPosition);
|
|
|
|
startToTarget -= start;
|
|
float len = startToTarget.LenSquared();
|
|
if (len >= 0.0f) {
|
|
len = sqrt(len);
|
|
direction *= len;
|
|
endDirection *= len;
|
|
}
|
|
|
|
if (!m_userNavFlag) {
|
|
direction *= -1.0f;
|
|
}
|
|
|
|
if (SetSpline(start, direction, targetPosition, endDirection) != SUCCESS) {
|
|
MxTrace("Warning: m_BADuration = %g, roi = %s\n", m_BADuration, m_roi->GetName());
|
|
return FAILURE;
|
|
}
|
|
|
|
m_traveledDistance = 0.0f;
|
|
return SUCCESS;
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x1002f650
|
|
// FUNCTION: BETA10 0x100afd67
|
|
void LegoPathActor::GetWalkingBehavior(MxBool& p_countCounterclockWise, MxS32& p_selectedEdgeIndex)
|
|
{
|
|
switch (GetActorId()) {
|
|
case c_pepper:
|
|
p_countCounterclockWise = TRUE;
|
|
p_selectedEdgeIndex = 2;
|
|
break;
|
|
case c_mama:
|
|
p_countCounterclockWise = FALSE;
|
|
p_selectedEdgeIndex = 1;
|
|
break;
|
|
case c_papa:
|
|
p_countCounterclockWise = TRUE;
|
|
p_selectedEdgeIndex = 1;
|
|
break;
|
|
case c_nick:
|
|
case c_brickster:
|
|
p_countCounterclockWise = TRUE;
|
|
p_selectedEdgeIndex = rand() % p_selectedEdgeIndex + 1;
|
|
break;
|
|
case c_laura:
|
|
p_countCounterclockWise = FALSE;
|
|
p_selectedEdgeIndex = 2;
|
|
break;
|
|
default:
|
|
p_countCounterclockWise = TRUE;
|
|
p_selectedEdgeIndex = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x1002f700
|
|
// FUNCTION: BETA10 0x100afe4c
|
|
void LegoPathActor::ApplyLocal2World()
|
|
{
|
|
m_transformTime = Timer()->GetTime();
|
|
m_roi->SetLocal2World(m_local2World);
|
|
m_roi->WrappedUpdateWorldData();
|
|
|
|
if (m_userNavFlag) {
|
|
m_roi->WrappedSetLocal2WorldWithWorldDataUpdate(m_local2World);
|
|
TransformPointOfView();
|
|
}
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x1002f770
|
|
void LegoPathActor::UpdatePlane(LegoNamedPlane& p_namedPlane)
|
|
{
|
|
p_namedPlane.SetName(m_boundary->GetName());
|
|
p_namedPlane.SetPosition(GetWorldPosition());
|
|
p_namedPlane.SetDirection(GetWorldDirection());
|
|
p_namedPlane.SetUp(GetWorldUp());
|
|
}
|
|
|
|
// FUNCTION: LEGO1 0x1002f830
|
|
void LegoPathActor::PlaceActor(LegoNamedPlane& p_namedPlane)
|
|
{
|
|
if (p_namedPlane.IsPresent()) {
|
|
LegoWorld* world = CurrentWorld();
|
|
world->PlaceActor(this, p_namedPlane.GetName(), 0, 0.5f, 1, 0.5f);
|
|
SetLocation(p_namedPlane.GetPosition(), p_namedPlane.GetDirection(), p_namedPlane.GetUp(), TRUE);
|
|
}
|
|
}
|