1593 lines
42 KiB
C++
1593 lines
42 KiB
C++
/*
|
|
** Command & Conquer Renegade(tm)
|
|
** Copyright 2025 Electronic Arts Inc.
|
|
**
|
|
** This program is free software: you can redistribute it and/or modify
|
|
** it under the terms of the GNU General Public License as published by
|
|
** the Free Software Foundation, either version 3 of the License, or
|
|
** (at your option) any later version.
|
|
**
|
|
** This program is distributed in the hope that it will be useful,
|
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
** GNU General Public License for more details.
|
|
**
|
|
** You should have received a copy of the GNU General Public License
|
|
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/***********************************************************************************************
|
|
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
|
|
***********************************************************************************************
|
|
* *
|
|
* Project Name : LevelEdit *
|
|
* *
|
|
* $Archive:: /Commando/Code/wwphys/Path.cpp $*
|
|
* *
|
|
* Author:: Patrick Smith *
|
|
* *
|
|
* $Modtime:: 3/20/02 6:52p $*
|
|
* *
|
|
* $Revision:: 52 $*
|
|
* *
|
|
*---------------------------------------------------------------------------------------------*
|
|
* Functions: *
|
|
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
|
|
#include "path.h"
|
|
#include <windows.h>
|
|
#include "colmathaabox.h"
|
|
#include "pathfind.h"
|
|
#include "pathfindportal.h"
|
|
#include "pathsolve.h"
|
|
#include "pathdebugplotter.h"
|
|
#include "cardinalspline.h"
|
|
#include "pathobject.h"
|
|
#include "vehiclecurve.h"
|
|
#include "waypath.h"
|
|
#include "waypoint.h"
|
|
#include "chunkio.h"
|
|
#include "persistfactory.h"
|
|
#include "assetmgr.h"
|
|
#include "wwmemlog.h"
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
// Save/Load constants
|
|
////////////////////////////////////////////////////////////////
|
|
enum
|
|
{
|
|
CHUNKID_VARIABLES = 0x11040535,
|
|
CHUNKID_SPLINE
|
|
};
|
|
|
|
enum
|
|
{
|
|
VARID_STATE = 1,
|
|
VARID_TRAVERSAL_TYPE,
|
|
VARID_START_POS,
|
|
VARID_DEST_POS,
|
|
VARID_EXPECTED_POS,
|
|
VARID_LOOK_AHEAD_DIST,
|
|
VARID_LOOK_AHEAD_TIME,
|
|
VARID_MOVEMENT_RADIUS,
|
|
VARID_SPLINE_TIME,
|
|
VARID_VELOCITY,
|
|
VARID_CURRENT_ACTION,
|
|
VARID_MOVEMENT_DIRECTIONS,
|
|
VARID_PATH_OBJECT,
|
|
VARID_START_TIME,
|
|
VARID_END_TIME,
|
|
VARID_ISLOOPING,
|
|
VARID_PATH_ACTION,
|
|
VARID_OLD_PTR,
|
|
VARID_TOTAL_DIST
|
|
};
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// Local macros
|
|
///////////////////////////////////////////////////////////////////////////
|
|
#define SAFE_DELETE(pobject) \
|
|
if (pobject) { \
|
|
delete pobject; \
|
|
pobject = NULL; \
|
|
} \
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// Constants
|
|
///////////////////////////////////////////////////////////////////////////
|
|
static const float DEF_VEHICLE_VELOCITY = 40.0F;
|
|
static const float DEF_HUMAN_VELOCITY = 2.5F; //5.0F;
|
|
static const float ASSUMED_FPS = 30.0F; //15.0F;
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// Static member initialization
|
|
///////////////////////////////////////////////////////////////////////////
|
|
bool PathClass::m_DisplayMoveVectors = false;
|
|
|
|
static int _InstanceCount = 0;
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// PathClass
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////
|
|
PathClass::PathClass (void)
|
|
: m_ExpectedPos (0, 0, 0),
|
|
m_StartPos (0, 0, 0),
|
|
m_DestPos (0, 0, 0),
|
|
m_StartTime (0),
|
|
m_EndTime (1.0F),
|
|
m_LookAheadTime (0),
|
|
m_LookAheadDist (0),
|
|
m_SplineTime (0),
|
|
m_State (ERROR_NOT_INITIALIZED),
|
|
m_TraversalType (SPLINE),
|
|
m_Velocity (DEF_VEHICLE_VELOCITY),
|
|
m_Spline (NULL),
|
|
m_MovementRadius (0.1F),
|
|
m_MovementDirections (MOVE_X | MOVE_Y),
|
|
m_CurrentAction (-1),
|
|
m_IsLooping (false),
|
|
m_TotalDist (0)
|
|
{
|
|
_InstanceCount ++;
|
|
return ;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ~PathClass
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////
|
|
PathClass::~PathClass (void)
|
|
{
|
|
_InstanceCount --;
|
|
SAFE_DELETE (m_Spline);
|
|
return ;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Initialize
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////
|
|
void
|
|
PathClass::Initialize (const Vector3 &start, const Vector3 &end)
|
|
{
|
|
m_State = STATE_TRAVERSING_PATH;
|
|
m_SplineTime = 0;
|
|
m_LookAheadTime = 0;
|
|
m_TotalDist = 0;
|
|
m_LookAheadDist = 0;
|
|
m_CurrentAction = -1;
|
|
m_StartTime = 0;
|
|
m_EndTime = 1.0F;
|
|
m_IsLooping = false;
|
|
|
|
PATH_NODE node1;
|
|
node1.time = 0;
|
|
node1.next_time = 1.0F;
|
|
node1.action_id = ACTION_NONE;
|
|
node1.mechanism_id = 0;
|
|
node1.tighten_spline = false;
|
|
node1.pos = start;
|
|
node1.dest_pos.Set (0, 0, 0);
|
|
|
|
PATH_NODE node2;
|
|
node2.time = 0;
|
|
node2.next_time = 1.0F;
|
|
node2.action_id = ACTION_NONE;
|
|
node2.mechanism_id = 0;
|
|
node2.tighten_spline = false;
|
|
node2.pos = end;
|
|
node2.dest_pos.Set (0, 0, 0);
|
|
|
|
DynamicVectorClass<PATH_NODE> node_list;
|
|
node_list.Add (node1);
|
|
node_list.Add (node2);
|
|
|
|
//
|
|
// Convert the nodes into a spline and a set of action requests
|
|
//
|
|
Initialize_Spline (node_list);
|
|
return ;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Initialize
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////
|
|
void
|
|
PathClass::Initialize (PathSolveClass &path_solve)
|
|
{
|
|
WWMEMLOG(MEM_PATHFIND);
|
|
|
|
m_State = STATE_TRAVERSING_PATH;
|
|
m_SplineTime = 0;
|
|
m_LookAheadTime = 0;
|
|
m_TotalDist = 0;
|
|
m_LookAheadDist = 0;
|
|
m_CurrentAction = -1;
|
|
m_StartTime = 0;
|
|
m_EndTime = 1.0F;
|
|
m_IsLooping = false;
|
|
|
|
DynamicVectorClass<PATH_NODE> node_list;
|
|
|
|
//
|
|
// Get the raw path data from the solver, and convert it into
|
|
// a format we can digest
|
|
//
|
|
PathSolveClass::PATHPOINT_LIST &raw_path = path_solve.Get_Raw_Path ();
|
|
for (int index = 0; index < raw_path.Count (); index ++) {
|
|
|
|
//
|
|
// Setup a path node that contains the information we
|
|
// need from the path solver
|
|
//
|
|
PATH_NODE node;
|
|
node.time = 0;
|
|
node.next_time = 1.0F;
|
|
node.action_id = ACTION_NONE;
|
|
node.mechanism_id = 0;
|
|
node.tighten_spline = false;
|
|
node.pos = raw_path[index].m_Point;
|
|
node.dest_pos.Set (0, 0, 0);
|
|
|
|
//
|
|
// Check for any action requirements
|
|
//
|
|
bool is_valid = true;
|
|
PathfindPortalClass *portal = raw_path[index].m_Portal;
|
|
if (portal != NULL) {
|
|
|
|
//
|
|
// See what kind of portal this is
|
|
//
|
|
PathfindActionPortalClass *action_portal = portal->As_PathfindActionPortalClass ();
|
|
PathfindWaypathPortalClass *waypath_portal = portal->As_PathfindWaypathPortalClass ();
|
|
if (action_portal != NULL) {
|
|
|
|
//
|
|
// Check to see if we are inadvertantly starting on an "exit" portal
|
|
//
|
|
if (index == 1 && action_portal->Get_Action_Type () == PathClass::ACTION_NONE) {
|
|
|
|
//
|
|
// Find the entrance portal
|
|
//
|
|
PathfindActionPortalClass *enter_portal = action_portal->Get_Enter_Portal ();
|
|
if (enter_portal != NULL) {
|
|
|
|
AABoxClass box;
|
|
enter_portal->Get_Bounding_Box (box);
|
|
|
|
//
|
|
// Insert a node that will force the unit to do the entrance portal
|
|
//
|
|
PATH_NODE temp_node;
|
|
temp_node.time = 0;
|
|
temp_node.next_time = 1.0F;
|
|
temp_node.tighten_spline = true;
|
|
temp_node.pos = box.Center;
|
|
temp_node.action_id = enter_portal->Get_Action_Type ();
|
|
temp_node.dest_pos = enter_portal->Get_Destination ();
|
|
temp_node.mechanism_id = enter_portal->Get_Mechanism_ID ();
|
|
node_list.Add (temp_node);
|
|
}
|
|
}
|
|
|
|
//
|
|
// This is an action portal so set the action parameters
|
|
//
|
|
node.action_id = action_portal->Get_Action_Type ();
|
|
node.dest_pos = action_portal->Get_Destination ();
|
|
node.mechanism_id = action_portal->Get_Mechanism_ID ();
|
|
node.tighten_spline = true;
|
|
|
|
//
|
|
// Override the positions so that its in the center
|
|
// of the entrance portal. (This ensures guys won't
|
|
// get stuck when the door opens).
|
|
//
|
|
AABoxClass box;
|
|
action_portal->Get_Bounding_Box (box);
|
|
node.pos = box.Center;
|
|
|
|
} else if (waypath_portal != NULL) {
|
|
is_valid = false;
|
|
|
|
//
|
|
// Lookup the next portal in the list (it should be the waypath exit portal)
|
|
//
|
|
if (index + 1 < raw_path.Count ()) {
|
|
PathfindWaypathPortalClass *next_portal = raw_path[index + 1].m_Portal->As_PathfindWaypathPortalClass ();
|
|
if (next_portal != NULL) {
|
|
|
|
//
|
|
// Create a temporary waypath segment from the portal information
|
|
//
|
|
const WaypathPositionClass &start_pos = waypath_portal->Get_Waypath_Pos ();
|
|
const WaypathPositionClass &end_pos = next_portal->Get_Waypath_Pos ();
|
|
WaypathClass *path_segment = new WaypathClass (start_pos, end_pos);
|
|
|
|
//
|
|
// Insert all the data from this waypath segment into our path
|
|
//
|
|
Add_Waypath_Data (node_list, path_segment, 0, path_segment->Get_Point_Count () - 1);
|
|
REF_PTR_RELEASE (path_segment);
|
|
|
|
//
|
|
// Skip past the next portal
|
|
//
|
|
index ++;
|
|
}
|
|
}
|
|
|
|
} else if (portal->Is_Two_Way_Portal () == false) {
|
|
|
|
/*if (index < (raw_path.Count () - 1) && raw_path[index+1].m_Portal != NULL) {
|
|
|
|
AABoxClass curr_box;
|
|
AABoxClass next_box;
|
|
|
|
portal->Get_Bounding_Box (curr_box);
|
|
raw_path[index+1].m_Portal->Get_Bounding_Box (next_box);
|
|
|
|
if (curr_box.Center.Z > (next_box.Center.Z + 1.5F)) {
|
|
curr_box.Center.Z = 0;
|
|
next_box.Center.Z = 0;
|
|
float portal_dist = (curr_box.Center - next_box.Center).Length ();
|
|
if (portal_dist < 665.0F) {
|
|
|
|
//
|
|
// Calculate an intermediate point that is one meter towards
|
|
// the center of the next sector
|
|
//
|
|
Vector3 temp_delta = (raw_path[index+1].m_SectorCenter - next_box.Center);
|
|
temp_delta.Normalize ();
|
|
Vector3 intermediate_point = next_box.Center + temp_delta;
|
|
|
|
//
|
|
// Insert a node that will force the unit to jump down
|
|
//
|
|
PATH_NODE temp_node;
|
|
temp_node.time = 0;
|
|
temp_node.next_time = 1.0F;
|
|
temp_node.tighten_spline = true;
|
|
temp_node.pos = intermediate_point;
|
|
temp_node.action_id = ACTION_JUMP;
|
|
temp_node.mechanism_id = 0;
|
|
temp_node.dest_pos.Set (intermediate_point);
|
|
node_list.Add (temp_node);
|
|
}
|
|
}
|
|
}*/
|
|
}
|
|
}
|
|
|
|
if (is_valid) {
|
|
node_list.Add (node);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Convert the nodes into a spline and a set of action requests
|
|
//
|
|
Initialize_Spline (node_list);
|
|
|
|
//
|
|
// Clip the spline to the sectors and portals that the solver knows is safe
|
|
//
|
|
if (m_PathObject.Is_Flag_Set (PathObjectClass::IS_VEHICLE) == false) {
|
|
Clip_Spline_To_Pathfind_Data (node_list, path_solve);
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Add_Waypoint_Info_To_Node_List
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////
|
|
void
|
|
PathClass::Add_Waypoint_Info_To_Node_List
|
|
(
|
|
DynamicVectorClass<PathClass::PATH_NODE> & node_list,
|
|
WaypointClass * waypoint,
|
|
WaypointClass * next_point
|
|
)
|
|
{
|
|
//
|
|
// Fill in a path node structure for this waypoint
|
|
//
|
|
PathClass::PATH_NODE node;
|
|
node.time = 0;
|
|
node.next_time = 1.0F;
|
|
node.action_id = ACTION_NONE;
|
|
node.mechanism_id = 0;
|
|
node.dest_pos.Set (0, 0, 0);
|
|
node.pos = waypoint->Get_Position ();
|
|
|
|
//
|
|
// Check to see if there are any special actions to perform at
|
|
// this waypoint
|
|
//
|
|
if (waypoint->Get_Flag (WaypointClass::FLAG_REQUIRES_JUMP)) {
|
|
node.action_id = ACTION_JUMP;
|
|
node.tighten_spline = true;
|
|
|
|
//
|
|
// Record the jump-to point (if there is one)
|
|
//
|
|
if (next_point != NULL) {
|
|
node.dest_pos = next_point->Get_Position ();
|
|
}
|
|
} else if (waypoint->Get_Flag (WaypointClass::FLAG_REQUIRES_ACTION)) {
|
|
|
|
//
|
|
// Lookup the action portal from the waypoint
|
|
//
|
|
PathfindActionPortalClass *action_portal = waypoint->Get_Action_Portal ();
|
|
if (action_portal != NULL) {
|
|
|
|
//
|
|
// Configure the node's action parameters
|
|
//
|
|
node.action_id = action_portal->Get_Action_Type ();
|
|
node.dest_pos = action_portal->Get_Destination ();
|
|
node.mechanism_id = action_portal->Get_Mechanism_ID ();
|
|
node.tighten_spline = true;
|
|
|
|
//
|
|
// Override the positions so that its in the center
|
|
// of the entrance portal. (This ensures guys won't
|
|
// get stuck when the door opens).
|
|
//
|
|
AABoxClass box;
|
|
action_portal->Get_Bounding_Box (box);
|
|
node.pos = box.Center;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Add the node to the list
|
|
//
|
|
node_list.Add (node);
|
|
return ;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Add_Waypath_Data
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////
|
|
void
|
|
PathClass::Add_Waypath_Data
|
|
(
|
|
DynamicVectorClass<PATH_NODE> & node_list,
|
|
WaypathClass * waypath,
|
|
int start_index,
|
|
int end_index
|
|
)
|
|
{
|
|
//
|
|
// If this is a looping path, then add the last waypoint first, so we can
|
|
// wrap the spline from "last_point-1" all the way through "first_point+1".
|
|
// If we don't do this, the spline will not look continuous.
|
|
//
|
|
if (m_IsLooping) {
|
|
Add_Waypoint_Info_To_Node_List (node_list, waypath->Get_Point (end_index),
|
|
waypath->Get_Point (start_index));
|
|
}
|
|
|
|
//
|
|
// Loop along the waypath either forwards or backwards, depending
|
|
// on what the caller has requested
|
|
//
|
|
int count = WWMath::Fabs (end_index - start_index);
|
|
int inc = (end_index > start_index) ? 1 : -1;
|
|
for (int index = start_index; count >= 0; index += inc, count --) {
|
|
|
|
WaypointClass *waypoint = waypath->Get_Point (index);
|
|
WaypointClass *next_point = waypath->Get_Point (index + inc);
|
|
|
|
//
|
|
// Add data from each waypoint into our path node list
|
|
//
|
|
if ((waypoint == NULL || next_point == NULL) || waypoint->Get_Position () != next_point->Get_Position ()) {
|
|
Add_Waypoint_Info_To_Node_List (node_list, waypoint, next_point);
|
|
}
|
|
}
|
|
|
|
//
|
|
// If this is a looping path, then add the first and second waypoints again.
|
|
// This is done so we can wrap the spline from "last_point-1" all the way
|
|
// through "first_point+1". If we don't do this, the spline will not look
|
|
// continuous.
|
|
//
|
|
if (m_IsLooping) {
|
|
|
|
Add_Waypoint_Info_To_Node_List (node_list, waypath->Get_Point (start_index),
|
|
waypath->Get_Point (start_index + inc));
|
|
|
|
Add_Waypoint_Info_To_Node_List (node_list, waypath->Get_Point (start_index + inc),
|
|
waypath->Get_Point (start_index + inc + inc));
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Initialize
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////
|
|
void
|
|
PathClass::Initialize (WaypathClass *waypath, int start_pt_id, int end_pt_id)
|
|
{
|
|
WWMEMLOG(MEM_PATHFIND);
|
|
|
|
m_State = ERROR_NOT_INITIALIZED;
|
|
m_SplineTime = 0;
|
|
m_LookAheadTime = 0;
|
|
m_LookAheadDist = 0;
|
|
m_CurrentAction = -1;
|
|
m_StartTime = 0;
|
|
m_EndTime = 1.0F;
|
|
m_IsLooping = false;
|
|
|
|
//
|
|
// Initialize the path from this waypath
|
|
//
|
|
if (waypath != NULL) {
|
|
|
|
//
|
|
// Determine if this is a looping path or not
|
|
//
|
|
m_IsLooping = waypath->Get_Flag (WaypathClass::FLAG_LOOPING);
|
|
|
|
//
|
|
// Get the number of waypoints on this path
|
|
//
|
|
int count = waypath->Get_Point_Count ();
|
|
if (count > 0) {
|
|
|
|
//
|
|
// Determine the starting and ending indicies
|
|
//
|
|
int start_index = 0;
|
|
int end_index = count - 1;
|
|
for (int index = 0; index < count; index ++) {
|
|
if (waypath->Get_Point (index)->Get_ID () == start_pt_id) {
|
|
start_index = index;
|
|
}
|
|
if (waypath->Get_Point (index)->Get_ID () == end_pt_id) {
|
|
end_index = index;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Initialize our first position
|
|
//
|
|
m_ExpectedPos = waypath->Get_Point (start_index)->Get_Position ();
|
|
m_State = STATE_TRAVERSING_PATH;
|
|
|
|
//
|
|
// Add the waypath data to our node list
|
|
//
|
|
DynamicVectorClass<PATH_NODE> node_list;
|
|
Add_Waypath_Data (node_list, waypath, start_index, end_index);
|
|
|
|
//
|
|
// Now create a spline from the waypath data
|
|
//
|
|
Initialize_Spline (node_list);
|
|
}
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Evaluate_Next_Point
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////
|
|
bool
|
|
PathClass::Evaluate_Next_Point (const Vector3 &curr_pos, Vector3 &new_pos)
|
|
{
|
|
//
|
|
// Be careful not to evaluate when we are in a invalid state
|
|
//
|
|
if (m_State >= FIRST_ERROR) {
|
|
new_pos = curr_pos;
|
|
return false;
|
|
} else if (m_State == STATE_PATH_COMPLETE) {
|
|
new_pos = m_ExpectedPos;
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// Get the delta from our current position to the point where we
|
|
// should be heading
|
|
//
|
|
Vector3 delta = m_ExpectedPos - curr_pos;
|
|
|
|
//
|
|
// Zero out any directions we aren't moving in
|
|
//
|
|
if ((m_MovementDirections & MOVE_X) == 0) {
|
|
delta.X = 0;
|
|
}
|
|
|
|
if ((m_MovementDirections & MOVE_Y) == 0) {
|
|
delta.Y = 0;
|
|
}
|
|
|
|
if ((m_MovementDirections & MOVE_Z) == 0) {
|
|
delta.Z = 0;
|
|
}
|
|
|
|
//
|
|
// Calculate the distance we have yet to travel to
|
|
// reach the next point
|
|
//
|
|
float delta_len = delta.Length ();
|
|
delta_len -= m_MovementRadius;
|
|
|
|
//
|
|
// Reset our state
|
|
//
|
|
m_State = STATE_TRAVERSING_PATH;
|
|
|
|
//
|
|
// Recalculate our look-ahead distance
|
|
//
|
|
//m_LookAheadDist = (m_Velocity * 4.0F) / ASSUMED_FPS;
|
|
|
|
//
|
|
// Did we reach the expected position?
|
|
//
|
|
bool advance_dest = delta_len < m_LookAheadDist;
|
|
if (advance_dest) {
|
|
|
|
float new_time = m_SplineTime + m_LookAheadTime;
|
|
if (new_time > (m_EndTime - (m_LookAheadTime * 0.5F))) {
|
|
new_time = m_EndTime;
|
|
}
|
|
|
|
//
|
|
// Have we successfully traversed the spline?
|
|
//
|
|
if (new_time >= m_EndTime) {
|
|
|
|
//
|
|
// Is this a looping path?
|
|
//
|
|
if (m_IsLooping) {
|
|
|
|
//
|
|
// If this path is looping, then wrap from the end of the
|
|
// path to the beginning of the path
|
|
//
|
|
m_SplineTime = (new_time - m_EndTime) + m_StartTime;
|
|
m_CurrentAction = -1;
|
|
|
|
} else {
|
|
|
|
//
|
|
// If we are close to the destination, then consider us 'complete'.
|
|
//
|
|
//if (delta_len < 0.25F) {
|
|
m_State = STATE_PATH_COMPLETE;
|
|
m_SplineTime = m_EndTime;
|
|
m_CurrentAction = -1;
|
|
//}
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Check to see if the object needs to perform an action at this point
|
|
//
|
|
if (m_CurrentAction + 1 < m_PathActions.Count ()) {
|
|
PATH_NODE &node = m_PathActions[m_CurrentAction + 1];
|
|
|
|
//
|
|
// Should we activate this action request?
|
|
//
|
|
float action_time = node.time;
|
|
if (WWMath::Fabs (action_time - m_SplineTime) < (m_LookAheadTime * 0.5F)) {
|
|
|
|
//
|
|
// Set the new state and remember which action we are to perform
|
|
//
|
|
m_State = STATE_ACTION_REQUIRED;
|
|
m_CurrentAction ++;
|
|
|
|
//
|
|
// Snap to the action point
|
|
//
|
|
new_time = node.next_time;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Increase our position along the spline
|
|
//
|
|
m_SplineTime = new_time;
|
|
}
|
|
|
|
//
|
|
// Evaluate the spline at the given 'time' to determine our new position
|
|
//
|
|
m_Spline->Evaluate (m_SplineTime, &m_ExpectedPos);
|
|
|
|
if (m_PathObject.Is_Flag_Set (PathObjectClass::IS_VEHICLE)) {
|
|
m_SplineTime = ((VehicleCurveClass *)m_Spline)->Get_Last_Eval_Time ();
|
|
}
|
|
}
|
|
|
|
new_pos = m_ExpectedPos;
|
|
|
|
/*static PhysClass *_DebugObj = NULL;
|
|
|
|
if (_DebugObj == NULL) {
|
|
_DebugObj = new DecorationPhysClass;
|
|
_DebugObj->Set_Model (WW3DAssetManager::Get_Instance ()->Create_Render_Obj ("C_HAVOC"));
|
|
_DebugObj->Inc_Ignore_Counter ();
|
|
PhysicsSceneClass::Get_Instance ()->Add_Dynamic_Object (_DebugObj);
|
|
}
|
|
|
|
_DebugObj->Set_Transform (Matrix3D (m_ExpectedPos));*/
|
|
|
|
if (m_DisplayMoveVectors) {
|
|
PhysicsSceneClass::Get_Instance ()->Add_Debug_AABox (AABoxClass (curr_pos, Vector3 (0.25F,0.25F,0.25F)), Vector3 (1, 0, 0));
|
|
PhysicsSceneClass::Get_Instance ()->Add_Debug_AABox (AABoxClass (m_ExpectedPos, Vector3 (0.25F,0.25F,0.25F)), Vector3 (0, 1, 0));
|
|
PhysicsSceneClass::Get_Instance ()->Add_Debug_Vector (curr_pos, m_ExpectedPos - curr_pos, Vector3 (0, 0.6F, 1));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Get_Curve_Sharpness
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////
|
|
float
|
|
PathClass::Get_Curve_Sharpness (Vector3 *position) const
|
|
{
|
|
float sharpness = 0;
|
|
|
|
if (m_PathObject.Is_Flag_Set (PathObjectClass::IS_VEHICLE)) {
|
|
Vector3 foo;
|
|
m_Spline->Evaluate (m_SplineTime, &foo);
|
|
sharpness = ((VehicleCurveClass *)m_Spline)->Get_Current_Sharpness (position);
|
|
}
|
|
|
|
return sharpness;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Get_Last_Eval_Time
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////
|
|
float
|
|
PathClass::Get_Last_Eval_Time (void) const
|
|
{
|
|
return m_SplineTime;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Get_Remaining_Path_Length
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////
|
|
float
|
|
PathClass::Get_Remaining_Path_Length (void)
|
|
{
|
|
float length = m_TotalDist;
|
|
|
|
//
|
|
// The length remaining should correspond to the amount of time
|
|
// remaining.
|
|
//
|
|
if (m_IsLooping == false && m_EndTime > 0) {
|
|
length = m_TotalDist * ((m_EndTime - m_SplineTime) / m_EndTime);
|
|
}
|
|
|
|
return length;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Display_Path
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////
|
|
void
|
|
PathClass::Display_Path (bool onoff)
|
|
{
|
|
if (onoff == false) {
|
|
PathDebugPlotterClass::Get_Instance ()->Display (false);
|
|
} else if (m_State < FIRST_ERROR) {
|
|
|
|
//
|
|
// Turn off painting
|
|
//
|
|
PathDebugPlotterClass::Get_Instance ()->Display (false);
|
|
PathDebugPlotterClass::Get_Instance ()->Reset ();
|
|
|
|
//
|
|
// Get the first position on the spline
|
|
//
|
|
Vector3 last_pos;
|
|
m_Spline->Evaluate (0, &last_pos);
|
|
|
|
//
|
|
// Plot the spline
|
|
//
|
|
for (float t = m_LookAheadTime; t <= 1.0F; t += m_LookAheadTime) {
|
|
//for (float t = m_LookAheadTime; t <= 1.0F; t += m_LookAheadTime / 2.0F) {
|
|
Vector3 curr_pos;
|
|
m_Spline->Evaluate (t, &curr_pos);
|
|
|
|
if (m_PathObject.Is_Flag_Set (PathObjectClass::IS_VEHICLE)) {
|
|
t = ((VehicleCurveClass *)m_Spline)->Get_Last_Eval_Time ();
|
|
}
|
|
|
|
PathDebugPlotterClass::Get_Instance ()->Add (last_pos, curr_pos, Vector3 (0, 0.5F, 1));
|
|
last_pos = curr_pos;
|
|
}
|
|
|
|
//
|
|
// Turn painting back on
|
|
//
|
|
PathDebugPlotterClass::Get_Instance ()->Display (true);
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Set_Traversal_Type
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
PathClass::Set_Traversal_Type (TRAVERSAL_TYPE type)
|
|
{
|
|
//
|
|
// Do we need to re-initialize any data?
|
|
//
|
|
if (m_TraversalType != type) {
|
|
m_TraversalType = type;
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Find_Intersection_X
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
bool
|
|
Find_Intersection_X
|
|
(
|
|
const Vector3 & start,
|
|
const Vector3 & end,
|
|
const Vector3 & rgn_center,
|
|
const Vector3 & rgn_extent,
|
|
Vector3 * intersect_point
|
|
)
|
|
{
|
|
float percent = (rgn_center.X - start.X) / (end.X - start.X);
|
|
(*intersect_point) = start + ((end - start) * percent);
|
|
|
|
bool retval = false;
|
|
if (percent >= 0 && percent < 1.0F) {
|
|
retval = (intersect_point->Y >= (rgn_center.Y - rgn_extent.Y));
|
|
retval &= (intersect_point->Y <= (rgn_center.Y + rgn_extent.Y));
|
|
retval &= (intersect_point->Z <= (rgn_center.Z + rgn_extent.Z));
|
|
retval &= (intersect_point->Z <= (rgn_center.Z + rgn_extent.Z));
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Find_Intersection_Y
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
bool
|
|
Find_Intersection_Y
|
|
(
|
|
const Vector3 & start,
|
|
const Vector3 & end,
|
|
const Vector3 & rgn_center,
|
|
const Vector3 & rgn_extent,
|
|
Vector3 * intersect_point
|
|
)
|
|
{
|
|
float percent = (rgn_center.Y - start.Y) / (end.Y - start.Y);
|
|
(*intersect_point) = start + ((end - start) * percent);
|
|
|
|
bool retval = false;
|
|
if (percent >= 0 && percent < 1.0F) {
|
|
retval = (intersect_point->X >= (rgn_center.X - rgn_extent.X));
|
|
retval &= (intersect_point->X <= (rgn_center.X + rgn_extent.X));
|
|
retval &= (intersect_point->Z <= (rgn_center.Z + rgn_extent.Z));
|
|
retval &= (intersect_point->Z <= (rgn_center.Z + rgn_extent.Z));
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Find_Side_Intersection
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
bool
|
|
Find_Side_Intersection
|
|
(
|
|
int index,
|
|
const AABoxClass &box,
|
|
const Vector3 & start,
|
|
const Vector3 & end,
|
|
Vector3 * intersection_point
|
|
)
|
|
{
|
|
bool retval = false;
|
|
|
|
if (index == 0) {
|
|
|
|
Vector3 center = box.Center;
|
|
center.X += box.Extent.X;
|
|
Vector3 extent = Vector3 (0, box.Extent.Y, box.Extent.Z);
|
|
|
|
retval = Find_Intersection_X (start, end, center, extent, intersection_point);
|
|
|
|
} else if (index == 1) {
|
|
|
|
Vector3 center = box.Center;
|
|
center.X -= box.Extent.X;
|
|
Vector3 extent = Vector3 (0, box.Extent.Y, box.Extent.Z);
|
|
|
|
retval = Find_Intersection_X (start, end, center, extent, intersection_point);
|
|
} else if (index == 2) {
|
|
|
|
Vector3 center = box.Center;
|
|
center.Y += box.Extent.Y;
|
|
Vector3 extent = Vector3 (box.Extent.X, 0, box.Extent.Z);
|
|
|
|
retval = Find_Intersection_Y (start, end, center, extent, intersection_point);
|
|
} else if (index == 3) {
|
|
|
|
Vector3 center = box.Center;
|
|
center.Y -= box.Extent.Y;
|
|
Vector3 extent = Vector3 (box.Extent.X, 0, box.Extent.Z);
|
|
|
|
retval = Find_Intersection_Y (start, end, center, extent, intersection_point);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Is_Point_In_Boxes
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
bool
|
|
PathClass::Is_Point_In_Boxes
|
|
(
|
|
const Vector3 & point,
|
|
BOX_LIST & box_list
|
|
)
|
|
{
|
|
bool retval = false;
|
|
|
|
//
|
|
// Test each box in the list
|
|
//
|
|
for (int index = 0; (index < box_list.Count ()) && !retval; index ++) {
|
|
retval |= box_list[index]->Contains (point);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Clip_Control_Point
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
PathClass::Clip_Control_Point
|
|
(
|
|
const Vector3 &start_point,
|
|
Vector3 * point,
|
|
BOX_LIST & sector_list,
|
|
BOX_LIST & portal_list
|
|
)
|
|
{
|
|
//
|
|
// Loop through all the boxes we need to clip against
|
|
//
|
|
for (int index = 0; index < sector_list.Count (); index ++) {
|
|
AABoxClass &box = *(sector_list[index]);
|
|
|
|
|
|
//
|
|
// Clip the line to each of the four sides of the box (don't care about
|
|
// top or bottom for the moment).
|
|
//
|
|
for (int side_index = 0; side_index < 4; side_index ++) {
|
|
|
|
//
|
|
// Does the line pass through this side?
|
|
//
|
|
Vector3 intersect_point;
|
|
if (Find_Side_Intersection (side_index, box, start_point, *point, &intersect_point)) {
|
|
|
|
//
|
|
// If there isn't a portal where this line passes through the side, then
|
|
// clip the line to the side.
|
|
//
|
|
if (Is_Point_In_Boxes (intersect_point, portal_list) == false) {
|
|
(*point) = intersect_point;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Initialize_Vehicle_Spline
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
PathClass::Initialize_Vehicle_Spline (DynamicVectorClass<PATH_NODE> &node_list)
|
|
{
|
|
m_Spline = new VehicleCurveClass (m_PathObject.Get_Turn_Radius ());
|
|
|
|
Vector3 point;
|
|
Vector3 last_point = m_StartPos;
|
|
|
|
//
|
|
// Setup the spline using each 'point' on the path
|
|
// as a key along the spline.
|
|
//
|
|
float current_dist = 0;
|
|
for (int index = 0; index < node_list.Count (); index ++) {
|
|
point = node_list[index].pos;
|
|
|
|
//
|
|
// Add this point as a key along the spline
|
|
//
|
|
current_dist += (point - last_point).Length ();
|
|
float curr_time = current_dist / m_TotalDist;
|
|
m_Spline->Add_Key (point, curr_time);
|
|
|
|
//
|
|
// Is this a looping path?
|
|
//
|
|
if (m_IsLooping) {
|
|
|
|
//
|
|
// Record where the loop starts and ends...
|
|
//
|
|
if (index == 1) {
|
|
m_StartTime = curr_time;
|
|
} else if (index == node_list.Count () - 2) {
|
|
m_EndTime = curr_time;
|
|
}
|
|
}
|
|
|
|
|
|
last_point = point;
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Initialize_Human_Spline
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
PathClass::Initialize_Human_Spline(DynamicVectorClass<PATH_NODE> &node_list)
|
|
{
|
|
CardinalSpline3DClass temp_spline;
|
|
float tightness = (m_TraversalType == SPLINE) ? 0.3F : 1.0F;
|
|
|
|
//
|
|
// Assume we do the whole path (looping paths only do a subset of
|
|
// the path)
|
|
//
|
|
|
|
Vector3 point;
|
|
Vector3 last_point = m_StartPos;
|
|
|
|
//
|
|
// Setup the temporary spline using each 'point' on the path
|
|
// as a key along the spline.
|
|
//
|
|
float current_dist = 0;
|
|
bool fixup_last_action = false;
|
|
for (int index = 0; index < node_list.Count (); index ++) {
|
|
point = node_list[index].pos;
|
|
|
|
//
|
|
// Add this point as a key along the spline
|
|
//
|
|
current_dist += (point - last_point).Length ();
|
|
float curr_time = current_dist / m_TotalDist;
|
|
temp_spline.Add_Key (point, curr_time);
|
|
|
|
//
|
|
// Is this a looping path?
|
|
//
|
|
if (m_IsLooping) {
|
|
|
|
//
|
|
// Record where the loop starts and ends...
|
|
//
|
|
if (index == 1) {
|
|
m_StartTime = curr_time;
|
|
} else if (index == node_list.Count () - 2) {
|
|
m_EndTime = curr_time;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Do we need to fix-up the time values for the last action node?
|
|
//
|
|
if (fixup_last_action) {
|
|
PATH_NODE &node = m_PathActions[m_PathActions.Count () - 1];
|
|
node.next_time = curr_time;
|
|
fixup_last_action = false;
|
|
}
|
|
|
|
//
|
|
// Do we have an action at this node that we need to store?
|
|
//
|
|
if (node_list[index].action_id != 0) {
|
|
|
|
//
|
|
// Add an action node to our list so when the unit gets
|
|
// to this point along the spline, we can have it perform
|
|
// the requested action.
|
|
//
|
|
PATH_NODE node;
|
|
node.time = curr_time;
|
|
node.next_time = 1.0F;
|
|
node.action_id = node_list[index].action_id;
|
|
node.mechanism_id = node_list[index].mechanism_id;
|
|
node.dest_pos = node_list[index].dest_pos;
|
|
node.pos = node_list[index].pos;
|
|
m_PathActions.Add (node);
|
|
|
|
//
|
|
// We need to fix up the 'next_time' member during the next iteration
|
|
//
|
|
fixup_last_action = true;
|
|
}
|
|
|
|
//
|
|
// For some type of nodes we need to tighten the spline so its
|
|
// as sharp as possible (actions generally require this)
|
|
//
|
|
if (node_list[index].tighten_spline) {
|
|
temp_spline.Set_Tightness (index, 1.0F);
|
|
} else {
|
|
temp_spline.Set_Tightness (index, tightness);
|
|
}
|
|
|
|
last_point = point;
|
|
}
|
|
|
|
//
|
|
// Convert the temp spline to a hermite spline so we
|
|
// can adjust the tangent points if necessary (for clipping).
|
|
//
|
|
temp_spline.Update_Tangents ();
|
|
m_Spline = new HermiteSpline3DClass;
|
|
(*((HermiteSpline3DClass*)m_Spline)) = temp_spline;
|
|
return ;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Clip_Spline_To_Pathfind_Data
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
PathClass::Clip_Spline_To_Pathfind_Data
|
|
(
|
|
DynamicVectorClass<PATH_NODE> &node_list,
|
|
PathSolveClass & path_solve
|
|
)
|
|
{
|
|
//
|
|
// Get the portal and sector list from the pathsolver
|
|
//
|
|
BOX_LIST sector_list;
|
|
BOX_LIST portal_list;
|
|
path_solve.Get_Volumes (sector_list, portal_list);
|
|
|
|
//
|
|
// Loop through the points in the hermite spline, convert
|
|
// each tangent to bezier curve control points, clip these
|
|
// points to the sectors and portals the path contains,
|
|
// and convert the clipped points back into hermite spline
|
|
// tangents.
|
|
//
|
|
for (int index = 0; index < node_list.Count () - 1; index ++) {
|
|
|
|
//
|
|
// Get the current and next points on the path
|
|
//
|
|
Vector3 point = node_list[index].pos;
|
|
Vector3 next_point = node_list[index + 1].pos;
|
|
|
|
//
|
|
// Get the in and out tangents for each of these points
|
|
//
|
|
Vector3 tangent_in;
|
|
Vector3 tangent_out;
|
|
Vector3 next_tangent_in;
|
|
Vector3 next_tangent_out;
|
|
((HermiteSpline3DClass*)m_Spline)->Get_Tangents (index, &tangent_in, &tangent_out);
|
|
((HermiteSpline3DClass*)m_Spline)->Get_Tangents (index + 1, &next_tangent_in, &next_tangent_out);
|
|
|
|
//
|
|
// Convert the tangents to bezier curve control points
|
|
//
|
|
float one_third = 1.0F / 3.0F;
|
|
Vector3 ctrl_pt1 = point + (tangent_out * one_third);
|
|
Vector3 ctrl_pt2 = next_point - (next_tangent_in * one_third);
|
|
|
|
//
|
|
// Clip the control points to the pathfind sectors and portals
|
|
//
|
|
Clip_Control_Point (point, &ctrl_pt1, sector_list, portal_list);
|
|
Clip_Control_Point (next_point, &ctrl_pt2, sector_list, portal_list);
|
|
|
|
//
|
|
// Convert the control points back into tangents
|
|
//
|
|
tangent_out = (ctrl_pt1 - point) * 3.0F;
|
|
next_tangent_in = (next_point - ctrl_pt2) * 3.0F;
|
|
|
|
//
|
|
// Pass the tangents back to the hermite spline
|
|
//
|
|
((HermiteSpline3DClass*)m_Spline)->Set_Tangents (index, tangent_in, tangent_out);
|
|
((HermiteSpline3DClass*)m_Spline)->Set_Tangents (index + 1, next_tangent_in, next_tangent_out);
|
|
}
|
|
|
|
//
|
|
// Free the temporary portal-box list
|
|
//
|
|
for (index = 0; index < portal_list.Count (); index ++) {
|
|
AABoxClass *portal_box = portal_list[index];
|
|
delete portal_box;
|
|
}
|
|
|
|
//
|
|
// Free the temporary sector-box list
|
|
//
|
|
for (index = 0; index < sector_list.Count (); index ++) {
|
|
AABoxClass *box = sector_list[index];
|
|
delete box;
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Initialize_Spline
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
PathClass::Initialize_Spline (DynamicVectorClass<PATH_NODE> &node_list)
|
|
{
|
|
if (node_list.Count () > 0) {
|
|
|
|
//
|
|
// Start fresh
|
|
//
|
|
m_PathActions.Delete_All ();
|
|
SAFE_DELETE (m_Spline);
|
|
|
|
//
|
|
// Get the first and last positions from node list
|
|
//
|
|
m_StartPos = node_list[0].pos;
|
|
m_DestPos = node_list[node_list.Count () - 1].pos;
|
|
|
|
if (m_IsLooping && node_list.Count () > 1) {
|
|
m_StartPos = node_list[1].pos;
|
|
m_DestPos = m_StartPos;
|
|
}
|
|
|
|
|
|
Vector3 point;
|
|
Vector3 last_point = m_StartPos;
|
|
|
|
//
|
|
// Determine how long the spline is
|
|
//
|
|
m_TotalDist = 0;
|
|
for (int index = 0; index < node_list.Count (); index ++) {
|
|
point = node_list[index].pos;
|
|
|
|
//
|
|
// Add the distance of this point from the last point
|
|
// into the total
|
|
//
|
|
m_TotalDist += (point - last_point).Length ();
|
|
last_point = point;
|
|
}
|
|
|
|
//
|
|
// Initialize the spline depending on the path object type
|
|
//
|
|
if (m_PathObject.Is_Flag_Set (PathObjectClass::IS_VEHICLE)) {
|
|
Initialize_Vehicle_Spline (node_list);
|
|
m_Velocity = DEF_VEHICLE_VELOCITY;
|
|
} else {
|
|
Initialize_Human_Spline (node_list);
|
|
m_Velocity = DEF_HUMAN_VELOCITY;
|
|
}
|
|
|
|
//
|
|
// Setup the variables to look-ahead 8 frames and switch
|
|
// the new look-ahead after the unit has traveled 4 frames
|
|
//
|
|
float approx_frames = (m_TotalDist * ASSUMED_FPS) / m_Velocity;
|
|
m_LookAheadTime = 8.0F / approx_frames;
|
|
m_LookAheadDist = (m_Velocity * 4.0F) / ASSUMED_FPS;
|
|
|
|
//
|
|
// Vehicles automatically look ahead to each turn, so make sure
|
|
// we don't skip one...
|
|
//
|
|
if (m_PathObject.Is_Flag_Set (PathObjectClass::IS_VEHICLE)) {
|
|
m_LookAheadTime = m_LookAheadTime / 5.0F;
|
|
}
|
|
|
|
/*if (m_TotalDist > 0) {
|
|
m_LookAheadTime = 2.0F / m_TotalDist;
|
|
m_LookAheadDist = 4.0F;
|
|
}*/
|
|
|
|
m_SplineTime = m_StartTime;
|
|
m_ExpectedPos = m_StartPos;
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Set_Path_Object
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
PathClass::Set_Path_Object (PathObjectClass &path_object)
|
|
{
|
|
m_PathObject = path_object;
|
|
return ;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Get_Path_Object
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
PathClass::Get_Path_Object (PathObjectClass &path_object) const
|
|
{
|
|
path_object = m_PathObject;
|
|
return ;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Save
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
PathClass::Save (ChunkSaveClass &csave)
|
|
{
|
|
if (m_Spline != NULL) {
|
|
|
|
//
|
|
// Use the spline's factory to save it to its own chunk
|
|
//
|
|
csave.Begin_Chunk (CHUNKID_SPLINE);
|
|
csave.Begin_Chunk (m_Spline->Get_Factory ().Chunk_ID ());
|
|
m_Spline->Get_Factory ().Save (csave, m_Spline);
|
|
csave.End_Chunk ();
|
|
csave.End_Chunk ();
|
|
}
|
|
|
|
csave.Begin_Chunk (CHUNKID_VARIABLES);
|
|
|
|
//
|
|
// Save each variable to its own microchunk
|
|
//
|
|
WRITE_MICRO_CHUNK (csave, VARID_STATE, m_State);
|
|
WRITE_MICRO_CHUNK (csave, VARID_TRAVERSAL_TYPE, m_TraversalType);
|
|
WRITE_MICRO_CHUNK (csave, VARID_START_POS, m_StartPos);
|
|
WRITE_MICRO_CHUNK (csave, VARID_DEST_POS, m_DestPos);
|
|
WRITE_MICRO_CHUNK (csave, VARID_EXPECTED_POS, m_ExpectedPos);
|
|
WRITE_MICRO_CHUNK (csave, VARID_LOOK_AHEAD_DIST, m_LookAheadDist);
|
|
WRITE_MICRO_CHUNK (csave, VARID_LOOK_AHEAD_TIME, m_LookAheadTime);
|
|
WRITE_MICRO_CHUNK (csave, VARID_MOVEMENT_RADIUS, m_MovementRadius);
|
|
WRITE_MICRO_CHUNK (csave, VARID_SPLINE_TIME, m_SplineTime);
|
|
WRITE_MICRO_CHUNK (csave, VARID_VELOCITY, m_Velocity);
|
|
WRITE_MICRO_CHUNK (csave, VARID_CURRENT_ACTION, m_CurrentAction);
|
|
WRITE_MICRO_CHUNK (csave, VARID_MOVEMENT_DIRECTIONS, m_MovementDirections);
|
|
WRITE_MICRO_CHUNK (csave, VARID_PATH_OBJECT, m_PathObject);
|
|
WRITE_MICRO_CHUNK (csave, VARID_START_TIME, m_StartTime);
|
|
WRITE_MICRO_CHUNK (csave, VARID_END_TIME, m_EndTime);
|
|
WRITE_MICRO_CHUNK (csave, VARID_ISLOOPING, m_IsLooping);
|
|
WRITE_MICRO_CHUNK (csave, VARID_TOTAL_DIST, m_TotalDist);
|
|
|
|
PathClass *this_ptr = this;
|
|
WRITE_MICRO_CHUNK (csave, VARID_OLD_PTR, this_ptr);
|
|
|
|
//
|
|
// Save each of the action nodes for this path
|
|
//
|
|
for (int index = 0; index < m_PathActions.Count (); index ++) {
|
|
PATH_NODE &node = m_PathActions[index];
|
|
WRITE_MICRO_CHUNK (csave, VARID_PATH_ACTION, node);
|
|
}
|
|
|
|
csave.End_Chunk ();
|
|
return ;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Load
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
PathClass::Load (ChunkLoadClass &cload)
|
|
{
|
|
while (cload.Open_Chunk ()) {
|
|
switch (cload.Cur_Chunk_ID ()) {
|
|
|
|
case CHUNKID_SPLINE:
|
|
{
|
|
//
|
|
// Use the spline's factory to load it from disk
|
|
//
|
|
if (cload.Open_Chunk ()) {
|
|
PersistFactoryClass *factory = SaveLoadSystemClass::Find_Persist_Factory (cload.Cur_Chunk_ID ());
|
|
if (factory != NULL) {
|
|
m_Spline = (Curve3DClass *)factory->Load (cload);
|
|
}
|
|
cload.Close_Chunk ();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case CHUNKID_VARIABLES:
|
|
Load_Variables (cload);
|
|
break;
|
|
}
|
|
|
|
cload.Close_Chunk ();
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Load_Variables
|
|
//
|
|
///////////////////////////////////////////////////////////////////////
|
|
void
|
|
PathClass::Load_Variables (ChunkLoadClass &cload)
|
|
{
|
|
PathClass *old_ptr = NULL;
|
|
|
|
//
|
|
// Loop through all the microchunks that define the variables
|
|
//
|
|
while (cload.Open_Micro_Chunk ()) {
|
|
switch (cload.Cur_Micro_Chunk_ID ()) {
|
|
|
|
READ_MICRO_CHUNK (cload, VARID_STATE, m_State);
|
|
READ_MICRO_CHUNK (cload, VARID_TRAVERSAL_TYPE, m_TraversalType);
|
|
READ_MICRO_CHUNK (cload, VARID_START_POS, m_StartPos);
|
|
READ_MICRO_CHUNK (cload, VARID_DEST_POS, m_DestPos);
|
|
READ_MICRO_CHUNK (cload, VARID_EXPECTED_POS, m_ExpectedPos);
|
|
READ_MICRO_CHUNK (cload, VARID_LOOK_AHEAD_DIST, m_LookAheadDist);
|
|
READ_MICRO_CHUNK (cload, VARID_LOOK_AHEAD_TIME, m_LookAheadTime);
|
|
READ_MICRO_CHUNK (cload, VARID_MOVEMENT_RADIUS, m_MovementRadius);
|
|
READ_MICRO_CHUNK (cload, VARID_SPLINE_TIME, m_SplineTime);
|
|
READ_MICRO_CHUNK (cload, VARID_VELOCITY, m_Velocity);
|
|
READ_MICRO_CHUNK (cload, VARID_CURRENT_ACTION, m_CurrentAction);
|
|
READ_MICRO_CHUNK (cload, VARID_MOVEMENT_DIRECTIONS, m_MovementDirections);
|
|
READ_MICRO_CHUNK (cload, VARID_PATH_OBJECT, m_PathObject);
|
|
READ_MICRO_CHUNK (cload, VARID_START_TIME, m_StartTime);
|
|
READ_MICRO_CHUNK (cload, VARID_END_TIME, m_EndTime);
|
|
READ_MICRO_CHUNK (cload, VARID_ISLOOPING, m_IsLooping);
|
|
READ_MICRO_CHUNK (cload, VARID_TOTAL_DIST, m_TotalDist);
|
|
READ_MICRO_CHUNK (cload, VARID_OLD_PTR, old_ptr);
|
|
|
|
case VARID_PATH_ACTION:
|
|
{
|
|
//
|
|
// Read the action information from disk and add it to our list
|
|
//
|
|
PATH_NODE node;
|
|
LOAD_MICRO_CHUNK (cload, node);
|
|
m_PathActions.Add (node);
|
|
}
|
|
break;
|
|
}
|
|
|
|
cload.Close_Micro_Chunk ();
|
|
}
|
|
|
|
//
|
|
// Register our old ptr so other objects can remap to us
|
|
//
|
|
if (old_ptr != NULL) {
|
|
SaveLoadSystemClass::Register_Pointer( old_ptr, this );
|
|
}
|
|
|
|
return ;
|
|
}
|