isle/LEGO1/lego/legoomni/src/paths/legopathactor.cpp
2026-02-14 11:04:00 +01:00

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