This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
CnC_Renegade/Code/wwphys/pathsolve.cpp

1945 lines
49 KiB
C++
Raw Permalink Normal View History

/*
** 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 : wwphys *
* *
* $Archive:: /Commando/Code/wwphys/pathsolve.cpp $*
* *
* Author:: Patrick Smith *
* *
* $Modtime:: 12/10/01 12:40p $*
* *
* $Revision:: 20 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "pathsolve.h"
#include <windows.h>
#include "pathfind.h"
#include "pathfindportal.h"
#include "pathnode.h"
#include "pathdebugplotter.h"
#include "pathobject.h"
#include "accessiblephys.h"
#include "chunkio.h"
#include "pathmgr.h"
#include "wwmemlog.h"
#include "systimer.h"
////////////////////////////////////////////////////////////////
// Save/Load constants
////////////////////////////////////////////////////////////////
enum
{
CHUNKID_VARIABLES = 0x11040604,
};
enum
{
VARID_STARTPOS = 1,
VARID_DESTPOS,
VARID_START_SECTOR,
VARID_DEST_SECTOR,
VARID_PATH_OBJECT,
VARID_OLD_PTR,
VARID_PRIORITY
};
///////////////////////////////////////////////////////////////////////////
// Static member initialization
///////////////////////////////////////////////////////////////////////////
__int64 PathSolveClass::_TicksPerMilliSec = 0;
///////////////////////////////////////////////////////////////////////////
// Local inlines
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
//
// Get_Time
//
///////////////////////////////////////////////////////////////////////////
static inline __int64
Get_Time (void)
{
__int64 curr_time = 0;
::QueryPerformanceCounter ((LARGE_INTEGER *)&curr_time);
return curr_time;
}
///////////////////////////////////////////////////////////////////////////
//
// Shrink_Box
//
///////////////////////////////////////////////////////////////////////////
static inline void
Shrink_Box (AABoxClass &box, float width)
{
if (box.Extent.X > width) {
box.Extent.X -= width;
} else {
box.Extent.X = 0.25F;
}
if (box.Extent.Y > width) {
box.Extent.Y -= width;
} else {
box.Extent.Y = 0.25F;
}
return ;
}
///////////////////////////////////////////////////////////////////////////
//
// Clip_Point
//
///////////////////////////////////////////////////////////////////////////
static inline bool
Clip_Point (Vector3 *point, const AABoxClass &box)
{
Vector3 temp_point = *point;
//
// Clip the temporary point
//
temp_point.X = max (temp_point.X, box.Center.X - box.Extent.X);
temp_point.Y = max (temp_point.Y, box.Center.Y - box.Extent.Y);
temp_point.Z = max (temp_point.Z, box.Center.Z - box.Extent.Z);
temp_point.X = min (temp_point.X, box.Center.X + box.Extent.X);
temp_point.Y = min (temp_point.Y, box.Center.Y + box.Extent.Y);
temp_point.Z = min (temp_point.Z, box.Center.Z + box.Extent.Z);
//
// Did the clip change the point?
//
bool retval = (point->X != temp_point.X);
retval |= (point->Y != temp_point.Y);
retval |= (point->Z != temp_point.Z);
//
// Pass the new point back to the caller
//
(*point) = temp_point;
return retval;
}
///////////////////////////////////////////////////////////////////////////
//
// Clip_Point
//
///////////////////////////////////////////////////////////////////////////
static inline void
Clip_Point (Vector3 *point, const AABoxClass &box, float edge_dist)
{
Vector3 temp_point = *point;
AABoxClass clip_box = box;
if (clip_box.Extent.X > edge_dist) {
clip_box.Extent.X -= edge_dist;
} else {
clip_box.Extent.X = 0;
}
if (clip_box.Extent.Y > edge_dist) {
clip_box.Extent.Y -= edge_dist;
} else {
clip_box.Extent.Y = 0;
}
//
// Clip the temporary point
//
temp_point.X = max (temp_point.X, clip_box.Center.X - clip_box.Extent.X);
temp_point.Y = max (temp_point.Y, clip_box.Center.Y - clip_box.Extent.Y);
temp_point.Z = max (temp_point.Z, clip_box.Center.Z - clip_box.Extent.Z);
temp_point.X = min (temp_point.X, clip_box.Center.X + clip_box.Extent.X);
temp_point.Y = min (temp_point.Y, clip_box.Center.Y + clip_box.Extent.Y);
temp_point.Z = min (temp_point.Z, clip_box.Center.Z + clip_box.Extent.Z);
//
// Pass the new point back to the caller
//
(*point) = temp_point;
return ;
}
///////////////////////////////////////////////////////////////////////////
//
// PathSolveClass
//
///////////////////////////////////////////////////////////////////////////
PathSolveClass::PathSolveClass (void)
: m_StartPos (0, 0, 0),
m_DestPos (0, 0, 0),
m_StartSector (NULL),
m_DestSector (NULL),
m_CompletedNode (NULL),
m_State (ERROR_INVALID_START_POS),
m_BinaryHeap (10000),
m_Priority (0.5F),
m_BirthTime (0)
{
//
// Determine how many performance-counter ticks
// per millisecond we will get.
//
if (_TicksPerMilliSec == 0) {
::QueryPerformanceFrequency ((LARGE_INTEGER *)&_TicksPerMilliSec);
_TicksPerMilliSec /= 1000;
}
m_NodeList.Set_Growth_Step (500);
return ;
}
///////////////////////////////////////////////////////////////////////////
//
// PathSolveClass
//
///////////////////////////////////////////////////////////////////////////
PathSolveClass::PathSolveClass (const Vector3 &start, const Vector3 &dest)
: m_StartPos (0, 0, 0),
m_DestPos (0, 0, 0),
m_StartSector (NULL),
m_DestSector (NULL),
m_CompletedNode (NULL),
m_State (ERROR_INVALID_START_POS),
m_BinaryHeap (10000),
m_Priority (0.5F),
m_BirthTime (0)
{
//
// Determine how many performance-counter ticks
// per millisecond we will get.
//
if (_TicksPerMilliSec == 0) {
::QueryPerformanceFrequency ((LARGE_INTEGER *)&_TicksPerMilliSec);
_TicksPerMilliSec /= 1000;
}
Reset (start, dest);
m_NodeList.Set_Growth_Step (500);
return ;
}
///////////////////////////////////////////////////////////////////////////
//
// ~PathSolveClass
//
///////////////////////////////////////////////////////////////////////////
PathSolveClass::~PathSolveClass (void)
{
Reset_Lists ();
return ;
}
///////////////////////////////////////////////////////////////////////////
//
// Reset
//
///////////////////////////////////////////////////////////////////////////
PathSolveClass::STATE_DESC
PathSolveClass::Reset
(
const Vector3 &start,
const Vector3 &dest,
float sector_fudge
)
{
WWMEMLOG(MEM_PATHFIND);
Reset_Lists ();
m_StartPos = start;
m_DestPos = dest;
Initialize (sector_fudge);
return m_State;
}
///////////////////////////////////////////////////////////////////////////
//
// Begin_Distributed_Solve
//
///////////////////////////////////////////////////////////////////////////
/*void
PathSolveClass::Begin_Distributed_Solve (void)
{
for (int index = 0; index < m_NodeList.Count (); index ++) {
PathNodeClass *node = m_NodeList[index];
if (node != NULL) {
node->Reconnect_To_Portal ();
}
}
return ;
}*/
///////////////////////////////////////////////////////////////////////////
//
// Unlink_Pathfind_Hooks
//
///////////////////////////////////////////////////////////////////////////
void
PathSolveClass::Unlink_Pathfind_Hooks (void)
{
for (int index = 0; index < m_NodeList.Count (); index ++) {
m_NodeList[index]->Disconnect_From_Portal ();
}
return ;
}
///////////////////////////////////////////////////////////////////////////
//
// Resolve_Path
//
///////////////////////////////////////////////////////////////////////////
void
PathSolveClass::Resolve_Path (unsigned int milliseconds)
{
WWMEMLOG(MEM_PATHFIND);
__int64 start_time = Get_Time ();
__int64 end_time = start_time + (((__int64)milliseconds) * _TicksPerMilliSec);
int iterations = 0;
//Begin_Distributed_Solve ();
do
{
//
// Pop the least cost path 'node' from the open list and process
// all of its adjacent sectors.
//
PathNodeClass *node = (PathNodeClass *)m_BinaryHeap.Remove_Min ();
//
// Have we found our path?
//
if (node == NULL) {
m_State = ERROR_NO_PATH;
} else if (node->Peek_Sector () == m_DestSector) {
m_State = SOLVED_PATH;
//
// Record this path as 'final'
//
m_CompletedNode = node;
m_CompletedNode->Add_Ref ();
//
// Mark all the nodes that are on the final path
//
for ( PathNodeClass *path_node = m_CompletedNode;
path_node != NULL;
path_node = path_node->Peek_Parent_Node ())
{
path_node->On_Final_Path (true);
}
//
// 'Straighten' the path as much as possible
//
Post_Process_Path ();
} else {
Process_Portals (node);
}
iterations ++;
//
//
// Keep going until we've either found the path, or we ran out of time.
//
} while ((m_State == THINKING) && (Get_Time () < end_time));
//End_Distributed_Solve ();
//WWDebug_Printf ("Time spent in pathfind: %d 1/100 millis, loops = %d, finished = %d.\r\n", (unsigned int)((Get_Time () - start_time) / (_TicksPerMilliSec/100)), iterations, (int)(m_State == TRAVERSING_PATH));
return ;
}
///////////////////////////////////////////////////////////////////////////
//
// Timestep
//
///////////////////////////////////////////////////////////////////////////
PathSolveClass::STATE_DESC
PathSolveClass::Timestep (unsigned int milliseconds)
{
//
// Spend some time trying to resolve the path
//
if (m_State == THINKING) {
Resolve_Path (milliseconds);
}
return m_State;
}
///////////////////////////////////////////////////////////////////////////
//
// Initialize
//
///////////////////////////////////////////////////////////////////////////
void
PathSolveClass::Initialize (float sector_fudge)
{
m_State = THINKING;
m_CompletedNode = NULL;
m_BirthTime = TIMEGETTIME ();
//
// Lookup the start and destination sectors
//
m_StartSector = PathfindClass::Get_Instance ()->Find_Sector (m_StartPos, sector_fudge);
m_DestSector = PathfindClass::Get_Instance ()->Find_Sector (m_DestPos, sector_fudge);
//
// Sanity check, are the starting and ending points
// inside of pathfind sectors?
//
if (m_StartSector == NULL) {
m_State = ERROR_INVALID_START_POS;
} else if (m_DestSector == NULL) {
m_State = ERROR_INVALID_DEST_POS;
} else {
//
// Clip the start and destination points inside the sectors if necessary.
//
//if (sector_fudge > 0) {
//::Clip_Point (&m_StartPos, m_StartSector->Get_Bounding_Box (), m_PathObject.Get_Width ());
::Clip_Point (&m_StartPos, m_StartSector->Get_Bounding_Box ());
::Clip_Point (&m_DestPos, m_DestSector->Get_Bounding_Box ());
//}
}
return ;
}
///////////////////////////////////////////////////////////////////////////
//
// Process_Initial_Sector
//
///////////////////////////////////////////////////////////////////////////
void
PathSolveClass::Process_Initial_Sector (void)
{
if (m_StartSector == NULL) {
return ;
}
//
// Create a set of path 'nodes' that represent all the portals of the
// starting sector.
//
int index = m_StartSector->Get_Portal_Count ();
while (index --) {
PathfindPortalClass *portal = m_StartSector->Peek_Portal (index);
if (portal != NULL) {
AABoxClass portal_box;
portal->Get_Bounding_Box (portal_box);
//
// Find where we should enter the portal...
//
Vector3 dest_point = m_StartPos;
::Clip_Point (&dest_point, portal_box, m_PathObject.Get_Width ());
dest_point.Z = portal_box.Center.Z - (portal_box.Extent.Z - 0.1F);
//
// Determine how we should be facing when we enter the portal.
//
Vector3 x_vector = dest_point - m_StartPos;
Vector3 y_vector;
Vector3 z_vector (0, 0, 1);
x_vector.Normalize ();
y_vector = Vector3::Cross_Product (x_vector, z_vector);
Matrix3D ending_tm (x_vector, y_vector, z_vector, dest_point);
//
// Sumbit a node for this portal
//
if (Does_Object_Have_Access_To_Portal (portal)) {
Submit_Node ( 0,
NULL,
portal,
portal->Peek_Dest_Sector (m_StartSector),
Matrix3D (m_StartPos),
ending_tm);
}
}
}
if (m_StartSector == m_DestSector) {
//
// If the start and destination are the same
// sector, then we can just beeline.
//
m_State = SOLVED_PATH;
m_Path.Delete_All ();
m_Path.Add (PathDataStruct (NULL, m_StartPos));
m_Path.Add (PathDataStruct (NULL, m_DestPos));
} else {
m_State = THINKING;
}
return ;
}
//
//
// Steps:
//
//
// - For each portal in the current sector:
// - Can the object drive to that portal?
// - Yes: Can the object fit in the portal?
// - Yes: Can the object fit in the dest sector?
// - Yes: Record the node, stick it on the open list.
// - No: Punt
// - No: Punt
// - No: Punt
//
//
//
//
///////////////////////////////////////////////////////////////////////////
//
// Find_Tangent_Angle
//
///////////////////////////////////////////////////////////////////////////
float
Find_Tangent_Angle
(
const Vector2 & center,
float radius,
const Vector2 & point,
bool clockwise
)
{
float angle = 0;
//
// Calculate the distance from the point to the center of the circle
//
float delta_x = point.X - center.X;
float delta_y = point.Y - center.Y;
float dist = ::sqrt (delta_x * delta_x + delta_y * delta_y);
//
// Determine the offset angle (from the line between the point and center)
// where the 2 tangent points lie.
//
float angle_offset = WWMath::Acos (radius / dist);
float base_angle = WWMath::Atan2 (delta_x, -delta_y);
base_angle = WWMath::Wrap (base_angle, 0, WWMATH_PI * 2);
//
// Return which ever tangent point we would come across first, depending
// on our orientation
//
if (clockwise) {
angle = (WWMATH_PI * 3) - (base_angle + angle_offset);
} else {
angle = base_angle - angle_offset;
}
angle = WWMath::Wrap (angle, 0, WWMATH_PI * 2);
return angle;
}
///////////////////////////////////////////////////////////////////////////
//
// Get_Turn_Arc_Center
//
///////////////////////////////////////////////////////////////////////////
void
Get_Turn_Arc_Center
(
float wheel_offset,
float turn_radius,
bool left_turn,
Vector2 * arc_center
)
{
arc_center->X = wheel_offset;
if (left_turn) {
arc_center->Y = turn_radius;
} else {
arc_center->Y = -turn_radius;
}
return ;
}
///////////////////////////////////////////////////////////////////////////
//
// Find_Adjacent_Portals
//
///////////////////////////////////////////////////////////////////////////
void
Find_Adjacent_Portals
(
PathfindSectorClass *sector,
PathfindPortalClass *portal,
PathfindSectorClass *dest_sector,
PathfindPortalClass **portal1,
PathfindPortalClass **portal2
)
{
AABoxClass portal_box;
portal->Get_Bounding_Box (portal_box);
float x_value1 = 0;
float x_value2 = 0;
float y_value1 = 0;
float y_value2 = 0;
bool compare_x = false;
if (portal_box.Extent.X > portal_box.Extent.Y) {
x_value1 = portal_box.Center.X - portal_box.Extent.X;
x_value2 = portal_box.Center.X + portal_box.Extent.X;
y_value1 = portal_box.Center.Y;
y_value2 = portal_box.Center.Y;
compare_x = true;
} else {
x_value1 = portal_box.Center.X;
x_value2 = portal_box.Center.X;
y_value1 = portal_box.Center.Y - portal_box.Extent.Y;
y_value2 = portal_box.Center.Y + portal_box.Extent.Y;
compare_x = false;
}
int index = sector->Get_Portal_Count ();
while (index --) {
PathfindPortalClass *curr_portal = sector->Peek_Portal (index);
if (curr_portal && curr_portal != portal) {
AABoxClass curr_box;
curr_portal->Get_Bounding_Box (curr_box);
if (compare_x) {
float curr_x1 = curr_box.Center.X + curr_box.Extent.X;
float curr_x2 = curr_box.Center.X - curr_box.Extent.X;
float curr_y = curr_box.Center.Y;
//
// Is this the y-value we are looking for?
//
if (WWMath::Fabs (curr_y - y_value1) <= WWMATH_EPSILON) {
//
// Is this either the left or right adjacent portal?
//
if (WWMath::Fabs (curr_x1 - x_value1) <= WWMATH_EPSILON) {
(*portal1) = curr_portal;
} else if (WWMath::Fabs (curr_x2 - x_value2) <= WWMATH_EPSILON) {
(*portal2) = curr_portal;
}
}
} else {
float curr_y1 = curr_box.Center.Y + curr_box.Extent.Y;
float curr_y2 = curr_box.Center.Y - curr_box.Extent.Y;
float curr_x = curr_box.Center.X;
//
// Is this the x-value we are looking for?
//
if (WWMath::Fabs (curr_x - x_value1) <= WWMATH_EPSILON) {
//
// Is this either the up or down adjacent portal?
//
if (WWMath::Fabs (curr_y1 - y_value1) <= WWMATH_EPSILON) {
(*portal1) = curr_portal;
} else if (WWMath::Fabs (curr_y2 - y_value2) <= WWMATH_EPSILON) {
(*portal2) = curr_portal;
}
}
}
}
}
return ;
}
///////////////////////////////////////////////////////////////////////////
//
// Does_Object_Fit_Through_Portal
//
///////////////////////////////////////////////////////////////////////////
bool
Does_Object_Fit_Through_Portal
(
const PathObjectClass & object,
PathfindSectorClass * sector,
PathfindPortalClass * portal
)
{
bool retval = false;
AABoxClass portal_box;
AABoxClass obj_box;
portal->Get_Bounding_Box (portal_box);
object.Get_Collision_Box (obj_box);
float min_pos = 0;
float max_pos = 0;
if (portal_box.Extent.X > portal_box.Extent.Y) {
min_pos = portal_box.Center.X - portal_box.Extent.X;
max_pos = portal_box.Center.X + portal_box.Extent.X;
} else {
min_pos = portal_box.Center.Y - portal_box.Extent.Y;
max_pos = portal_box.Center.Y + portal_box.Extent.Y;
}
//
// Can we fit through this portal?
//
if (object.Get_Width () <= (max_pos - min_pos)) {
retval = true;
} else {
//
// This object could not fit through the given portal, so try to
// find any adjacent portals that might pass through a different
// sector but would end up in the correct sector. We will combine
// the total length of these portals and see if the object fits...
//
PathfindSectorClass *dest_sector = portal->Peek_Dest_Sector (sector);
//
// Try to find two adjacent portals which can make it to the destination
// sector...
//
PathfindPortalClass *portal1 = NULL;
PathfindPortalClass *portal2 = NULL;
::Find_Adjacent_Portals (sector, portal, dest_sector, &portal1, &portal2);
//
// If the vehicle can fit in through the combined portals, then we will
// allow the vehicle to pass...
//
if (portal1 != NULL || portal2 != NULL) {
//
// We assume portal1 is 'less-then' the test portal, so use its
// position and size to determine the lower bounds of our extended portal.
//
if (portal1 != NULL) {
AABoxClass temp_box;
portal1->Get_Bounding_Box (temp_box);
if (portal_box.Extent.X > portal_box.Extent.Y) {
min_pos = temp_box.Center.X - temp_box.Extent.X;
} else {
min_pos = temp_box.Center.Y - temp_box.Extent.Y;
}
}
//
// We assume portal2 is 'greater-then' the test portal, so use its
// position and size to determine the upper bounds of our extended portal.
//
if (portal2 != NULL) {
AABoxClass temp_box;
portal2->Get_Bounding_Box (temp_box);
if (portal_box.Extent.X > portal_box.Extent.Y) {
max_pos = temp_box.Center.X + temp_box.Extent.X;
} else {
max_pos = temp_box.Center.Y + temp_box.Extent.Y;
}
}
//
// Can we fit through this extended portal?
//
if (object.Get_Width () <= (max_pos - min_pos)) {
retval = true;
}
}
}
return retval;
}
///////////////////////////////////////////////////////////////////////////
//
// Does_Object_Have_Access_To_Portal
//
///////////////////////////////////////////////////////////////////////////
bool
PathSolveClass::Does_Object_Have_Access_To_Portal (PathfindPortalClass *portal)
{
bool retval = true;
//
// Does this portal interact with some sort of mechanism
//
PathfindActionPortalClass *action_portal = portal->As_PathfindActionPortalClass ();
if ( action_portal != NULL &&
action_portal->Get_Action_Type () == PathClass::ACTION_MECHANISM)
{
//
// Lookup the mechanism this portal uses
//
uint32 mechanism_id = action_portal->Get_Mechanism_ID ();
StaticPhysClass *mechanism = PhysicsSceneClass::Get_Instance ()->Find_Static_Object (mechanism_id);
if (mechanism != NULL) {
AccessiblePhysClass *accessible_obj = mechanism->As_AccessiblePhysClass ();
//
// See if we can unlock this mechanism
//
if (accessible_obj != NULL && accessible_obj->Get_Lock_Code () != 0) {
retval = accessible_obj->Can_Unlock (m_PathObject.Get_Key_Ring ());
}
}
}
return retval;
}
///////////////////////////////////////////////////////////////////////////
//
// Can_Object_Go_Through_Portal
//
///////////////////////////////////////////////////////////////////////////
bool
PathSolveClass::Can_Object_Go_Through_Portal
(
const Matrix3D & current_tm,
PathfindSectorClass * sector,
PathfindPortalClass * portal,
Matrix3D * ending_tm
)
{
bool retval = false;
//
// Calculate the size of the portal we are trying
// to pass through
//
AABoxClass portal_box;
portal->Get_Bounding_Box (portal_box);
//
// Can we fit through this portal?
//
if ( portal->Does_Size_Matter () == false ||
::Does_Object_Fit_Through_Portal (m_PathObject, sector, portal))
{
//
// Find where we should enter the portal...
//
Vector3 curr_pos = current_tm.Get_Translation ();
Vector3 dest_point = curr_pos;
if (portal->As_PathfindActionPortalClass () != NULL) {
dest_point = portal_box.Center;
//::Clip_Point (&dest_point, portal_box, m_PathObject.Get_Width ());
} else {
::Clip_Point (&dest_point, portal_box, m_PathObject.Get_Width () * 2);
}
dest_point.Z = portal_box.Center.Z - (portal_box.Extent.Z - 0.1F);
//
// Calculate a complete transform for the portal enter position
//
Vector3 x_vector = dest_point - curr_pos;
Vector3 y_vector;
Vector3 z_vector (0, 0, 1);
x_vector.Normalize ();
y_vector = Vector3::Cross_Product (x_vector, z_vector);
ending_tm->Set (x_vector, y_vector, z_vector, dest_point);
retval = true;
}
return retval;
}
///////////////////////////////////////////////////////////////////////////
//
// Can_Object_Enter_Portal
//
///////////////////////////////////////////////////////////////////////////
bool
Can_Object_Enter_Portal
(
const AABoxClass & portal_box,
const PathObjectClass & object,
const Vector3 & direction_vector
)
{
bool retval = false;
AABoxClass obj_box;
object.Get_Collision_Box (obj_box);
//
// Can this object be rotated about Z?
//
if (object.Is_Flag_Set (PathObjectClass::CAN_BOX_ROTATE)) {
//
// Build a rotation matrix from the direction vector
//
Vector3 x_vector = ::Normalize (direction_vector);
Vector3 z_vector (0, 0, 1);
Vector3 y_vector = Vector3::Cross_Product (x_vector, z_vector);
Matrix3 tm (x_vector, y_vector, z_vector);
//
// Rotate the edges of the box (toss the z component, we don't care)
//
Vector3 v1 (obj_box.Extent.X, -obj_box.Extent.Y, 0);
Vector3 v2 (obj_box.Extent.X, obj_box.Extent.Y, 0);
Vector3 v3 (-obj_box.Extent.X, obj_box.Extent.Y, 0);
Vector3 v4 (-obj_box.Extent.X, -obj_box.Extent.Y, 0);
v1 = tm * v1;
v2 = tm * v2;
v3 = tm * v3;
v4 = tm * v4;
//
// Determine the new axis-aligned extents from this rotated box
//
float max_x = max (WWMath::Fabs (v1.X), WWMath::Fabs (v2.X));
max_x = max (max_x, WWMath::Fabs (v3.X));
max_x = max (max_x, WWMath::Fabs (v4.X));
float max_y = max (WWMath::Fabs (v1.Y), WWMath::Fabs (v2.Y));
max_y = max (max_y, WWMath::Fabs (v3.Y));
max_y = max (max_y, WWMath::Fabs (v4.Y));
obj_box.Extent.X = max_x / 2;
obj_box.Extent.Y = max_y / 2;
}
//
// Portal boxes are generally long and very skinny.
// So skinny in fact they are conceptually planes.
// We find out which direction they are skinny in
// and simulate 'shoving' the object through in
// that direction.
//
if (portal_box.Extent.X > portal_box.Extent.Y) {
retval = (obj_box.Extent.X < portal_box.Extent.X);
} else {
retval = (obj_box.Extent.Y < portal_box.Extent.Y);
}
return retval;
}
///////////////////////////////////////////////////////////////////////////
//
// Process_Portals
//
///////////////////////////////////////////////////////////////////////////
void
PathSolveClass::Process_Portals (PathNodeClass *node)
{
PathfindSectorClass *sector = node->Peek_Sector ();
PathfindPortalClass *last_portal = node->Peek_Portal ();
const Matrix3D &current_tm = node->Get_Transform ();
//
// Loop over all the portals that this sector has access to
//
for (int index = 0; index < sector->Get_Portal_Count (); index ++) {
PathfindPortalClass *portal = sector->Peek_Portal (index);
//
// Don't process any portals we can't get to from the current portal
//
if (sector->Can_Access_Portal (last_portal, portal)) {
//
// Get this portal's destination
//
PathfindSectorClass *dest_sector = portal->Peek_Dest_Sector (sector);
if (dest_sector != NULL) {
//
// Determine if we can pass through this portal, and if so where
// we should pass through...
//
Matrix3D ending_tm (1);
if ( Does_Object_Have_Access_To_Portal (portal) &&
Can_Object_Go_Through_Portal (current_tm, sector, portal, &ending_tm))
{
//
// Submit a new node for the destination sector
//
Submit_Node (node->Get_Traversal_Cost (), node, portal,
dest_sector, current_tm, ending_tm);
}
}
}
}
if (last_portal != NULL) {
last_portal->m_ClosedListPtr = node;
}
return ;
}
///////////////////////////////////////////////////////////////////////////
//
// Calculate_Heuristic_Cost
//
///////////////////////////////////////////////////////////////////////////
float
Calculate_Heuristic_Cost
(
const PathObjectClass & object,
const PathfindPortalClass &portal,
const PathfindSectorClass &sector,
const Vector3 & curr_pos,
const Vector3 & dest_pos
)
{
float cost = (curr_pos - dest_pos).Length () * 0.9F;
if (sector.As_PathfindWaypathSectorClass () != NULL) {
//
// Make waypath sectors slightly more appealing
//
cost = cost * 0.5F;
} else if (object.Is_Flag_Set (PathObjectClass::IS_VEHICLE)) {
//
// Do some special case heuristics for vehicles
//
//
// Make one-way portals very expensive for vehicles
//
if (portal.Does_Size_Matter () && portal.Is_Two_Way_Portal () == false) {
cost = cost * 2.0F;
}
AABoxClass sector_box = sector.Get_Bounding_Box ();
float sector_area = (sector_box.Extent.X * sector_box.Extent.Y);
//AABoxClass object_box;
//object.Get_Collision_Box (object_box);
//float object_area = (object_box.Extent.X * object_box.Extent.Y);
float object_area = 50.0F;
float factor = (object_area / sector_area);
factor = max (factor, 0.01F);
factor = min (factor, 100.0F);
cost = cost * factor;
}
return cost;
}
///////////////////////////////////////////////////////////////////////////
//
// Submit_Node
//
///////////////////////////////////////////////////////////////////////////
void
PathSolveClass::Submit_Node
(
float traversal_cost,
PathNodeClass * current_node,
PathfindPortalClass * portal,
PathfindSectorClass * dest_sector,
const Matrix3D & current_tm,
const Matrix3D & ending_tm
)
{
const Vector3 &current_pos = current_tm.Get_Translation ();
const Vector3 &dest_pos = ending_tm.Get_Translation ();
//
// Calculate the total traversal cost for this path.
//
Vector3 dest_vector = current_pos - dest_pos;
dest_vector.Z = 0;
float dest_dist = dest_vector.Length ();
float current_traversal_cost = traversal_cost + dest_dist;
//
// Calculate the heuristic for this portal
//
float heuristic_cost = ::Calculate_Heuristic_Cost (m_PathObject, *portal, *dest_sector,
dest_pos, m_DestPos);
//
// Try to keep vehicles from taking one-way portals
//
if ( m_PathObject.Is_Flag_Set (PathObjectClass::IS_VEHICLE) &&
portal->Is_Two_Way_Portal () == false)
{
current_traversal_cost += dest_dist * 2.0F;
}
//
// Is this sector already in the open list?
//
int open_index = portal->Get_Heap_Location ();
if (open_index > 0) {
PathNodeClass *open_version = (PathNodeClass *)m_BinaryHeap.Peek_Node (open_index);
//
// If the traversal cost is lower from our current 'path', then
// remove the old path and re-insert the new path.
//
if (current_traversal_cost < open_version->Get_Traversal_Cost ()) {
open_version->Set_Sector (dest_sector);
open_version->Set_Parent_Node (current_node);
open_version->Set_Traversal_Cost (current_traversal_cost);
open_version->Set_Transform (ending_tm);
open_version->Set_Heuristic_Cost (heuristic_cost);
m_BinaryHeap.Percolate_Up (open_index);
}
} else {
//
// Is this sector already in the closed list?
//
if (portal->m_ClosedListPtr != NULL) {
PathNodeClass *closed_version = portal->m_ClosedListPtr;
//
// If the traversal cost is lower from our current 'path', then
// remove the old path and re-insert the new path.
//
if (current_traversal_cost < closed_version->Get_Traversal_Cost ()) {
portal->m_ClosedListPtr = NULL;
closed_version->Set_Sector (dest_sector);
closed_version->Set_Parent_Node (current_node);
closed_version->Set_Traversal_Cost (current_traversal_cost);
closed_version->Set_Transform (ending_tm);
closed_version->Set_Heuristic_Cost (heuristic_cost);
m_BinaryHeap.Insert (closed_version);
}
} else {
//
// Create a new path 'node' that represents that path from start
// up until this sector.
//
PathNodeClass *new_node = new PathNodeClass;
new_node->Set_Sector (dest_sector);
new_node->Set_Parent_Node (current_node);
new_node->Set_Portal (portal);
//
// Calculate the path's total traversal cost
//
new_node->Set_Traversal_Cost (current_traversal_cost);
new_node->Set_Transform (ending_tm);
new_node->Set_Heuristic_Cost (heuristic_cost);
//
// Insert this node into the open list
//
m_BinaryHeap.Insert (new_node);
//
// Keep track of this node's pointer (for cleanup)
//
m_NodeList.Add (new_node);
}
}
return ;
}
///////////////////////////////////////////////////////////////////////////
//
// Reset_Lists
//
///////////////////////////////////////////////////////////////////////////
void
PathSolveClass::Reset_Lists (void)
{
//
// Ensure we unlink our data before we destroy it...
//
if (PathMgrClass::Peek_Active_Path () == this) {
Unlink_Pathfind_Hooks ();
}
REF_PTR_RELEASE (m_CompletedNode);
//
// Free all our nodes
//
for (int index = 0; index < m_NodeList.Count (); index ++) {
PathNodeClass *node = m_NodeList[index];
REF_PTR_RELEASE (node);
}
m_BinaryHeap.Flush_Array ();
m_NodeList.Reset_Active ();
return ;
}
/////////////////////////////////////////////////////////////////////////////////
//
// Does_Line_Go_Through_Portal
//
/////////////////////////////////////////////////////////////////////////////////
bool
PathSolveClass::Does_Line_Go_Through_Portal
(
const Vector3 & start,
const Vector3 & end,
const AABoxClass & box
)
{
float delta_x = end.X - start.X;
float delta_y = end.Y - start.Y;
float percent = 1000;
if ((box.Extent.X <= box.Extent.Y) && (delta_x != 0)) {
//
// Calculate where the line intersects the X plane
//
percent = (box.Center.X - start.X) / delta_x;
} else if ((box.Extent.Y <= box.Extent.X) && (delta_y != 0)) {
//
// Calculate where the line intersects the Y plane
//
percent = (box.Center.Y - start.Y) / delta_y;
}
//
// Calculate the point where the line intersects one of the
// planes of the box.
//
Vector3 dest_point = start + ((end - start) * percent);
//
// Check to make sure this point isn't outside the bounds of the
// box, if it is outside the bounds, then the line DOES NOT pass
// through the portal.
//
bool retval = true;
if ((box.Center.X + box.Extent.X) < dest_point.X) retval = false;
if ((box.Center.Y + box.Extent.Y) < dest_point.Y) retval = false;
if ((box.Center.X - box.Extent.X) > dest_point.X) retval = false;
if ((box.Center.Y - box.Extent.Y) > dest_point.Y) retval = false;
return retval;
}
/////////////////////////////////////////////////////////////////////////////////
//
// Relax_Line
//
/////////////////////////////////////////////////////////////////////////////////
Vector3
PathSolveClass::Relax_Line (const Vector3 &start, const Vector3 &end, const AABoxClass &box)
{
Vector3 result = box.Center;
float delta_x = end.X - start.X;
float delta_y = end.Y - start.Y;
float percent = 1000;
if ((box.Extent.X < box.Extent.Y) && (delta_x != 0)) {
//
// Calculate where the line intersects the X plane
//
percent = (box.Center.X - start.X) / delta_x;
} else if ((box.Extent.Y < box.Extent.X) && (delta_y != 0)) {
//
// Calculate where the line intersects the Y plane
//
percent = (box.Center.Y - start.Y) / delta_y;
}
//
// Calculate the point where the line intersects one of the
// planes of the box.
//
//if (percent >= 0 && percent < 1.0F) {
result = start + ((end - start) * percent);
Clip_Point (&result, box, m_PathObject.Get_Width () * 3.0F);
//result = box.Center;
//result.Z = box.Center.Z - (box.Extent.Z - 0.1F);
/*} else {
int test = 0;
}*/
return result;
}
/////////////////////////////////////////////////////////////////////////////////
//
// Check_Line
//
// Note: The purpose of this function is to determine if the unit's collision
// box will be jutting outside the sector at the point where the path begins
// to enter the portal.
//
//
/////////////////////////////////////////////////////////////////////////////////
bool
Check_Line
(
const Vector3 & p0,
const Vector3 & p1,
const AABoxClass & sector,
const AABoxClass & portal,
float width,
Vector3 * result
)
{
bool retval = false;
if ( WWMath::Fabs (portal.Center.X - sector.Center.X + sector.Extent.X) < WWMATH_EPSILON ||
WWMath::Fabs (portal.Center.X - sector.Center.X - sector.Extent.X) < WWMATH_EPSILON)
{
float y_val0 = portal.Center.Y - portal.Extent.Y;
float y_val1 = portal.Center.Y + portal.Extent.Y;
bool do_it = false;
float y_val = 0;
if ( (p0.Y < y_val0 && p1.Y > y_val0) ||
(p0.Y > y_val0 && p1.Y < y_val0))
{
do_it = true;
y_val = y_val0;
} else if ( (p0.Y < y_val1 && p1.Y > y_val1) ||
(p0.Y > y_val1 && p1.Y < y_val1))
{
do_it = true;
y_val = y_val1;
}
if (do_it) {
float percent = (y_val - p0.Y) / (p1.Y - p0.Y);
Vector3 pos = p0 + (p1 - p0) * percent;
if (pos.X + width > (sector.Center.X + sector.Extent.X)) {
(*result) = pos;
result->X = (sector.Center.X + sector.Extent.X) - width;
result->X = max (result->X, sector.Center.X);
retval = true;
} else if (pos.X - width < (sector.Center.X - sector.Extent.X)) {
(*result) = pos;
result->X = (sector.Center.X - sector.Extent.X) + width;
result->X = min (result->X, sector.Center.X);
retval = true;
}
}
} else if ( WWMath::Fabs (portal.Center.Y - sector.Center.Y + sector.Extent.Y) < WWMATH_EPSILON ||
WWMath::Fabs (portal.Center.Y - sector.Center.Y - sector.Extent.Y) < WWMATH_EPSILON)
{
float x_val0 = portal.Center.X - portal.Extent.X;
float x_val1 = portal.Center.X + portal.Extent.X;
bool do_it = false;
float x_val = 0;
if ( (p0.X < x_val0 && p1.X > x_val0) ||
(p0.X > x_val0 && p1.X < x_val0))
{
do_it = true;
x_val = x_val0;
} else if ( (p0.X < x_val1 && p1.X > x_val1) ||
(p0.X > x_val1 && p1.X < x_val1))
{
do_it = true;
x_val = x_val1;
}
if (do_it) {
float percent = (x_val - p0.X) / (p1.X - p0.X);
Vector3 pos = p0 + (p1 - p0) * percent;
if (pos.Y + width > (sector.Center.Y + sector.Extent.Y)) {
(*result) = pos;
result->Y = (sector.Center.Y + sector.Extent.Y) - width;
result->Y = max (result->Y, sector.Center.Y);
retval = true;
} else if (pos.Y - width < (sector.Center.Y - sector.Extent.Y)) {
(*result) = pos;
result->Y = (sector.Center.Y - sector.Extent.Y) + width;
result->Y = min (result->Y, sector.Center.Y);
retval = true;
}
}
}
return retval;
}
///////////////////////////////////////////////////////////////////////////
//
// Post_Process_Path
//
///////////////////////////////////////////////////////////////////////////
PathSolveClass::PATHNODE_LIST PathSolveClass::temp_node_list;
void
PathSolveClass::Post_Process_Path (void)
{
m_Path.Delete_All ();
//
// Build a list of the nodes (in order) the path passes through.
//
temp_node_list.Reset_Active();
for (PathNodeClass *node = m_CompletedNode; node != NULL; node = node->Peek_Parent_Node ()) {
temp_node_list.Add_Head (node);
}
//
// Just do it
//
m_Path.Add (PathDataStruct (NULL, m_StartPos));
m_Path[m_Path.Count () - 1].m_SectorCenter = m_StartSector->Get_Bounding_Box ().Center;
m_Path[m_Path.Count () - 1].m_SectorExtent = m_StartSector->Get_Bounding_Box ().Extent;
for (int index = 0; index < temp_node_list.Count (); index ++) {
PathNodeClass *node = temp_node_list[index];
PathfindPortalClass *portal = node->Peek_Portal ();
m_Path.Add (PathDataStruct (portal, node->Get_Position ()));
m_Path[m_Path.Count () - 1].m_SectorCenter.Set (0, 0, 0);
m_Path[m_Path.Count () - 1].m_SectorExtent.Set (0, 0, 0);
PathfindSectorClass *sector = node->Peek_Sector ();
if (sector != NULL) {
m_Path[m_Path.Count () - 1].m_SectorCenter = sector->Get_Bounding_Box ().Center;
m_Path[m_Path.Count () - 1].m_SectorExtent = sector->Get_Bounding_Box ().Extent;
}
}
m_Path.Add (PathDataStruct (NULL, m_DestPos));
m_Path[m_Path.Count () - 1].m_SectorCenter = m_DestSector->Get_Bounding_Box ().Center;
m_Path[m_Path.Count () - 1].m_SectorExtent = m_DestSector->Get_Bounding_Box ().Extent;
//
// Try to adjust the path so it doesn't make any sharp turns. To do this
// we will average the closest portal points starting from the beginning
// and then starting from the end.
//
typedef struct
{
Vector3 backward;
Vector3 forward;
} PATH_POINT;
int count = m_Path.Count ();
PATH_POINT *path_points = new PATH_POINT[count];
Vector3 next_point = m_DestPos;
for (index = m_Path.Count () - 2; index > 0; index --) {
//
// Do we have a portal we can clip the point to?
//
PathfindPortalClass *portal = m_Path[index].m_Portal;
if (portal != NULL && portal->As_PathfindActionPortalClass () == NULL) {
//
// Clip the point to the portal's box
//
AABoxClass portal_box;
portal->Get_Bounding_Box (portal_box);
Vector3 curr_point = next_point;
::Clip_Point (&curr_point, portal_box, m_PathObject.Get_Width () * 3.0F);
//
// Move the point to the floor
//
curr_point.Z = portal_box.Center.Z - portal_box.Extent.Z;
//
// Add this point to the array
//
path_points[index].backward = curr_point;
next_point = curr_point;
} else {
//
// No portal, so simply add the point to the array
//
path_points[index].backward = m_Path[index].m_Point;
next_point = m_Path[index].m_Point;
}
}
next_point = m_StartPos;
for (index = 1; index < m_Path.Count () - 1; index ++) {
//
// Do we have a portal we can clip the point to?
//
PathfindPortalClass *portal = m_Path[index].m_Portal;
if (portal != NULL && portal->As_PathfindActionPortalClass () == NULL) {
//
// Clip the point to the portal's box
//
AABoxClass portal_box;
portal->Get_Bounding_Box (portal_box);
Vector3 curr_point = next_point;
::Clip_Point (&curr_point, portal_box, m_PathObject.Get_Width () * 3.0F);
//
// Move the point to the floor
//
curr_point.Z = portal_box.Center.Z - portal_box.Extent.Z;
//
// Add this point to the array
//
path_points[index].forward = curr_point;
next_point = curr_point;
} else {
//
// No portal, so simply add the point to the array
//
path_points[index].forward = m_Path[index].m_Point;
next_point = m_Path[index].m_Point;
}
}
//
// Now average the points
//
for (index = 1; index < m_Path.Count () - 1; index ++) {
Vector3 avg_point = (path_points[index].forward + path_points[index].backward) * 0.5F;
m_Path[index].m_Point = avg_point;
}
delete [] path_points;
//
// Relax the points
//
for (index = m_Path.Count () - 2; index > 1; index --) {
Vector3 &prev_point = m_Path[index + 1].m_Point;
Vector3 &next_point = m_Path[index - 1].m_Point;
PathfindPortalClass *portal = m_Path[index].m_Portal;
if (portal != NULL && portal->As_PathfindActionPortalClass () == NULL) {
//
// Relax the between the last point and the next point, making
// sure the point lies in the poral box
//
AABoxClass portal_box;
portal->Get_Bounding_Box (portal_box);
m_Path[index].m_Point = Relax_Line (prev_point, next_point, portal_box);
}
}
//
// Ensure that no object will poke its bounding box outside of
// the sector it's in
//
Keep_Unit_Inside_Sectors ();
return ;
}
/////////////////////////////////////////////////////////////////////////////////
//
// Keep_Unit_Inside_Sectors
//
/////////////////////////////////////////////////////////////////////////////////
void
PathSolveClass::Keep_Unit_Inside_Sectors (void)
{
if (m_Path.Count () == 0) {
return ;
}
//
// Clip the starting point so that the unit will stay on the inside of its sector
//
Vector3 temp_point = m_Path[0].m_Point;
AABoxClass sector_box (m_Path[0].m_SectorCenter, m_Path[0].m_SectorExtent);
::Clip_Point (&temp_point, sector_box, m_PathObject.Get_Width () * 2);
m_Path[0].m_Point = temp_point;
//
// Now check each "line" along the path to see if the
// unit would jut out at any point
//
for (int index = 0; index < m_Path.Count () - 1; index ++) {
Vector3 curr_point = m_Path[index].m_Point;
Vector3 next_point = m_Path[index + 1].m_Point;
//
// Is the current point contained in a sector, and will it
// be passing through a portal
//
if ( (m_Path[index].m_SectorExtent.X > 0) ||
(m_Path[index].m_SectorExtent.Y > 0))
{
//
// Get the sector's box
//
AABoxClass sector_box (m_Path[index].m_SectorCenter, m_Path[index].m_SectorExtent);
float width = m_PathObject.Get_Width () * 3;
Vector3 start_point = curr_point;
//
// Lookup the current and next portals
//
PathfindPortalClass *prev_portal = m_Path[index].m_Portal;
PathfindPortalClass *next_portal = m_Path[index + 1].m_Portal;
//
// Test the object at the point it will be entering the sector
//
if (prev_portal != NULL && prev_portal->As_PathfindActionPortalClass () == NULL) {
//
// Get the portal's box
//
AABoxClass portal_box;
prev_portal->Get_Bounding_Box (portal_box);
//
// Check to see if the object will jut outside the sector as it moves
// along this line
//
Vector3 new_point (0, 0, 0);
if (::Check_Line (start_point, next_point, sector_box, portal_box, width, &new_point)) {
//
// Yup, we would jut outside the sector, so add a new point
// to avoid this
//
m_Path.Insert (index + 1, PathDataStruct (NULL, new_point));
index ++;
start_point = new_point;
}
}
//
// Test the object at the point it will be exiting the sector
//
if (next_portal != NULL && next_portal->As_PathfindActionPortalClass () == NULL) {
//
// Get the portal's box
//
AABoxClass portal_box;
next_portal->Get_Bounding_Box (portal_box);
//
// Check to see if the object will jut outside the sector as it moves
// along this line
//
Vector3 new_point (0, 0, 0);
if (::Check_Line (start_point, next_point, sector_box, portal_box, width, &new_point)) {
//
// Yup, we would jut outside the sector, so add a new point
// to avoid this
//
m_Path.Insert (index + 1, PathDataStruct (NULL, new_point));
index ++;
}
}
}
}
return ;
}
/////////////////////////////////////////////////////////////////////////////////
//
// Set_Path_Object
//
/////////////////////////////////////////////////////////////////////////////////
void
PathSolveClass::Set_Path_Object (PathObjectClass &path_object)
{
m_PathObject = path_object;
return ;
}
/////////////////////////////////////////////////////////////////////////////////
//
// Get_Path_Object
//
/////////////////////////////////////////////////////////////////////////////////
void
PathSolveClass::Get_Path_Object (PathObjectClass &path_object) const
{
path_object = m_PathObject;
return ;
}
/////////////////////////////////////////////////////////////////////////////////
//
// Get_Volumes
//
/////////////////////////////////////////////////////////////////////////////////
void
PathSolveClass::Get_Volumes
(
BOX_LIST &sector_list,
BOX_LIST &portal_list
)
{
//
// Build a list of the nodes (in order) the path passes through.
//
for ( PathNodeClass *node = m_CompletedNode;
node != NULL;
node = node->Peek_Parent_Node ())
{
//
// Add the box from the sector to the list
//
AABoxClass *box = new AABoxClass (node->Peek_Sector ()->Get_Bounding_Box ());
//box->Extent.X = max (0.0F, (box->Extent.X - m_PathObject.Get_Width () * 3));
//box->Extent.Y = max (0.0F, (box->Extent.Y - m_PathObject.Get_Width () * 3));
sector_list.Add (box);
//
// If this node has a portal, then add the portal's box to the list
//
if (node->Peek_Portal () != NULL) {
AABoxClass *portal_box = new AABoxClass;
node->Peek_Portal ()->Get_Bounding_Box (*portal_box);
portal_list.Add (portal_box);
}
}
return ;
}
////////////////////////////////////////////////////////////////////////////////////////////
//
// Save
//
////////////////////////////////////////////////////////////////////////////////////////////
void
PathSolveClass::Save (ChunkSaveClass &csave)
{
csave.Begin_Chunk (CHUNKID_VARIABLES);
//
// Save each variable to its own microchunk
//
WRITE_MICRO_CHUNK (csave, VARID_STARTPOS, m_StartPos);
WRITE_MICRO_CHUNK (csave, VARID_DESTPOS, m_DestPos);
WRITE_MICRO_CHUNK (csave, VARID_START_SECTOR, m_StartSector);
WRITE_MICRO_CHUNK (csave, VARID_DEST_SECTOR, m_DestSector);
WRITE_MICRO_CHUNK (csave, VARID_PATH_OBJECT, m_PathObject);
WRITE_MICRO_CHUNK (csave, VARID_PRIORITY, m_Priority);
PathSolveClass *this_ptr = this;
WRITE_MICRO_CHUNK (csave, VARID_OLD_PTR, this_ptr);
csave.End_Chunk ();
return ;
}
////////////////////////////////////////////////////////////////////////////////////////////
//
// Load
//
////////////////////////////////////////////////////////////////////////////////////////////
void
PathSolveClass::Load (ChunkLoadClass &cload)
{
while (cload.Open_Chunk ()) {
switch (cload.Cur_Chunk_ID ()) {
case CHUNKID_VARIABLES:
Load_Variables (cload);
break;
}
cload.Close_Chunk ();
}
SaveLoadSystemClass::Register_Post_Load_Callback (this);
return ;
}
///////////////////////////////////////////////////////////////////////
//
// On_Post_Load
//
///////////////////////////////////////////////////////////////////////
void
PathSolveClass::On_Post_Load (void)
{
//
// This basically kicks off the pathfind
//
Initialize (1.0F);
return ;
}
///////////////////////////////////////////////////////////////////////
//
// Load_Variables
//
///////////////////////////////////////////////////////////////////////
void
PathSolveClass::Load_Variables (ChunkLoadClass &cload)
{
PathSolveClass *old_ptr = NULL;
m_StartSector = NULL;
m_DestSector = 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_STARTPOS, m_StartPos);
READ_MICRO_CHUNK (cload, VARID_DESTPOS, m_DestPos);
READ_MICRO_CHUNK (cload, VARID_PATH_OBJECT, m_PathObject);
READ_MICRO_CHUNK (cload, VARID_OLD_PTR, old_ptr);
READ_MICRO_CHUNK (cload, VARID_PRIORITY, m_Priority);
}
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 ;
}