RingRacers/src/k_waypoint.c

1763 lines
52 KiB
C

// SONIC ROBO BLAST 2 KART
//-----------------------------------------------------------------------------
// Copyright (C) 2018-2020 by Sean "Sryder" Ryder
// Copyright (C) 2018-2020 by Kart Krew
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file k_waypoint.c
/// \brief Waypoint handling from the relevant mobjs
/// Setup and interfacing with waypoints for the main game
#include "k_waypoint.h"
#include "d_netcmd.h"
#include "p_local.h"
#include "p_tick.h"
#include "z_zone.h"
#include "g_game.h"
// The number of sparkles per waypoint connection in the waypoint visualisation
static const UINT32 SPARKLES_PER_CONNECTION = 16U;
// Some defaults for the size of the dynamically allocated sets for pathfinding. These are kept for the purpose of
// allocating a size that is less likely to need reallocating again during the pathfinding.
#define OPENSET_BASE_SIZE (16U)
#define CLOSEDSET_BASE_SIZE (256U)
#define NODESARRAY_BASE_SIZE (256U)
static waypoint_t *waypointheap = NULL;
static waypoint_t *firstwaypoint = NULL;
static waypoint_t *finishline = NULL;
static UINT32 circuitlength = 0U;
static size_t numwaypoints = 0U;
static size_t numwaypointmobjs = 0U;
static size_t baseopensetsize = OPENSET_BASE_SIZE;
static size_t baseclosedsetsize = CLOSEDSET_BASE_SIZE;
static size_t basenodesarraysize = NODESARRAY_BASE_SIZE;
/*--------------------------------------------------
waypoint_t *K_GetFinishLineWaypoint(void)
See header file for description.
--------------------------------------------------*/
waypoint_t *K_GetFinishLineWaypoint(void)
{
return finishline;
}
/*--------------------------------------------------
boolean K_GetWaypointIsFinishline(waypoint_t *waypoint)
See header file for description.
--------------------------------------------------*/
boolean K_GetWaypointIsFinishline(waypoint_t *waypoint)
{
boolean waypointisfinishline = false;
if (waypoint == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "NULL waypoint in K_GetWaypointIsFinishline.\n");
}
else if ((waypoint->mobj == NULL) || (P_MobjWasRemoved(waypoint->mobj) == true))
{
CONS_Debug(DBG_GAMELOGIC, "NULL waypoint mobj in K_GetWaypointIsFinishline.\n");
}
else
{
waypointisfinishline = (waypoint->mobj->extravalue2 == 1);
}
return waypointisfinishline;
}
/*--------------------------------------------------
boolean K_GetWaypointIsShortcut(waypoint_t *waypoint)
See header file for description.
--------------------------------------------------*/
boolean K_GetWaypointIsShortcut(waypoint_t *waypoint)
{
boolean waypointisshortcut = false;
if (waypoint == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "NULL waypoint in K_GetWaypointIsShortcut.\n");
}
else if ((waypoint->mobj == NULL) || (P_MobjWasRemoved(waypoint->mobj) == true))
{
CONS_Debug(DBG_GAMELOGIC, "NULL waypoint mobj in K_GetWaypointIsShortcut.\n");
}
else
{
waypointisshortcut = (waypoint->mobj->lastlook == 1);
}
return waypointisshortcut;
}
/*--------------------------------------------------
boolean K_GetWaypointIsEnabled(waypoint_t *waypoint)
See header file for description.
--------------------------------------------------*/
boolean K_GetWaypointIsEnabled(waypoint_t *waypoint)
{
boolean waypointisenabled = true;
if (waypoint == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "NULL waypoint in K_GetWaypointIsEnabled.\n");
}
else if ((waypoint->mobj == NULL) || (P_MobjWasRemoved(waypoint->mobj) == true))
{
CONS_Debug(DBG_GAMELOGIC, "NULL waypoint mobj in K_GetWaypointIsEnabled.\n");
}
else
{
waypointisenabled = (waypoint->mobj->extravalue1 == 1);
}
return waypointisenabled;
}
/*--------------------------------------------------
boolean K_GetWaypointIsSpawnpoint(waypoint_t *waypoint)
See header file for description.
--------------------------------------------------*/
boolean K_GetWaypointIsSpawnpoint(waypoint_t *waypoint)
{
boolean waypointisspawnpoint = true;
if (waypoint == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "NULL waypoint in K_GetWaypointIsEnabled.\n");
}
else if ((waypoint->mobj == NULL) || (P_MobjWasRemoved(waypoint->mobj) == true))
{
CONS_Debug(DBG_GAMELOGIC, "NULL waypoint mobj in K_GetWaypointIsEnabled.\n");
}
else
{
waypointisspawnpoint = (waypoint->mobj->reactiontime == 1);
}
return waypointisspawnpoint;
}
/*--------------------------------------------------
INT32 K_GetWaypointNextID(waypoint_t *waypoint)
See header file for description.
--------------------------------------------------*/
INT32 K_GetWaypointNextID(waypoint_t *waypoint)
{
INT32 nextwaypointid = -1;
if (waypoint == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "NULL waypoint in K_GetWaypointNextID.\n");
}
else if ((waypoint->mobj == NULL) || (P_MobjWasRemoved(waypoint->mobj) == true))
{
CONS_Debug(DBG_GAMELOGIC, "NULL waypoint mobj in K_GetWaypointNextID.\n");
}
else
{
nextwaypointid = waypoint->mobj->threshold;
}
return nextwaypointid;
}
/*--------------------------------------------------
INT32 K_GetWaypointID(waypoint_t *waypoint)
See header file for description.
--------------------------------------------------*/
INT32 K_GetWaypointID(waypoint_t *waypoint)
{
INT32 waypointid = -1;
if (waypoint == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "NULL waypoint in K_GetWaypointID.\n");
}
else if ((waypoint->mobj == NULL) || (P_MobjWasRemoved(waypoint->mobj) == true))
{
CONS_Debug(DBG_GAMELOGIC, "NULL waypoint mobj in K_GetWaypointID.\n");
}
else
{
waypointid = waypoint->mobj->movecount;
}
return waypointid;
}
/*--------------------------------------------------
UINT32 K_GetCircuitLength(void)
See header file for description.
--------------------------------------------------*/
UINT32 K_GetCircuitLength(void)
{
return circuitlength;
}
/*--------------------------------------------------
waypoint_t *K_GetClosestWaypointToMobj(mobj_t *const mobj)
See header file for description.
--------------------------------------------------*/
waypoint_t *K_GetClosestWaypointToMobj(mobj_t *const mobj)
{
waypoint_t *closestwaypoint = NULL;
if ((mobj == NULL) || P_MobjWasRemoved(mobj))
{
CONS_Debug(DBG_GAMELOGIC, "NULL mobj in K_GetClosestWaypointToMobj.\n");
}
else
{
size_t i = 0U;
waypoint_t *checkwaypoint = NULL;
fixed_t closestdist = INT32_MAX;
fixed_t checkdist = INT32_MAX;
for (i = 0; i < numwaypoints; i++)
{
checkwaypoint = &waypointheap[i];
checkdist = P_AproxDistance(
(mobj->x / FRACUNIT) - (checkwaypoint->mobj->x / FRACUNIT),
(mobj->y / FRACUNIT) - (checkwaypoint->mobj->y / FRACUNIT));
checkdist = P_AproxDistance(checkdist, (mobj->z / FRACUNIT) - (checkwaypoint->mobj->z / FRACUNIT));
if (checkdist < closestdist)
{
closestwaypoint = checkwaypoint;
closestdist = checkdist;
}
}
}
return closestwaypoint;
}
/*--------------------------------------------------
waypoint_t *K_GetBestWaypointForMobj(mobj_t *const mobj)
See header file for description.
--------------------------------------------------*/
waypoint_t *K_GetBestWaypointForMobj(mobj_t *const mobj)
{
waypoint_t *bestwaypoint = NULL;
if ((mobj == NULL) || P_MobjWasRemoved(mobj))
{
CONS_Debug(DBG_GAMELOGIC, "NULL mobj in K_GetBestWaypointForMobj.\n");
}
else
{
size_t i = 0U;
waypoint_t *checkwaypoint = NULL;
fixed_t closestdist = INT32_MAX;
fixed_t checkdist = INT32_MAX;
for (i = 0; i < numwaypoints; i++)
{
checkwaypoint = &waypointheap[i];
checkdist = P_AproxDistance(
(mobj->x / FRACUNIT) - (checkwaypoint->mobj->x / FRACUNIT),
(mobj->y / FRACUNIT) - (checkwaypoint->mobj->y / FRACUNIT));
checkdist = P_AproxDistance(checkdist, ((mobj->z / FRACUNIT) - (checkwaypoint->mobj->z / FRACUNIT)) * 4);
if (checkdist < closestdist)
{
if (!P_CheckSight(mobj, checkwaypoint->mobj))
{
// Save sight checks for the end, so we only do it if we have to
continue;
}
bestwaypoint = checkwaypoint;
closestdist = checkdist;
}
}
}
return bestwaypoint;
}
/*--------------------------------------------------
size_t K_GetWaypointHeapIndex(waypoint_t *waypoint)
See header file for description.
--------------------------------------------------*/
size_t K_GetWaypointHeapIndex(waypoint_t *waypoint)
{
size_t waypointindex = SIZE_MAX;
if (waypoint == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "NULL waypoint in K_GetWaypointID.\n");
}
else
{
waypointindex = waypoint - waypointheap;
}
return waypointindex;
}
/*--------------------------------------------------
waypoint_t *K_GetWaypointFromIndex(size_t waypointindex)
See header file for description.
--------------------------------------------------*/
waypoint_t *K_GetWaypointFromIndex(size_t waypointindex)
{
waypoint_t *waypoint = NULL;
if (waypointindex >= numwaypoints)
{
CONS_Debug(DBG_GAMELOGIC, "waypointindex higher than number of waypoints in K_GetWaypointFromIndex");
}
else
{
waypoint = &waypointheap[waypointindex];
}
return waypoint;
}
/*--------------------------------------------------
static UINT32 K_DistanceBetweenWaypoints(waypoint_t *const waypoint1, waypoint_t *const waypoint2)
Gets the Euclidean distance between 2 waypoints by using their mobjs. Used for the heuristic.
Input Arguments:-
waypoint1 - The first waypoint
waypoint2 - The second waypoint
Return:-
Euclidean distance between the 2 waypoints
--------------------------------------------------*/
static UINT32 K_DistanceBetweenWaypoints(waypoint_t *const waypoint1, waypoint_t *const waypoint2)
{
UINT32 finaldist = UINT32_MAX;
I_Assert(waypoint1 != NULL);
I_Assert(waypoint2 != NULL);
{
const fixed_t xydist =
P_AproxDistance(waypoint1->mobj->x - waypoint2->mobj->x, waypoint1->mobj->y - waypoint2->mobj->y);
const fixed_t xyzdist = P_AproxDistance(xydist, waypoint1->mobj->z - waypoint2->mobj->z);
finaldist = ((UINT32)xyzdist >> FRACBITS);
}
return finaldist;
}
/*--------------------------------------------------
void K_DebugWaypointsSpawnLine(waypoint_t *const waypoint1, waypoint_t *const waypoint2)
Draw a debugging line between 2 waypoints
Input Arguments:-
waypoint1 - A waypoint to draw the line between
waypoint2 - The other waypoint to draw the line between
--------------------------------------------------*/
static void K_DebugWaypointsSpawnLine(waypoint_t *const waypoint1, waypoint_t *const waypoint2)
{
mobj_t *waypointmobj1, *waypointmobj2;
mobj_t *spawnedmobj;
fixed_t stepx, stepy, stepz;
fixed_t x, y, z;
UINT32 waypointdist;
INT32 n;
skincolors_t linkcolour = SKINCOLOR_GREEN;
// This array is used to choose which colour should be on this connection
const skincolors_t linkcolours[] = {
SKINCOLOR_RED,
SKINCOLOR_BLUE,
SKINCOLOR_ORANGE,
SKINCOLOR_PINK,
SKINCOLOR_DREAM,
SKINCOLOR_CYAN,
SKINCOLOR_WHITE,
};
const size_t linkcolourssize = sizeof(linkcolours) / sizeof(skincolors_t);
// Error conditions
I_Assert(waypoint1 != NULL);
I_Assert(waypoint1->mobj != NULL);
I_Assert(waypoint2 != NULL);
I_Assert(waypoint2->mobj != NULL);
I_Assert(cv_kartdebugwaypoints.value != 0);
linkcolour = K_GetWaypointID(waypoint1)%linkcolourssize;
waypointmobj1 = waypoint1->mobj;
waypointmobj2 = waypoint2->mobj;
n = SPARKLES_PER_CONNECTION;
// For every 2048 fracunits, double the number of sparkles
waypointdist = K_DistanceBetweenWaypoints(waypoint1, waypoint2);
n *= (waypointdist / 2048) + 1;
// Draw the line
stepx = (waypointmobj2->x - waypointmobj1->x) / n;
stepy = (waypointmobj2->y - waypointmobj1->y) / n;
stepz = (waypointmobj2->z - waypointmobj1->z) / n;
x = waypointmobj1->x;
y = waypointmobj1->y;
z = waypointmobj1->z;
do
{
if ((leveltime + n) % 16 <= 4)
{
spawnedmobj = P_SpawnMobj(x, y, z, MT_SPARK);
P_SetMobjState(spawnedmobj, S_THOK);
spawnedmobj->state->nextstate = S_NULL;
spawnedmobj->state->tics = 1;
spawnedmobj->frame = spawnedmobj->frame & ~FF_TRANSMASK;
spawnedmobj->color = linkcolours[linkcolour];
spawnedmobj->scale = FixedMul(FRACUNIT/4, FixedDiv((15 - ((leveltime + n) % 16))*FRACUNIT, 15*FRACUNIT));
}
x += stepx;
y += stepy;
z += stepz;
} while (n--);
}
/*--------------------------------------------------
void K_DebugWaypointsVisualise(void)
See header file for description.
--------------------------------------------------*/
void K_DebugWaypointsVisualise(void)
{
mobj_t *waypointmobj;
mobj_t *debugmobj;
waypoint_t *waypoint;
waypoint_t *otherwaypoint;
UINT32 i;
if (waypointcap == NULL)
{
// No point putting a debug message here when it could easily happen when turning on the cvar in battle
return;
}
if (cv_kartdebugwaypoints.value == 0)
{
// Going to nip this in the bud and say no drawing all this without the cvar, it's not particularly optimised
return;
}
// Hunt through the waypointcap so we can show all waypoint mobjs and not just ones that were able to be graphed
for (waypointmobj = waypointcap; waypointmobj != NULL; waypointmobj = waypointmobj->tracer)
{
waypoint = K_SearchWaypointHeapForMobj(waypointmobj);
debugmobj = P_SpawnMobj(waypointmobj->x, waypointmobj->y, waypointmobj->z, MT_SPARK);
P_SetMobjState(debugmobj, S_THOK);
debugmobj->frame &= ~FF_TRANSMASK;
debugmobj->frame |= FF_TRANS20;
// There's a waypoint setup for this mobj! So draw that it's a valid waypoint and draw lines to its connections
if (waypoint != NULL)
{
if (waypoint->numnextwaypoints == 0 && waypoint->numprevwaypoints == 0)
{
debugmobj->color = SKINCOLOR_RED;
}
else if (waypoint->numnextwaypoints == 0 || waypoint->numprevwaypoints == 0)
{
debugmobj->color = SKINCOLOR_YELLOW;
}
else if (waypoint == players[displayplayers[0]].nextwaypoint)
{
debugmobj->color = SKINCOLOR_GREEN;
}
else
{
debugmobj->color = SKINCOLOR_BLUE;
}
// Valid waypoint, so draw lines of SPARKLES to its next or previous waypoints
if (cv_kartdebugwaypoints.value == 1)
{
for (i = 0; i < waypoint->numnextwaypoints; i++)
{
if (waypoint->nextwaypoints[i] != NULL)
{
otherwaypoint = waypoint->nextwaypoints[i];
K_DebugWaypointsSpawnLine(waypoint, otherwaypoint);
}
}
}
else if (cv_kartdebugwaypoints.value == 2)
{
for (i = 0; i < waypoint->numprevwaypoints; i++)
{
if (waypoint->prevwaypoints[i] != NULL)
{
otherwaypoint = waypoint->prevwaypoints[i];
K_DebugWaypointsSpawnLine(waypoint, otherwaypoint);
}
}
}
}
else
{
debugmobj->color = SKINCOLOR_RED;
}
debugmobj->state->tics = 1;
debugmobj->state->nextstate = S_NULL;
}
}
/*--------------------------------------------------
static size_t K_GetOpensetBaseSize(void)
Gets the base size the Openset hinary heap should have
Input Arguments:-
None
Return:-
The base size the Openset binary heap should have
--------------------------------------------------*/
static size_t K_GetOpensetBaseSize(void)
{
size_t returnsize = 0;
returnsize = baseopensetsize;
return returnsize;
}
/*--------------------------------------------------
static size_t K_GetClosedsetBaseSize(void)
Gets the base size the Closedset heap should have
Input Arguments:-
None
Return:-
The base size the Closedset heap should have
--------------------------------------------------*/
static size_t K_GetClosedsetBaseSize(void)
{
size_t returnsize = 0;
returnsize = baseclosedsetsize;
return returnsize;
}
/*--------------------------------------------------
static size_t K_GetNodesArrayBaseSize(void)
Gets the base size the Nodes array should have
Input Arguments:-
None
Return:-
The base size the Nodes array should have
--------------------------------------------------*/
static size_t K_GetNodesArrayBaseSize(void)
{
size_t returnsize = 0;
returnsize = basenodesarraysize;
return returnsize;
}
/*--------------------------------------------------
static void K_UpdateOpensetBaseSize(size_t newbaseopensetsize)
Sets the new base size of the openset binary heap, if it is bigger than before.
Input Arguments:-
newbaseopensetsize - The size to try and set the base Openset size to
Return:-
None
--------------------------------------------------*/
static void K_UpdateOpensetBaseSize(size_t newbaseopensetsize)
{
if (newbaseopensetsize > baseopensetsize)
{
baseopensetsize = newbaseopensetsize;
}
}
/*--------------------------------------------------
static void K_UpdateClosedsetBaseSize(size_t newbaseclosedsetsize)
Sets the new base size of the closedset heap, if it is bigger than before.
Input Arguments:-
newbaseclosedsetsize - The size to try and set the base Closedset size to
Return:-
None
--------------------------------------------------*/
static void K_UpdateClosedsetBaseSize(size_t newbaseclosedsetsize)
{
if (newbaseclosedsetsize > baseopensetsize)
{
baseclosedsetsize = newbaseclosedsetsize;
}
}
/*--------------------------------------------------
static void K_UpdateNodesArrayBaseSize(size_t newnodesarraysize)
Sets the new base size of the nodes array, if it is bigger than before.
Input Arguments:-
newnodesarraysize - The size to try and set the base nodes array size to
Return:-
None
--------------------------------------------------*/
static void K_UpdateNodesArrayBaseSize(size_t newnodesarraysize)
{
if (newnodesarraysize > basenodesarraysize)
{
basenodesarraysize = newnodesarraysize;
}
}
/*--------------------------------------------------
static void **K_WaypointPathfindGetNext(void *data, size_t *numconnections)
Gets the list of next waypoints as the connecting waypoints. For pathfinding only.
Input Arguments:-
data - Should point to a waypoint_t to get nextwaypoints from
numconnections - Should point to a size_t to return the number of next waypoints
Return:-
None
--------------------------------------------------*/
static void **K_WaypointPathfindGetNext(void *data, size_t *numconnections)
{
waypoint_t **connectingwaypoints = NULL;
if (data == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "K_WaypointPathfindGetNext received NULL data.\n");
}
else if (numconnections == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "K_WaypointPathfindGetNext received NULL numconnections.\n");
}
else
{
waypoint_t *waypoint = (waypoint_t *)data;
connectingwaypoints = waypoint->nextwaypoints;
*numconnections = waypoint->numnextwaypoints;
}
return (void**)connectingwaypoints;
}
/*--------------------------------------------------
static void **K_WaypointPathfindGetPrev(void *data, size_t *numconnections)
Gets the list of previous waypoints as the connecting waypoints. For pathfinding only.
Input Arguments:-
data - Should point to a waypoint_t to get prevwaypoints from
numconnections - Should point to a size_t to return the number of previous waypoints
Return:-
None
--------------------------------------------------*/
static void **K_WaypointPathfindGetPrev(void *data, size_t *numconnections)
{
waypoint_t **connectingwaypoints = NULL;
if (data == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "K_WaypointPathfindGetPrev received NULL data.\n");
}
else if (numconnections == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "K_WaypointPathfindGetPrev received NULL numconnections.\n");
}
else
{
waypoint_t *waypoint = (waypoint_t *)data;
connectingwaypoints = waypoint->prevwaypoints;
*numconnections = waypoint->numprevwaypoints;
}
return (void**)connectingwaypoints;
}
/*--------------------------------------------------
static UINT32 *K_WaypointPathfindGetNextCosts(void* data)
Gets the list of costs the next waypoints have. For pathfinding only.
Input Arguments:-
data - Should point to a waypoint_t to get nextwaypointdistances from
Return:-
A pointer to an array of UINT32's describing the cost of going from a waypoint to a next waypoint
--------------------------------------------------*/
static UINT32 *K_WaypointPathfindGetNextCosts(void* data)
{
UINT32 *connectingnodecosts = NULL;
if (data == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "K_WaypointPathfindGetNextCosts received NULL data.\n");
}
else
{
waypoint_t *waypoint = (waypoint_t *)data;
connectingnodecosts = waypoint->nextwaypointdistances;
}
return connectingnodecosts;
}
/*--------------------------------------------------
static UINT32 *K_WaypointPathfindGetPrevCosts(void* data)
Gets the list of costs the previous waypoints have. For pathfinding only.
Input Arguments:-
data - Should point to a waypoint_t to get prevwaypointdistances from
Return:-
A pointer to an array of UINT32's describing the cost of going from a waypoint to a previous waypoint
--------------------------------------------------*/
static UINT32 *K_WaypointPathfindGetPrevCosts(void* data)
{
UINT32 *connectingnodecosts = NULL;
if (data == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "K_WaypointPathfindGetPrevCosts received NULL data.\n");
}
else
{
waypoint_t *waypoint = (waypoint_t *)data;
connectingnodecosts = waypoint->prevwaypointdistances;
}
return connectingnodecosts;
}
/*--------------------------------------------------
static UINT32 K_WaypointPathfindGetHeuristic(void *data1, void *data2)
Gets the heuristic (euclidean distance) between 2 waypoints. For pathfinding only.
Input Arguments:-
data1 - Should point to a waypoint_t for the first waypoint
data2 - Should point to a waypoint_t for the second waypoint
Return:-
A UINT32 for the heuristic of the 2 waypoints.
--------------------------------------------------*/
static UINT32 K_WaypointPathfindGetHeuristic(void *data1, void *data2)
{
UINT32 nodeheuristic = UINT32_MAX;
if (data1 == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "K_WaypointPathfindGetHeuristic received NULL data1.\n");
}
else if (data2 == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "K_WaypointPathfindGetHeuristic received NULL data2.\n");
}
else
{
waypoint_t *waypoint1 = (waypoint_t *)data1;
waypoint_t *waypoint2 = (waypoint_t *)data2;
nodeheuristic = K_DistanceBetweenWaypoints(waypoint1, waypoint2);
}
return nodeheuristic;
}
/*--------------------------------------------------
static boolean K_WaypointPathfindTraversableAllEnabled(void *data)
Checks if a waypoint used as a pathfindnode is traversable. For pathfinding only.
Variant that accepts shortcut waypoints as traversable.
Input Arguments:-
data - Should point to a waypoint_t to check traversability of
Return:-
True if the waypoint is traversable, false otherwise.
--------------------------------------------------*/
static boolean K_WaypointPathfindTraversableAllEnabled(void *data)
{
boolean traversable = false;
if (data == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "K_WaypointPathfindTraversableAllEnabled received NULL data.\n");
}
else
{
waypoint_t *waypoint = (waypoint_t *)data;
traversable = (K_GetWaypointIsEnabled(waypoint) == true);
}
return traversable;
}
/*--------------------------------------------------
static boolean K_WaypointPathfindTraversableNoShortcuts(void *data)
Checks if a waypoint used as a pathfindnode is traversable. For pathfinding only.
Variant that does not accept shortcut waypoints as traversable.
Input Arguments:-
data - Should point to a waypoint_t to check traversability of
Return:-
True if the waypoint is traversable, false otherwise.
--------------------------------------------------*/
static boolean K_WaypointPathfindTraversableNoShortcuts(void *data)
{
boolean traversable = false;
if (data == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "K_WaypointPathfindTraversableNoShortcuts received NULL data.\n");
}
else
{
waypoint_t *waypoint = (waypoint_t *)data;
traversable = ((K_GetWaypointIsShortcut(waypoint) == false) && (K_GetWaypointIsEnabled(waypoint) == true));
}
return traversable;
}
/*--------------------------------------------------
boolean K_PathfindToWaypoint(
waypoint_t *const sourcewaypoint,
waypoint_t *const destinationwaypoint,
path_t *const returnpath,
const boolean useshortcuts,
const boolean huntbackwards)
See header file for description.
--------------------------------------------------*/
boolean K_PathfindToWaypoint(
waypoint_t *const sourcewaypoint,
waypoint_t *const destinationwaypoint,
path_t *const returnpath,
const boolean useshortcuts,
const boolean huntbackwards)
{
boolean pathfound = false;
if (sourcewaypoint == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "NULL sourcewaypoint in K_PathfindToWaypoint.\n");
}
else if (destinationwaypoint == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "NULL destinationwaypoint in K_PathfindToWaypoint.\n");
}
else if (((huntbackwards == false) && (sourcewaypoint->numnextwaypoints == 0))
|| ((huntbackwards == true) && (sourcewaypoint->numprevwaypoints == 0)))
{
CONS_Debug(DBG_GAMELOGIC,
"K_PathfindToWaypoint: sourcewaypoint with ID %d has no next waypoint\n",
K_GetWaypointID(sourcewaypoint));
}
else if (((huntbackwards == false) && (destinationwaypoint->numprevwaypoints == 0))
|| ((huntbackwards == true) && (destinationwaypoint->numnextwaypoints == 0)))
{
CONS_Debug(DBG_GAMELOGIC,
"K_PathfindToWaypoint: destinationwaypoint with ID %d has no previous waypoint\n",
K_GetWaypointID(destinationwaypoint));
}
else
{
pathfindsetup_t pathfindsetup = {};
getconnectednodesfunc nextnodesfunc = K_WaypointPathfindGetNext;
getnodeconnectioncostsfunc nodecostsfunc = K_WaypointPathfindGetNextCosts;
getnodeheuristicfunc heuristicfunc = K_WaypointPathfindGetHeuristic;
getnodetraversablefunc traversablefunc = K_WaypointPathfindTraversableNoShortcuts;
if (huntbackwards)
{
nextnodesfunc = K_WaypointPathfindGetPrev;
nodecostsfunc = K_WaypointPathfindGetPrevCosts;
}
if (useshortcuts)
{
traversablefunc = K_WaypointPathfindTraversableAllEnabled;
}
pathfindsetup.opensetcapacity = K_GetOpensetBaseSize();
pathfindsetup.closedsetcapacity = K_GetClosedsetBaseSize();
pathfindsetup.nodesarraycapacity = K_GetNodesArrayBaseSize();
pathfindsetup.startnodedata = sourcewaypoint;
pathfindsetup.endnodedata = destinationwaypoint;
pathfindsetup.getconnectednodes = nextnodesfunc;
pathfindsetup.getconnectioncosts = nodecostsfunc;
pathfindsetup.getheuristic = heuristicfunc;
pathfindsetup.gettraversable = traversablefunc;
pathfound = K_PathfindAStar(returnpath, &pathfindsetup);
K_UpdateOpensetBaseSize(pathfindsetup.opensetcapacity);
K_UpdateClosedsetBaseSize(pathfindsetup.closedsetcapacity);
K_UpdateNodesArrayBaseSize(pathfindsetup.nodesarraycapacity);
}
return pathfound;
}
/*--------------------------------------------------
waypoint_t *K_GetNextWaypointToDestination(
waypoint_t *const sourcewaypoint,
waypoint_t *const destinationwaypoint,
const boolean useshortcuts,
const boolean huntbackwards)
See header file for description.
--------------------------------------------------*/
waypoint_t *K_GetNextWaypointToDestination(
waypoint_t *const sourcewaypoint,
waypoint_t *const destinationwaypoint,
const boolean useshortcuts,
const boolean huntbackwards)
{
waypoint_t *nextwaypoint = NULL;
if (sourcewaypoint == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "NULL sourcewaypoint in K_GetNextWaypointToDestination.\n");
}
else if (destinationwaypoint == NULL)
{
CONS_Debug(DBG_GAMELOGIC, "NULL destinationwaypoint in K_GetNextWaypointToDestination.\n");
}
else if (sourcewaypoint == destinationwaypoint)
{
// Source and destination waypoint are the same, we're already there
nextwaypoint = destinationwaypoint;
}
else if (((huntbackwards == false) && (sourcewaypoint->numnextwaypoints == 0))
|| ((huntbackwards == true) && (sourcewaypoint->numprevwaypoints == 0)))
{
CONS_Debug(DBG_GAMELOGIC,
"K_GetNextWaypointToDestination: sourcewaypoint with ID %d has no next waypoint\n",
K_GetWaypointID(sourcewaypoint));
}
else if (((huntbackwards == false) && (destinationwaypoint->numprevwaypoints == 0))
|| ((huntbackwards == true) && (destinationwaypoint->numnextwaypoints == 0)))
{
CONS_Debug(DBG_GAMELOGIC,
"K_GetNextWaypointToDestination: destinationwaypoint with ID %d has no previous waypoint\n",
K_GetWaypointID(destinationwaypoint));
}
else
{
// If there is only 1 next waypoint it doesn't matter if it's a shortcut
if ((huntbackwards == false) && sourcewaypoint->numnextwaypoints == 1)
{
nextwaypoint = sourcewaypoint->nextwaypoints[0];
}
else if ((huntbackwards == true) && sourcewaypoint->numprevwaypoints == 1)
{
nextwaypoint = sourcewaypoint->prevwaypoints[0];
}
else
{
path_t pathtowaypoint = {};
pathfindsetup_t pathfindsetup = {};
boolean pathfindsuccess = false;
getconnectednodesfunc nextnodesfunc = K_WaypointPathfindGetNext;
getnodeconnectioncostsfunc nodecostsfunc = K_WaypointPathfindGetNextCosts;
getnodeheuristicfunc heuristicfunc = K_WaypointPathfindGetHeuristic;
getnodetraversablefunc traversablefunc = K_WaypointPathfindTraversableNoShortcuts;
if (huntbackwards)
{
nextnodesfunc = K_WaypointPathfindGetPrev;
nodecostsfunc = K_WaypointPathfindGetPrevCosts;
}
if (useshortcuts)
{
traversablefunc = K_WaypointPathfindTraversableAllEnabled;
}
pathfindsetup.opensetcapacity = K_GetOpensetBaseSize();
pathfindsetup.closedsetcapacity = K_GetClosedsetBaseSize();
pathfindsetup.nodesarraycapacity = K_GetNodesArrayBaseSize();
pathfindsetup.startnodedata = sourcewaypoint;
pathfindsetup.endnodedata = destinationwaypoint;
pathfindsetup.getconnectednodes = nextnodesfunc;
pathfindsetup.getconnectioncosts = nodecostsfunc;
pathfindsetup.getheuristic = heuristicfunc;
pathfindsetup.gettraversable = traversablefunc;
pathfindsuccess = K_PathfindAStar(&pathtowaypoint, &pathfindsetup);
K_UpdateOpensetBaseSize(pathfindsetup.opensetcapacity);
K_UpdateClosedsetBaseSize(pathfindsetup.closedsetcapacity);
K_UpdateNodesArrayBaseSize(pathfindsetup.nodesarraycapacity);
if (pathfindsuccess)
{
// A direct path to the destination has been found.
if (pathtowaypoint.numnodes > 1)
{
nextwaypoint = (waypoint_t*)pathtowaypoint.array[1].nodedata;
}
else
{
// Shouldn't happen, as this is the source waypoint.
CONS_Debug(DBG_GAMELOGIC, "Only one waypoint pathfound in K_GetNextWaypointToDestination.\n");
nextwaypoint = (waypoint_t*)pathtowaypoint.array[0].nodedata;
}
Z_Free(pathtowaypoint.array);
}
else
{
size_t i = 0U;
waypoint_t **nextwaypointlist = NULL;
size_t numnextwaypoints = 0U;
boolean waypointisenabled = true;
boolean waypointisshortcut = false;
if (huntbackwards)
{
nextwaypointlist = sourcewaypoint->prevwaypoints;
numnextwaypoints = sourcewaypoint->numprevwaypoints;
}
else
{
nextwaypointlist = sourcewaypoint->nextwaypoints;
numnextwaypoints = sourcewaypoint->numnextwaypoints;
}
// No direct path to the destination has been found, choose a next waypoint from what is available
// 1. If shortcuts are allowed, pick the first shortcut path that is enabled
// 2. If shortcuts aren't allowed, or there are no shortcuts, pick the first enabled waypoint
// 3. If there's no waypoints enabled, then nothing can be done and there is no next waypoint
if (useshortcuts)
{
for (i = 0U; i < numnextwaypoints; i++)
{
waypointisenabled = K_GetWaypointIsEnabled(nextwaypointlist[i]);
waypointisshortcut = K_GetWaypointIsShortcut(nextwaypointlist[i]);
if (waypointisenabled && waypointisshortcut)
{
nextwaypoint = nextwaypointlist[i];
break;
}
}
}
if (nextwaypoint == NULL)
{
for (i = 0U; i < numnextwaypoints; i++)
{
waypointisenabled = K_GetWaypointIsEnabled(nextwaypointlist[i]);
if (waypointisenabled)
{
nextwaypoint = nextwaypointlist[i];
break;
}
}
}
}
}
}
return nextwaypoint;
}
/*--------------------------------------------------
boolean K_CheckWaypointForMobj(waypoint_t *const waypoint, void *const mobjpointer)
Compares a waypoint's mobj and a void pointer that *should* point to an mobj. Intended for use with the
K_SearchWaypoint functions ONLY. No, it is not my responsibility to make sure the pointer you sent in is
actually an mobj.
Input Arguments:-
waypoint - The waypoint that is currently being compared against
mobjpointer - A pointer that should be to an mobj to check with the waypoint for matching
Return:-
The waypoint that uses that mobj, NULL if it wasn't found, NULL if it isn't an MT_WAYPOINT
--------------------------------------------------*/
static boolean K_CheckWaypointForMobj(waypoint_t *const waypoint, void *const mobjpointer)
{
boolean mobjsmatch = false;
// Error Conditions
I_Assert(waypoint != NULL);
I_Assert(waypoint->mobj != NULL);
I_Assert(mobjpointer != NULL);
{
mobj_t *const mobj = (mobj_t *)mobjpointer;
if (P_MobjWasRemoved(mobj))
{
CONS_Debug(DBG_GAMELOGIC, "Mobj Was Removed in K_CheckWaypointForMobj");
}
else if (mobj->type != MT_WAYPOINT)
{
CONS_Debug(DBG_GAMELOGIC, "Non MT_WAYPOINT mobj in K_CheckWaypointForMobj. Type=%d.\n", mobj->type);
}
else
{
// All that error checking for 3 lines :^)
if (waypoint->mobj == mobj)
{
mobjsmatch = true;
}
}
}
return mobjsmatch;
}
/*--------------------------------------------------
waypoint_t *K_TraverseWaypoints(
waypoint_t *waypoint,
boolean (*conditionalfunc)(waypoint_t *const, void *const),
void *const condition,
boolean *const visitedarray)
Searches through the waypoint list for a waypoint that matches a condition, just does a simple flood search
of the graph with no pathfinding
Input Arguments:-
waypoint - The waypoint that is currently being checked, goes through nextwaypoints after this one
conditionalfunc - The function that will be used to check a waypoint against condition
condition - the condition being checked by conditionalfunc
visitedarray - An array of booleans that let us know if a waypoint has already been checked, marked to true
when one is, so we don't repeat going down a path. Cannot be changed to a different pointer
Return:-
The waypoint that uses that mobj, NULL if it wasn't found, NULL if it isn't an MT_WAYPOINT
--------------------------------------------------*/
static waypoint_t *K_TraverseWaypoints(
waypoint_t *waypoint,
boolean (*conditionalfunc)(waypoint_t *const, void *const),
void *const condition,
boolean *const visitedarray)
{
waypoint_t *foundwaypoint = NULL;
// Error conditions
I_Assert(condition != NULL);
I_Assert(conditionalfunc != NULL);
I_Assert(visitedarray != NULL);
searchwaypointstart:
I_Assert(waypoint != NULL);
{
size_t waypointindex = K_GetWaypointHeapIndex(waypoint);
// If we've already visited this waypoint, we've already checked the next waypoints, no point continuing
if ((waypointindex != SIZE_MAX) && (visitedarray[waypointindex] != true))
{
// Mark this waypoint as being visited
visitedarray[waypointindex] = true;
if (conditionalfunc(waypoint, condition) == true)
{
foundwaypoint = waypoint;
}
else
{
// If this waypoint only has one next waypoint, set the waypoint to be the next one and jump back
// to the start, this is to avoid going too deep into the stack where we can
// Yes this is a horrible horrible goto, but the alternative is a do while loop with an extra
// variable, which is slightly more confusing. This is probably the fastest and least confusing
// option that keeps this functionality
if (waypoint->numnextwaypoints == 1 && waypoint->nextwaypoints[0] != NULL)
{
waypoint = waypoint->nextwaypoints[0];
goto searchwaypointstart;
}
else if (waypoint->numnextwaypoints != 0)
{
// The nesting here is a bit nasty, but it's better than potentially a lot of function calls on
// the stack, and another function would be very small in this case
UINT32 i;
// For each next waypoint, Search through it's path continuation until we hopefully find the one
// we're looking for
for (i = 0; i < waypoint->numnextwaypoints; i++)
{
if (waypoint->nextwaypoints[i] != NULL)
{
foundwaypoint = K_TraverseWaypoints(waypoint->nextwaypoints[i], conditionalfunc,
condition, visitedarray);
if (foundwaypoint != NULL)
{
break;
}
}
}
}
else
{
// No next waypoints, this function will be returned from
}
}
}
}
return foundwaypoint;
}
/*--------------------------------------------------
waypoint_t *K_SearchWaypointGraph(
boolean (*conditionalfunc)(waypoint_t *const, void *const),
void *const condition)
Searches through the waypoint graph for a waypoint that matches the conditional
Input Arguments:-
conditionalfunc - The function that will be used to check a waypoint against condition
condition - the condition being checked by conditionalfunc
Return:-
The waypoint that uses that mobj, NULL if it wasn't found, NULL if it isn't an MT_WAYPOINT
--------------------------------------------------*/
static waypoint_t *K_SearchWaypointGraph(
boolean (*conditionalfunc)(waypoint_t *const, void *const),
void *const condition)
{
boolean *visitedarray = NULL;
waypoint_t *foundwaypoint = NULL;
// Error conditions
I_Assert(condition != NULL);
I_Assert(conditionalfunc != NULL);
I_Assert(firstwaypoint != NULL);
visitedarray = Z_Calloc(numwaypoints * sizeof(boolean), PU_STATIC, NULL);
foundwaypoint = K_TraverseWaypoints(firstwaypoint, conditionalfunc, condition, visitedarray);
Z_Free(visitedarray);
return foundwaypoint;
}
/*--------------------------------------------------
waypoint_t *K_SearchWaypointGraphForMobj(mobj_t * const mobj)
See header file for description.
--------------------------------------------------*/
waypoint_t *K_SearchWaypointGraphForMobj(mobj_t *const mobj)
{
waypoint_t *foundwaypoint = NULL;
if (mobj == NULL || P_MobjWasRemoved(mobj))
{
CONS_Debug(DBG_GAMELOGIC, "NULL mobj in K_SearchWaypointGraphForMobj.\n");
}
else if (mobj->type != MT_WAYPOINT)
{
CONS_Debug(DBG_GAMELOGIC, "Non MT_WAYPOINT mobj in K_SearchWaypointGraphForMobj. Type=%d.\n", mobj->type);
}
else
{
foundwaypoint = K_SearchWaypointGraph(K_CheckWaypointForMobj, (void *)mobj);
}
return foundwaypoint;
}
/*--------------------------------------------------
waypoint_t *K_SearchWaypointHeap(
boolean (*conditionalfunc)(waypoint_t *const, void *const),
void *const condition)
Searches through the waypoint heap for a waypoint that matches the conditional
Input Arguments:-
conditionalfunc - The function that will be used to check a waypoint against condition
condition - the condition being checked by conditionalfunc
Return:-
The waypoint that uses that mobj, NULL if it wasn't found, NULL if it isn't an MT_WAYPOINT
--------------------------------------------------*/
static waypoint_t *K_SearchWaypointHeap(
boolean (*conditionalfunc)(waypoint_t *const, void *const),
void *const condition)
{
UINT32 i = 0;
waypoint_t *foundwaypoint = NULL;
// Error conditions
I_Assert(condition != NULL);
I_Assert(conditionalfunc != NULL);
I_Assert(waypointheap != NULL);
// Simply search through the waypointheap for the waypoint which matches the condition. Much simpler when no
// pathfinding is needed. Search up to numwaypoints and NOT numwaypointmobjs as numwaypoints is the real number of
// waypoints setup in the heap while numwaypointmobjs ends up being the capacity
for (i = 0; i < numwaypoints; i++)
{
if (conditionalfunc(&waypointheap[i], condition) == true)
{
foundwaypoint = &waypointheap[i];
break;
}
}
return foundwaypoint;
}
/*--------------------------------------------------
waypoint_t *K_SearchWaypointHeapForMobj(mobj_t *const mobj)
See header file for description.
--------------------------------------------------*/
waypoint_t *K_SearchWaypointHeapForMobj(mobj_t *const mobj)
{
waypoint_t *foundwaypoint = NULL;
if (mobj == NULL || P_MobjWasRemoved(mobj))
{
CONS_Debug(DBG_GAMELOGIC, "NULL mobj in K_SearchWaypointHeapForMobj.\n");
}
else if (mobj->type != MT_WAYPOINT)
{
CONS_Debug(DBG_GAMELOGIC, "Non MT_WAYPOINT mobj in K_SearchWaypointHeapForMobj. Type=%d.\n", mobj->type);
}
else
{
foundwaypoint = K_SearchWaypointHeap(K_CheckWaypointForMobj, (void *)mobj);
}
return foundwaypoint;
}
/*--------------------------------------------------
static UINT32 K_SetupCircuitLength(void)
Sets up the Circuit Length by getting the best path from the finishwaypoint back to itself.
On sprint maps, circuitlength is 0.
Input Arguments:-
None
Return:-
Length of the circuit
--------------------------------------------------*/
static UINT32 K_SetupCircuitLength(void)
{
I_Assert(firstwaypoint != NULL);
I_Assert(numwaypoints > 0U);
I_Assert(finishline != NULL);
// The circuit length only makes sense in circuit maps, sprint maps do not need to use it
// The main usage of the circuit length is to add onto a player's distance to finish line so crossing the finish
// line places people correctly relative to each other
if ((mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE) == LF_SECTIONRACE)
{
circuitlength = 0U;
}
else
{
// Create a fake finishline waypoint, then try and pathfind to the finishline from it
waypoint_t fakefinishline = *finishline;
path_t bestcircuitpath = {};
const boolean useshortcuts = false;
const boolean huntbackwards = false;
K_PathfindToWaypoint(&fakefinishline, finishline, &bestcircuitpath, useshortcuts, huntbackwards);
circuitlength = bestcircuitpath.totaldist;
Z_Free(bestcircuitpath.array);
}
return circuitlength;
}
/*--------------------------------------------------
static void K_AddPrevToWaypoint(waypoint_t *const waypoint, waypoint_t *const prevwaypoint)
Adds another waypoint to a waypoint's previous waypoint list, this needs to be done like this because there is no
way to identify previous waypoints from just IDs, so we need to reallocate the memory for every previous waypoint
Input Arguments:-
waypoint - The waypoint which is having its previous waypoint list added to
prevwaypoint - The waypoint which is being added to the previous waypoint list
Return:-
Pointer to waypoint_t for the rest of the waypoint data to be placed into
--------------------------------------------------*/
static void K_AddPrevToWaypoint(waypoint_t *const waypoint, waypoint_t *const prevwaypoint)
{
// Error conditions
I_Assert(waypoint != NULL);
I_Assert(prevwaypoint != NULL);
waypoint->numprevwaypoints++;
waypoint->prevwaypoints =
Z_Realloc(waypoint->prevwaypoints, waypoint->numprevwaypoints * sizeof(waypoint_t *), PU_LEVEL, NULL);
if (!waypoint->prevwaypoints)
{
I_Error("K_AddPrevToWaypoint: Failed to reallocate memory for previous waypoints.");
}
waypoint->prevwaypointdistances =
Z_Realloc(waypoint->prevwaypointdistances, waypoint->numprevwaypoints * sizeof(fixed_t), PU_LEVEL, NULL);
if (!waypoint->prevwaypointdistances)
{
I_Error("K_AddPrevToWaypoint: Failed to reallocate memory for previous waypoint distances.");
}
waypoint->prevwaypoints[waypoint->numprevwaypoints - 1] = prevwaypoint;
}
/*--------------------------------------------------
static waypoint_t *K_MakeWaypoint(mobj_t *const mobj)
Make a new waypoint from a map object. Setups up most of the data for it, and allocates most memory
Remaining creation is handled in K_SetupWaypoint
Input Arguments:-
mobj - The map object that this waypoint is represented by
Return:-
Pointer to the setup waypoint, NULL if one was not setup
--------------------------------------------------*/
static waypoint_t *K_MakeWaypoint(mobj_t *const mobj)
{
waypoint_t *madewaypoint = NULL;
mobj_t *otherwaypointmobj = NULL;
// Error conditions
I_Assert(mobj != NULL);
I_Assert(!P_MobjWasRemoved(mobj));
I_Assert(waypointcap != NULL); // No waypoint mobjs in map load
I_Assert(numwaypoints < numwaypointmobjs); // waypoint array reached max capacity
// numwaypoints is incremented later in K_SetupWaypoint
madewaypoint = &waypointheap[numwaypoints];
numwaypoints++;
P_SetTarget(&madewaypoint->mobj, mobj);
// Don't allow a waypoint that has its next ID set to itself to work
if (mobj->threshold != mobj->movecount) {
// Go through the other waypoint mobjs in the map to find out how many waypoints are after this one
for (otherwaypointmobj = waypointcap; otherwaypointmobj != NULL; otherwaypointmobj = otherwaypointmobj->tracer)
{
// threshold = next waypoint id, movecount = my id
if (mobj->threshold == otherwaypointmobj->movecount)
{
madewaypoint->numnextwaypoints++;
}
}
}
// No next waypoints
if (madewaypoint->numnextwaypoints != 0)
{
// Allocate memory to hold enough pointers to all of the next waypoints
madewaypoint->nextwaypoints =
Z_Calloc(madewaypoint->numnextwaypoints * sizeof(waypoint_t *), PU_LEVEL, NULL);
if (madewaypoint->nextwaypoints == NULL)
{
I_Error("K_MakeWaypoint: Out of Memory allocating next waypoints.");
}
madewaypoint->nextwaypointdistances =
Z_Calloc(madewaypoint->numnextwaypoints * sizeof(fixed_t), PU_LEVEL, NULL);
if (madewaypoint->nextwaypointdistances == NULL)
{
I_Error("K_MakeWaypoint: Out of Memory allocating next waypoint distances.");
}
}
return madewaypoint;
}
/*--------------------------------------------------
static waypoint_t *K_SetupWaypoint(mobj_t *const mobj)
Either gets an already made waypoint, or sets up a new waypoint for an mobj,
including next and previous waypoints
Input Arguments:-
mobj - The map object that this waypoint is represented by
Return:-
Pointer to the setup waypoint, NULL if one was not setup
--------------------------------------------------*/
static waypoint_t *K_SetupWaypoint(mobj_t *const mobj)
{
waypoint_t *thiswaypoint = NULL;
// Error conditions
I_Assert(mobj != NULL);
I_Assert(!P_MobjWasRemoved(mobj));
I_Assert(mobj->type == MT_WAYPOINT);
I_Assert(waypointcap != NULL); // no waypoint mobjs in map load
// If we have waypoints already created, search through them first to see if this mobj is already added.
if (firstwaypoint != NULL)
{
thiswaypoint = K_SearchWaypointHeapForMobj(mobj);
}
// The waypoint hasn't already been made, so make it
if (thiswaypoint == NULL)
{
mobj_t *otherwaypointmobj = NULL;
UINT32 nextwaypointindex = 0;
thiswaypoint = K_MakeWaypoint(mobj);
if (thiswaypoint != NULL)
{
// Set the first waypoint if it isn't already
if (firstwaypoint == NULL)
{
firstwaypoint = thiswaypoint;
}
if (K_GetWaypointIsFinishline(thiswaypoint))
{
if (finishline != NULL)
{
const INT32 oldfinishlineid = K_GetWaypointID(finishline);
const INT32 thiswaypointid = K_GetWaypointID(thiswaypoint);
CONS_Alert(
CONS_WARNING, "Multiple finish line waypoints with IDs %d and %d! Using %d.",
oldfinishlineid, thiswaypointid, thiswaypointid);
}
finishline = thiswaypoint;
}
if (thiswaypoint->numnextwaypoints > 0)
{
waypoint_t *nextwaypoint = NULL;
fixed_t nextwaypointdistance = 0;
// Go through the waypoint mobjs to setup the next waypoints and make this waypoint know they're its
// next. I kept this out of K_MakeWaypoint so the stack isn't gone down as deep
for (otherwaypointmobj = waypointcap;
otherwaypointmobj != NULL;
otherwaypointmobj = otherwaypointmobj->tracer)
{
// threshold = next waypoint id, movecount = my id
if (mobj->threshold == otherwaypointmobj->movecount)
{
nextwaypoint = K_SetupWaypoint(otherwaypointmobj);
nextwaypointdistance = K_DistanceBetweenWaypoints(thiswaypoint, nextwaypoint);
thiswaypoint->nextwaypoints[nextwaypointindex] = nextwaypoint;
thiswaypoint->nextwaypointdistances[nextwaypointindex] = nextwaypointdistance;
K_AddPrevToWaypoint(nextwaypoint, thiswaypoint);
nextwaypoint->prevwaypointdistances[nextwaypoint->numprevwaypoints - 1] = nextwaypointdistance;
nextwaypointindex++;
}
if (nextwaypointindex >= thiswaypoint->numnextwaypoints)
{
break;
}
}
}
else
{
CONS_Alert(
CONS_WARNING, "Waypoint with ID %d has no next waypoint.\n", K_GetWaypointID(thiswaypoint));
}
}
else
{
CONS_Debug(DBG_SETUP, "K_SetupWaypoint failed to make new waypoint with ID %d.\n", mobj->movecount);
}
}
return thiswaypoint;
}
/*--------------------------------------------------
static boolean K_AllocateWaypointHeap(void)
Allocates the waypoint heap enough space for the number of waypoint mobjs on the map
Return:-
True if the allocation was successful, false if it wasn't. Will I_Error if out of memory still.
--------------------------------------------------*/
static boolean K_AllocateWaypointHeap(void)
{
mobj_t *waypointmobj = NULL;
boolean allocationsuccessful = false;
// Error conditions
I_Assert(waypointheap == NULL); // waypointheap is already allocated
I_Assert(waypointcap != NULL); // no waypoint mobjs at map load
// This should be an allocation for the first time. Reset the number of mobjs back to 0 if it's not already
numwaypointmobjs = 0;
// Find how many waypoint mobjs there are in the map, this is the maximum number of waypoints there CAN be
for (waypointmobj = waypointcap; waypointmobj != NULL; waypointmobj = waypointmobj->tracer)
{
if (waypointmobj->type != MT_WAYPOINT)
{
CONS_Debug(DBG_SETUP,
"Non MT_WAYPOINT mobj in waypointcap in K_AllocateWaypointHeap. Type=%d\n.", waypointmobj->type);
continue;
}
numwaypointmobjs++;
}
if (numwaypointmobjs > 0)
{
// Allocate space in the heap for every mobj, it's possible some mobjs aren't linked up and not all of the
// heap allocated will be used, but it's a fairly reasonable assumption that this isn't going to be awful
waypointheap = Z_Calloc(numwaypointmobjs * sizeof(waypoint_t), PU_LEVEL, NULL);
if (waypointheap == NULL)
{
// We could theoretically CONS_Debug here and continue without using waypoints, but I feel that will
// require error checks that will end up spamming the console when we think waypoints SHOULD be working.
// Safer to just exit if out of memory
I_Error("K_AllocateWaypointHeap: Out of memory.");
}
allocationsuccessful = true;
}
else
{
CONS_Debug(DBG_SETUP, "No waypoint mobjs in waypointcap.\n");
}
return allocationsuccessful;
}
/*--------------------------------------------------
void K_FreeWaypoints(void)
For safety, this will free the waypointheap and all the waypoints allocated if they aren't already before they
are setup. If the PU_LEVEL tag is cleared, make sure to call K_ClearWaypoints or this will try to free already
freed memory!
--------------------------------------------------*/
static void K_FreeWaypoints(void)
{
if (waypointheap != NULL)
{
// Free the waypointheap
Z_Free(waypointheap);
}
K_ClearWaypoints();
}
/*--------------------------------------------------
boolean K_SetupWaypointList(void)
See header file for description.
--------------------------------------------------*/
boolean K_SetupWaypointList(void)
{
boolean setupsuccessful = false;
K_FreeWaypoints();
if (!waypointcap)
{
CONS_Alert(CONS_ERROR, "No waypoints in map.\n");
}
else
{
if (K_AllocateWaypointHeap() == true)
{
mobj_t *waypointmobj = NULL;
// Loop through the waypointcap here so that all waypoints are added to the heap, and allow easier debugging
for (waypointmobj = waypointcap; waypointmobj; waypointmobj = waypointmobj->tracer)
{
K_SetupWaypoint(waypointmobj);
}
if (firstwaypoint == NULL)
{
CONS_Alert(CONS_ERROR, "No waypoints in map.\n");
}
else
{
CONS_Debug(DBG_SETUP, "Successfully setup %s waypoints.\n", sizeu1(numwaypoints));
if (finishline == NULL)
{
CONS_Alert(
CONS_WARNING, "No finish line waypoint in the map! Using first setup waypoint with ID %d.\n",
K_GetWaypointID(firstwaypoint));
finishline = firstwaypoint;
}
if (K_SetupCircuitLength() == 0
&& ((mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE) != LF_SECTIONRACE))
{
CONS_Alert(CONS_ERROR, "Circuit track waypoints do not form a circuit.\n");
}
setupsuccessful = true;
}
}
}
return setupsuccessful;
}
/*--------------------------------------------------
void K_ClearWaypoints(void)
See header file for description.
--------------------------------------------------*/
void K_ClearWaypoints(void)
{
waypointheap = NULL;
firstwaypoint = NULL;
finishline = NULL;
numwaypoints = 0U;
numwaypointmobjs = 0U;
circuitlength = 0U;
}