From 3ae8623ebb98f51ac93517d24fd4444b75e33bfe Mon Sep 17 00:00:00 2001 From: Sryder13 Date: Sun, 12 Nov 2017 01:10:02 +0000 Subject: [PATCH 001/105] Waypoint Spawning Uses object MT_WAYPOINT, mapthing type 2001 Placing one by itself will always place it on the floor or ceiling (depending on object flip) Placing one alongside another that uses Object Special will make the original use the z height of the 2nd Angle will be used to determine the waypoint's ID Z Height (threshold on mobjs) will be used to determine the ID of the previous waypoint Waypoints with the Ambush Flag will be unable to be respawned at (reactiontime on mobjs internally) --- src/info.c | 4 ++-- src/p_mobj.c | 38 ++++++++++++++++++++++++++++++++++++++ src/p_setup.c | 24 ++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/src/info.c b/src/info.c index 0c2416133..e8a75f565 100644 --- a/src/info.c +++ b/src/info.c @@ -15195,7 +15195,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = { // MT_WAYPOINT 2001, // doomednum - S_NULL, // spawnstate + S_UNKNOWN, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound @@ -15216,7 +15216,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass 0, // damage sfx_None, // activesound - MF_NOBLOCKMAP|MF_NOSECTOR|MF_NOCLIP|MF_NOGRAVITY, // flags + MF_NOBLOCKMAP/*|MF_NOSECTOR*/|MF_NOCLIP|MF_NOGRAVITY, // flags S_NULL // raisestate }, diff --git a/src/p_mobj.c b/src/p_mobj.c index db812519d..2fae94040 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -9641,6 +9641,26 @@ void P_SpawnMapThing(mapthing_t *mthing) else mthing->z = (INT16)(z>>FRACBITS); } + else if (i == MT_WAYPOINT && !(mthing->options & MTF_OBJECTSPECIAL)) + { + // just gets set on either the floor or ceiling + boolean flip = (!!(mobjinfo[i].flags & MF_SPAWNCEILING) ^ !!(mthing->options & MTF_OBJECTFLIP)); + + // applying offsets! (if any) + if (flip) + { + z = ONCEILINGZ; + } + else + { + z = ONFLOORZ; + } + + if (z == ONFLOORZ) + mthing->z = 0; + else + mthing->z = (INT16)(z>>FRACBITS); + } else { fixed_t offset = 0; @@ -9893,6 +9913,24 @@ ML_NOCLIMB : Direction not controllable case MT_TRAPGOYLELONG: if (mthing->angle >= 360) mobj->tics += 7*(mthing->angle / 360) + 1; // starting delay + break; + case MT_WAYPOINT: + if (!(mthing->options & MTF_OBJECTSPECIAL)) + { + // Z is already altered to account for proper height + // Use threshold to store the previous waypoint ID + // Angle is being used for the current waypoint ID + mobj->threshold = ((mthing->options >> ZSHIFT)); + } + if (mthing->options & MTF_AMBUSH) + { + mobj->reactiontime = 1; + } + else + { + mobj->reactiontime = 0; + } + break; default: break; } diff --git a/src/p_setup.c b/src/p_setup.c index e91acab69..913796253 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -915,6 +915,24 @@ void P_ScanThings(INT16 mapnum, INT16 wadnum, INT16 lumpnum) } #endif +static void P_SetWaypointsHeight(mapthing_t *mt) +{ + mapthing_t *mt2 = mapthings; + size_t i; + for (i = 0; i < nummapthings; i++, mt2++) + { + if (!(mt2->type == mobjinfo[MT_WAYPOINT].doomednum && (mt2->options & MTF_OBJECTSPECIAL))) + continue; + + if (mt->angle == mt2->angle) // same waypoint id + { + mt->z = mt2->z; + mt->mobj->z = mt2->mobj->z; + P_RemoveMobj(mt2->mobj); + } + } +} + // // P_LoadThings // @@ -1050,6 +1068,12 @@ static void P_LoadThings(void) P_SpawnHoopsAndRings (mt); } + + if (mt->type == mobjinfo[MT_WAYPOINT].doomednum && !(mt->options & MTF_OBJECTSPECIAL)) + { + // use any "2nd" waypoint mobjs to set the height of original, then remove the 2nds mobjs + P_SetWaypointsHeight(mt); + } } } From 71851fb20c39f197ea28eb54ecb7fcad3701fb7a Mon Sep 17 00:00:00 2001 From: Sryder Date: Mon, 25 Jun 2018 22:21:42 +0100 Subject: [PATCH 002/105] Waypoint spawning update Use Linedef Action 2000 as Waypoint Parameters to set height and radius, instead of having separate mobjs use movecount as current ID instead --- src/p_mobj.c | 31 +++++++++++++++++++++++++------ src/p_setup.c | 24 ------------------------ src/p_spec.c | 6 ++++-- 3 files changed, 29 insertions(+), 32 deletions(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 5baba64c5..5f95bd2f9 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -10222,7 +10222,7 @@ void P_SpawnMapThing(mapthing_t *mthing) else mthing->z = (INT16)(z>>FRACBITS); } - else if (i == MT_WAYPOINT && !(mthing->options & MTF_OBJECTSPECIAL)) + else if (i == MT_WAYPOINT) { // just gets set on either the floor or ceiling boolean flip = (!!(mobjinfo[i].flags & MF_SPAWNCEILING) ^ !!(mthing->options & MTF_OBJECTFLIP)); @@ -10496,13 +10496,31 @@ ML_NOCLIMB : Direction not controllable mobj->tics += 7*(mthing->angle / 360) + 1; // starting delay break; case MT_WAYPOINT: - if (!(mthing->options & MTF_OBJECTSPECIAL)) + { + size_t line; + mobj->radius = 256*FRACUNIT; + // Same reason as for MT_SPINMACEPOINT we can't use the function to find the linedef + for (line = 0; line < numlines; line++) { - // Z is already altered to account for proper height - // Use threshold to store the previous waypoint ID - // Angle is being used for the current waypoint ID - mobj->threshold = ((mthing->options >> ZSHIFT)); + if (lines[line].special == 2000 && lines[line].tag == mthing->angle) + break; } + // Set the radius, mobj z, and mthing z to match what the parameters want + if (line < numlines) + { + fixed_t lineradius = sides[lines[line].sidenum[0]].textureoffset; + fixed_t linez = sides[lines[line].sidenum[0]].rowoffset; + + if (lineradius > 0) + mobj->radius = lineradius; + mobj->z += linez; + mthing->z += linez >> FRACBITS; + } + // Use threshold to store the next waypoint ID + // movecount is being used for the current waypoint ID + // reactiontime lets us know if we can respawn at it + mobj->threshold = ((mthing->options >> ZSHIFT)); + mobj->movecount = mthing->angle; if (mthing->options & MTF_AMBUSH) { mobj->reactiontime = 1; @@ -10512,6 +10530,7 @@ ML_NOCLIMB : Direction not controllable mobj->reactiontime = 0; } break; + } default: break; } diff --git a/src/p_setup.c b/src/p_setup.c index 40fcc4dcf..bc29cc6cd 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -923,24 +923,6 @@ void P_ScanThings(INT16 mapnum, INT16 wadnum, INT16 lumpnum) } #endif -static void P_SetWaypointsHeight(mapthing_t *mt) -{ - mapthing_t *mt2 = mapthings; - size_t i; - for (i = 0; i < nummapthings; i++, mt2++) - { - if (!(mt2->type == mobjinfo[MT_WAYPOINT].doomednum && (mt2->options & MTF_OBJECTSPECIAL))) - continue; - - if (mt->angle == mt2->angle) // same waypoint id - { - mt->z = mt2->z; - mt->mobj->z = mt2->mobj->z; - P_RemoveMobj(mt2->mobj); - } - } -} - // // P_LoadThings // @@ -1079,12 +1061,6 @@ static void P_LoadThings(void) P_SpawnHoopsAndRings (mt); } - - if (mt->type == mobjinfo[MT_WAYPOINT].doomednum && !(mt->options & MTF_OBJECTSPECIAL)) - { - // use any "2nd" waypoint mobjs to set the height of original, then remove the 2nds mobjs - P_SetWaypointsHeight(mt); - } } } diff --git a/src/p_spec.c b/src/p_spec.c index 7883a2ca7..8a1ef0609 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -1709,14 +1709,14 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller { if (maptol & TOL_NIGHTS) lap = actor->player->mare; - else + else lap = actor->player->laps; } else { if (maptol & TOL_NIGHTS) lap = P_FindLowestMare(); - else + else lap = P_FindLowestLap(); } @@ -6566,6 +6566,8 @@ void P_SpawnSpecials(INT32 fromnetsave) break; #endif + case 2000: // Waypoint Parameters + break; default: break; } From 93e63d42cb2b5b41030d29912541c75f7d0d047a Mon Sep 17 00:00:00 2001 From: Sryder Date: Tue, 4 Dec 2018 22:35:14 +0000 Subject: [PATCH 003/105] Add MF_SCENERY to the waypoints, if what I remember from toaster is correct, we probably can't use MF_NOTHINK due to it not being synched properly for netplay. --- src/info.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/info.c b/src/info.c index d952fd1c6..b1512376a 100644 --- a/src/info.c +++ b/src/info.c @@ -16050,7 +16050,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass 0, // damage sfx_None, // activesound - MF_NOBLOCKMAP|MF_NOSECTOR|MF_NOCLIP|MF_NOGRAVITY, // flags + MF_NOBLOCKMAP|MF_NOSECTOR|MF_NOCLIP|MF_NOGRAVITY|MF_SCENERY, // flags S_NULL // raisestate }, From d1a10d70f5b5b2c6b87b857a8697f6fd3a1647c5 Mon Sep 17 00:00:00 2001 From: Sryder Date: Fri, 7 Dec 2018 23:40:28 +0000 Subject: [PATCH 004/105] Initial step of Waypoints Basically just copied what I planned out months ago and put it into code for now Also changed waypointcap to be made from MT_WAYPOINTs and definitely broke position in this commit from that. --- src/Makefile | 1 + src/k_waypoint.c | 268 +++++++++++++++++++++++++++++++++++++++++++++++ src/k_waypoint.h | 26 +++++ src/p_mobj.c | 10 +- 4 files changed, 299 insertions(+), 6 deletions(-) create mode 100644 src/k_waypoint.c create mode 100644 src/k_waypoint.h diff --git a/src/Makefile b/src/Makefile index a2279a94d..32cda91fa 100644 --- a/src/Makefile +++ b/src/Makefile @@ -477,6 +477,7 @@ OBJS:=$(i_main_o) \ $(OBJDIR)/y_inter.o \ $(OBJDIR)/st_stuff.o \ $(OBJDIR)/k_kart.o \ + $(OBJDIR)/k_waypoint.o\ $(OBJDIR)/m_aatree.o \ $(OBJDIR)/m_anigif.o \ $(OBJDIR)/m_argv.o \ diff --git a/src/k_waypoint.c b/src/k_waypoint.c new file mode 100644 index 000000000..be0ff44fc --- /dev/null +++ b/src/k_waypoint.c @@ -0,0 +1,268 @@ +#include "k_waypoint.h" + +#include "doomdef.h" +#include "p_mobj.h" +#include "p_tick.h" +#include "z_zone.h" + +static waypoint_t *firstwaypoint = NULL; +static UINT32 numwaypoints = 0; + +/*-------------------------------------------------- + waypoint_t *K_SearchWaypoints(waypoint_t *waypoint, mobj_t * const mobj, boolean * const visitedarray) + + Searches through the waypoint list for a waypoint that has an mobj, just does a simple flood search for the + waypoint that uses this mobj, no pathfinding + + Input Arguments:- + waypoint - The waypoint that is currently being checked, goes through nextWaypoints after this one + mobj - the mobj that we are searching for, cannot be changed to a different pointer + 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_SearchWaypoints(waypoint_t *waypoint, mobj_t * const mobj, boolean * const visitedarray) +{ + UINT32 i; + waypoint_t *foundwaypoint; + + if (mobj->type != MT_WAYPOINT) + { + return NULL; + } + +searchwaypointstart: + // If we've already visited this waypoint, we've already checked the next waypoint, and going further is useless + if (visitedarray[waypoint->id] == true) + { + return NULL; + } + + // Mark this waypoint as being visited + visitedarray[waypoint->id] = true; + + if (waypoint->mobj == mobj) + { + return waypoint; + } + + // If this waypoint only has one next waypoint, set the waypoint to be the only next one and jump back to the start + // this is to avoid going too deep into the stack where we can + if (waypoint->numnextwaypoints == 1) + { + waypoint = waypoint->nextwaypoints[0]; + goto searchwaypointstart; + } + + // This waypoint has no next waypoint, so just stop searching + if (waypoint->numnextwaypoints == 0) + { + return NULL; + } + + // 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++) + { + foundwaypoint = K_SearchWaypoints(waypoint->nextwaypoints[i], mobj, visitedarray); + + if (foundwaypoint != NULL) + { + return foundwaypoint; + } + } + + // Matching waypoint was found down this path + return NULL; +} + +/*-------------------------------------------------- + waypoint_t *K_SearchWaypointsForMobj(mobj_t * const mobj) + + Searches through the waypoint list for a waypoint that has an mobj + + Input Arguments:- + mobj - The mobj that we are searching for, 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 +--------------------------------------------------*/ +waypoint_t *K_SearchWaypointsForMobj(mobj_t * const mobj) +{ + boolean *visitedarray; + waypoint_t *foundwaypoint; + + // If the mobj it's looking for isn't even a waypoint, then reject it + if (mobj->type != MT_WAYPOINT) + { + return NULL; + } + + visitedarray = Z_Calloc(numwaypoints * sizeof(boolean), PU_STATIC, NULL); + foundwaypoint = K_SearchWaypoints(firstwaypoint, mobj, visitedarray); + Z_Free(visitedarray); + return foundwaypoint; +} + +/*-------------------------------------------------- + static void K_AddPrevToWaypoint(waypoint_t *waypoint, waypoint_t *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 *waypoint, waypoint_t *prevwaypoint) +{ + waypoint->numprevwaypoints++; + waypoint->prevwaypoints = Z_Realloc(waypoint->prevwaypoints, waypoint->numprevwaypoints * sizeof(waypoint_t *), + PU_LEVEL, NULL); + + if (!waypoint->prevwaypoints) + { + I_Error("K_AddPrevToWaypoint: Out of Memory"); + } + + waypoint->prevwaypoints[waypoint->numprevwaypoints - 1] = prevwaypoint; +} + +/*-------------------------------------------------- + static waypoint_t *K_NewWaypoint(mobj_t *mobj) + + Creates memory for a new waypoint + + Input Arguments:- + mobj - The map object that this waypoint is represented by + + Return:- + Pointer to waypoint_t for the rest of the waypoint data to be placed into +--------------------------------------------------*/ +static waypoint_t *K_NewWaypoint(mobj_t *mobj) +{ + waypoint_t *waypoint = Z_Calloc(sizeof(waypoint_t), PU_LEVEL, NULL); + + if (!waypoint) + { + I_Error("K_NewWaypoint: Failed to allocate memory for waypoint."); + } + P_SetTarget(&waypoint->mobj, mobj); + waypoint->id = numwaypoints++; + return waypoint; +} + +/*-------------------------------------------------- + static waypoint_t *K_SetupWaypoint(mobj_t *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 *mobj) +{ + UINT32 nextwaypointindex = 0; + waypoint_t *thiswaypoint = NULL; + mobj_t *otherwpmobj = NULL; + + // If this mobj is not an MT_WAYPOINT, don't create waypoints from it + if (mobj->type != MT_WAYPOINT) + { + CONS_Debug(DBG_GAMELOGIC, "WARNING: Non MT_WAYPOINT mobj in K_SetupWaypoint."); + return NULL; + } + + // If we have waypoints already created, search through them first to see if this mobj is already added. + if (firstwaypoint != NULL) + { + thiswaypoint = K_SearchWaypointsForMobj(mobj); + } + + // return the waypoint if we already made it + if (thiswaypoint != NULL) + { + return thiswaypoint; + } + + // If we haven't already made the waypoint, make it now. + thiswaypoint = K_NewWaypoint(mobj); + + // Temporarily set the first waypoint to be the first waypoint we setup, this is so that we can search through them + // as they're made and added to the linked list + if (firstwaypoint != NULL) + { + firstwaypoint = thiswaypoint; + } + + // Go through the other waypoint mobjs in the map to find out how many waypoints are after this one + for (otherwpmobj = waypointcap; otherwpmobj != NULL; otherwpmobj = otherwpmobj->tracer) + { + if (mobj->threshold == otherwpmobj->threshold) + { + thiswaypoint->numnextwaypoints++; + } + } + + // No next waypoints + if (thiswaypoint->numnextwaypoints == 0) + { + return NULL; + } + + // Allocate memory to hold enough pointers to all of the next waypoints + thiswaypoint->nextwaypoints = Z_Malloc(thiswaypoint->numnextwaypoints * sizeof(waypoint_t *), PU_LEVEL, NULL); + if (!thiswaypoint->numnextwaypoints) + { + I_Error("K_SetupWaypoint: Out of Memory"); + } + + // Go through the waypoint mobjs again to setup the next waypoints and make this waypoint know they're the next one + for (otherwpmobj = waypointcap; otherwpmobj != NULL; otherwpmobj = otherwpmobj->tracer) + { + if (mobj->threshold == otherwpmobj->movecount) + { + thiswaypoint->nextwaypoints[nextwaypointindex] = K_SetupWaypoint(otherwpmobj); + K_AddPrevToWaypoint(thiswaypoint->nextwaypoints[nextwaypointindex], thiswaypoint); + nextwaypointindex++; + } + if (nextwaypointindex >= thiswaypoint->numnextwaypoints) + { + break; + } + } + + // Should always be returning a valid waypoint here + return thiswaypoint; +} + +/*-------------------------------------------------- + boolean K_SetupWaypointList() + + Sets up the waypoint list for Kart race maps, does not return a status to say whether creation was fully + successful, but we're able to print out warnings if something is wrong. +--------------------------------------------------*/ +void K_SetupWaypointList() +{ + if (waypointcap == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "WARNING: K_SetupWaypointList called with no waypointcap."); + } + else + { + // The waypoint in the waypointcap is going to be considered our first waypoint + K_SetupWaypoint(waypointcap); + + if (!firstwaypoint) + { + CONS_Debug(DBG_GAMELOGIC, "WARNING: K_SetupWaypointList made no waypoints."); + } + } +} \ No newline at end of file diff --git a/src/k_waypoint.h b/src/k_waypoint.h new file mode 100644 index 000000000..41b13b631 --- /dev/null +++ b/src/k_waypoint.h @@ -0,0 +1,26 @@ +#ifndef __K_WAYPOINT__ +#define __K_WAYPOINT__ + +#include "doomdef.h" +#include "p_mobj.h" + +typedef struct waypoint_s +{ + mobj_t *mobj; + UINT32 id; + struct waypoint_s **nextwaypoints; + struct waypoint_s **prevwaypoints; + fixed_t *nextwaypointdistances; + fixed_t *prevwaypointdistances; + UINT32 numnextwaypoints; + UINT32 numprevwaypoints; +} waypoint_t; + + +// AVAILABLE FOR LUA +waypoint_t *K_SearchWaypointsForMobj(mobj_t * const mobj); + +// NOT AVAILABLE FOR LUA +void K_SetupWaypointList(void); + +#endif \ No newline at end of file diff --git a/src/p_mobj.c b/src/p_mobj.c index 18458f34c..28dff38d2 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -11826,6 +11826,10 @@ ML_NOCLIMB : Direction not controllable { mobj->reactiontime = 0; } + + // Sryder 2018-12-7: Grabbed this from the old MT_BOSS3WAYPOINT section so they'll be in the waypointcap instead + P_SetTarget(&mobj->tracer, waypointcap); + P_SetTarget(&waypointcap, mobj); break; } // SRB2Kart @@ -11963,12 +11967,6 @@ ML_NOCLIMB : Direction not controllable if (!foundanother) numstarposts++; } - else if (i == MT_BOSS3WAYPOINT) // SRB2kart 120217 - Used to store checkpoint num - { - mobj->health = mthing->angle; - P_SetTarget(&mobj->tracer, waypointcap); - P_SetTarget(&waypointcap, mobj); - } else if (i == MT_SPIKE) { // Pop up spikes! From 3efbe62a414cec973d49b462dbe5de301c5b48b6 Mon Sep 17 00:00:00 2001 From: Sryder Date: Sat, 8 Dec 2018 20:34:38 +0000 Subject: [PATCH 005/105] Fix a comment and other minor edits --- src/k_waypoint.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/k_waypoint.c b/src/k_waypoint.c index be0ff44fc..a34215f42 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -73,7 +73,7 @@ searchwaypointstart: } } - // Matching waypoint was found down this path + // Matching waypoint was not found down this path return NULL; } @@ -126,7 +126,7 @@ static void K_AddPrevToWaypoint(waypoint_t *waypoint, waypoint_t *prevwaypoint) if (!waypoint->prevwaypoints) { - I_Error("K_AddPrevToWaypoint: Out of Memory"); + I_Error("K_AddPrevToWaypoint: Failed to reallocate memory for previous waypoints."); } waypoint->prevwaypoints[waypoint->numprevwaypoints - 1] = prevwaypoint; @@ -251,7 +251,7 @@ static waypoint_t *K_SetupWaypoint(mobj_t *mobj) --------------------------------------------------*/ void K_SetupWaypointList() { - if (waypointcap == NULL) + if (!waypointcap) { CONS_Debug(DBG_GAMELOGIC, "WARNING: K_SetupWaypointList called with no waypointcap."); } From 5fba15a58bb7d609051c18a9aceb04c6e99be963 Mon Sep 17 00:00:00 2001 From: Sryder Date: Sat, 2 Feb 2019 13:50:22 +0000 Subject: [PATCH 006/105] Make waypoints setup on map load Refactor some things to make slightly clearer code Change comments to use completely tabs Add extra error checks to methods for helpful debugging and safety Use a heap of waypoints during creation to be able to search for them to make links, to not freeze the game Add a way to clear waypoints so that the methods don't error after more than one map load Flip the ambush flag so that adding it will make the waypoint unable to be respawned at when the feature happens Temporarily disable K_UpdateKartPosition so that it doesn't crash on map load --- src/k_kart.c | 2 + src/k_waypoint.c | 555 +++++++++++++++++++++++++++++++++++------------ src/k_waypoint.h | 58 ++++- src/p_mobj.c | 4 +- src/p_setup.c | 14 +- 5 files changed, 487 insertions(+), 146 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index f01f0d0b2..51763384a 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -4767,6 +4767,8 @@ void K_KartUpdatePosition(player_t *player) fixed_t pmo, imo; mobj_t *mo; + return; + if (player->spectator || !player->mo) return; diff --git a/src/k_waypoint.c b/src/k_waypoint.c index a34215f42..0491c23a0 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -1,101 +1,129 @@ #include "k_waypoint.h" #include "doomdef.h" +#include "p_local.h" #include "p_mobj.h" #include "p_tick.h" #include "z_zone.h" +static waypoint_t **waypointheap = NULL; static waypoint_t *firstwaypoint = NULL; -static UINT32 numwaypoints = 0; +static size_t numwaypoints = 0; +static size_t numwaypointmobjs = 0; /*-------------------------------------------------- - waypoint_t *K_SearchWaypoints(waypoint_t *waypoint, mobj_t * const mobj, boolean * const visitedarray) + waypoint_t *K_SearchWaypoints(waypoint_t *waypoint, mobj_t * const mobj, boolean * const visitedarray) - Searches through the waypoint list for a waypoint that has an mobj, just does a simple flood search for the - waypoint that uses this mobj, no pathfinding + Searches through the waypoint list for a waypoint that has an mobj, just does a simple flood search for the + waypoint that uses this mobj, no pathfinding - Input Arguments:- - waypoint - The waypoint that is currently being checked, goes through nextWaypoints after this one - mobj - the mobj that we are searching for, cannot be changed to a different pointer - 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 + Input Arguments:- + waypoint - The waypoint that is currently being checked, goes through nextwaypoints after this one + mobj - the mobj that we are searching for, cannot be changed to a different pointer + 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 + The waypoint that uses that mobj, NULL if it wasn't found, NULL if it isn't an MT_WAYPOINT --------------------------------------------------*/ static waypoint_t *K_SearchWaypoints(waypoint_t *waypoint, mobj_t * const mobj, boolean * const visitedarray) { - UINT32 i; - waypoint_t *foundwaypoint; + waypoint_t *foundwaypoint = NULL; + // Error conditions + if (mobj == NULL || P_MobjWasRemoved(mobj)) + { + CONS_Debug(DBG_GAMELOGIC, "NULL mobj in K_SearchWaypoints.\n"); + return NULL; + } if (mobj->type != MT_WAYPOINT) { + CONS_Debug(DBG_GAMELOGIC, "Non MT_WAYPOINT mobj in K_SearchWaypoints. Type=%d.\n", mobj->type); + return NULL; + } + if (visitedarray == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL visitedarray in K_SearchWaypoints.\n"); return NULL; } searchwaypointstart: - // If we've already visited this waypoint, we've already checked the next waypoint, and going further is useless - if (visitedarray[waypoint->id] == true) + if (waypoint == NULL) { + CONS_Debug(DBG_GAMELOGIC, "NULL waypoint in K_SearchWaypoints.\n"); return NULL; } - // Mark this waypoint as being visited - visitedarray[waypoint->id] = true; - - if (waypoint->mobj == mobj) + // If we've already visited this waypoint, we've already checked the next waypoints, and continuing is useless + if (visitedarray[waypoint->id] != true) { - return waypoint; - } + // Mark this waypoint as being visited + visitedarray[waypoint->id] = true; - // If this waypoint only has one next waypoint, set the waypoint to be the only next one and jump back to the start - // this is to avoid going too deep into the stack where we can - if (waypoint->numnextwaypoints == 1) - { - waypoint = waypoint->nextwaypoints[0]; - goto searchwaypointstart; - } - - // This waypoint has no next waypoint, so just stop searching - if (waypoint->numnextwaypoints == 0) - { - return NULL; - } - - // 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++) - { - foundwaypoint = K_SearchWaypoints(waypoint->nextwaypoints[i], mobj, visitedarray); - - if (foundwaypoint != NULL) + if (waypoint->mobj == mobj) { - return foundwaypoint; + 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) + { + 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_SearchWaypoints(waypoint->nextwaypoints[i], mobj, visitedarray); + + if (foundwaypoint != NULL) + { + break; + } + } + } + } } } - // Matching waypoint was not found down this path - return NULL; + return foundwaypoint; } /*-------------------------------------------------- - waypoint_t *K_SearchWaypointsForMobj(mobj_t * const mobj) + waypoint_t *K_SearchWaypointGraphForMobj(mobj_t * const mobj) - Searches through the waypoint list for a waypoint that has an mobj - - Input Arguments:- - mobj - The mobj that we are searching for, 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 + See header file for description. --------------------------------------------------*/ -waypoint_t *K_SearchWaypointsForMobj(mobj_t * const mobj) +waypoint_t *K_SearchWaypointGraphForMobj(mobj_t * const mobj) { - boolean *visitedarray; - waypoint_t *foundwaypoint; + boolean *visitedarray = NULL; + waypoint_t *foundwaypoint = NULL; - // If the mobj it's looking for isn't even a waypoint, then reject it + // Error conditions + if (mobj == NULL || P_MobjWasRemoved(mobj)) + { + CONS_Debug(DBG_GAMELOGIC, "NULL mobj in K_SearchWaypointsForMobj.\n"); + return NULL; + } if (mobj->type != MT_WAYPOINT) { + CONS_Debug(DBG_GAMELOGIC, "Non MT_WAYPOINT mobj in K_SearchWaypointsForMobj. Type=%d.\n", mobj->type); + return NULL; + } + if (firstwaypoint == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "K_SearchWaypointsForMobj called when no first waypoint.\n"); return NULL; } @@ -106,23 +134,77 @@ waypoint_t *K_SearchWaypointsForMobj(mobj_t * const mobj) } /*-------------------------------------------------- - static void K_AddPrevToWaypoint(waypoint_t *waypoint, waypoint_t *prevwaypoint) + waypoint_t *K_SearchWaypointHeapForMobj(mobj_t * const mobj) - 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 + See header file for description. +--------------------------------------------------*/ +waypoint_t *K_SearchWaypointHeapForMobj(mobj_t * const mobj) +{ + UINT32 i = 0; + waypoint_t *foundwaypoint = NULL; - 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 + // Error conditions + if (mobj == NULL || P_MobjWasRemoved(mobj)) + { + CONS_Debug(DBG_GAMELOGIC, "NULL mobj in K_SearchWaypointsForMobj.\n"); + return NULL; + } + if (mobj->type != MT_WAYPOINT) + { + CONS_Debug(DBG_GAMELOGIC, "Non MT_WAYPOINT mobj in K_SearchWaypointsForMobj. Type=%d.\n", mobj->type); + return NULL; + } + if (waypointheap == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "K_SearchWaypointsForMobj called when no waypointheap.\n"); + return NULL; + } - Return:- - Pointer to waypoint_t for the rest of the waypoint data to be placed into + // Simply search through the waypointheap for the waypoint with the mobj! Much simpler when no pathfinding 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 (waypointheap[i]->mobj == mobj) + { + foundwaypoint = waypointheap[i]; + break; + } + } + + return foundwaypoint; +} + +/*-------------------------------------------------- + static void K_AddPrevToWaypoint(waypoint_t *waypoint, waypoint_t *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 *waypoint, waypoint_t *prevwaypoint) { + // Error conditions + if (waypoint == NULL) + { + CONS_Debug(DBG_SETUP, "NULL waypoint in K_AddPrevToWaypoint.\n"); + return; + } + if (prevwaypoint == NULL) + { + CONS_Debug(DBG_SETUP, "NULL prevwaypoint in K_AddPrevToWaypoint.\n"); + return; + } + waypoint->numprevwaypoints++; - waypoint->prevwaypoints = Z_Realloc(waypoint->prevwaypoints, waypoint->numprevwaypoints * sizeof(waypoint_t *), - PU_LEVEL, NULL); + waypoint->prevwaypoints = + Z_Realloc(waypoint->prevwaypoints, waypoint->numprevwaypoints * sizeof(waypoint_t *), PU_LEVEL, NULL); if (!waypoint->prevwaypoints) { @@ -133,136 +215,327 @@ static void K_AddPrevToWaypoint(waypoint_t *waypoint, waypoint_t *prevwaypoint) } /*-------------------------------------------------- - static waypoint_t *K_NewWaypoint(mobj_t *mobj) + static waypoint_t *K_NewWaypoint(mobj_t *mobj) - Creates memory for a new waypoint + Creates memory for a new waypoint - Input Arguments:- - mobj - The map object that this waypoint is represented by + Input Arguments:- + mobj - The map object that this waypoint is represented by - Return:- - Pointer to waypoint_t for the rest of the waypoint data to be placed into + Return:- + Pointer to waypoint_t for the rest of the waypoint data to be placed into --------------------------------------------------*/ static waypoint_t *K_NewWaypoint(mobj_t *mobj) { - waypoint_t *waypoint = Z_Calloc(sizeof(waypoint_t), PU_LEVEL, NULL); + waypoint_t *waypoint; + + // Error conditions + if (mobj == NULL || P_MobjWasRemoved(mobj)) + { + CONS_Debug(DBG_SETUP, "NULL mobj in K_NewWaypoint.\n"); + return NULL; + } + if (waypointheap == NULL) + { + CONS_Debug(DBG_SETUP, "NULL waypointheap in K_NewWaypoint.\n"); + return NULL; + } + + // Each made waypoint is placed directly into the waypoint heap to be able to search it during creation + waypointheap[numwaypoints] = Z_Calloc(sizeof(waypoint_t), PU_LEVEL, NULL); + waypoint = waypointheap[numwaypoints]; + // numwaypoints is incremented later when waypoint->id is set if (!waypoint) { I_Error("K_NewWaypoint: Failed to allocate memory for waypoint."); } + P_SetTarget(&waypoint->mobj, mobj); waypoint->id = numwaypoints++; + return waypoint; } /*-------------------------------------------------- - static waypoint_t *K_SetupWaypoint(mobj_t *mobj) + static waypoint_t *K_MakeWaypoint(mobj_t *mobj) - Either gets an already made waypoint, or sets up a new waypoint for an mobj, including next and previous waypoints + 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 + Input Arguments:- + mobj - The map object that this waypoint is represented by - Return:- - Pointer to the setup waypoint, NULL if one was not setup + Return:- + Pointer to the setup waypoint, NULL if one was not setup +--------------------------------------------------*/ +static waypoint_t *K_MakeWaypoint(mobj_t *mobj) +{ + waypoint_t *madewaypoint = NULL; + mobj_t *otherwaypointmobj = NULL; + + // Error conditions + if (mobj == NULL || P_MobjWasRemoved(mobj)) + { + CONS_Debug(DBG_SETUP, "NULL mobj in K_MakeWaypoint.\n"); + return NULL; + } + if (waypointcap == NULL) + { + CONS_Debug(DBG_SETUP, "K_MakeWaypoint called with NULL waypointcap."); + return false; + } + + madewaypoint = K_NewWaypoint(mobj); + + // 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"); + } + } + + return madewaypoint; +} + +/*-------------------------------------------------- + static waypoint_t *K_SetupWaypoint(mobj_t *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 *mobj) { - UINT32 nextwaypointindex = 0; waypoint_t *thiswaypoint = NULL; - mobj_t *otherwpmobj = NULL; - // If this mobj is not an MT_WAYPOINT, don't create waypoints from it + // Error conditions + if (mobj == NULL || P_MobjWasRemoved(mobj)) + { + CONS_Debug(DBG_SETUP, "NULL mobj in K_SetupWaypoint.\n"); + return NULL; + } if (mobj->type != MT_WAYPOINT) { - CONS_Debug(DBG_GAMELOGIC, "WARNING: Non MT_WAYPOINT mobj in K_SetupWaypoint."); + CONS_Debug(DBG_SETUP, "Non MT_WAYPOINT mobj in K_SetupWaypoint. Type=%d.\n", mobj->type); return NULL; } + if (waypointcap == NULL) + { + CONS_Debug(DBG_SETUP, "K_SetupWaypoint called with NULL waypointcap."); + return false; + } // If we have waypoints already created, search through them first to see if this mobj is already added. if (firstwaypoint != NULL) { - thiswaypoint = K_SearchWaypointsForMobj(mobj); + thiswaypoint = K_SearchWaypointHeapForMobj(mobj); } - // return the waypoint if we already made it - if (thiswaypoint != NULL) + // The waypoint hasn't already been made, so make it + if (thiswaypoint == NULL) { - return thiswaypoint; - } + mobj_t *otherwaypointmobj = NULL; + UINT32 nextwaypointindex = 0; - // If we haven't already made the waypoint, make it now. - thiswaypoint = K_NewWaypoint(mobj); + thiswaypoint = K_MakeWaypoint(mobj); - // Temporarily set the first waypoint to be the first waypoint we setup, this is so that we can search through them - // as they're made and added to the linked list - if (firstwaypoint != NULL) - { - firstwaypoint = thiswaypoint; - } - - // Go through the other waypoint mobjs in the map to find out how many waypoints are after this one - for (otherwpmobj = waypointcap; otherwpmobj != NULL; otherwpmobj = otherwpmobj->tracer) - { - if (mobj->threshold == otherwpmobj->threshold) + // Temporarily set the first waypoint to be the first waypoint we setup, this is so that we can search + // through them as they're made and added to the linked list + if (firstwaypoint == NULL) { - thiswaypoint->numnextwaypoints++; + firstwaypoint = thiswaypoint; + } + + if (thiswaypoint->numnextwaypoints > 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) + { + thiswaypoint->nextwaypoints[nextwaypointindex] = K_SetupWaypoint(otherwaypointmobj); + K_AddPrevToWaypoint(thiswaypoint->nextwaypoints[nextwaypointindex], thiswaypoint); + nextwaypointindex++; + } + if (nextwaypointindex >= thiswaypoint->numnextwaypoints) + { + break; + } + } } } - // No next waypoints - if (thiswaypoint->numnextwaypoints == 0) - { - return NULL; - } - - // Allocate memory to hold enough pointers to all of the next waypoints - thiswaypoint->nextwaypoints = Z_Malloc(thiswaypoint->numnextwaypoints * sizeof(waypoint_t *), PU_LEVEL, NULL); - if (!thiswaypoint->numnextwaypoints) - { - I_Error("K_SetupWaypoint: Out of Memory"); - } - - // Go through the waypoint mobjs again to setup the next waypoints and make this waypoint know they're the next one - for (otherwpmobj = waypointcap; otherwpmobj != NULL; otherwpmobj = otherwpmobj->tracer) - { - if (mobj->threshold == otherwpmobj->movecount) - { - thiswaypoint->nextwaypoints[nextwaypointindex] = K_SetupWaypoint(otherwpmobj); - K_AddPrevToWaypoint(thiswaypoint->nextwaypoints[nextwaypointindex], thiswaypoint); - nextwaypointindex++; - } - if (nextwaypointindex >= thiswaypoint->numnextwaypoints) - { - break; - } - } - - // Should always be returning a valid waypoint here return thiswaypoint; } /*-------------------------------------------------- - boolean K_SetupWaypointList() + static boolean K_AllocateWaypointHeap() - Sets up the waypoint list for Kart race maps, does not return a status to say whether creation was fully - successful, but we're able to print out warnings if something is wrong. + 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. --------------------------------------------------*/ -void K_SetupWaypointList() +boolean K_AllocateWaypointHeap() { - if (!waypointcap) + mobj_t *waypointmobj = NULL; + boolean allocationsuccessful = false; + + // Error conditions + if (waypointheap != NULL) { - CONS_Debug(DBG_GAMELOGIC, "WARNING: K_SetupWaypointList called with no waypointcap."); + CONS_Debug(DBG_SETUP, "K_AllocateWaypointHeap called when waypointheap is already allocated."); + return false; + } + if (waypointcap == NULL) + { + CONS_Debug(DBG_SETUP, "K_AllocateWaypointHeap called with NULL waypointcap."); + return false; + } + + // 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 if true + 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 and not end up constantly trying to use the waypoints in this case + I_Error("K_AllocateWaypointHeap: Out of memory."); + } + allocationsuccessful = true; } else { - // The waypoint in the waypointcap is going to be considered our first waypoint - K_SetupWaypoint(waypointcap); + CONS_Debug(DBG_SETUP, "No waypoint mobjs in waypointcap."); + } - if (!firstwaypoint) + return allocationsuccessful; +} + +/*-------------------------------------------------- + void K_FreeWaypoints() + + 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! +--------------------------------------------------*/ +void K_FreeWaypoints() +{ + if (waypointheap != NULL) + { + // Free each waypoint if it's not already + INT32 i; + for (i = 0; i < numwaypoints; i++) { - CONS_Debug(DBG_GAMELOGIC, "WARNING: K_SetupWaypointList made no waypoints."); + if (waypointheap[i] != NULL) + { + Z_Free(waypointheap[i]); + } + else + { + CONS_Debug(DBG_SETUP, "NULL waypoint %d attempted to be freed.", i); + } + } + + // Free the waypointheap + Z_Free(waypointheap); + } + + K_ClearWaypoints(); +} + +/*-------------------------------------------------- + boolean K_SetupWaypointList() + + See header file for description. +--------------------------------------------------*/ +boolean K_SetupWaypointList() +{ + boolean setupsuccessful = false; + + K_FreeWaypoints(); + + if (!waypointcap) + { + CONS_Debug(DBG_SETUP, "K_SetupWaypointList called with no waypointcap.\n"); + } + else + { + if (K_AllocateWaypointHeap() == true) + { + // The waypoint in the waypointcap is going to be considered our first waypoint + K_SetupWaypoint(waypointcap); + + if (!firstwaypoint) + { + CONS_Debug(DBG_SETUP, "K_SetupWaypointList made no waypoints.\n"); + } + else + { + CONS_Debug(DBG_SETUP, "Successfully setup %zu waypoints.\n", numwaypoints); + if (numwaypoints < numwaypointmobjs) + { + CONS_Printf("Not all waypoints in the map are connected! %zu waypoints setup but %zu mobjs.", + numwaypoints, numwaypointmobjs); + } + setupsuccessful = true; + } } } -} \ No newline at end of file + + return setupsuccessful; +} + +/*-------------------------------------------------- + void K_ClearWaypoints() + + See header file for description. +--------------------------------------------------*/ +void K_ClearWaypoints() +{ + waypointheap = NULL; + numwaypoints = 0; + numwaypointmobjs = 0; +} diff --git a/src/k_waypoint.h b/src/k_waypoint.h index 41b13b631..9184ce103 100644 --- a/src/k_waypoint.h +++ b/src/k_waypoint.h @@ -18,9 +18,63 @@ typedef struct waypoint_s // AVAILABLE FOR LUA -waypoint_t *K_SearchWaypointsForMobj(mobj_t * const mobj); + + +/*-------------------------------------------------- + waypoint_t *K_SearchWaypointGraphForMobj(mobj_t * const mobj) + + Searches through the waypoint graph for a waypoint that has an mobj, if a waypoint can be found through here it + does mean that the waypoint graph can be traversed to find it + + Input Arguments:- + mobj - The mobj that we are searching for, 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 +--------------------------------------------------*/ + +waypoint_t *K_SearchWaypointGraphForMobj(mobj_t * const mobj); + +/*-------------------------------------------------- + waypoint_t *K_SearchWaypointHeapForMobj(mobj_t * const mobj) + + Searches through the waypoint heap for a waypoint that has an mobj, this does not necessarily mean the waypoint + can be reached from another waypoint + + Input Arguments:- + mobj - The mobj that we are searching for, 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 +--------------------------------------------------*/ + +waypoint_t *K_SearchWaypointHeapForMobj(mobj_t * const mobj); // NOT AVAILABLE FOR LUA -void K_SetupWaypointList(void); + + +/*-------------------------------------------------- + boolean K_SetupWaypointList() + + Sets up the waypoint list for Kart race maps, prints out warnings if something is wrong. + + Return:- + true if waypoint setup was seemingly successful, false if no waypoints were setup + A true return value does not necessarily mean that the waypoints on the map are completely correct +--------------------------------------------------*/ + +boolean K_SetupWaypointList(void); + + +/*-------------------------------------------------- + void K_ClearWaypoints() + + Clears waypointheap, firstwaypoint, numwaypoints, and numwaypointmobjs + WARNING: This does *not* Free waypointheap or any waypoints! They are stored in PU_LEVEL so they are freed once + the level is completed! This is called just before K_SetupWaypointList in P_SetupLevel as they are freed then. + A separate method is called in K_SetupWaypointList that will free everything specifically if they aren't already +--------------------------------------------------*/ + +void K_ClearWaypoints(void); #endif \ No newline at end of file diff --git a/src/p_mobj.c b/src/p_mobj.c index f2ce9c824..a0ad0d716 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -11816,11 +11816,11 @@ ML_NOCLIMB : Direction not controllable mobj->movecount = mthing->angle; if (mthing->options & MTF_AMBUSH) { - mobj->reactiontime = 1; + mobj->reactiontime = 0; // Can't respawn at if Ambush is off } else { - mobj->reactiontime = 0; + mobj->reactiontime = 1; } // Sryder 2018-12-7: Grabbed this from the old MT_BOSS3WAYPOINT section so they'll be in the waypointcap instead diff --git a/src/p_setup.c b/src/p_setup.c index 49b22184e..3cc4b874b 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -80,6 +80,7 @@ // SRB2Kart #include "k_kart.h" +#include "k_waypoint.h" // // Map MD5, calculated on level load. @@ -3058,6 +3059,17 @@ boolean P_SetupLevel(boolean skipprecip) globalweather = mapheaderinfo[gamemap-1]->weather; + // The waypoint data that's in PU_LEVEL needs to be reset back to 0/NULL now since PU_LEVEL was cleared + K_ClearWaypoints(); + // Load the waypoints please! + if (G_RaceGametype()) + { + if (K_SetupWaypointList() == false) + { + CONS_Printf("Waypoints were not able to be setup! Player positions will not work correctly."); + } + } + #ifdef HWRENDER // not win32 only 19990829 by Kin if (rendermode != render_soft && rendermode != render_none) { @@ -3435,7 +3447,7 @@ boolean P_AddWadFile(const char *wadfilename) // R_AddSkins(wadnum); // faB: wadfile index in wadfiles[] - // + // // edit music defs // S_LoadMusicDefs(wadnum); From 79f381ebd4ff31b460a9115c8a37929cd23f9aef Mon Sep 17 00:00:00 2001 From: Sryder Date: Sat, 2 Feb 2019 21:35:21 +0000 Subject: [PATCH 007/105] Add visual debugging mode for waypoints Fix some compiler warnings --- src/d_netcmd.c | 2 + src/d_netcmd.h | 1 + src/info.c | 2 +- src/k_kart.c | 1 + src/k_waypoint.c | 173 ++++++++++++++++++++++++++++++++++++++++++----- src/k_waypoint.h | 19 ++++-- src/p_tick.c | 6 ++ 7 files changed, 182 insertions(+), 22 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index a015b8e73..8b5e9063a 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -372,6 +372,8 @@ consvar_t cv_kartdebugamount = {"kartdebugamount", "1", CV_NETVAR|CV_CHEAT|CV_NO consvar_t cv_kartdebugshrink = {"kartdebugshrink", "Off", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_kartdebugdistribution = {"kartdebugdistribution", "Off", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_kartdebughuddrop = {"kartdebughuddrop", "Off", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +static CV_PossibleValue_t kartdebugwaypoint_cons_t[] = {{0, "Off"}, {1, "Forwards"}, {2, "Backwards"}, {0, NULL}}; +consvar_t cv_kartdebugwaypoints = {"kartdebugwaypoints", "Off", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, kartdebugwaypoint_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_kartdebugcheckpoint = {"kartdebugcheckpoint", "Off", CV_NOSHOWHELP, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_kartdebugnodes = {"kartdebugnodes", "Off", CV_NOSHOWHELP, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 2269996fb..f78ba3097 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -126,6 +126,7 @@ extern consvar_t cv_karteliminatelast; extern consvar_t cv_votetime; extern consvar_t cv_kartdebugitem, cv_kartdebugamount, cv_kartdebugshrink, cv_kartdebugdistribution, cv_kartdebughuddrop; +extern consvar_t cv_kartdebugwaypoints; extern consvar_t cv_kartdebugcheckpoint, cv_kartdebugnodes; extern consvar_t cv_itemfinder; diff --git a/src/info.c b/src/info.c index b0eaa85b5..f111fa950 100644 --- a/src/info.c +++ b/src/info.c @@ -16029,7 +16029,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = { // MT_WAYPOINT 2001, // doomednum - S_NULL, // spawnstate + S_INVISIBLE, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound diff --git a/src/k_kart.c b/src/k_kart.c index 51763384a..1167b22a6 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -440,6 +440,7 @@ void K_RegisterKartStuff(void) CV_RegisterVar(&cv_kartdebugshrink); CV_RegisterVar(&cv_kartdebugdistribution); CV_RegisterVar(&cv_kartdebughuddrop); + CV_RegisterVar(&cv_kartdebugwaypoints); CV_RegisterVar(&cv_kartdebugcheckpoint); CV_RegisterVar(&cv_kartdebugnodes); diff --git a/src/k_waypoint.c b/src/k_waypoint.c index 0491c23a0..7bfb1bcb3 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -1,6 +1,7 @@ #include "k_waypoint.h" #include "doomdef.h" +#include "d_netcmd.h" #include "p_local.h" #include "p_mobj.h" #include "p_tick.h" @@ -11,6 +12,146 @@ static waypoint_t *firstwaypoint = NULL; static size_t numwaypoints = 0; static size_t numwaypointmobjs = 0; +#define SPARKLES_PER_CONNECTION 16 + +/*-------------------------------------------------- + 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; + INT32 n; + + // Error conditions + if (waypoint1 == NULL || waypoint2 == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL waypoint in K_DebugWaypointsSpawnLine.\n"); + return; + } + if (waypoint1->mobj == NULL || waypoint2->mobj == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL mobj on waypoint in K_DebugWaypointsSpawnLine.\n"); + return; + } + if (cv_kartdebugwaypoints.value == 0) + { + CONS_Debug(DBG_GAMELOGIC, "In K_DebugWaypointsSpawnLine when kartdebugwaypoints is off.\n"); + return; + } + + waypointmobj1 = waypoint1->mobj; + waypointmobj2 = waypoint2->mobj; + + n = SPARKLES_PER_CONNECTION; + + // Draw the sparkles + 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 + { + spawnedmobj = P_SpawnMobj(x, y, z, MT_SPARK); + P_SetMobjState(spawnedmobj, S_SPRK1 + ((leveltime + n) % 16)); + spawnedmobj->state->nextstate = S_NULL; + spawnedmobj->state->tics = 1; + x += stepx; + y += stepy; + z += stepz; + } while (n--); +} + +#undef DEBUG_SPARKLE_DISTANCE + +/*-------------------------------------------------- + void K_DebugWaypointsVisualise() + + 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); + + // 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_ORANGE; + } + 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; + } +} + + /*-------------------------------------------------- waypoint_t *K_SearchWaypoints(waypoint_t *waypoint, mobj_t * const mobj, boolean * const visitedarray) @@ -282,7 +423,7 @@ static waypoint_t *K_MakeWaypoint(mobj_t *mobj) } if (waypointcap == NULL) { - CONS_Debug(DBG_SETUP, "K_MakeWaypoint called with NULL waypointcap."); + CONS_Debug(DBG_SETUP, "K_MakeWaypoint called with NULL waypointcap.\n"); return false; } @@ -341,7 +482,7 @@ static waypoint_t *K_SetupWaypoint(mobj_t *mobj) } if (waypointcap == NULL) { - CONS_Debug(DBG_SETUP, "K_SetupWaypoint called with NULL waypointcap."); + CONS_Debug(DBG_SETUP, "K_SetupWaypoint called with NULL waypointcap.\n"); return false; } @@ -391,14 +532,14 @@ static waypoint_t *K_SetupWaypoint(mobj_t *mobj) } /*-------------------------------------------------- - static boolean K_AllocateWaypointHeap() + 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. --------------------------------------------------*/ -boolean K_AllocateWaypointHeap() +static boolean K_AllocateWaypointHeap(void) { mobj_t *waypointmobj = NULL; boolean allocationsuccessful = false; @@ -406,12 +547,12 @@ boolean K_AllocateWaypointHeap() // Error conditions if (waypointheap != NULL) { - CONS_Debug(DBG_SETUP, "K_AllocateWaypointHeap called when waypointheap is already allocated."); + CONS_Debug(DBG_SETUP, "K_AllocateWaypointHeap called when waypointheap is already allocated.\n"); return false; } if (waypointcap == NULL) { - CONS_Debug(DBG_SETUP, "K_AllocateWaypointHeap called with NULL waypointcap."); + CONS_Debug(DBG_SETUP, "K_AllocateWaypointHeap called with NULL waypointcap.\n"); return false; } @@ -448,25 +589,25 @@ boolean K_AllocateWaypointHeap() } else { - CONS_Debug(DBG_SETUP, "No waypoint mobjs in waypointcap."); + CONS_Debug(DBG_SETUP, "No waypoint mobjs in waypointcap.\n"); } return allocationsuccessful; } /*-------------------------------------------------- - void K_FreeWaypoints() + 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! --------------------------------------------------*/ -void K_FreeWaypoints() +static void K_FreeWaypoints(void) { if (waypointheap != NULL) { // Free each waypoint if it's not already - INT32 i; + UINT32 i; for (i = 0; i < numwaypoints; i++) { if (waypointheap[i] != NULL) @@ -475,7 +616,7 @@ void K_FreeWaypoints() } else { - CONS_Debug(DBG_SETUP, "NULL waypoint %d attempted to be freed.", i); + CONS_Debug(DBG_SETUP, "NULL waypoint %d attempted to be freed.\n", i); } } @@ -487,11 +628,11 @@ void K_FreeWaypoints() } /*-------------------------------------------------- - boolean K_SetupWaypointList() + boolean K_SetupWaypointList(void) See header file for description. --------------------------------------------------*/ -boolean K_SetupWaypointList() +boolean K_SetupWaypointList(void) { boolean setupsuccessful = false; @@ -517,7 +658,7 @@ boolean K_SetupWaypointList() CONS_Debug(DBG_SETUP, "Successfully setup %zu waypoints.\n", numwaypoints); if (numwaypoints < numwaypointmobjs) { - CONS_Printf("Not all waypoints in the map are connected! %zu waypoints setup but %zu mobjs.", + CONS_Printf("Not all waypoints in the map are connected! %zu waypoints setup but %zu mobjs.\n", numwaypoints, numwaypointmobjs); } setupsuccessful = true; @@ -529,11 +670,11 @@ boolean K_SetupWaypointList() } /*-------------------------------------------------- - void K_ClearWaypoints() + void K_ClearWaypoints(void) See header file for description. --------------------------------------------------*/ -void K_ClearWaypoints() +void K_ClearWaypoints(void) { waypointheap = NULL; numwaypoints = 0; diff --git a/src/k_waypoint.h b/src/k_waypoint.h index 9184ce103..21cf43b5e 100644 --- a/src/k_waypoint.h +++ b/src/k_waypoint.h @@ -7,13 +7,13 @@ typedef struct waypoint_s { mobj_t *mobj; - UINT32 id; + size_t id; struct waypoint_s **nextwaypoints; struct waypoint_s **prevwaypoints; fixed_t *nextwaypointdistances; fixed_t *prevwaypointdistances; - UINT32 numnextwaypoints; - UINT32 numprevwaypoints; + size_t numnextwaypoints; + size_t numprevwaypoints; } waypoint_t; @@ -54,7 +54,16 @@ waypoint_t *K_SearchWaypointHeapForMobj(mobj_t * const mobj); /*-------------------------------------------------- - boolean K_SetupWaypointList() + void K_DebugWaypointsVisualise() + + Creates mobjs in order to visualise waypoints for debugging. +--------------------------------------------------*/ + +void K_DebugWaypointsVisualise(void); + + +/*-------------------------------------------------- + boolean K_SetupWaypointList(void) Sets up the waypoint list for Kart race maps, prints out warnings if something is wrong. @@ -67,7 +76,7 @@ boolean K_SetupWaypointList(void); /*-------------------------------------------------- - void K_ClearWaypoints() + void K_ClearWaypoints(void) Clears waypointheap, firstwaypoint, numwaypoints, and numwaypointmobjs WARNING: This does *not* Free waypointheap or any waypoints! They are stored in PU_LEVEL so they are freed once diff --git a/src/p_tick.c b/src/p_tick.c index b46b248bb..8509a7621 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -22,6 +22,7 @@ #include "lua_script.h" #include "lua_hook.h" #include "k_kart.h" +#include "k_waypoint.h" // Object place #include "m_cheat.h" @@ -714,6 +715,11 @@ void P_Ticker(boolean run) && --mapreset <= 1 && server) // Remember: server uses it for mapchange, but EVERYONE ticks down for the animation D_MapChange(gamemap, gametype, encoremode, true, 0, false, false); + + if (cv_kartdebugwaypoints.value != 0) + { + K_DebugWaypointsVisualise(); + } } // Always move the camera. From ce4ba3141f4b3df8a31cc8d5e1dd508d0ab283a8 Mon Sep 17 00:00:00 2001 From: Sryder Date: Mon, 4 Feb 2019 21:50:14 +0000 Subject: [PATCH 008/105] Update to search functions to make them more extendable Some more minor formatting changes --- src/k_waypoint.c | 230 +++++++++++++++++++++++++++++++++++++---------- src/k_waypoint.h | 4 +- 2 files changed, 185 insertions(+), 49 deletions(-) diff --git a/src/k_waypoint.c b/src/k_waypoint.c index 7bfb1bcb3..385ba1211 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -15,7 +15,7 @@ static size_t numwaypointmobjs = 0; #define SPARKLES_PER_CONNECTION 16 /*-------------------------------------------------- - void K_DebugWaypointsSpawnLine(waypoint_t * const waypoint1, waypoint_t * const waypoint2) + void K_DebugWaypointsSpawnLine(waypoint_t *const waypoint1, waypoint_t *const waypoint2) Draw a debugging line between 2 waypoints @@ -23,13 +23,14 @@ static size_t numwaypointmobjs = 0; 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) +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; INT32 n; + UINT32 numofframes = 1; // If this was 0 it could divide by 0 // Error conditions if (waypoint1 == NULL || waypoint2 == NULL) @@ -52,6 +53,7 @@ static void K_DebugWaypointsSpawnLine(waypoint_t * const waypoint1, waypoint_t * waypointmobj2 = waypoint2->mobj; n = SPARKLES_PER_CONNECTION; + numofframes = S_SPRK16 - S_SPRK1; // Draw the sparkles stepx = (waypointmobj2->x - waypointmobj1->x) / n; @@ -63,7 +65,7 @@ static void K_DebugWaypointsSpawnLine(waypoint_t * const waypoint1, waypoint_t * do { spawnedmobj = P_SpawnMobj(x, y, z, MT_SPARK); - P_SetMobjState(spawnedmobj, S_SPRK1 + ((leveltime + n) % 16)); + P_SetMobjState(spawnedmobj, S_SPRK1 + ((leveltime + n) % (numofframes + 1))); spawnedmobj->state->nextstate = S_NULL; spawnedmobj->state->tics = 1; x += stepx; @@ -72,7 +74,7 @@ static void K_DebugWaypointsSpawnLine(waypoint_t * const waypoint1, waypoint_t * } while (n--); } -#undef DEBUG_SPARKLE_DISTANCE +#undef SPARKLES_PER_CONNECTION /*-------------------------------------------------- void K_DebugWaypointsVisualise() @@ -109,7 +111,11 @@ void K_DebugWaypointsVisualise(void) // 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) + if (waypoint->numnextwaypoints == 0 && waypoint->numprevwaypoints == 0) + { + debugmobj->color = SKINCOLOR_RED; + } + else if (waypoint->numnextwaypoints == 0 || waypoint->numprevwaypoints == 0) { debugmobj->color = SKINCOLOR_ORANGE; } @@ -151,47 +157,112 @@ void K_DebugWaypointsVisualise(void) } } +/*-------------------------------------------------- + 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) +{ + mobj_t *mobj; + boolean mobjsmatch = false; + + // Error Conditions + if (waypoint == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL waypoint in K_CheckWaypointForMobj.\n"); + return false; + } + if (waypoint->mobj == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "Waypoint has NULL mobj in K_CheckWaypointForMobj.\n"); + return false; + } + if (mobjpointer == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL mobjpointer in K_CheckWaypointForMobj.\n"); + return false; + } + + mobj = (mobj_t *)mobjpointer; + + if (P_MobjWasRemoved(mobj)) + { + CONS_Debug(DBG_GAMELOGIC, "Mobj Was Removed in K_CheckWaypointForMobj"); + return false; + } + if (mobj->type != MT_WAYPOINT) + { + CONS_Debug(DBG_GAMELOGIC, "Non MT_WAYPOINT mobj in K_CheckWaypointForMobj. Type=%d.\n", mobj->type); + return false; + } + + // All that error checking for 3 lines :^) + if (waypoint->mobj == mobj) + { + mobjsmatch = true; + } + + return mobjsmatch; +} /*-------------------------------------------------- - waypoint_t *K_SearchWaypoints(waypoint_t *waypoint, mobj_t * const mobj, boolean * const visitedarray) + 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 has an mobj, just does a simple flood search for the - waypoint that uses this mobj, no pathfinding + 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 - mobj - the mobj that we are searching for, cannot be changed to a different pointer + 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_SearchWaypoints(waypoint_t *waypoint, mobj_t * const mobj, boolean * const visitedarray) +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 - if (mobj == NULL || P_MobjWasRemoved(mobj)) + if (condition == NULL) { - CONS_Debug(DBG_GAMELOGIC, "NULL mobj in K_SearchWaypoints.\n"); + CONS_Debug(DBG_GAMELOGIC, "NULL condition in K_TraverseWaypoints.\n"); return NULL; } - if (mobj->type != MT_WAYPOINT) + if (conditionalfunc == NULL) { - CONS_Debug(DBG_GAMELOGIC, "Non MT_WAYPOINT mobj in K_SearchWaypoints. Type=%d.\n", mobj->type); - return NULL; + CONS_Debug(DBG_GAMELOGIC, "NULL conditionalfunc in K_TraverseWaypoints.\n"); } if (visitedarray == NULL) { - CONS_Debug(DBG_GAMELOGIC, "NULL visitedarray in K_SearchWaypoints.\n"); + CONS_Debug(DBG_GAMELOGIC, "NULL visitedarray in K_TraverseWaypoints.\n"); return NULL; } searchwaypointstart: if (waypoint == NULL) { - CONS_Debug(DBG_GAMELOGIC, "NULL waypoint in K_SearchWaypoints.\n"); + CONS_Debug(DBG_GAMELOGIC, "NULL waypoint in K_TraverseWaypoints.\n"); return NULL; } @@ -201,7 +272,7 @@ searchwaypointstart: // Mark this waypoint as being visited visitedarray[waypoint->id] = true; - if (waypoint->mobj == mobj) + if (conditionalfunc(waypoint, condition) == true) { foundwaypoint = waypoint; } @@ -219,6 +290,8 @@ 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 @@ -226,7 +299,8 @@ searchwaypointstart: { if (waypoint->nextwaypoints[i] != NULL) { - foundwaypoint = K_SearchWaypoints(waypoint->nextwaypoints[i], mobj, visitedarray); + foundwaypoint = K_TraverseWaypoints(waypoint->nextwaypoints[i], conditionalfunc, condition, + visitedarray); if (foundwaypoint != NULL) { @@ -242,25 +316,35 @@ searchwaypointstart: } /*-------------------------------------------------- - waypoint_t *K_SearchWaypointGraphForMobj(mobj_t * const mobj) + waypoint_t *K_SearchWaypointGraph( + boolean (*conditionalfunc)(waypoint_t *const, void *const), + void *const condition) - See header file for description. + 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 --------------------------------------------------*/ -waypoint_t *K_SearchWaypointGraphForMobj(mobj_t * const mobj) +static waypoint_t *K_SearchWaypointGraph( + boolean (*conditionalfunc)(waypoint_t *const, void *const), + void *const condition) { boolean *visitedarray = NULL; waypoint_t *foundwaypoint = NULL; // Error conditions - if (mobj == NULL || P_MobjWasRemoved(mobj)) + if (condition == NULL) { - CONS_Debug(DBG_GAMELOGIC, "NULL mobj in K_SearchWaypointsForMobj.\n"); + CONS_Debug(DBG_GAMELOGIC, "NULL condition in K_SearchWaypointGraph.\n"); return NULL; } - if (mobj->type != MT_WAYPOINT) + if (conditionalfunc == NULL) { - CONS_Debug(DBG_GAMELOGIC, "Non MT_WAYPOINT mobj in K_SearchWaypointsForMobj. Type=%d.\n", mobj->type); - return NULL; + CONS_Debug(DBG_GAMELOGIC, "NULL conditionalfunc in K_SearchWaypointGraph.\n"); } if (firstwaypoint == NULL) { @@ -269,44 +353,75 @@ waypoint_t *K_SearchWaypointGraphForMobj(mobj_t * const mobj) } visitedarray = Z_Calloc(numwaypoints * sizeof(boolean), PU_STATIC, NULL); - foundwaypoint = K_SearchWaypoints(firstwaypoint, mobj, visitedarray); + foundwaypoint = K_TraverseWaypoints(firstwaypoint, conditionalfunc, condition, visitedarray); Z_Free(visitedarray); return foundwaypoint; } /*-------------------------------------------------- - waypoint_t *K_SearchWaypointHeapForMobj(mobj_t * const mobj) + waypoint_t *K_SearchWaypointGraphForMobj(mobj_t * const mobj) See header file for description. --------------------------------------------------*/ -waypoint_t *K_SearchWaypointHeapForMobj(mobj_t * const mobj) +waypoint_t *K_SearchWaypointGraphForMobj(mobj_t *const mobj) +{ + if (mobj == NULL || P_MobjWasRemoved(mobj)) + { + CONS_Debug(DBG_GAMELOGIC, "NULL mobj in K_SearchWaypointGraphForMobj.\n"); + return NULL; + } + if (mobj->type != MT_WAYPOINT) + { + CONS_Debug(DBG_GAMELOGIC, "Non MT_WAYPOINT mobj in K_SearchWaypointGraphForMobj. Type=%d.\n", mobj->type); + return NULL; + } + + return K_SearchWaypointGraph(K_CheckWaypointForMobj, (void *)mobj); +} + +/*-------------------------------------------------- + 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 - if (mobj == NULL || P_MobjWasRemoved(mobj)) + if (condition == NULL) { - CONS_Debug(DBG_GAMELOGIC, "NULL mobj in K_SearchWaypointsForMobj.\n"); + CONS_Debug(DBG_GAMELOGIC, "NULL condition in K_SearchWaypointHeap.\n"); return NULL; } - if (mobj->type != MT_WAYPOINT) + if (conditionalfunc == NULL) { - CONS_Debug(DBG_GAMELOGIC, "Non MT_WAYPOINT mobj in K_SearchWaypointsForMobj. Type=%d.\n", mobj->type); - return NULL; + CONS_Debug(DBG_GAMELOGIC, "NULL conditionalfunc in K_SearchWaypointHeap.\n"); } if (waypointheap == NULL) { - CONS_Debug(DBG_GAMELOGIC, "K_SearchWaypointsForMobj called when no waypointheap.\n"); + CONS_Debug(DBG_GAMELOGIC, "K_SearchWaypointHeap called when no waypointheap.\n"); return NULL; } - // Simply search through the waypointheap for the waypoint with the mobj! Much simpler when no pathfinding 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 + // 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 (waypointheap[i]->mobj == mobj) + if (conditionalfunc(waypointheap[i], condition) == true) { foundwaypoint = waypointheap[i]; break; @@ -317,7 +432,28 @@ waypoint_t *K_SearchWaypointHeapForMobj(mobj_t * const mobj) } /*-------------------------------------------------- - static void K_AddPrevToWaypoint(waypoint_t *waypoint, waypoint_t *prevwaypoint) + waypoint_t *K_SearchWaypointHeapForMobj(mobj_t *const mobj) + + See header file for description. +--------------------------------------------------*/ +waypoint_t *K_SearchWaypointHeapForMobj(mobj_t *const mobj) +{ + if (mobj == NULL || P_MobjWasRemoved(mobj)) + { + CONS_Debug(DBG_GAMELOGIC, "NULL mobj in K_SearchWaypointHeapForMobj.\n"); + return NULL; + } + if (mobj->type != MT_WAYPOINT) + { + CONS_Debug(DBG_GAMELOGIC, "Non MT_WAYPOINT mobj in K_SearchWaypointHeapForMobj. Type=%d.\n", mobj->type); + return NULL; + } + + return K_SearchWaypointHeap(K_CheckWaypointForMobj, (void *)mobj); +} + +/*-------------------------------------------------- + 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 @@ -329,7 +465,7 @@ waypoint_t *K_SearchWaypointHeapForMobj(mobj_t * const mobj) Return:- Pointer to waypoint_t for the rest of the waypoint data to be placed into --------------------------------------------------*/ -static void K_AddPrevToWaypoint(waypoint_t *waypoint, waypoint_t *prevwaypoint) +static void K_AddPrevToWaypoint(waypoint_t *const waypoint, waypoint_t *const prevwaypoint) { // Error conditions if (waypoint == NULL) @@ -366,7 +502,7 @@ static void K_AddPrevToWaypoint(waypoint_t *waypoint, waypoint_t *prevwaypoint) Return:- Pointer to waypoint_t for the rest of the waypoint data to be placed into --------------------------------------------------*/ -static waypoint_t *K_NewWaypoint(mobj_t *mobj) +static waypoint_t *K_NewWaypoint(mobj_t *const mobj) { waypoint_t *waypoint; @@ -399,7 +535,7 @@ static waypoint_t *K_NewWaypoint(mobj_t *mobj) } /*-------------------------------------------------- - static waypoint_t *K_MakeWaypoint(mobj_t *mobj) + 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 @@ -410,7 +546,7 @@ static waypoint_t *K_NewWaypoint(mobj_t *mobj) Return:- Pointer to the setup waypoint, NULL if one was not setup --------------------------------------------------*/ -static waypoint_t *K_MakeWaypoint(mobj_t *mobj) +static waypoint_t *K_MakeWaypoint(mobj_t *const mobj) { waypoint_t *madewaypoint = NULL; mobj_t *otherwaypointmobj = NULL; @@ -454,7 +590,7 @@ static waypoint_t *K_MakeWaypoint(mobj_t *mobj) } /*-------------------------------------------------- - static waypoint_t *K_SetupWaypoint(mobj_t *mobj) + 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 @@ -465,7 +601,7 @@ static waypoint_t *K_MakeWaypoint(mobj_t *mobj) Return:- Pointer to the setup waypoint, NULL if one was not setup --------------------------------------------------*/ -static waypoint_t *K_SetupWaypoint(mobj_t *mobj) +static waypoint_t *K_SetupWaypoint(mobj_t *const mobj) { waypoint_t *thiswaypoint = NULL; diff --git a/src/k_waypoint.h b/src/k_waypoint.h index 21cf43b5e..6da2688e2 100644 --- a/src/k_waypoint.h +++ b/src/k_waypoint.h @@ -21,7 +21,7 @@ typedef struct waypoint_s /*-------------------------------------------------- - waypoint_t *K_SearchWaypointGraphForMobj(mobj_t * const mobj) + waypoint_t *K_SearchWaypointGraphForMobj(mobj_t *const mobj) Searches through the waypoint graph for a waypoint that has an mobj, if a waypoint can be found through here it does mean that the waypoint graph can be traversed to find it @@ -36,7 +36,7 @@ typedef struct waypoint_s waypoint_t *K_SearchWaypointGraphForMobj(mobj_t * const mobj); /*-------------------------------------------------- - waypoint_t *K_SearchWaypointHeapForMobj(mobj_t * const mobj) + waypoint_t *K_SearchWaypointHeapForMobj(mobj_t *const mobj) Searches through the waypoint heap for a waypoint that has an mobj, this does not necessarily mean the waypoint can be reached from another waypoint From c38be00196bd1f194bdca7d058bd38e19e8ca9f5 Mon Sep 17 00:00:00 2001 From: Sryder Date: Sun, 2 Jun 2019 21:44:07 +0100 Subject: [PATCH 009/105] Calculate euclidean distances between each waypoint on map load. Change some Debug prints to Alert prints for easier debugging for mappers. --- src/k_waypoint.c | 52 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/src/k_waypoint.c b/src/k_waypoint.c index 385ba1211..d4c12c6a1 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -416,7 +416,7 @@ static waypoint_t *K_SearchWaypointHeap( return NULL; } - // Simply search through the waypointheap for the waypoint which matches the condition Much simpler when no + // 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++) @@ -452,6 +452,24 @@ waypoint_t *K_SearchWaypointHeapForMobj(mobj_t *const mobj) return K_SearchWaypointHeap(K_CheckWaypointForMobj, (void *)mobj); } +/*-------------------------------------------------- + static fixed_t 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 fixed_t K_DistanceBetweenWaypoints(waypoint_t *const waypoint1, waypoint_t *const waypoint2) +{ + fixed_t xydist = P_AproxDistance(waypoint1->mobj->x - waypoint2->mobj->x, waypoint1->mobj->y - waypoint2->mobj->y); + return P_AproxDistance(xydist, waypoint1->mobj->z - waypoint2->mobj->z); +} + /*-------------------------------------------------- static void K_AddPrevToWaypoint(waypoint_t *const waypoint, waypoint_t *const prevwaypoint) @@ -488,6 +506,14 @@ static void K_AddPrevToWaypoint(waypoint_t *const waypoint, waypoint_t *const pr 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; } @@ -582,7 +608,12 @@ static waypoint_t *K_MakeWaypoint(mobj_t *const mobj) madewaypoint->nextwaypoints = Z_Calloc(madewaypoint->numnextwaypoints * sizeof(waypoint_t *), PU_LEVEL, NULL); if (madewaypoint->nextwaypoints == NULL) { - I_Error("K_MakeWaypoint: Out of Memory"); + 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."); } } @@ -645,6 +676,8 @@ static waypoint_t *K_SetupWaypoint(mobj_t *const mobj) 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) @@ -652,8 +685,12 @@ static waypoint_t *K_SetupWaypoint(mobj_t *const mobj) // threshold = next waypoint id, movecount = my id if (mobj->threshold == otherwaypointmobj->movecount) { - thiswaypoint->nextwaypoints[nextwaypointindex] = K_SetupWaypoint(otherwaypointmobj); - K_AddPrevToWaypoint(thiswaypoint->nextwaypoints[nextwaypointindex], thiswaypoint); + 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) @@ -776,7 +813,7 @@ boolean K_SetupWaypointList(void) if (!waypointcap) { - CONS_Debug(DBG_SETUP, "K_SetupWaypointList called with no waypointcap.\n"); + CONS_Alert(CONS_ERROR, "No waypoints in map.\n"); } else { @@ -787,14 +824,15 @@ boolean K_SetupWaypointList(void) if (!firstwaypoint) { - CONS_Debug(DBG_SETUP, "K_SetupWaypointList made no waypoints.\n"); + CONS_Alert(CONS_ERROR, "No waypoints in map.\n"); } else { CONS_Debug(DBG_SETUP, "Successfully setup %zu waypoints.\n", numwaypoints); if (numwaypoints < numwaypointmobjs) { - CONS_Printf("Not all waypoints in the map are connected! %zu waypoints setup but %zu mobjs.\n", + CONS_Alert(CONS_WARNING, + "Not all waypoints in the map are connected! %zu waypoints setup but %zu waypoint mobjs.\n", numwaypoints, numwaypointmobjs); } setupsuccessful = true; From 31d76596c0459e3bc9af64f26c60bd6e89c07655 Mon Sep 17 00:00:00 2001 From: Sryder Date: Sat, 8 Jun 2019 21:46:23 +0100 Subject: [PATCH 010/105] Binary Heap Implementation Currently untested. --- src/Makefile | 1 + src/k_bheap.c | 577 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/k_bheap.h | 103 +++++++++ 3 files changed, 681 insertions(+) create mode 100644 src/k_bheap.c create mode 100644 src/k_bheap.h diff --git a/src/Makefile b/src/Makefile index 105f1f4c4..b04a58110 100644 --- a/src/Makefile +++ b/src/Makefile @@ -490,6 +490,7 @@ OBJS:=$(i_main_o) \ $(OBJDIR)/st_stuff.o \ $(OBJDIR)/k_kart.o \ $(OBJDIR)/k_waypoint.o\ + $(OBJDIR)/k_bheap.o \ $(OBJDIR)/m_aatree.o \ $(OBJDIR)/m_anigif.o \ $(OBJDIR)/m_argv.o \ diff --git a/src/k_bheap.c b/src/k_bheap.c new file mode 100644 index 000000000..4f2285f9c --- /dev/null +++ b/src/k_bheap.c @@ -0,0 +1,577 @@ +#include "k_bheap.h" + +#include "z_zone.h" + +/*-------------------------------------------------- + static boolean K_BHeapItemValidate(bheap_t *heap, bheapitem_t *item) + + Validates an item on a heap to ensure it is correct and on that heap. + + Input Arguments:- + heap - The heap to validate the item with + item - The item to validate + + Return:- + True if the item is valid, false if it isn't. +--------------------------------------------------*/ +static boolean K_BHeapItemValidate(bheap_t *heap, bheapitem_t *item) +{ + boolean heapitemvalid = false; + + if (heap == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL heap in K_BHeapItemValidate.\n"); + } + else if (item == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL item in K_BHeapItemValidate.\n"); + } + else + { + if ((item->data != NULL) && (item->heapindex < SIZE_MAX / 2) && (item->owner == heap)) + { + heapitemvalid = true; + } + } + + return heapitemvalid; +} + +/*-------------------------------------------------- + static bheapitem_t *K_BHeapItemsCompare(bheap_t *heap, bheapitem_t *item1, bheapitem_t *item2) + + Compares 2 items in the heap to find the better (lower) value one. + + Input Arguments:- + heap - The heap to compare items + item1 - The first item to compare + item2 - The second item to compare + + Return:- + The item out of the 2 sent in that has the better value, returns item2 if they are identical +--------------------------------------------------*/ +static bheapitem_t *K_BHeapItemsCompare(bheap_t *heap, bheapitem_t *item1, bheapitem_t *item2) +{ + bheapitem_t *lowervalueitem = NULL; + if (heap == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL heap in K_BHeapItemsCompare.\n"); + } + else if (K_BHeapValid(heap) == false) + { + CONS_Debug(DBG_GAMELOGIC, "Invalid heap in K_BHeapSwapItems.\n"); + } + else if (item1 == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL item1 in K_BHeapItemsCompare.\n"); + } + else if (item2 == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL item2 in K_BHeapItemsCompare.\n"); + } + else if (K_BHeapItemValidate(heap, item1) == false) + { + CONS_Debug(DBG_GAMELOGIC, "Invalid item1 in K_BHeapItemsCompare.\n"); + } + else if (K_BHeapItemValidate(heap, item2) == false) + { + CONS_Debug(DBG_GAMELOGIC, "Invalid item2 in K_BHeapItemsCompare.\n"); + } + else + { + if (item1->value < item2->value) + { + lowervalueitem = item1; + } + else + { + lowervalueitem = item2; + } + } + + return lowervalueitem; +} + +/*-------------------------------------------------- + static void K_BHeapSwapItems(bheap_t *heap, bheapitem_t *item1, bheapitem_t *item2) + + Swaps 2 items in the heap + + Input Arguments:- + heap - The heap to swap items in + item1 - The first item to swap in the heap + item2 - The second item to swap in the heap + + Return:- + None +--------------------------------------------------*/ +static void K_BHeapSwapItems(bheap_t *heap, bheapitem_t *item1, bheapitem_t *item2) +{ + if (heap == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL heap in K_BHeapSwapItems.\n"); + } + else if (K_BHeapValid(heap) == false) + { + CONS_Debug(DBG_GAMELOGIC, "Invalid heap in K_BHeapSwapItems.\n"); + } + else if (item1 == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL item1 in K_BHeapSwapItems.\n"); + } + else if (item2 == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL item2 in K_BHeapSwapItems.\n"); + } + else if (K_BHeapItemValidate(heap, item1) == false) + { + CONS_Debug(DBG_GAMELOGIC, "Invalid item1 in K_BHeapSwapItems.\n"); + } + else if (K_BHeapItemValidate(heap, item2) == false) + { + CONS_Debug(DBG_GAMELOGIC, "Invalid item2 in K_BHeapSwapItems.\n"); + } + else + { + size_t tempitemindex = item1->heapindex; + bheapitem_t tempitemstore = *item1; + + // Swap the items fully with each other + *item1 = *item2; + *item2 = tempitemstore; + + // Swap the heap index on each item to be correct + item2->heapindex = item1->heapindex; + item1->heapindex = tempitemindex; + } +} + +/*-------------------------------------------------- + static size_t K_BHeapItemGetParentIndex(bheapitem_t *item) + + Gets the parent index of a heap item + + Input Arguments:- + item - The item to get the parent index of + + Return:- + The parent index of the item +--------------------------------------------------*/ +static size_t K_BHeapItemGetParentIndex(bheapitem_t *item) +{ + size_t parentindex = SIZE_MAX; + + if (item == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL item in K_BHeapItemGetParentIndex.\n"); + } + else if (item->heapindex >= (SIZE_MAX / 2)) + { + CONS_Debug(DBG_GAMELOGIC, "Bad item heapindex in K_BHeapItemGetParentIndex.\n"); + } + else + { + parentindex = (item->heapindex - 1U) / 2U; + } + + return parentindex; +} + +/*-------------------------------------------------- + static size_t K_BHeapItemGetLeftChildIndex(bheapitem_t *item) + + Gets the left child index of a heap item + + Input Arguments:- + item - The item to get the left child index of + + Return:- + The left child index of the item +--------------------------------------------------*/ +static size_t K_BHeapItemGetLeftChildIndex(bheapitem_t *item) +{ + size_t leftchildindex = SIZE_MAX; + + if (item == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL item in K_BHeapItemGetLeftChildIndex.\n"); + } + else if (item->heapindex >= (SIZE_MAX / 2)) + { + CONS_Debug(DBG_GAMELOGIC, "Bad item heapindex in K_BHeapItemGetLeftChildIndex.\n"); + } + else + { + leftchildindex = (item->heapindex * 2U) + 1U; + } + + return leftchildindex; +} + +/*-------------------------------------------------- + static size_t K_BHeapItemGetRightChildIndex(bheapitem_t *item) + + Gets the right child index of a heap item + + Input Arguments:- + item - The item to get the right child index of + + Return:- + The right child index of the item +--------------------------------------------------*/ +static size_t K_BHeapItemGetRightChildIndex(bheapitem_t *item) +{ + size_t rightchildindex = SIZE_MAX; + + if (item == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL item in K_BHeapItemGetRightChildIndex.\n"); + } + else if (item->heapindex >= (SIZE_MAX / 2)) + { + CONS_Debug(DBG_GAMELOGIC, "Bad item heapindex in K_BHeapItemGetRightChildIndex.\n"); + } + else + { + rightchildindex = (item->heapindex * 2U) + 2U; + } + + return rightchildindex; +} + +/*-------------------------------------------------- + static void K_BHeapSortUp(bheap_t *heap, bheapitem_t *item) + + Sorts a heapitem up the list to its correct index, lower value items are higher up + + Input Arguments:- + heap - The heap to sort the item up. + item - The item to sort up the heap + + Return:- + None +--------------------------------------------------*/ +static void K_BHeapSortUp(bheap_t *heap, bheapitem_t *item) +{ + if (heap == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL heap in K_BHeapSortUp.\n"); + } + else if (K_BHeapValid(heap) == false) + { + CONS_Debug(DBG_GAMELOGIC, "Invalid heap in K_BHeapSortUp.\n"); + } + else if (item == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL item in K_BHeapSortUp.\n"); + } + else + { + if (item->heapindex > 0U) + { + size_t parentindex = SIZE_MAX; + do + { + parentindex = K_BHeapItemGetParentIndex(item); + + // Swap the nodes if the parent has a higher value + if (K_BHeapItemsCompare(heap, item, &heap->array[parentindex]) == item) + { + K_BHeapSwapItems(heap, item, &heap->array[parentindex]); + } + else + { + break; + } + } while (parentindex > 0U); + } + } +} + +/*-------------------------------------------------- + static void K_BHeapSortDown(bheap_t *heap, bheapitem_t *item) + + Sorts a heapitem down the list to its correct index, higher value items are further down + + Input Arguments:- + heap - The heap to sort the item down. + item - The item to sort down the heap + + Return:- + None +--------------------------------------------------*/ +static void K_BHeapSortDown(bheap_t *heap, bheapitem_t *item) +{ + if (heap == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL heap in K_BHeapSortDown.\n"); + } + else if (K_BHeapValid(heap) == false) + { + CONS_Debug(DBG_GAMELOGIC, "Invalid heap in K_BHeapSortDown.\n"); + } + else if (item == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL item in K_BHeapSortDown.\n"); + } + else + { + if (heap->count > 0U) + { + size_t leftchildindex = SIZE_MAX; + size_t rightchildindex = SIZE_MAX; + bheapitem_t *leftchild = NULL; + bheapitem_t *rightchild = NULL; + bheapitem_t *swapchild = NULL; + boolean noswapneeded = false; + + do + { + leftchildindex = K_BHeapItemGetLeftChildIndex(item); + rightchildindex = K_BHeapItemGetRightChildIndex(item); + + if (leftchildindex < heap->count) + { + leftchild = &heap->array[leftchildindex]; + swapchild = leftchild; + if (rightchildindex < heap->count) + { + rightchild = &heap->array[rightchildindex]; + // Choose the lower child node to swap with + if (K_BHeapItemsCompare(heap, leftchild, rightchild) == rightchild) + { + swapchild = rightchild; + } + } + + // Swap with the lower child, if it's lower than item + if (K_BHeapItemsCompare(heap, swapchild, item) == swapchild) + { + K_BHeapSwapItems(heap, item, swapchild); + } + else + { + noswapneeded = true; + } + + } + else + { + noswapneeded = true; + } + + if (noswapneeded) + { + break; + } + + } while (item->heapindex < (heap->count - 1U)); + } + } +} + +/*-------------------------------------------------- + boolean K_BHeapInit(bheap_t *const heap, size_t initialcapacity) + + See header file for description. +--------------------------------------------------*/ +boolean K_BHeapInit(bheap_t *const heap, size_t initialcapacity) +{ + boolean initsuccess = false; + + if (heap == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL heap in K_BHeapInit.\n"); + } + else if (initialcapacity == 0U) + { + CONS_Debug(DBG_GAMELOGIC, "initialcapacity is 0 in K_BHeapInit.\n"); + } + else + { + heap->array = Z_Calloc(initialcapacity * sizeof(bheapitem_t), PU_STATIC, NULL); + + if (heap->array == NULL) + { + I_Error("K_BHeapInit: Out of Memory."); + } + + heap->capacity = initialcapacity; + heap->count = 0U; + + initsuccess = true; + } + + return initsuccess; +} + +/*-------------------------------------------------- + boolean K_BHeapValid(bheap_t *const heap) + + See header file for description. +--------------------------------------------------*/ +boolean K_BHeapValid(bheap_t *const heap) +{ + boolean heapvalid = false; + + if (heap == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL heap in K_BHeapValid.\n"); + } + else + { + if ((heap->capacity > 0U) && (heap->array != NULL)) + { + heapvalid = true; + } + } + + return heapvalid; +} + +/*-------------------------------------------------- + boolean K_BHeapPush(bheap_t *const heap, void *const item, UINT32 value) + + See header file for description. +--------------------------------------------------*/ +boolean K_BHeapPush(bheap_t *const heap, void *const item, UINT32 value) +{ + boolean pushsuccess = false; + if (heap == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL heap in K_BHeapPush.\n"); + } + else if (!K_BHeapValid(heap)) + { + CONS_Debug(DBG_GAMELOGIC, "Uninitialised heap in K_BHeapPush.\n"); + } + else if (item == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL item in K_BHeapPush.\n"); + } + else if (heap->count >= (SIZE_MAX / 2)) + { + CONS_Debug(DBG_GAMELOGIC, "Tried to push too many items on binary heap in K_BHeapPush.\n"); + } + else + { + // If the capacity of the heap has been reached, a realloc is needed + // I'm just doing a basic double of capacity for simplicity + if (heap->count >= heap->capacity) + { + size_t newarraycapacity = heap->capacity * 2; + heap->array = Z_Realloc(heap->array, newarraycapacity, PU_STATIC, NULL); + + if (heap->array == NULL) + { + I_Error("K_BHeapPush: Out of Memory."); + } + + heap->capacity = newarraycapacity; + } + + heap->array[heap->count].heapindex = heap->count; + heap->array[heap->count].owner = heap; + heap->array[heap->count].data = item; + heap->array[heap->count].value = value; + + heap->count++; + + K_BHeapSortUp(heap, &heap->array[heap->count - 1U]); + + pushsuccess = true; + } + + return pushsuccess; +} + +/*-------------------------------------------------- + boolean K_BHeapPop(bheap_t *const heap, bheapitem_t *const returnitemstorage) + + See header file for description. +--------------------------------------------------*/ +boolean K_BHeapPop(bheap_t *const heap, bheapitem_t *const returnitemstorage) +{ + boolean popsuccess = false; + if (heap == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL heap in K_BHeapPop.\n"); + } + else if (!K_BHeapValid(heap)) + { + CONS_Debug(DBG_GAMELOGIC, "Uninitialised heap in K_BHeapPop.\n"); + } + else if (heap->count = 0U) + { + CONS_Debug(DBG_GAMELOGIC, "Tried to Pop from empty heap in K_BHeapPop.\n"); + } + else if (returnitemstorage == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL returnitemstorage in K_BHeapPop.\n"); + } + else + { + *returnitemstorage = heap->array[0]; + + // Invalidate the heap related data from the return item + returnitemstorage->owner = NULL; + returnitemstorage->heapindex = SIZE_MAX; + + heap->count--; + + heap->array[0] = heap->array[heap->count]; + heap->array[0].heapindex = 0U; + memset(&heap->array[heap->count], 0x00, sizeof(bheapitem_t)); + + K_BHeapSortDown(heap, &heap->array[0]); + popsuccess = true; + } + + return popsuccess; +} + +/*-------------------------------------------------- + boolean K_UpdateHeapItemValue(bheapitem_t *const item, const UINT32 newvalue) + + See header file for description. +--------------------------------------------------*/ +boolean K_UpdateHeapItemValue(bheapitem_t *const item, const UINT32 newvalue) +{ + boolean updatevaluesuccess = false; + if (item == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL item in K_UpdateHeapItemValue.\n"); + } + else if (item->owner == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "item has NULL owner in K_UpdateHeapItemValue.\n"); + } + else if (K_BHeapItemValidate(item->owner, item) == false) + { + CONS_Debug(DBG_GAMELOGIC, "Invalid item in K_UpdateHeapItemValue.\n"); + } + else if (K_BHeapValid(item->owner) == false) + { + CONS_Debug(DBG_GAMELOGIC, "Invalid item owner in K_UpdateHeapItemValue.\n"); + } + else + { + size_t oldvalue = item->value; + item->value = newvalue; + if (newvalue < oldvalue) + { + K_BHeapSortUp(item->owner, item); + } + else if (newvalue > oldvalue) + { + K_BHeapSortDown(item->owner, item); + } + else + { + // No change is needed as the value is the same + } + + } + + return updatevaluesuccess; +} diff --git a/src/k_bheap.h b/src/k_bheap.h new file mode 100644 index 000000000..494a4bd20 --- /dev/null +++ b/src/k_bheap.h @@ -0,0 +1,103 @@ +#ifndef __K_BHEAP__ +#define __K_BHEAP__ + +#include "doomdef.h" + +typedef struct bheapitem_s +{ + size_t heapindex; // The index in the heap this item is + bheap_t *owner; // The heap that owns this item + void *data; // data for this heap item + UINT32 value; // The value of this item, the lowest value item is first in the array +} bheapitem_t; + +typedef struct bheap_s +{ + size_t capacity; // capacity of the heap + size_t count; // number of items in the heap + bheapitem_t *array; // pointer to the heap items array +} bheap_t; + + +/*-------------------------------------------------- + boolean K_BHeapInit(bheap_t *const heap, size_t initialcapacity) + + Initialises a binary heap. + + Input Arguments:- + heap - The heap to initialise + initialcapacity - The initial capacity the heap should hold + + Return:- + True if the initialisation was successful, false if it wasn't. +--------------------------------------------------*/ + +boolean K_BHeapInit(bheap_t *const heap, size_t initialcapacity); + + +/*-------------------------------------------------- + boolean K_BHeapValid(bheap_t *const heap) + + Checks a binary heap for validity + + Input Arguments:- + heap - The heap to validate + + Return:- + True if the binary heap is valid, false if it isn't +--------------------------------------------------*/ + +boolean K_BHeapValid(bheap_t *const heap); + + +/*-------------------------------------------------- + boolean K_BHeapPush(bheap_t *const heap, void *const item, const UINT32 value) + + Adds a new item to a binary heap. + + Input Arguments:- + heap - The heap to add to. + item - The item to add to the heap. + value - The value of this item for the heap, lowest is first in the heap + + Return:- + True if the push to the heap was successful, false if it wasn't due to invalid parameters +--------------------------------------------------*/ + +boolean K_BHeapPush(bheap_t *const heap, void *const item, UINT32 value); + + +/*-------------------------------------------------- + boolean K_BHeapPop(bheap_t *const heap, bheapitem_t *const returnitemstorage) + + Pops the first item off of the heap, then orders it back to be correct. + + Input Arguments:- + heap - The heap to pop from. + returnitemstorage - The first item on the Heap is placed in here + + Return:- + true if the pop from the heap was successful, false if it wasn't. +--------------------------------------------------*/ + +boolean K_BHeapPop(bheap_t *const heap, bheapitem_t *const returnitemstorage); + + +/*-------------------------------------------------- + boolean K_UpdateHeapItemValue(bheapitem_t *const item, const UINT32 newvalue) + + Updates the heap item's value, and reorders it in the array appropriately. Only works if the item is in a heap + validly. If it's a heapitem that is not currently in a heap (ie it's been popped off) just change the value + manually. + + Input Arguments:- + item - The item to update the value of. + newvalue - The new value the item will hold + + Return:- + true if the update was successful, false if it wasn't +--------------------------------------------------*/ + +boolean K_UpdateHeapItemValue(bheapitem_t *const item, const UINT32 newvalue); + +#endif From 3a25bb38fed13d6ead5ad00c7acc0f11a88fa0ce Mon Sep 17 00:00:00 2001 From: Sryder Date: Sat, 8 Jun 2019 22:55:02 +0100 Subject: [PATCH 011/105] Add callback to heapitem to alert data when the heap index is changed. Add K_BHeapContains to give an easy way to find if the heap contains data. --- src/k_bheap.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++----- src/k_bheap.h | 44 +++++++++++++++++++------- 2 files changed, 111 insertions(+), 19 deletions(-) diff --git a/src/k_bheap.c b/src/k_bheap.c index 4f2285f9c..3b3d0fad9 100644 --- a/src/k_bheap.c +++ b/src/k_bheap.c @@ -143,6 +143,15 @@ static void K_BHeapSwapItems(bheap_t *heap, bheapitem_t *item1, bheapitem_t *ite // Swap the heap index on each item to be correct item2->heapindex = item1->heapindex; item1->heapindex = tempitemindex; + + if (item1->indexchanged != NULL) + { + item1->indexchanged(item1->data, item1->heapindex); + } + if (item2->indexchanged != NULL) + { + item2->indexchanged(item2->data, item2->heapindex); + } } } @@ -430,11 +439,11 @@ boolean K_BHeapValid(bheap_t *const heap) } /*-------------------------------------------------- - boolean K_BHeapPush(bheap_t *const heap, void *const item, UINT32 value) + boolean K_BHeapPush(bheap_t *const heap, void *const item, UINT32 value, updateindexfunc changeindexcallback) See header file for description. --------------------------------------------------*/ -boolean K_BHeapPush(bheap_t *const heap, void *const item, UINT32 value) +boolean K_BHeapPush(bheap_t *const heap, void *const item, UINT32 value, updateindexfunc changeindexcallback) { boolean pushsuccess = false; if (heap == NULL) @@ -455,6 +464,7 @@ boolean K_BHeapPush(bheap_t *const heap, void *const item, UINT32 value) } else { + bheapitem_t *newitem = NULL; // If the capacity of the heap has been reached, a realloc is needed // I'm just doing a basic double of capacity for simplicity if (heap->count >= heap->capacity) @@ -470,10 +480,18 @@ boolean K_BHeapPush(bheap_t *const heap, void *const item, UINT32 value) heap->capacity = newarraycapacity; } - heap->array[heap->count].heapindex = heap->count; - heap->array[heap->count].owner = heap; - heap->array[heap->count].data = item; - heap->array[heap->count].value = value; + newitem = &heap->array[heap->count]; + + newitem->heapindex = heap->count; + newitem->owner = heap; + newitem->data = item; + newitem->value = value; + newitem->indexchanged = changeindexcallback; + + if (newitem->indexchanged != NULL) + { + newitem->indexchanged(newitem->data, newitem->heapindex); + } heap->count++; @@ -517,6 +535,11 @@ boolean K_BHeapPop(bheap_t *const heap, bheapitem_t *const returnitemstorage) returnitemstorage->owner = NULL; returnitemstorage->heapindex = SIZE_MAX; + if (returnitemstorage->indexchanged != NULL) + { + returnitemstorage->indexchanged(returnitemstorage->data, returnitemstorage->heapindex); + } + heap->count--; heap->array[0] = heap->array[heap->count]; @@ -531,11 +554,11 @@ boolean K_BHeapPop(bheap_t *const heap, bheapitem_t *const returnitemstorage) } /*-------------------------------------------------- - boolean K_UpdateHeapItemValue(bheapitem_t *const item, const UINT32 newvalue) + boolean K_UpdateBHeapItemValue(bheapitem_t *const item, const UINT32 newvalue) See header file for description. --------------------------------------------------*/ -boolean K_UpdateHeapItemValue(bheapitem_t *const item, const UINT32 newvalue) +boolean K_UpdateBHeapItemValue(bheapitem_t *const item, const UINT32 newvalue) { boolean updatevaluesuccess = false; if (item == NULL) @@ -575,3 +598,50 @@ boolean K_UpdateHeapItemValue(bheapitem_t *const item, const UINT32 newvalue) return updatevaluesuccess; } + +/*-------------------------------------------------- + UINT32 K_BHeapContains(bheap_t *const heap, void *const data, size_t index) + + See header file for description. +--------------------------------------------------*/ +UINT32 K_BHeapContains(bheap_t *const heap, void *const data, size_t index) +{ + UINT32 heapindexwithdata = SIZE_MAX; + + if (heap == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL heap in K_BHeapContains.\n"); + } + else if (!K_BHeapValid(heap)) + { + CONS_Debug(DBG_GAMELOGIC, "Uninitialised heap in K_BHeapContains.\n"); + } + else if (data == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL data in K_BHeapContains.\n"); + } + else + { + if ((heap->count != 0U) && (index < heap->count)) + { + if (heap->array[index].data == data) + { + heapindexwithdata = index; + } + } + else if (index == SIZE_MAX) + { + size_t i; + for (i = 0; i < heap->count; i++) + { + if (heap->array[i].data == data) + { + heapindexwithdata = i; + break; + } + } + } + } + + return heapindexwithdata; +} diff --git a/src/k_bheap.h b/src/k_bheap.h index 494a4bd20..d8a4abb50 100644 --- a/src/k_bheap.h +++ b/src/k_bheap.h @@ -3,12 +3,15 @@ #include "doomdef.h" +typedef void(*updateindexfunc)(void *const, const UINT32); + typedef struct bheapitem_s { - size_t heapindex; // The index in the heap this item is - bheap_t *owner; // The heap that owns this item - void *data; // data for this heap item - UINT32 value; // The value of this item, the lowest value item is first in the array + size_t heapindex; // The index in the heap this item is + updateindexfunc indexchanged; // A callback function that is called when this item changes index to alert data + bheap_t *owner; // The heap that owns this item + void *data; // data for this heap item + UINT32 value; // The value of this item, the lowest value item is first in the array } bheapitem_t; typedef struct bheap_s @@ -51,20 +54,21 @@ boolean K_BHeapValid(bheap_t *const heap); /*-------------------------------------------------- - boolean K_BHeapPush(bheap_t *const heap, void *const item, const UINT32 value) + boolean K_BHeapPush(bheap_t *const heap, void *const item, const UINT32 value, updateindexfunc changeindexcallback) Adds a new item to a binary heap. Input Arguments:- - heap - The heap to add to. - item - The item to add to the heap. - value - The value of this item for the heap, lowest is first in the heap + heap - The heap to add to. + item - The item to add to the heap. + value - The value of this item for the heap, lowest is first in the heap + changeindexcallback - A callback function that is called when the item's index changes, can be NULL Return:- True if the push to the heap was successful, false if it wasn't due to invalid parameters --------------------------------------------------*/ -boolean K_BHeapPush(bheap_t *const heap, void *const item, UINT32 value); +boolean K_BHeapPush(bheap_t *const heap, void *const item, UINT32 value, updateindexfunc changeindexcallback); /*-------------------------------------------------- @@ -84,7 +88,7 @@ boolean K_BHeapPop(bheap_t *const heap, bheapitem_t *const returnitemstorage); /*-------------------------------------------------- - boolean K_UpdateHeapItemValue(bheapitem_t *const item, const UINT32 newvalue) + boolean K_UpdateBHeapItemValue(bheapitem_t *const item, const UINT32 newvalue) Updates the heap item's value, and reorders it in the array appropriately. Only works if the item is in a heap validly. If it's a heapitem that is not currently in a heap (ie it's been popped off) just change the value @@ -98,6 +102,24 @@ boolean K_BHeapPop(bheap_t *const heap, bheapitem_t *const returnitemstorage); true if the update was successful, false if it wasn't --------------------------------------------------*/ -boolean K_UpdateHeapItemValue(bheapitem_t *const item, const UINT32 newvalue); +boolean K_UpdateBHeapItemValue(bheapitem_t *const item, const UINT32 newvalue); + + +/*-------------------------------------------------- + boolean K_BHeapContains(bheap_t *const heap, void *const data, size_t index) + + Checks to see if data is contained in the heap. If index is not SIZE_MAX, then only the index sent in is + checked. Otherwise every index is checked linearly. + + Input Arguments:- + heap - The heap to check the contents of + data - The data that is being checked for + index - The index of the heap to check, if SIZE_MAX, check every index + + Return:- + The heap index that contains data, SIZE_MAX if it is not in the heap +--------------------------------------------------*/ + +UINT32 K_BHeapContains(bheap_t *const heap, void *const data, size_t index); #endif From b1fa5f6d34cee0034149ff321e6c84a3faa248f2 Mon Sep 17 00:00:00 2001 From: Sryder Date: Sat, 8 Jun 2019 22:59:13 +0100 Subject: [PATCH 012/105] Correct compiler issues. --- src/k_bheap.c | 8 ++++---- src/k_bheap.h | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/k_bheap.c b/src/k_bheap.c index 3b3d0fad9..6b4ebe75d 100644 --- a/src/k_bheap.c +++ b/src/k_bheap.c @@ -519,7 +519,7 @@ boolean K_BHeapPop(bheap_t *const heap, bheapitem_t *const returnitemstorage) { CONS_Debug(DBG_GAMELOGIC, "Uninitialised heap in K_BHeapPop.\n"); } - else if (heap->count = 0U) + else if (heap->count == 0U) { CONS_Debug(DBG_GAMELOGIC, "Tried to Pop from empty heap in K_BHeapPop.\n"); } @@ -600,13 +600,13 @@ boolean K_UpdateBHeapItemValue(bheapitem_t *const item, const UINT32 newvalue) } /*-------------------------------------------------- - UINT32 K_BHeapContains(bheap_t *const heap, void *const data, size_t index) + size_t K_BHeapContains(bheap_t *const heap, void *const data, size_t index) See header file for description. --------------------------------------------------*/ -UINT32 K_BHeapContains(bheap_t *const heap, void *const data, size_t index) +size_t K_BHeapContains(bheap_t *const heap, void *const data, size_t index) { - UINT32 heapindexwithdata = SIZE_MAX; + size_t heapindexwithdata = SIZE_MAX; if (heap == NULL) { diff --git a/src/k_bheap.h b/src/k_bheap.h index d8a4abb50..e56d7299b 100644 --- a/src/k_bheap.h +++ b/src/k_bheap.h @@ -3,13 +3,13 @@ #include "doomdef.h" -typedef void(*updateindexfunc)(void *const, const UINT32); +typedef void(*updateindexfunc)(void *const, const size_t); typedef struct bheapitem_s { size_t heapindex; // The index in the heap this item is updateindexfunc indexchanged; // A callback function that is called when this item changes index to alert data - bheap_t *owner; // The heap that owns this item + struct bheap_s *owner; // The heap that owns this item void *data; // data for this heap item UINT32 value; // The value of this item, the lowest value item is first in the array } bheapitem_t; @@ -106,7 +106,7 @@ boolean K_UpdateBHeapItemValue(bheapitem_t *const item, const UINT32 newvalue); /*-------------------------------------------------- - boolean K_BHeapContains(bheap_t *const heap, void *const data, size_t index) + size_t K_BHeapContains(bheap_t *const heap, void *const data, size_t index) Checks to see if data is contained in the heap. If index is not SIZE_MAX, then only the index sent in is checked. Otherwise every index is checked linearly. @@ -120,6 +120,6 @@ boolean K_UpdateBHeapItemValue(bheapitem_t *const item, const UINT32 newvalue); The heap index that contains data, SIZE_MAX if it is not in the heap --------------------------------------------------*/ -UINT32 K_BHeapContains(bheap_t *const heap, void *const data, size_t index); +size_t K_BHeapContains(bheap_t *const heap, void *const data, size_t index); #endif From 773ddd98a8c3797401c2ce37a93b24d81e0c6c88 Mon Sep 17 00:00:00 2001 From: Sryder Date: Sun, 9 Jun 2019 19:36:36 +0100 Subject: [PATCH 013/105] Add function for freeing binary heap. --- src/k_bheap.c | 24 ++++++++++++++++++++++++ src/k_bheap.h | 16 ++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/src/k_bheap.c b/src/k_bheap.c index 6b4ebe75d..527af1bfc 100644 --- a/src/k_bheap.c +++ b/src/k_bheap.c @@ -645,3 +645,27 @@ size_t K_BHeapContains(bheap_t *const heap, void *const data, size_t index) return heapindexwithdata; } + +boolean K_BHeapFree(bheap_t *const heap) +{ + boolean freesuccess = false; + + if (heap == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL heap in K_BHeapFree.\n"); + } + else if (!K_BHeapValid(heap)) + { + CONS_Debug(DBG_GAMELOGIC, "Uninitialised heap in K_BHeapFree.\n"); + } + else + { + Z_Free(heap->array); + heap->array = NULL; + heap->capacity = 0U; + heap->count = 0U; + freesuccess = true; + } + + return freesuccess; +} diff --git a/src/k_bheap.h b/src/k_bheap.h index e56d7299b..f1c6d2d5c 100644 --- a/src/k_bheap.h +++ b/src/k_bheap.h @@ -122,4 +122,20 @@ boolean K_UpdateBHeapItemValue(bheapitem_t *const item, const UINT32 newvalue); size_t K_BHeapContains(bheap_t *const heap, void *const data, size_t index); + +/*-------------------------------------------------- + boolean K_BHeapFree(bheap_t *const heap) + + Free the binary heap. + This does NOT free the data held within the binary heap items. Make sure those can still be freed manually. + + Input Arguments:- + heap - The heap to free + + Return:- + True if the heap was freed successfully, false if the heap wasn't valid to free +--------------------------------------------------*/ + +boolean K_BHeapFree(bheap_t *const heap); + #endif From 128d970baddaa9c2a59f5fb18bdcd58fc6e70bd7 Mon Sep 17 00:00:00 2001 From: Sryder Date: Sun, 9 Jun 2019 19:37:24 +0100 Subject: [PATCH 014/105] Turn CONS_Printf to CONS_Alert in waypoint initialisation. --- src/p_setup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_setup.c b/src/p_setup.c index b812fc92b..2e88f664c 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -3096,7 +3096,7 @@ boolean P_SetupLevel(boolean skipprecip) { if (K_SetupWaypointList() == false) { - CONS_Printf("Waypoints were not able to be setup! Player positions will not work correctly."); + CONS_Alert(CONS_ERROR, "Waypoints were not able to be setup! Player positions will not work correctly.\n"); } } #ifdef HWRENDER // not win32 only 19990829 by Kin From e9786fa95640523a69251ebf7371554dc2cc34f0 Mon Sep 17 00:00:00 2001 From: Sryder Date: Sun, 9 Jun 2019 19:49:21 +0100 Subject: [PATCH 015/105] Waypoint pathfinding exists! Includes some small refactoring of older functions - single return is a bit safer and easier to debug. Still needs doing are: Finish Line Waypoint, Shortcut waypoints, disabled waypoints. The actual A* method itself is a bit of a monster, but I can't figure out how to refactor it too much more. --- src/k_waypoint.c | 1441 +++++++++++++++++++++++++++++++++++++--------- src/k_waypoint.h | 137 ++++- 2 files changed, 1316 insertions(+), 262 deletions(-) diff --git a/src/k_waypoint.c b/src/k_waypoint.c index d4c12c6a1..b039ca7f0 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -1,18 +1,130 @@ #include "k_waypoint.h" -#include "doomdef.h" #include "d_netcmd.h" #include "p_local.h" -#include "p_mobj.h" #include "p_tick.h" #include "z_zone.h" +#include "k_bheap.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. When the sets reach their max capacity +// they are reallocated to contain their old capacity plus these defines. Openset is smaller because most of the time +// the nodes will quickly be moved to closedset, closedset could contain an entire maps worth of waypoints. +// Additonally, in order to keep later calls to pathfinding quick and avoid reallocation, the highest size of the +// allocation is saved into a variable. +static const size_t OPENSET_BASE_SIZE = 16U; +static const size_t CLOSEDSET_BASE_SIZE = 256U; +static const size_t NODESARRAY_BASE_SIZE = 256U; static waypoint_t **waypointheap = NULL; static waypoint_t *firstwaypoint = NULL; -static size_t numwaypoints = 0; -static size_t numwaypointmobjs = 0; +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; -#define SPARKLES_PER_CONNECTION 16 +/*-------------------------------------------------- + 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) + { + CONS_Debug(DBG_GAMELOGIC, "NULL waypoint mobj in K_GetWaypointIsShortcut.\n"); + } + else + { + // TODO + } + + return waypointisshortcut; +} + +/*-------------------------------------------------- + boolean K_GetWaypointIsEnabled(waypoint_t *waypoint) + + See header file for description. +--------------------------------------------------*/ +boolean K_GetWaypointIsEnabled(waypoint_t *waypoint) +{ + boolean waypointisshortcut = true; + + if (waypoint == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL waypoint in K_GetWaypointIsEnabled.\n"); + } + else if (waypoint->mobj == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL waypoint mobj in K_GetWaypointIsEnabled.\n"); + } + else + { + // TODO + } + + return waypointisshortcut; +} + +/*-------------------------------------------------- + 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) + { + 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) + { + CONS_Debug(DBG_GAMELOGIC, "NULL waypoint mobj in K_GetWaypointID.\n"); + } + else + { + waypointid = waypoint->mobj->movecount; + } + + return waypointid; +} /*-------------------------------------------------- void K_DebugWaypointsSpawnLine(waypoint_t *const waypoint1, waypoint_t *const waypoint2) @@ -74,10 +186,8 @@ static void K_DebugWaypointsSpawnLine(waypoint_t *const waypoint1, waypoint_t *c } while (n--); } -#undef SPARKLES_PER_CONNECTION - /*-------------------------------------------------- - void K_DebugWaypointsVisualise() + void K_DebugWaypointsVisualise(void) See header file for description. --------------------------------------------------*/ @@ -157,6 +267,805 @@ void K_DebugWaypointsVisualise(void) } } +/*-------------------------------------------------- + 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 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; + if (waypoint1 == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL waypoint1 in K_DistanceBetweenWaypoints.\n"); + } + else if (waypoint2 == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL waypoint2 in K_DistanceBetweenWaypoints.\n"); + } + else + { + 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; +} + +/*-------------------------------------------------- + static UINT32 K_GetNodeFScore(pathfindnode_t *node) + + Gets the FScore of a node. The FScore is the GScore plus the HScore. + + Input Arguments:- + node - The node to get the FScore of + + Return:- + The FScore of the node. +--------------------------------------------------*/ +static UINT32 K_GetNodeFScore(pathfindnode_t *node) +{ + UINT32 nodefscore = UINT32_MAX; + + if (node == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL node in K_GetNodeFScore.\n"); + } + else + { + nodefscore = node->gscore + node->hscore; + } + + return nodefscore; +} + +/*-------------------------------------------------- + static boolean K_ClosedsetContainsNode(pathfindnode_t **closedset, pathfindnode_t *node, size_t closedsetcount) + + Checks whether the Closedset contains a node. Searches from the end to the start for speed reasons. + + Input Arguments:- + closedset - The closed set within the A* algorithm + node - The node to check is within the closed set + closedsetcount - The current size of the closedset + + Return:- + True if the node is in the closed set, false if it isn't +--------------------------------------------------*/ +static boolean K_ClosedsetContainsNode(pathfindnode_t **closedset, pathfindnode_t *node, size_t closedsetcount) +{ + boolean nodeisinclosedset = false; + + if (closedset == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL closedset in K_SetContainsWaypoint.\n"); + } + else if (node == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL node in K_SetContainsWaypoint.\n"); + } + else + { + size_t i; + // It is more likely that we'll find the node we are looking for from the end of the array + // Yes, the for loop looks weird, remember that size_t is unsigned and we want to check 0, after it hits 0 it + // will loop back up to SIZE_MAX + for (i = closedsetcount - 1U; i < closedsetcount; i--) + { + if (closedset[i] == node) + { + nodeisinclosedset = true; + break; + } + } + } + return nodeisinclosedset; +} + +/*-------------------------------------------------- + static pathfindnode_t *K_NodesArrayContainsWaypoint( + pathfindnode_t *nodesarray, + waypoint_t* waypoint, + size_t nodesarraycount) + + Checks whether the Nodes Array contains a node with a waypoint. Searches from the end to the start for speed + reasons. + + Input Arguments:- + nodesarray - The nodes array within the A* algorithm + waypoint - The waypoint to check is within the nodes array + nodesarraycount - The current size of the nodes array + + Return:- + The pathfind node that has the waypoint if there is one. NULL if the waypoint is not in the nodes array. +--------------------------------------------------*/ +static pathfindnode_t *K_NodesArrayContainsWaypoint( + pathfindnode_t *nodesarray, + waypoint_t* waypoint, + size_t nodesarraycount) +{ + pathfindnode_t *foundnode = NULL; + + if (nodesarray == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL nodesarray in K_NodesArrayContainsWaypoint.\n"); + } + else if (waypoint == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL waypoint in K_NodesArrayContainsWaypoint.\n"); + } + else + { + size_t i; + // It is more likely that we'll find the node we are looking for from the end of the array + // Yes, the for loop looks weird, remember that size_t is unsigned and we want to check 0, after it hits 0 it + // will loop back up to SIZE_MAX + for (i = nodesarraycount - 1U; i < nodesarraycount; i--) + { + if (nodesarray[i].waypoint == waypoint) + { + foundnode = &nodesarray[i]; + break; + } + } + } + return foundnode; +} + +/*-------------------------------------------------- + static void K_NodeUpdateHeapIndex(void *const node, const size_t newheapindex) + + A callback for the Openset Binary Heap to be able to update the heapindex of the pathfindnodes when they are + moved. + + Input Arguments:- + node - The node that has been updated, should be a pointer to a pathfindnode_t + newheapindex - The new heapindex of the node. + + Return:- + None +--------------------------------------------------*/ +static void K_NodeUpdateHeapIndex(void *const node, const size_t newheapindex) +{ + if (node == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL node in K_NodeUpdateHeapIndex.\n"); + } + else + { + pathfindnode_t *truenode = (pathfindnode_t*)node; + truenode->heapindex = newheapindex; + } +} + +/*-------------------------------------------------- + static boolean K_ReconstructPath(path_t *const returnpath, pathfindnode_t *const destinationnode) + + From a pathfindnode that should be the destination, reconstruct a path from start to finish. + + Input Arguments:- + returnpath - The location of the path that is being created + destinationnode - The node that is the destination from the pathfinding + + Return:- + True if the path reconstruction was successful, false if it wasn't. +--------------------------------------------------*/ +static boolean K_ReconstructPath(path_t *const returnpath, pathfindnode_t *const destinationnode) +{ + boolean reconstructsuccess = false; + + if (returnpath == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL returnpath in K_ReconstructPath.\n"); + } + else if (destinationnode == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL destinationnode in K_ReconstructPath.\n"); + } + else + { + size_t numnodes = 0U; + pathfindnode_t *thisnode = destinationnode; + + // If the path we're placing our new path into already has data, free it + if (returnpath->array != NULL) + { + Z_Free(returnpath->array); + returnpath->numnodes = 0U; + returnpath->totaldist = 0U; + } + + // Do a fast check of how many nodes there are so we know how much space to allocate + for (thisnode = destinationnode; thisnode; thisnode = thisnode->camefrom) + { + numnodes++; + } + + if (numnodes > 0U) + { + // Allocate memory for the path + returnpath->numnodes = numnodes; + returnpath->array = Z_Calloc(numnodes * sizeof(pathfindnode_t), PU_STATIC, NULL); + returnpath->totaldist = destinationnode->gscore; + if (returnpath->array == NULL) + { + I_Error("K_ReconstructPath: Out of memory."); + } + + // Put the nodes into the return array + for (thisnode = destinationnode; thisnode; thisnode = thisnode->camefrom) + { + returnpath->array[numnodes - 1U] = *thisnode; + // Correct the camefrom element to point to the previous element in the array instead + if ((returnpath->array[numnodes - 1U].camefrom != NULL) && (numnodes > 1U)) + { + returnpath->array[numnodes - 1U].camefrom = &returnpath->array[numnodes - 2U]; + } + else + { + returnpath->array[numnodes - 1U].camefrom = NULL; + } + + numnodes--; + } + + reconstructsuccess = true; + } + } + + return reconstructsuccess; +} + +/*-------------------------------------------------- + static boolean K_WaypointAStar( + waypoint_t *const sourcewaypoint, + waypoint_t *const destinationwaypoint, + path_t *const returnpath, + const boolean useshortcuts, + const boolean huntbackwards) + + From a source waypoint and destination waypoint, find the best path between them using the A* algorithm. + + Input Arguments:- + sourcewaypoint - The source waypoint to pathfind from + destinationwaypoint - The destination waypoint to pathfind to + returnpath - The path to return to if the pathfinding was successful. + useshortcuts - Whether the pathfinding can use shortcut waypoints. + huntbackwards - Whether the pathfinding should hunt through previous or next waypoints + + Return:- + True if a path was found between source and destination, false otherwise. +--------------------------------------------------*/ +static boolean K_WaypointAStar( + waypoint_t *const sourcewaypoint, + waypoint_t *const destinationwaypoint, + path_t *const returnpath, + const boolean useshortcuts, + const boolean huntbackwards) +{ + boolean pathfindsuccess = false; + + if (sourcewaypoint == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL sourcewaypoint in K_WaypointAStar.\n"); + } + else if (destinationwaypoint == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL destinationwaypoint in K_WaypointAStar.\n"); + } + else if (returnpath == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL returnpath in K_WaypointAStar.\n"); + } + else if (sourcewaypoint == destinationwaypoint) + { + // Source and destination waypoint are the same, we're already there + // Just for simplicity's sake, create a single node on the destination and reconstruct path + pathfindnode_t singlenode; + singlenode.camefrom = NULL; + singlenode.waypoint = destinationwaypoint; + singlenode.heapindex = SIZE_MAX; + singlenode.hscore = 0U; + singlenode.gscore = 0U; + + pathfindsuccess = K_ReconstructPath(returnpath, &singlenode); + } + else if (((huntbackwards == false) && (sourcewaypoint->numnextwaypoints == 0)) + || ((huntbackwards == true) && (sourcewaypoint->numprevwaypoints == 0))) + { + CONS_Debug(DBG_GAMELOGIC, + "K_WaypointAStar: 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_WaypointAStar: destinationwaypoint with ID %d has no previous waypoint\n", + K_GetWaypointID(destinationwaypoint)); + } + else if ((K_GetWaypointIsEnabled(destinationwaypoint) == false) || + (!useshortcuts &&(K_GetWaypointIsShortcut(destinationwaypoint) == true))) + { + // No path to the destination is possible + } + else + { + size_t opensetcapacity = K_GetOpensetBaseSize(); + size_t nodesarraycapacity = K_GetNodesArrayBaseSize(); + size_t closedsetcapacity = K_GetClosedsetBaseSize(); + bheapitem_t poppeditem = {}; + pathfindnode_t *currentnode = NULL; + pathfindnode_t *neighbournode = NULL; + bheap_t openset = {}; + pathfindnode_t **closedset = Z_Calloc(closedsetcapacity * sizeof(pathfindnode_t*), PU_STATIC, NULL); + pathfindnode_t *nodesarray = Z_Calloc(nodesarraycapacity * sizeof(pathfindnode_t), PU_STATIC, NULL); + pathfindnode_t *newnode = NULL; + size_t closedsetcount = 0U; + size_t nodesarraycount = 0U; + size_t numcheckwaypoints = 0U; + waypoint_t **checkwaypoints = NULL; + UINT32 *checkwaypointsdists = NULL; + UINT32 tentativegscore = UINT32_MAX; + size_t i = 0U; + size_t findopensetindex = 0U; + + if (closedset == NULL || nodesarray == NULL) + { + I_Error("K_WaypointAStar: Out of memory."); + } + + K_BHeapInit(&openset, opensetcapacity); + + newnode = &nodesarray[0]; + newnode->waypoint = sourcewaypoint; + newnode->hscore = K_DistanceBetweenWaypoints(sourcewaypoint, destinationwaypoint); + newnode->gscore = 0U; + newnode->camefrom = NULL; + nodesarraycount++; + + K_BHeapPush(&openset, &nodesarray[0], K_GetNodeFScore(&nodesarray[0]), K_NodeUpdateHeapIndex); + + if (opensetcapacity != openset.capacity) + { + opensetcapacity = openset.capacity; + K_UpdateOpensetBaseSize(opensetcapacity); + } + + while (openset.count > 0) + { + K_BHeapPop(&openset, &poppeditem); + currentnode = ((pathfindnode_t*)poppeditem.data); + + if (currentnode->waypoint == destinationwaypoint) + { + pathfindsuccess = K_ReconstructPath(returnpath, currentnode); + break; + } + + // The node is now placed into the closed set because it is evaluated + if (closedsetcount >= closedsetcapacity) + { + K_UpdateClosedsetBaseSize(closedsetcapacity * 2); + + closedsetcapacity = K_GetClosedsetBaseSize(); + closedset = Z_Realloc(closedset, closedsetcapacity * sizeof (pathfindnode_t*), PU_STATIC, NULL); + + if (closedset == NULL) + { + I_Error("K_WaypointAStar: Out of memory"); + } + } + closedset[closedsetcount] = currentnode; + closedsetcount++; + + if (huntbackwards) + { + numcheckwaypoints = currentnode->waypoint->numprevwaypoints; + checkwaypoints = currentnode->waypoint->prevwaypoints; + checkwaypointsdists = currentnode->waypoint->prevwaypointdistances; + } + else + { + numcheckwaypoints = currentnode->waypoint->numnextwaypoints; + checkwaypoints = currentnode->waypoint->nextwaypoints; + checkwaypointsdists = currentnode->waypoint->nextwaypointdistances; + } + + for (i = 0; i < numcheckwaypoints; i++) + { + tentativegscore = currentnode->gscore + checkwaypointsdists[i]; + + // Can this double search be sped up at all? I feel like allocating and deallocating memory for nodes + // constantly would be slower + // Find if the neighbournode is already created first, if it is then check if it's in the closedset + // If it's in the closedset, then skip as we don't need to check it again, if it isn't then see if the + // new route from currentnode is faster to it and update accordingly + neighbournode = K_NodesArrayContainsWaypoint(nodesarray, checkwaypoints[i], nodesarraycount); + + if (neighbournode != NULL) + { + // If the closedset contains the node, then it is already evaluated and doesn't need to be checked + if (K_ClosedsetContainsNode(closedset, neighbournode, closedsetcount) != false) + { + continue; + } + + if (tentativegscore < neighbournode->gscore) + { + neighbournode->camefrom = currentnode; + neighbournode->gscore = tentativegscore; + + findopensetindex = K_BHeapContains(&openset, neighbournode, neighbournode->heapindex); + if (findopensetindex != SIZE_MAX) + { + K_UpdateBHeapItemValue(&openset.array[findopensetindex], K_GetNodeFScore(neighbournode)); + } + else + { + // What??? How is this node NOT in the openset??? + // A node should always be in either the openset or closedset + CONS_Debug(DBG_GAMELOGIC, "Node unexpectedly not in openset in K_WaypointAStar.\n"); + K_BHeapPush(&openset, neighbournode, K_GetNodeFScore(neighbournode), K_NodeUpdateHeapIndex); + } + } + } + else + { + // Don't process this waypoint if it's not traversable + if ((K_GetWaypointIsEnabled(checkwaypoints[i]) == false) + || (!useshortcuts && K_GetWaypointIsShortcut(checkwaypoints[i]) == true)) + { + continue; + } + + // reallocate the nodesarray if needed + if (nodesarraycount >= nodesarraycapacity) + { + K_UpdateNodesArrayBaseSize(nodesarraycapacity * 2); + nodesarraycapacity = K_GetNodesArrayBaseSize(); + nodesarray = Z_Realloc(nodesarray, nodesarraycapacity * sizeof(pathfindnode_t), PU_STATIC, NULL); + + if (nodesarray == NULL) + { + I_Error("K_WaypointAStar: Out of memory"); + } + } + + // There is currently no node created for this waypoint, so make one + newnode = &nodesarray[nodesarraycount]; + newnode->camefrom = currentnode; + newnode->heapindex = SIZE_MAX; + newnode->gscore = tentativegscore; + newnode->hscore = K_DistanceBetweenWaypoints(checkwaypoints[i], destinationwaypoint); + newnode->waypoint = checkwaypoints[i]; + nodesarraycount++; + + // because there was no node for the waypoint, it's also not in the openset, add it + K_BHeapPush(&openset, newnode, K_GetNodeFScore(newnode), K_NodeUpdateHeapIndex); + } + } + } + + // Clean up the memory + K_BHeapFree(&openset); + Z_Free(closedset); + Z_Free(nodesarray); + } + + return pathfindsuccess; +} + +/*-------------------------------------------------- + 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 + { + pathfound = K_WaypointAStar(sourcewaypoint, destinationwaypoint, returnpath, useshortcuts, huntbackwards); + } + + 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; + boolean pathfindsuccess = + K_WaypointAStar(sourcewaypoint, destinationwaypoint, &pathtowaypoint, useshortcuts, huntbackwards); + + if (pathfindsuccess) + { + // A direct path to the destination has been found. + if (pathtowaypoint.numnodes > 1) + { + nextwaypoint = pathtowaypoint.array[1].waypoint; + } + else + { + // Shouldn't happen, as this is the source waypoint. + CONS_Debug(DBG_GAMELOGIC, "Only one waypoint pathfound in K_GetNextWaypointToDestination.\n"); + nextwaypoint = pathtowaypoint.array[0].waypoint; + } + + 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) @@ -165,7 +1074,7 @@ void K_DebugWaypointsVisualise(void) actually an mobj. Input Arguments:- - waypoint - The waypoint that is currently being compared against + 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:- @@ -173,43 +1082,41 @@ void K_DebugWaypointsVisualise(void) --------------------------------------------------*/ static boolean K_CheckWaypointForMobj(waypoint_t *const waypoint, void *const mobjpointer) { - mobj_t *mobj; boolean mobjsmatch = false; // Error Conditions if (waypoint == NULL) { CONS_Debug(DBG_GAMELOGIC, "NULL waypoint in K_CheckWaypointForMobj.\n"); - return false; } - if (waypoint->mobj == NULL) + else if (waypoint->mobj == NULL) { CONS_Debug(DBG_GAMELOGIC, "Waypoint has NULL mobj in K_CheckWaypointForMobj.\n"); - return false; } - if (mobjpointer == NULL) + else if (mobjpointer == NULL) { CONS_Debug(DBG_GAMELOGIC, "NULL mobjpointer in K_CheckWaypointForMobj.\n"); - return false; } - - mobj = (mobj_t *)mobjpointer; - - if (P_MobjWasRemoved(mobj)) + else { - CONS_Debug(DBG_GAMELOGIC, "Mobj Was Removed in K_CheckWaypointForMobj"); - return false; - } - if (mobj->type != MT_WAYPOINT) - { - CONS_Debug(DBG_GAMELOGIC, "Non MT_WAYPOINT mobj in K_CheckWaypointForMobj. Type=%d.\n", mobj->type); - return false; - } + mobj_t *mobj = (mobj_t *)mobjpointer; - // All that error checking for 3 lines :^) - if (waypoint->mobj == mobj) - { - mobjsmatch = true; + 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; @@ -226,11 +1133,11 @@ static boolean K_CheckWaypointForMobj(waypoint_t *const waypoint, void *const mo of the graph with no pathfinding Input Arguments:- - waypoint - The waypoint that is currently being checked, goes through nextwaypoints after this one + 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 + 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 @@ -247,66 +1154,73 @@ static waypoint_t *K_TraverseWaypoints( if (condition == NULL) { CONS_Debug(DBG_GAMELOGIC, "NULL condition in K_TraverseWaypoints.\n"); - return NULL; } - if (conditionalfunc == NULL) + else if (conditionalfunc == NULL) { CONS_Debug(DBG_GAMELOGIC, "NULL conditionalfunc in K_TraverseWaypoints.\n"); } - if (visitedarray == NULL) + else if (visitedarray == NULL) { CONS_Debug(DBG_GAMELOGIC, "NULL visitedarray in K_TraverseWaypoints.\n"); - return NULL; } + else + { searchwaypointstart: - if (waypoint == NULL) - { - CONS_Debug(DBG_GAMELOGIC, "NULL waypoint in K_TraverseWaypoints.\n"); - return NULL; - } - - // If we've already visited this waypoint, we've already checked the next waypoints, and continuing is useless - if (visitedarray[waypoint->id] != true) - { - // Mark this waypoint as being visited - visitedarray[waypoint->id] = true; - - if (conditionalfunc(waypoint, condition) == true) + if (waypoint == NULL) { - foundwaypoint = waypoint; + CONS_Debug(DBG_GAMELOGIC, "NULL waypoint in K_TraverseWaypoints.\n"); } 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) + // If we've already visited this waypoint, we've already checked the next waypoints, no point continuing + if (visitedarray[waypoint->id] != true) { - 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); + // Mark this waypoint as being visited + visitedarray[waypoint->id] = true; - if (foundwaypoint != NULL) + 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++) { - break; + 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 + } + } } } @@ -324,7 +1238,7 @@ searchwaypointstart: Input Arguments:- conditionalfunc - The function that will be used to check a waypoint against condition - condition - the condition being checked by conditionalfunc + 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 @@ -340,21 +1254,22 @@ static waypoint_t *K_SearchWaypointGraph( if (condition == NULL) { CONS_Debug(DBG_GAMELOGIC, "NULL condition in K_SearchWaypointGraph.\n"); - return NULL; } - if (conditionalfunc == NULL) + else if (conditionalfunc == NULL) { CONS_Debug(DBG_GAMELOGIC, "NULL conditionalfunc in K_SearchWaypointGraph.\n"); } - if (firstwaypoint == NULL) + else if (firstwaypoint == NULL) { CONS_Debug(DBG_GAMELOGIC, "K_SearchWaypointsForMobj called when no first waypoint.\n"); - return NULL; + } + else + { + visitedarray = Z_Calloc(numwaypoints * sizeof(boolean), PU_STATIC, NULL); + foundwaypoint = K_TraverseWaypoints(firstwaypoint, conditionalfunc, condition, visitedarray); + Z_Free(visitedarray); } - visitedarray = Z_Calloc(numwaypoints * sizeof(boolean), PU_STATIC, NULL); - foundwaypoint = K_TraverseWaypoints(firstwaypoint, conditionalfunc, condition, visitedarray); - Z_Free(visitedarray); return foundwaypoint; } @@ -365,18 +1280,22 @@ static waypoint_t *K_SearchWaypointGraph( --------------------------------------------------*/ 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"); - return NULL; } - if (mobj->type != MT_WAYPOINT) + else if (mobj->type != MT_WAYPOINT) { CONS_Debug(DBG_GAMELOGIC, "Non MT_WAYPOINT mobj in K_SearchWaypointGraphForMobj. Type=%d.\n", mobj->type); - return NULL; + } + else + { + foundwaypoint = K_SearchWaypointGraph(K_CheckWaypointForMobj, (void *)mobj); } - return K_SearchWaypointGraph(K_CheckWaypointForMobj, (void *)mobj); + return foundwaypoint; } /*-------------------------------------------------- @@ -388,7 +1307,7 @@ waypoint_t *K_SearchWaypointGraphForMobj(mobj_t *const mobj) Input Arguments:- conditionalfunc - The function that will be used to check a waypoint against condition - condition - the condition being checked by conditionalfunc + 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 @@ -404,27 +1323,27 @@ static waypoint_t *K_SearchWaypointHeap( if (condition == NULL) { CONS_Debug(DBG_GAMELOGIC, "NULL condition in K_SearchWaypointHeap.\n"); - return NULL; } - if (conditionalfunc == NULL) + else if (conditionalfunc == NULL) { CONS_Debug(DBG_GAMELOGIC, "NULL conditionalfunc in K_SearchWaypointHeap.\n"); } - if (waypointheap == NULL) + else if (waypointheap == NULL) { CONS_Debug(DBG_GAMELOGIC, "K_SearchWaypointHeap called when no waypointheap.\n"); - return 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++) + else { - if (conditionalfunc(waypointheap[i], condition) == true) + // 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++) { - foundwaypoint = waypointheap[i]; - break; + if (conditionalfunc(waypointheap[i], condition) == true) + { + foundwaypoint = waypointheap[i]; + break; + } } } @@ -438,36 +1357,22 @@ static waypoint_t *K_SearchWaypointHeap( --------------------------------------------------*/ 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"); - return NULL; } - if (mobj->type != MT_WAYPOINT) + else if (mobj->type != MT_WAYPOINT) { CONS_Debug(DBG_GAMELOGIC, "Non MT_WAYPOINT mobj in K_SearchWaypointHeapForMobj. Type=%d.\n", mobj->type); - return NULL; + } + else + { + foundwaypoint = K_SearchWaypointHeap(K_CheckWaypointForMobj, (void *)mobj); } - return K_SearchWaypointHeap(K_CheckWaypointForMobj, (void *)mobj); -} - -/*-------------------------------------------------- - static fixed_t 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 fixed_t K_DistanceBetweenWaypoints(waypoint_t *const waypoint1, waypoint_t *const waypoint2) -{ - fixed_t xydist = P_AproxDistance(waypoint1->mobj->x - waypoint2->mobj->x, waypoint1->mobj->y - waypoint2->mobj->y); - return P_AproxDistance(xydist, waypoint1->mobj->z - waypoint2->mobj->z); + return foundwaypoint; } /*-------------------------------------------------- @@ -477,7 +1382,7 @@ static fixed_t K_DistanceBetweenWaypoints(waypoint_t *const waypoint1, waypoint_ 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 + 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:- @@ -489,32 +1394,32 @@ static void K_AddPrevToWaypoint(waypoint_t *const waypoint, waypoint_t *const pr if (waypoint == NULL) { CONS_Debug(DBG_SETUP, "NULL waypoint in K_AddPrevToWaypoint.\n"); - return; } - if (prevwaypoint == NULL) + else if (prevwaypoint == NULL) { CONS_Debug(DBG_SETUP, "NULL prevwaypoint in K_AddPrevToWaypoint.\n"); - return; } - - waypoint->numprevwaypoints++; - waypoint->prevwaypoints = - Z_Realloc(waypoint->prevwaypoints, waypoint->numprevwaypoints * sizeof(waypoint_t *), PU_LEVEL, NULL); - - if (!waypoint->prevwaypoints) + else { - I_Error("K_AddPrevToWaypoint: Failed to reallocate memory for previous waypoints."); + 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; } - - 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; } /*-------------------------------------------------- @@ -530,32 +1435,32 @@ static void K_AddPrevToWaypoint(waypoint_t *const waypoint, waypoint_t *const pr --------------------------------------------------*/ static waypoint_t *K_NewWaypoint(mobj_t *const mobj) { - waypoint_t *waypoint; + waypoint_t *waypoint = NULL; // Error conditions if (mobj == NULL || P_MobjWasRemoved(mobj)) { CONS_Debug(DBG_SETUP, "NULL mobj in K_NewWaypoint.\n"); - return NULL; } - if (waypointheap == NULL) + else if (waypointheap == NULL) { CONS_Debug(DBG_SETUP, "NULL waypointheap in K_NewWaypoint.\n"); - return NULL; } - - // Each made waypoint is placed directly into the waypoint heap to be able to search it during creation - waypointheap[numwaypoints] = Z_Calloc(sizeof(waypoint_t), PU_LEVEL, NULL); - waypoint = waypointheap[numwaypoints]; - // numwaypoints is incremented later when waypoint->id is set - - if (!waypoint) + else { - I_Error("K_NewWaypoint: Failed to allocate memory for waypoint."); - } + // Each made waypoint is placed directly into the waypoint heap to be able to search it during creation + waypointheap[numwaypoints] = Z_Calloc(sizeof(waypoint_t), PU_LEVEL, NULL); + waypoint = waypointheap[numwaypoints]; + // numwaypoints is incremented later when waypoint->id is set - P_SetTarget(&waypoint->mobj, mobj); - waypoint->id = numwaypoints++; + if (waypoint == NULL) + { + I_Error("K_NewWaypoint: Failed to allocate memory for waypoint."); + } + + P_SetTarget(&waypoint->mobj, mobj); + waypoint->id = numwaypoints++; + } return waypoint; } @@ -581,39 +1486,41 @@ static waypoint_t *K_MakeWaypoint(mobj_t *const mobj) if (mobj == NULL || P_MobjWasRemoved(mobj)) { CONS_Debug(DBG_SETUP, "NULL mobj in K_MakeWaypoint.\n"); - return NULL; } - if (waypointcap == NULL) + else if (waypointcap == NULL) { CONS_Debug(DBG_SETUP, "K_MakeWaypoint called with NULL waypointcap.\n"); - return false; } - - madewaypoint = K_NewWaypoint(mobj); - - // 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) + else { - // threshold = next waypoint id, movecount = my id - if (mobj->threshold == otherwaypointmobj->movecount) - { - madewaypoint->numnextwaypoints++; - } - } + madewaypoint = K_NewWaypoint(mobj); - // 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) + // 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) { - I_Error("K_MakeWaypoint: Out of Memory allocating next waypoints."); + // threshold = next waypoint id, movecount = my id + if (mobj->threshold == otherwaypointmobj->movecount) + { + madewaypoint->numnextwaypoints++; + } } - madewaypoint->nextwaypointdistances = Z_Calloc(madewaypoint->numnextwaypoints * sizeof(fixed_t), PU_LEVEL, NULL); - if (madewaypoint->nextwaypointdistances == NULL) + + // No next waypoints + if (madewaypoint->numnextwaypoints != 0) { - I_Error("K_MakeWaypoint: Out of Memory allocating next waypoint distances."); + // 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."); + } } } @@ -640,63 +1547,76 @@ static waypoint_t *K_SetupWaypoint(mobj_t *const mobj) if (mobj == NULL || P_MobjWasRemoved(mobj)) { CONS_Debug(DBG_SETUP, "NULL mobj in K_SetupWaypoint.\n"); - return NULL; } - if (mobj->type != MT_WAYPOINT) + else if (mobj->type != MT_WAYPOINT) { CONS_Debug(DBG_SETUP, "Non MT_WAYPOINT mobj in K_SetupWaypoint. Type=%d.\n", mobj->type); - return NULL; } - if (waypointcap == NULL) + else if (waypointcap == NULL) { CONS_Debug(DBG_SETUP, "K_SetupWaypoint called with NULL waypointcap.\n"); - return false; } - - // If we have waypoints already created, search through them first to see if this mobj is already added. - if (firstwaypoint != NULL) + else { - 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); - - // Temporarily set the first waypoint to be the first waypoint we setup, this is so that we can search - // through them as they're made and added to the linked list - if (firstwaypoint == NULL) + // If we have waypoints already created, search through them first to see if this mobj is already added. + if (firstwaypoint != NULL) { - firstwaypoint = thiswaypoint; + thiswaypoint = K_SearchWaypointHeapForMobj(mobj); } - if (thiswaypoint->numnextwaypoints > 0) + // The waypoint hasn't already been made, so make it + if (thiswaypoint == NULL) { - 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) + mobj_t *otherwaypointmobj = NULL; + UINT32 nextwaypointindex = 0; + + thiswaypoint = K_MakeWaypoint(mobj); + + if (thiswaypoint != NULL) { - // threshold = next waypoint id, movecount = my id - if (mobj->threshold == otherwaypointmobj->movecount) + // Temporarily set the first waypoint to be the first waypoint we setup, this is so that we can search + // through them as they're made and added to the linked list + if (firstwaypoint == NULL) { - 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++; + firstwaypoint = thiswaypoint; } - if (nextwaypointindex >= thiswaypoint->numnextwaypoints) + + if (thiswaypoint->numnextwaypoints > 0) { - break; + 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_GetWaypointNextID(thiswaypoint)); + } + } + else + { + CONS_Debug(DBG_SETUP, "K_SetupWaypoint failed to make new waypoint with ID %d.\n", mobj->movecount); } } } @@ -721,48 +1641,48 @@ static boolean K_AllocateWaypointHeap(void) if (waypointheap != NULL) { CONS_Debug(DBG_SETUP, "K_AllocateWaypointHeap called when waypointheap is already allocated.\n"); - return false; } - if (waypointcap == NULL) + else if (waypointcap == NULL) { CONS_Debug(DBG_SETUP, "K_AllocateWaypointHeap called with NULL waypointcap.\n"); - return false; - } - - // 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 if true - 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 and not end up constantly trying to use the waypoints in this case - I_Error("K_AllocateWaypointHeap: Out of memory."); - } - allocationsuccessful = true; } else { - CONS_Debug(DBG_SETUP, "No waypoint mobjs in waypointcap.\n"); + // 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; @@ -851,6 +1771,7 @@ boolean K_SetupWaypointList(void) void K_ClearWaypoints(void) { waypointheap = NULL; + firstwaypoint = NULL; numwaypoints = 0; numwaypointmobjs = 0; } diff --git a/src/k_waypoint.h b/src/k_waypoint.h index 6da2688e2..c54cb439e 100644 --- a/src/k_waypoint.h +++ b/src/k_waypoint.h @@ -10,16 +10,149 @@ typedef struct waypoint_s size_t id; struct waypoint_s **nextwaypoints; struct waypoint_s **prevwaypoints; - fixed_t *nextwaypointdistances; - fixed_t *prevwaypointdistances; + UINT32 *nextwaypointdistances; + UINT32 *prevwaypointdistances; size_t numnextwaypoints; size_t numprevwaypoints; } waypoint_t; +typedef struct pathfindnode_s { + size_t heapindex; // The index in the openset binary heap. Only valid while the node is in the openset. + waypoint_t *waypoint; + struct pathfindnode_s *camefrom; // should eventually be the most efficient predecessor node + UINT32 gscore; // The accumulated distance from the start to this node + UINT32 hscore; // The heuristic from this node to the goal, saved to avoid expensive recalculation +} pathfindnode_t; + +typedef struct path_s { + size_t numnodes; + struct pathfindnode_s *array; + UINT32 totaldist; +} path_t; + // AVAILABLE FOR LUA +/*-------------------------------------------------- + boolean K_GetWaypointIsShortcut(waypoint_t *waypoint) + + Returns whether the waypoint is part of a shortcut. + + Input Arguments:- + waypoint - The waypoint to return shortcut status of. + + Return:- + true if the waypoint is a shortcut, false if it isn't. +--------------------------------------------------*/ + +boolean K_GetWaypointIsShortcut(waypoint_t *waypoint); + + +/*-------------------------------------------------- + boolean K_GetWaypointIsEnabled(waypoint_t *waypoint) + + Returns whether the waypoint is enabled. + + Input Arguments:- + waypoint - The waypoint to return enabled status of. + + Return:- + true if the waypoint is enabled, false if it isn't. +--------------------------------------------------*/ + +boolean K_GetWaypointIsEnabled(waypoint_t *waypoint); + + +/*-------------------------------------------------- + INT32 K_GetWaypointNextID(waypoint_t *waypoint) + + Returns the waypoint's next waypoint ID. + + Input Arguments:- + waypoint - The waypoint to return the next waypoint ID of. + + Return:- + The next waypoint ID, -1 if there is no waypoint or mobj. +--------------------------------------------------*/ + +INT32 K_GetWaypointNextID(waypoint_t *waypoint); + + +/*-------------------------------------------------- + INT32 K_GetWaypointID(waypoint_t *waypoint) + + Returns the waypoint's ID. + + Input Arguments:- + waypoint - The waypoint to return the ID of. + + Return:- + The waypoint ID, -1 if there is no waypoint or mobj. +--------------------------------------------------*/ +INT32 K_GetWaypointID(waypoint_t *waypoint); + + +/*-------------------------------------------------- + boolean K_PathfindToWaypoint( + waypoint_t *const sourcewaypoint, + waypoint_t *const destinationwaypoint, + path_t *const returnpath, + const boolean useshortcuts, + const boolean huntbackwards) + + Use pathfinding to try and find the best route to the destination. Data is allocated into the returnpath, + and should be freed when done with. A call to this with a path already in the returnpath will free the data + already in there if one is found. + + Input Arguments:- + sourcewaypoint - The waypoint to start searching from + destinationwaypoint - The waypoint to try and get to. + returnpath - The path_t that will contain the final found path + useshortcuts - Whether to use waypoints that are marked as being shortcuts in the search + huntbackwards - Goes through the waypoints backwards if true + + Return:- + True if a path was found to the waypoint, false if there wasn't. +--------------------------------------------------*/ + +boolean K_PathfindToWaypoint( + waypoint_t *const sourcewaypoint, + waypoint_t *const destinationwaypoint, + path_t *const returnpath, + const boolean useshortcuts, + const boolean huntbackwards); + + +/*-------------------------------------------------- + waypoint_t *K_GetNextWaypointToDestination( + waypoint_t *const sourcewaypoint, + waypoint_t *const destinationwaypoint, + const boolean useshortcuts, + const boolean huntbackwards) + + Uses pathfinding to find the next waypoint to go to in order to get to the destination waypoint, from the source + waypoint. If the source waypoint only has one next waypoint it will always pick that one and not do any + pathfinding. + + Input Arguments:- + sourcewaypoint - The waypoint to start searching from + destinationwaypoint - The waypoint to try and get to. + useshortcuts - Whether to use waypoints that are marked as being shortcuts in the search + huntbackwards - Goes through the waypoints backwards if true + + Return:- + The next waypoint on the way to the destination waypoint. Returns the source waypoint if the source and + destination are the same. +--------------------------------------------------*/ + +waypoint_t *K_GetNextWaypointToDestination( + waypoint_t *const sourcewaypoint, + waypoint_t *const destinationwaypoint, + const boolean useshortcuts, + const boolean huntbackwards); + + /*-------------------------------------------------- waypoint_t *K_SearchWaypointGraphForMobj(mobj_t *const mobj) From 379ef52c258f19f4c1486d38ce36ca967e65533d Mon Sep 17 00:00:00 2001 From: Sryder Date: Sun, 9 Jun 2019 20:37:47 +0100 Subject: [PATCH 016/105] Correct no next waypoint warning message. --- src/k_waypoint.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_waypoint.c b/src/k_waypoint.c index b039ca7f0..ae7823cb7 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -1611,7 +1611,7 @@ static waypoint_t *K_SetupWaypoint(mobj_t *const mobj) else { CONS_Alert( - CONS_WARNING, "Waypoint with ID %d has no next waypoint.\n", K_GetWaypointNextID(thiswaypoint)); + CONS_WARNING, "Waypoint with ID %d has no next waypoint.\n", K_GetWaypointID(thiswaypoint)); } } else From 7efe868c9999d61e471d1542f4889ca4d7b2bb2d Mon Sep 17 00:00:00 2001 From: Sryder Date: Sun, 9 Jun 2019 21:49:48 +0100 Subject: [PATCH 017/105] Loop through all the waypoint mobjs when setting up waypoints always This means all the mobjs have waypoints setup and debugvisualise can work better. --- src/k_waypoint.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/k_waypoint.c b/src/k_waypoint.c index ae7823cb7..13a203c07 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -1739,8 +1739,13 @@ boolean K_SetupWaypointList(void) { if (K_AllocateWaypointHeap() == true) { - // The waypoint in the waypointcap is going to be considered our first waypoint - K_SetupWaypoint(waypointcap); + 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) { From 4c26589d5c5efa00e480cd55afd7a1a22a484f53 Mon Sep 17 00:00:00 2001 From: Sryder Date: Mon, 10 Jun 2019 00:02:48 +0100 Subject: [PATCH 018/105] Add support for Finishline, shortcut, and disabled waypoints. Remove warning that should be impossible to trip, and doesn't describe problem anymore. --- src/doomdata.h | 3 ++ src/k_waypoint.c | 76 ++++++++++++++++++++++++++++++++++++++++-------- src/k_waypoint.h | 30 +++++++++++++++++++ src/p_mobj.c | 30 ++++++++++++++++++- 4 files changed, 126 insertions(+), 13 deletions(-) diff --git a/src/doomdata.h b/src/doomdata.h index 6319238b7..aa4ea1a54 100644 --- a/src/doomdata.h +++ b/src/doomdata.h @@ -46,6 +46,9 @@ enum ML_BLOCKMAP, // LUT, motion clipping, walls/grid element }; +// Extra flag for objects +#define MTF_EXTRA 1 + // Reverse gravity flag for objects. #define MTF_OBJECTFLIP 2 diff --git a/src/k_waypoint.c b/src/k_waypoint.c index 13a203c07..d179d68c2 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -20,12 +20,49 @@ static const size_t NODESARRAY_BASE_SIZE = 256U; static waypoint_t **waypointheap = NULL; static waypoint_t *firstwaypoint = NULL; +static waypoint_t *finishline = NULL; 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_GetWaypointIsShortcut.\n"); + } + else if (waypoint->mobj == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL waypoint mobj in K_GetWaypointIsShortcut.\n"); + } + else + { + waypointisfinishline = (waypoint->mobj->extravalue2 == 1); + } + + return waypointisfinishline; +} + /*-------------------------------------------------- boolean K_GetWaypointIsShortcut(waypoint_t *waypoint) @@ -45,7 +82,7 @@ boolean K_GetWaypointIsShortcut(waypoint_t *waypoint) } else { - // TODO + waypointisshortcut = (waypoint->mobj->lastlook == 1); } return waypointisshortcut; @@ -58,7 +95,7 @@ boolean K_GetWaypointIsShortcut(waypoint_t *waypoint) --------------------------------------------------*/ boolean K_GetWaypointIsEnabled(waypoint_t *waypoint) { - boolean waypointisshortcut = true; + boolean waypointisenabled = true; if (waypoint == NULL) { @@ -70,10 +107,10 @@ boolean K_GetWaypointIsEnabled(waypoint_t *waypoint) } else { - // TODO + waypointisenabled = (waypoint->mobj->extravalue1 == 1); } - return waypointisshortcut; + return waypointisenabled; } /*-------------------------------------------------- @@ -1581,6 +1618,19 @@ static waypoint_t *K_SetupWaypoint(mobj_t *const mobj) 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; @@ -1747,18 +1797,19 @@ boolean K_SetupWaypointList(void) K_SetupWaypoint(waypointmobj); } - if (!firstwaypoint) + if (firstwaypoint == NULL) { CONS_Alert(CONS_ERROR, "No waypoints in map.\n"); } else { CONS_Debug(DBG_SETUP, "Successfully setup %zu waypoints.\n", numwaypoints); - if (numwaypoints < numwaypointmobjs) + if (finishline == NULL) { - CONS_Alert(CONS_WARNING, - "Not all waypoints in the map are connected! %zu waypoints setup but %zu waypoint mobjs.\n", - numwaypoints, numwaypointmobjs); + CONS_Alert( + CONS_WARNING, "No finish line waypoint in the map! Using first setup waypoint with ID %d.\n", + K_GetWaypointID(firstwaypoint)); + finishline = firstwaypoint; } setupsuccessful = true; } @@ -1775,8 +1826,9 @@ boolean K_SetupWaypointList(void) --------------------------------------------------*/ void K_ClearWaypoints(void) { - waypointheap = NULL; - firstwaypoint = NULL; - numwaypoints = 0; + waypointheap = NULL; + firstwaypoint = NULL; + finishline = NULL; + numwaypoints = 0; numwaypointmobjs = 0; } diff --git a/src/k_waypoint.h b/src/k_waypoint.h index c54cb439e..f93df802f 100644 --- a/src/k_waypoint.h +++ b/src/k_waypoint.h @@ -34,6 +34,36 @@ typedef struct path_s { // AVAILABLE FOR LUA +/*-------------------------------------------------- + waypoint_t *K_GetFinishLineWaypoint(void); + + Returns the waypoint actually being used as the finish line. + + Input Arguments:- + None + + Return:- + The waypoint that is being used as the finishline. +--------------------------------------------------*/ + +waypoint_t *K_GetFinishLineWaypoint(void); + + +/*-------------------------------------------------- + boolean K_GetWaypointIsFinishline(waypoint_t *waypoint) + + Returns whether the waypoint is marked as the finishline. This may not actually be the finishline. + + Input Arguments:- + waypoint - The waypoint to return finishline status of. + + Return:- + true if the waypoint is marked as being the finishline, false if it isn't. +--------------------------------------------------*/ + +boolean K_GetWaypointIsFinishline(waypoint_t *waypoint); + + /*-------------------------------------------------- boolean K_GetWaypointIsShortcut(waypoint_t *waypoint) diff --git a/src/p_mobj.c b/src/p_mobj.c index d4fd3c693..ffac8e678 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -11805,16 +11805,44 @@ ML_NOCLIMB : Direction not controllable // Use threshold to store the next waypoint ID // movecount is being used for the current waypoint ID // reactiontime lets us know if we can respawn at it + // lastlook is used for indicating the waypoint is a shortcut + // extravalue1 is used for indicating the waypoint is disabled + // extravalue2 is used for indicating the waypoint is the finishline mobj->threshold = ((mthing->options >> ZSHIFT)); mobj->movecount = mthing->angle; + if (mthing->options & MTF_EXTRA) + { + mobj->extravalue1 = 0; // The waypoint is disabled if extra is on + } + else + { + mobj->extravalue1 = 1; + } + if (mthing->options & MTF_OBJECTSPECIAL) + { + mobj->lastlook = 1; // the waypoint is a shortcut if objectspecial is on + } + else + { + mobj->lastlook = 0; + } if (mthing->options & MTF_AMBUSH) { - mobj->reactiontime = 0; // Can't respawn at if Ambush is off + mobj->reactiontime = 0; // Can't respawn at if Ambush is on } else { mobj->reactiontime = 1; } + if (mthing->extrainfo == 1) + { + mobj->extravalue2 = 1; // extrainfo of 1 means the waypoint is at the finish line + } + else + { + mobj->extravalue2 = 0; + } + // Sryder 2018-12-7: Grabbed this from the old MT_BOSS3WAYPOINT section so they'll be in the waypointcap instead P_SetTarget(&mobj->tracer, waypointcap); From e937e35a789f1d0bf6c2ef8436331737d2ff6780 Mon Sep 17 00:00:00 2001 From: Sryder Date: Wed, 12 Jun 2019 23:43:55 +0100 Subject: [PATCH 019/105] Split pathfinding itself into its own module. --- src/Makefile | 1 + src/k_pathfind.c | 521 +++++++++++++++++++++++++++++++++++++++++++ src/k_pathfind.h | 70 ++++++ src/k_waypoint.c | 561 ++++++++++++----------------------------------- src/k_waypoint.h | 17 +- 5 files changed, 739 insertions(+), 431 deletions(-) create mode 100644 src/k_pathfind.c create mode 100644 src/k_pathfind.h diff --git a/src/Makefile b/src/Makefile index b04a58110..cf9e38bf3 100644 --- a/src/Makefile +++ b/src/Makefile @@ -490,6 +490,7 @@ OBJS:=$(i_main_o) \ $(OBJDIR)/st_stuff.o \ $(OBJDIR)/k_kart.o \ $(OBJDIR)/k_waypoint.o\ + $(OBJDIR)/k_pathfind.o\ $(OBJDIR)/k_bheap.o \ $(OBJDIR)/m_aatree.o \ $(OBJDIR)/m_anigif.o \ diff --git a/src/k_pathfind.c b/src/k_pathfind.c new file mode 100644 index 000000000..d80f20f32 --- /dev/null +++ b/src/k_pathfind.c @@ -0,0 +1,521 @@ +#include "k_pathfind.h" + +#include "doomdef.h" +#include "z_zone.h" +#include "k_bheap.h" + +static const size_t DEFAULT_NODEARRAY_CAPACITY = 8U; +static const size_t DEFAULT_OPENSET_CAPACITY = 8U; +static const size_t DEFAULT_CLOSEDSET_CAPACITY = 8U; + + +/*-------------------------------------------------- + static UINT32 K_NodeGetFScore(const pathfindnode_t *const node) + + Gets the FScore of a node. The FScore is the GScore plus the HScore. + + Input Arguments:- + node - The node to get the FScore of + + Return:- + The FScore of the node. +--------------------------------------------------*/ +static UINT32 K_NodeGetFScore(const pathfindnode_t *const node) +{ + UINT32 fscore = UINT32_MAX; + if (node == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL node in K_PathfindNodeGetFScore."); + } + else + { + fscore = node->gscore + node->hscore; + } + + return fscore; +} + +/*-------------------------------------------------- + static void K_NodeUpdateHeapIndex(void *const node, const size_t newheapindex) + + A callback for the Openset Binary Heap to be able to update the heapindex of the pathfindnodes when they are + moved. + + Input Arguments:- + node - The node that has been updated, should be a pointer to a pathfindnode_t + newheapindex - The new heapindex of the node. + + Return:- + None +--------------------------------------------------*/ +static void K_NodeUpdateHeapIndex(void *const node, const size_t newheapindex) +{ + if (node == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL node in K_PathfindNodeUpdateHeapIndex.\n"); + } + else + { + pathfindnode_t *truenode = (pathfindnode_t*)node; + truenode->heapindex = newheapindex; + } +} + +/*-------------------------------------------------- + static pathfindnode_t *K_NodesArrayContainsNodeData( + pathfindnode_t *nodesarray, + void* nodedata, + size_t nodesarraycount) + + Checks whether the Nodes Array contains a node with a waypoint. Searches from the end to the start for speed + reasons. + + Input Arguments:- + nodesarray - The nodes array within the A* algorithm + waypoint - The waypoint to check is within the nodes array + nodesarraycount - The current size of the nodes array + + Return:- + The pathfind node that has the waypoint if there is one. NULL if the waypoint is not in the nodes array. +--------------------------------------------------*/ +static pathfindnode_t *K_NodesArrayContainsNodeData( + pathfindnode_t *nodesarray, + void* nodedata, + size_t nodesarraycount) +{ + pathfindnode_t *foundnode = NULL; + + if (nodesarray == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL nodesarray in K_NodesArrayContainsWaypoint.\n"); + } + else if (nodedata == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL nodedata in K_NodesArrayContainsWaypoint.\n"); + } + else + { + size_t i; + // It is more likely that we'll find the node we are looking for from the end of the array + // Yes, the for loop looks weird, remember that size_t is unsigned and we want to check 0, after it hits 0 it + // will loop back up to SIZE_MAX + for (i = nodesarraycount - 1U; i < nodesarraycount; i--) + { + if (nodesarray[i].nodedata == nodedata) + { + foundnode = &nodesarray[i]; + break; + } + } + } + return foundnode; +} + +/*-------------------------------------------------- + static boolean K_ClosedsetContainsNode(pathfindnode_t **closedset, pathfindnode_t *node, size_t closedsetcount) + + Checks whether the Closedset contains a node. Searches from the end to the start for speed reasons. + + Input Arguments:- + closedset - The closed set within the A* algorithm + node - The node to check is within the closed set + closedsetcount - The current size of the closedset + + Return:- + True if the node is in the closed set, false if it isn't +--------------------------------------------------*/ +static boolean K_ClosedsetContainsNode(pathfindnode_t **closedset, pathfindnode_t *node, size_t closedsetcount) +{ + boolean nodeisinclosedset = false; + + if (closedset == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL closedset in K_PathfindClosedsetContainsNode.\n"); + } + else if (node == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL node in K_PathfindClosedsetContainsNode.\n"); + } + else + { + size_t i; + // It is more likely that we'll find the node we are looking for from the end of the array + // Yes, the for loop looks weird, remember that size_t is unsigned and we want to check 0, after it hits 0 it + // will loop back up to SIZE_MAX + for (i = closedsetcount - 1U; i < closedsetcount; i--) + { + if (closedset[i] == node) + { + nodeisinclosedset = true; + break; + } + } + } + return nodeisinclosedset; +} + +/*-------------------------------------------------- + static boolean K_PathfindSetupValid(const pathfindsetup_t *const pathfindsetup) + + Checks that the setup given for pathfinding is valid and can be used. + + Input Arguments:- + pathfindsetup - The setup for the pathfinding given + + Return:- + True if pathfinding setup is valid, false if it isn't. +--------------------------------------------------*/ +static boolean K_PathfindSetupValid(const pathfindsetup_t *const pathfindsetup) +{ + boolean pathfindsetupvalid = false; + size_t sourcenodenumconnectednodes = 0U; + size_t endnodenumconnectednodes = 0U; + + if (pathfindsetup == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL pathfindsetup in K_PathfindSetupValid.\n"); + } + else if (pathfindsetup->startnodedata == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "Pathfindsetup has NULL startnodedata.\n"); + } + else if (pathfindsetup->endnodedata == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "Pathfindsetup has NULL endnodedata.\n"); + } + else if (pathfindsetup->getconnectednodes == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "Pathfindsetup has NULL getconnectednodes function.\n"); + } + else if (pathfindsetup->getconnectioncosts == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "Pathfindsetup has NULL getconnectioncosts function.\n"); + } + else if (pathfindsetup->getheuristic == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "Pathfindsetup has NULL getheuristic function.\n"); + } + else if (pathfindsetup->gettraversable == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "Pathfindsetup has NULL gettraversable function.\n"); + } + else if (pathfindsetup->getconnectednodes(pathfindsetup->startnodedata, &sourcenodenumconnectednodes) == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "K_PathfindSetupValid: Source node returned NULL connecting nodes.\n"); + } + else if (sourcenodenumconnectednodes == 0U) + { + CONS_Debug(DBG_GAMELOGIC, "K_PathfindSetupValid: Source node has 0 connecting nodes.\n"); + } + else if (pathfindsetup->getconnectednodes(pathfindsetup->endnodedata, &endnodenumconnectednodes) == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "K_PathfindSetupValid: End node returned NULL connecting nodes.\n"); + } + else if (endnodenumconnectednodes == 0U) + { + CONS_Debug(DBG_GAMELOGIC, "K_PathfindSetupValid: End node has 0 connecting nodes.\n"); + } + else + { + pathfindsetupvalid = true; + } + + return pathfindsetupvalid; +} + +static boolean K_ReconstructPath(path_t *const path, pathfindnode_t *const destinationnode) +{ + boolean reconstructsuccess = false; + + if (path == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL path in K_ReconstructPath.\n"); + } + else if (destinationnode == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL destinationnode in K_ReconstructPath.\n"); + } + else + { + size_t numnodes = 0U; + pathfindnode_t *thisnode = destinationnode; + + // If the path we're placing our new path into already has data, free it + if (path->array != NULL) + { + Z_Free(path->array); + path->numnodes = 0U; + path->totaldist = 0U; + } + + // Do a fast check of how many nodes there are so we know how much space to allocate + for (thisnode = destinationnode; thisnode; thisnode = thisnode->camefrom) + { + numnodes++; + } + + if (numnodes > 0U) + { + // Allocate memory for the path + path->numnodes = numnodes; + path->array = Z_Calloc(numnodes * sizeof(pathfindnode_t), PU_STATIC, NULL); + path->totaldist = destinationnode->gscore; + if (path->array == NULL) + { + I_Error("K_ReconstructPath: Out of memory."); + } + + // Put the nodes into the return array + for (thisnode = destinationnode; thisnode; thisnode = thisnode->camefrom) + { + path->array[numnodes - 1U] = *thisnode; + // Correct the camefrom element to point to the previous element in the array instead + if ((path->array[numnodes - 1U].camefrom != NULL) && (numnodes > 1U)) + { + path->array[numnodes - 1U].camefrom = &path->array[numnodes - 2U]; + } + else + { + path->array[numnodes - 1U].camefrom = NULL; + } + + numnodes--; + } + + reconstructsuccess = true; + } + } + + return reconstructsuccess; +} + +/*-------------------------------------------------- + boolean K_PathfindAStar(path_t *const path, pathfindsetup_t *const pathfindsetup) + + See header file for description. +--------------------------------------------------*/ +boolean K_PathfindAStar(path_t *const path, pathfindsetup_t *const pathfindsetup) +{ + boolean pathfindsuccess = false; + + if (path == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL path in K_PathfindAStar.\n"); + } + else if (pathfindsetup == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL pathfindsetup in K_PathfindAStar.\n"); + } + else if (!K_PathfindSetupValid(pathfindsetup)) + { + CONS_Debug(DBG_GAMELOGIC, "K_PathfindAStar: Pathfinding setup is not valid.\n"); + } + else if (pathfindsetup->startnodedata == pathfindsetup->endnodedata) + { + // At the destination, return a simple 1 node path + pathfindnode_t singlenode = {}; + singlenode.camefrom = NULL; + singlenode.nodedata = pathfindsetup->endnodedata; + singlenode.heapindex = SIZE_MAX; + singlenode.hscore = 0U; + singlenode.gscore = 0U; + + K_ReconstructPath(path, &singlenode); + + pathfindsuccess = true; + } + else + { + bheap_t openset = {}; + bheapitem_t poppedbheapitem = {}; + pathfindnode_t *nodesarray = NULL; + pathfindnode_t **closedset = NULL; + pathfindnode_t *newnode = NULL; + pathfindnode_t *currentnode = NULL; + pathfindnode_t *connectingnode = NULL; + void **connectingnodesdata = NULL; + void *checknodedata = NULL; + UINT32 *connectingnodecosts = NULL; + size_t numconnectingnodes = 0U; + size_t connectingnodeheapindex = 0U; + size_t nodesarraycount = 0U; + size_t closedsetcount = 0U; + size_t i = 0U; + UINT32 tentativegscore = 0U; + + // Set the dynamic structure capacites to defaults if they are 0 + if (pathfindsetup->nodesarraycapacity == 0U) + { + pathfindsetup->nodesarraycapacity = DEFAULT_NODEARRAY_CAPACITY; + } + if (pathfindsetup->opensetcapacity == 0U) + { + pathfindsetup->opensetcapacity = DEFAULT_OPENSET_CAPACITY; + } + if (pathfindsetup->closedsetcapacity == 0U) + { + pathfindsetup->closedsetcapacity = DEFAULT_CLOSEDSET_CAPACITY; + } + + // Allocate the necessary memory + nodesarray = Z_Calloc(pathfindsetup->nodesarraycapacity * sizeof(pathfindnode_t), PU_STATIC, NULL); + if (nodesarray == NULL) + { + I_Error("K_PathfindAStar: Out of memory allocating nodes array."); + } + closedset = Z_Calloc(pathfindsetup->closedsetcapacity * sizeof(pathfindnode_t*), PU_STATIC, NULL); + if (closedset == NULL) + { + I_Error("K_PathfindAStar: Out of memory allocating closed set."); + } + K_BHeapInit(&openset, pathfindsetup->opensetcapacity); + + // Create the first node and add it to the open set + newnode = &nodesarray[nodesarraycount]; + newnode->heapindex = SIZE_MAX; + newnode->nodedata = pathfindsetup->startnodedata; + newnode->camefrom = NULL; + newnode->gscore = 0U; + newnode->hscore = pathfindsetup->getheuristic(newnode->nodedata, pathfindsetup->endnodedata); + nodesarraycount++; + K_BHeapPush(&openset, newnode, K_NodeGetFScore(newnode), K_NodeUpdateHeapIndex); + + // update openset capacity if it changed + if (openset.capacity != pathfindsetup->opensetcapacity) + { + pathfindsetup->opensetcapacity = openset.capacity; + } + + // Go through each node in the openset, adding new ones from each node to it + // this continues until a path is found or there are no more nodes to check + while (openset.count > 0U) + { + // pop the best node off of the openset + K_BHeapPop(&openset, &poppedbheapitem); + currentnode = (pathfindnode_t*)poppedbheapitem.data; + + if (currentnode->nodedata == pathfindsetup->endnodedata) + { + pathfindsuccess = K_ReconstructPath(path, currentnode); + break; + } + + // Place the node we just popped into the closed set, as we are now evaluating it + if (closedsetcount >= pathfindsetup->closedsetcapacity) + { + // Need to reallocate closedset to fit another node + pathfindsetup->closedsetcapacity = pathfindsetup->closedsetcapacity * 2; + closedset = + Z_Realloc(closedset, pathfindsetup->closedsetcapacity * sizeof(pathfindnode_t*), PU_STATIC, NULL); + if (closedset == NULL) + { + I_Error("K_PathfindAStar: Out of memory reallocating closed set."); + } + } + closedset[closedsetcount] = currentnode; + closedsetcount++; + + // Get the needed data for the next nodes from the current node + connectingnodesdata = pathfindsetup->getconnectednodes(currentnode->nodedata, &numconnectingnodes); + connectingnodecosts = pathfindsetup->getconnectioncosts(currentnode->nodedata); + + if (connectingnodesdata == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "K_PathfindAStar: A Node returned NULL connecting node data.\n"); + } + else if (connectingnodecosts == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "K_PathfindAStar: A Node returned NULL connecting node costs.\n"); + } + else + { + // For each connecting node add it to the openset if it's unevaluated and not there, + // skip it if it's in the closedset or not traversable + for (i = 0; i < numconnectingnodes; i++) + { + checknodedata = connectingnodesdata[i]; + + if (checknodedata == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "K_PathfindAStar: A Node has a NULL connecting node.\n"); + } + else + { + // skip this node if it isn't traversable + if (pathfindsetup->gettraversable(checknodedata) == false) + { + continue; + } + + // Figure out what the gscore of this route for the connecting node is + tentativegscore = currentnode->gscore + connectingnodecosts[i]; + + // find this data in the nodes array if it's been generated before + connectingnode = K_NodesArrayContainsNodeData(nodesarray, checknodedata, nodesarraycount); + + if (connectingnode != NULL) + { + // The connecting node has been seen before, so it must be in either the closedset (skip it) + // or the openset (re-evaluate it's gscore) + if (K_ClosedsetContainsNode(closedset, connectingnode, closedsetcount) == true) + { + continue; + } + else if (tentativegscore < connectingnode->gscore) + { + // The node is not in the closedset, update it's gscore if this path to it is faster + connectingnode->gscore = tentativegscore; + connectingnode->camefrom = currentnode; + + connectingnodeheapindex = + K_BHeapContains(&openset, connectingnode, connectingnode->heapindex); + if (connectingnodeheapindex != SIZE_MAX) + { + K_UpdateBHeapItemValue( + &openset.array[connectingnodeheapindex], K_NodeGetFScore(connectingnode)); + } + else + { + // SOMEHOW the node is not in either the closed set OR the open set + CONS_Debug(DBG_GAMELOGIC, "K_PathfindAStar: A Node is not in either set.\n"); + } + } + } + else + { + // Node is not created yet, so it hasn't been seen so far + // Reallocate nodesarray if it's full + if (nodesarraycount >= pathfindsetup->nodesarraycapacity) + { + pathfindsetup->nodesarraycapacity = pathfindsetup->nodesarraycapacity * 2; + nodesarray = Z_Realloc(nodesarray, pathfindsetup->nodesarraycapacity, PU_STATIC, NULL); + + if (nodesarray == NULL) + { + I_Error("K_PathfindAStar: Out of memory reallocating nodes array."); + } + } + + // Create the new node and add it to the nodes array and open set + newnode = &nodesarray[nodesarraycount]; + newnode->heapindex = SIZE_MAX; + newnode->nodedata = checknodedata; + newnode->camefrom = currentnode; + newnode->gscore = tentativegscore; + newnode->hscore = pathfindsetup->getheuristic(newnode->nodedata, pathfindsetup->endnodedata); + nodesarraycount++; + K_BHeapPush(&openset, newnode, K_NodeGetFScore(newnode), K_NodeUpdateHeapIndex); + } + } + } + } + } + + // Clean up the memory + K_BHeapFree(&openset); + Z_Free(closedset); + Z_Free(nodesarray); + } + + return pathfindsuccess; +} diff --git a/src/k_pathfind.h b/src/k_pathfind.h new file mode 100644 index 000000000..dac23373c --- /dev/null +++ b/src/k_pathfind.h @@ -0,0 +1,70 @@ +#ifndef __K_PATHFIND__ +#define __K_PATHFIND__ + +#include "doomtype.h" + +// function pointer for returning a node's connected node data +// should return a pointer to an array of pointers to the data, as arguments takes a node's data and a pointer that the +// number of connected nodes should be placed into +typedef void**(*getconnectednodesfunc)(void*, size_t*); + +// function pointer for getting the list of connected node costs/distances +typedef UINT32*(*getnodeconnectioncostsfunc)(void*); + +// function pointer for getting a heuristic between 2 nodes from their base data +typedef UINT32(*getnodeheuristicfunc)(void*, void*); + +// function pointer for getting if a node is traversable from its base data +typedef boolean(*getnodetraversablefunc)(void*); + + +// A pathfindnode contains information about a node from the pathfinding +// heapindex is only used within the pathfinding algorithm itself, and is always 0 after it is completed +typedef struct pathfindnode_s { + size_t heapindex; // The index in the openset binary heap. Only valid while the node is in the openset. + void *nodedata; + struct pathfindnode_s *camefrom; // should eventually be the most efficient predecessor node + UINT32 gscore; // The accumulated distance from the start to this node + UINT32 hscore; // The heuristic from this node to the goal +} pathfindnode_t; + +// Contains the final created path after pathfinding is completed +typedef struct path_s { + size_t numnodes; + struct pathfindnode_s *array; + UINT32 totaldist; +} path_t; + +// Contains info about the pathfinding used to setup the algorithm +// (e.g. the base capacities of the dynamically allocated arrays) +// should be setup by the caller before starting pathfinding +// base capacities will be 8 if they aren't setup, missing callback functions will cause an error. +// Can be accessed after the pathfinding is complete to get the final capacities of them +typedef struct pathfindsetup_s { + size_t opensetcapacity; + size_t closedsetcapacity; + size_t nodesarraycapacity; + void *startnodedata; + void *endnodedata; + getconnectednodesfunc getconnectednodes; + getnodeconnectioncostsfunc getconnectioncosts; + getnodeheuristicfunc getheuristic; + getnodetraversablefunc gettraversable; +} pathfindsetup_t; + + +/*-------------------------------------------------- + boolean K_PathfindAStar(path_t *const path, pathfindsetup_t *const pathfindsetup); + + From a source waypoint and destination waypoint, find the best path between them using the A* algorithm. + + Input Arguments:- + path - The return location of the found path + pathfindsetup - The information regarding pathfinding setup, see pathfindsetup_t + + Return:- + True if a path was found between source and destination, false otherwise. +--------------------------------------------------*/ +boolean K_PathfindAStar(path_t *const path, pathfindsetup_t *const pathfindsetup); + +#endif diff --git a/src/k_waypoint.c b/src/k_waypoint.c index d179d68c2..79017b4dd 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -4,16 +4,12 @@ #include "p_local.h" #include "p_tick.h" #include "z_zone.h" -#include "k_bheap.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. When the sets reach their max capacity -// they are reallocated to contain their old capacity plus these defines. Openset is smaller because most of the time -// the nodes will quickly be moved to closedset, closedset could contain an entire maps worth of waypoints. -// Additonally, in order to keep later calls to pathfinding quick and avoid reallocation, the highest size of the -// allocation is saved into a variable. +// 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. static const size_t OPENSET_BASE_SIZE = 16U; static const size_t CLOSEDSET_BASE_SIZE = 256U; static const size_t NODESARRAY_BASE_SIZE = 256U; @@ -21,6 +17,7 @@ static const size_t NODESARRAY_BASE_SIZE = 256U; static waypoint_t **waypointheap = NULL; static waypoint_t *firstwaypoint = NULL; static waypoint_t *finishline = NULL; + static size_t numwaypoints = 0U; static size_t numwaypointmobjs = 0U; static size_t baseopensetsize = OPENSET_BASE_SIZE; @@ -455,468 +452,139 @@ static UINT32 K_DistanceBetweenWaypoints(waypoint_t *const waypoint1, waypoint_t return finaldist; } -/*-------------------------------------------------- - static UINT32 K_GetNodeFScore(pathfindnode_t *node) - - Gets the FScore of a node. The FScore is the GScore plus the HScore. - - Input Arguments:- - node - The node to get the FScore of - - Return:- - The FScore of the node. ---------------------------------------------------*/ -static UINT32 K_GetNodeFScore(pathfindnode_t *node) +static void **K_WaypointPathfindGetNext(void *data, size_t *numconnections) { - UINT32 nodefscore = UINT32_MAX; + waypoint_t **connectingwaypoints = NULL; - if (node == NULL) + if (data == NULL) { - CONS_Debug(DBG_GAMELOGIC, "NULL node in K_GetNodeFScore.\n"); + 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 { - nodefscore = node->gscore + node->hscore; + waypoint_t *waypoint = (waypoint_t *)data; + connectingwaypoints = waypoint->nextwaypoints; + *numconnections = waypoint->numnextwaypoints; } - return nodefscore; + return (void**)connectingwaypoints; } -/*-------------------------------------------------- - static boolean K_ClosedsetContainsNode(pathfindnode_t **closedset, pathfindnode_t *node, size_t closedsetcount) - - Checks whether the Closedset contains a node. Searches from the end to the start for speed reasons. - - Input Arguments:- - closedset - The closed set within the A* algorithm - node - The node to check is within the closed set - closedsetcount - The current size of the closedset - - Return:- - True if the node is in the closed set, false if it isn't ---------------------------------------------------*/ -static boolean K_ClosedsetContainsNode(pathfindnode_t **closedset, pathfindnode_t *node, size_t closedsetcount) +static void **K_WaypointPathfindGetPrev(void *data, size_t *numconnections) { - boolean nodeisinclosedset = false; + waypoint_t **connectingwaypoints = NULL; - if (closedset == NULL) + if (data == NULL) { - CONS_Debug(DBG_GAMELOGIC, "NULL closedset in K_SetContainsWaypoint.\n"); + CONS_Debug(DBG_GAMELOGIC, "K_WaypointPathfindGetPrev received NULL data.\n"); } - else if (node == NULL) + else if (numconnections == NULL) { - CONS_Debug(DBG_GAMELOGIC, "NULL node in K_SetContainsWaypoint.\n"); + CONS_Debug(DBG_GAMELOGIC, "K_WaypointPathfindGetPrev received NULL numconnections.\n"); } else { - size_t i; - // It is more likely that we'll find the node we are looking for from the end of the array - // Yes, the for loop looks weird, remember that size_t is unsigned and we want to check 0, after it hits 0 it - // will loop back up to SIZE_MAX - for (i = closedsetcount - 1U; i < closedsetcount; i--) - { - if (closedset[i] == node) - { - nodeisinclosedset = true; - break; - } - } + waypoint_t *waypoint = (waypoint_t *)data; + connectingwaypoints = waypoint->prevwaypoints; + *numconnections = waypoint->numprevwaypoints; } - return nodeisinclosedset; + + return (void**)connectingwaypoints; } -/*-------------------------------------------------- - static pathfindnode_t *K_NodesArrayContainsWaypoint( - pathfindnode_t *nodesarray, - waypoint_t* waypoint, - size_t nodesarraycount) - - Checks whether the Nodes Array contains a node with a waypoint. Searches from the end to the start for speed - reasons. - - Input Arguments:- - nodesarray - The nodes array within the A* algorithm - waypoint - The waypoint to check is within the nodes array - nodesarraycount - The current size of the nodes array - - Return:- - The pathfind node that has the waypoint if there is one. NULL if the waypoint is not in the nodes array. ---------------------------------------------------*/ -static pathfindnode_t *K_NodesArrayContainsWaypoint( - pathfindnode_t *nodesarray, - waypoint_t* waypoint, - size_t nodesarraycount) +static UINT32 *K_WaypointPathfindGetNextCosts(void* data) { - pathfindnode_t *foundnode = NULL; + UINT32 *connectingnodecosts = NULL; - if (nodesarray == NULL) + if (data == NULL) { - CONS_Debug(DBG_GAMELOGIC, "NULL nodesarray in K_NodesArrayContainsWaypoint.\n"); - } - else if (waypoint == NULL) - { - CONS_Debug(DBG_GAMELOGIC, "NULL waypoint in K_NodesArrayContainsWaypoint.\n"); + CONS_Debug(DBG_GAMELOGIC, "K_WaypointPathfindGetNextCosts received NULL data.\n"); } else { - size_t i; - // It is more likely that we'll find the node we are looking for from the end of the array - // Yes, the for loop looks weird, remember that size_t is unsigned and we want to check 0, after it hits 0 it - // will loop back up to SIZE_MAX - for (i = nodesarraycount - 1U; i < nodesarraycount; i--) - { - if (nodesarray[i].waypoint == waypoint) - { - foundnode = &nodesarray[i]; - break; - } - } + waypoint_t *waypoint = (waypoint_t *)data; + connectingnodecosts = waypoint->nextwaypointdistances; } - return foundnode; + + return connectingnodecosts; } -/*-------------------------------------------------- - static void K_NodeUpdateHeapIndex(void *const node, const size_t newheapindex) - - A callback for the Openset Binary Heap to be able to update the heapindex of the pathfindnodes when they are - moved. - - Input Arguments:- - node - The node that has been updated, should be a pointer to a pathfindnode_t - newheapindex - The new heapindex of the node. - - Return:- - None ---------------------------------------------------*/ -static void K_NodeUpdateHeapIndex(void *const node, const size_t newheapindex) +static UINT32 *K_WaypointPathfindGetPrevCosts(void* data) { - if (node == NULL) + UINT32 *connectingnodecosts = NULL; + + if (data == NULL) { - CONS_Debug(DBG_GAMELOGIC, "NULL node in K_NodeUpdateHeapIndex.\n"); + CONS_Debug(DBG_GAMELOGIC, "K_WaypointPathfindGetPrevCosts received NULL data.\n"); } else { - pathfindnode_t *truenode = (pathfindnode_t*)node; - truenode->heapindex = newheapindex; + waypoint_t *waypoint = (waypoint_t *)data; + connectingnodecosts = waypoint->prevwaypointdistances; } + + return connectingnodecosts; } -/*-------------------------------------------------- - static boolean K_ReconstructPath(path_t *const returnpath, pathfindnode_t *const destinationnode) - - From a pathfindnode that should be the destination, reconstruct a path from start to finish. - - Input Arguments:- - returnpath - The location of the path that is being created - destinationnode - The node that is the destination from the pathfinding - - Return:- - True if the path reconstruction was successful, false if it wasn't. ---------------------------------------------------*/ -static boolean K_ReconstructPath(path_t *const returnpath, pathfindnode_t *const destinationnode) +static UINT32 K_WaypointPathfindGetHeuristic(void *data1, void *data2) { - boolean reconstructsuccess = false; + UINT32 nodeheuristic = UINT32_MAX; - if (returnpath == NULL) + if (data1 == NULL) { - CONS_Debug(DBG_GAMELOGIC, "NULL returnpath in K_ReconstructPath.\n"); + CONS_Debug(DBG_GAMELOGIC, "K_WaypointPathfindGetHeuristic received NULL data1.\n"); } - else if (destinationnode == NULL) + else if (data2 == NULL) { - CONS_Debug(DBG_GAMELOGIC, "NULL destinationnode in K_ReconstructPath.\n"); + CONS_Debug(DBG_GAMELOGIC, "K_WaypointPathfindGetHeuristic received NULL data2.\n"); } else { - size_t numnodes = 0U; - pathfindnode_t *thisnode = destinationnode; + waypoint_t *waypoint1 = (waypoint_t *)data1; + waypoint_t *waypoint2 = (waypoint_t *)data2; - // If the path we're placing our new path into already has data, free it - if (returnpath->array != NULL) - { - Z_Free(returnpath->array); - returnpath->numnodes = 0U; - returnpath->totaldist = 0U; - } - - // Do a fast check of how many nodes there are so we know how much space to allocate - for (thisnode = destinationnode; thisnode; thisnode = thisnode->camefrom) - { - numnodes++; - } - - if (numnodes > 0U) - { - // Allocate memory for the path - returnpath->numnodes = numnodes; - returnpath->array = Z_Calloc(numnodes * sizeof(pathfindnode_t), PU_STATIC, NULL); - returnpath->totaldist = destinationnode->gscore; - if (returnpath->array == NULL) - { - I_Error("K_ReconstructPath: Out of memory."); - } - - // Put the nodes into the return array - for (thisnode = destinationnode; thisnode; thisnode = thisnode->camefrom) - { - returnpath->array[numnodes - 1U] = *thisnode; - // Correct the camefrom element to point to the previous element in the array instead - if ((returnpath->array[numnodes - 1U].camefrom != NULL) && (numnodes > 1U)) - { - returnpath->array[numnodes - 1U].camefrom = &returnpath->array[numnodes - 2U]; - } - else - { - returnpath->array[numnodes - 1U].camefrom = NULL; - } - - numnodes--; - } - - reconstructsuccess = true; - } + nodeheuristic = K_DistanceBetweenWaypoints(waypoint1, waypoint2); } - return reconstructsuccess; + return nodeheuristic; } -/*-------------------------------------------------- - static boolean K_WaypointAStar( - waypoint_t *const sourcewaypoint, - waypoint_t *const destinationwaypoint, - path_t *const returnpath, - const boolean useshortcuts, - const boolean huntbackwards) - - From a source waypoint and destination waypoint, find the best path between them using the A* algorithm. - - Input Arguments:- - sourcewaypoint - The source waypoint to pathfind from - destinationwaypoint - The destination waypoint to pathfind to - returnpath - The path to return to if the pathfinding was successful. - useshortcuts - Whether the pathfinding can use shortcut waypoints. - huntbackwards - Whether the pathfinding should hunt through previous or next waypoints - - Return:- - True if a path was found between source and destination, false otherwise. ---------------------------------------------------*/ -static boolean K_WaypointAStar( - waypoint_t *const sourcewaypoint, - waypoint_t *const destinationwaypoint, - path_t *const returnpath, - const boolean useshortcuts, - const boolean huntbackwards) +static boolean K_WaypointPathfindTraversableAllEnabled(void *data) { - boolean pathfindsuccess = false; + boolean traversable = false; - if (sourcewaypoint == NULL) + if (data == NULL) { - CONS_Debug(DBG_GAMELOGIC, "NULL sourcewaypoint in K_WaypointAStar.\n"); - } - else if (destinationwaypoint == NULL) - { - CONS_Debug(DBG_GAMELOGIC, "NULL destinationwaypoint in K_WaypointAStar.\n"); - } - else if (returnpath == NULL) - { - CONS_Debug(DBG_GAMELOGIC, "NULL returnpath in K_WaypointAStar.\n"); - } - else if (sourcewaypoint == destinationwaypoint) - { - // Source and destination waypoint are the same, we're already there - // Just for simplicity's sake, create a single node on the destination and reconstruct path - pathfindnode_t singlenode; - singlenode.camefrom = NULL; - singlenode.waypoint = destinationwaypoint; - singlenode.heapindex = SIZE_MAX; - singlenode.hscore = 0U; - singlenode.gscore = 0U; - - pathfindsuccess = K_ReconstructPath(returnpath, &singlenode); - } - else if (((huntbackwards == false) && (sourcewaypoint->numnextwaypoints == 0)) - || ((huntbackwards == true) && (sourcewaypoint->numprevwaypoints == 0))) - { - CONS_Debug(DBG_GAMELOGIC, - "K_WaypointAStar: 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_WaypointAStar: destinationwaypoint with ID %d has no previous waypoint\n", - K_GetWaypointID(destinationwaypoint)); - } - else if ((K_GetWaypointIsEnabled(destinationwaypoint) == false) || - (!useshortcuts &&(K_GetWaypointIsShortcut(destinationwaypoint) == true))) - { - // No path to the destination is possible + CONS_Debug(DBG_GAMELOGIC, "K_WaypointPathfindTraversableAllEnabled received NULL data.\n"); } else { - size_t opensetcapacity = K_GetOpensetBaseSize(); - size_t nodesarraycapacity = K_GetNodesArrayBaseSize(); - size_t closedsetcapacity = K_GetClosedsetBaseSize(); - bheapitem_t poppeditem = {}; - pathfindnode_t *currentnode = NULL; - pathfindnode_t *neighbournode = NULL; - bheap_t openset = {}; - pathfindnode_t **closedset = Z_Calloc(closedsetcapacity * sizeof(pathfindnode_t*), PU_STATIC, NULL); - pathfindnode_t *nodesarray = Z_Calloc(nodesarraycapacity * sizeof(pathfindnode_t), PU_STATIC, NULL); - pathfindnode_t *newnode = NULL; - size_t closedsetcount = 0U; - size_t nodesarraycount = 0U; - size_t numcheckwaypoints = 0U; - waypoint_t **checkwaypoints = NULL; - UINT32 *checkwaypointsdists = NULL; - UINT32 tentativegscore = UINT32_MAX; - size_t i = 0U; - size_t findopensetindex = 0U; - - if (closedset == NULL || nodesarray == NULL) - { - I_Error("K_WaypointAStar: Out of memory."); - } - - K_BHeapInit(&openset, opensetcapacity); - - newnode = &nodesarray[0]; - newnode->waypoint = sourcewaypoint; - newnode->hscore = K_DistanceBetweenWaypoints(sourcewaypoint, destinationwaypoint); - newnode->gscore = 0U; - newnode->camefrom = NULL; - nodesarraycount++; - - K_BHeapPush(&openset, &nodesarray[0], K_GetNodeFScore(&nodesarray[0]), K_NodeUpdateHeapIndex); - - if (opensetcapacity != openset.capacity) - { - opensetcapacity = openset.capacity; - K_UpdateOpensetBaseSize(opensetcapacity); - } - - while (openset.count > 0) - { - K_BHeapPop(&openset, &poppeditem); - currentnode = ((pathfindnode_t*)poppeditem.data); - - if (currentnode->waypoint == destinationwaypoint) - { - pathfindsuccess = K_ReconstructPath(returnpath, currentnode); - break; - } - - // The node is now placed into the closed set because it is evaluated - if (closedsetcount >= closedsetcapacity) - { - K_UpdateClosedsetBaseSize(closedsetcapacity * 2); - - closedsetcapacity = K_GetClosedsetBaseSize(); - closedset = Z_Realloc(closedset, closedsetcapacity * sizeof (pathfindnode_t*), PU_STATIC, NULL); - - if (closedset == NULL) - { - I_Error("K_WaypointAStar: Out of memory"); - } - } - closedset[closedsetcount] = currentnode; - closedsetcount++; - - if (huntbackwards) - { - numcheckwaypoints = currentnode->waypoint->numprevwaypoints; - checkwaypoints = currentnode->waypoint->prevwaypoints; - checkwaypointsdists = currentnode->waypoint->prevwaypointdistances; - } - else - { - numcheckwaypoints = currentnode->waypoint->numnextwaypoints; - checkwaypoints = currentnode->waypoint->nextwaypoints; - checkwaypointsdists = currentnode->waypoint->nextwaypointdistances; - } - - for (i = 0; i < numcheckwaypoints; i++) - { - tentativegscore = currentnode->gscore + checkwaypointsdists[i]; - - // Can this double search be sped up at all? I feel like allocating and deallocating memory for nodes - // constantly would be slower - // Find if the neighbournode is already created first, if it is then check if it's in the closedset - // If it's in the closedset, then skip as we don't need to check it again, if it isn't then see if the - // new route from currentnode is faster to it and update accordingly - neighbournode = K_NodesArrayContainsWaypoint(nodesarray, checkwaypoints[i], nodesarraycount); - - if (neighbournode != NULL) - { - // If the closedset contains the node, then it is already evaluated and doesn't need to be checked - if (K_ClosedsetContainsNode(closedset, neighbournode, closedsetcount) != false) - { - continue; - } - - if (tentativegscore < neighbournode->gscore) - { - neighbournode->camefrom = currentnode; - neighbournode->gscore = tentativegscore; - - findopensetindex = K_BHeapContains(&openset, neighbournode, neighbournode->heapindex); - if (findopensetindex != SIZE_MAX) - { - K_UpdateBHeapItemValue(&openset.array[findopensetindex], K_GetNodeFScore(neighbournode)); - } - else - { - // What??? How is this node NOT in the openset??? - // A node should always be in either the openset or closedset - CONS_Debug(DBG_GAMELOGIC, "Node unexpectedly not in openset in K_WaypointAStar.\n"); - K_BHeapPush(&openset, neighbournode, K_GetNodeFScore(neighbournode), K_NodeUpdateHeapIndex); - } - } - } - else - { - // Don't process this waypoint if it's not traversable - if ((K_GetWaypointIsEnabled(checkwaypoints[i]) == false) - || (!useshortcuts && K_GetWaypointIsShortcut(checkwaypoints[i]) == true)) - { - continue; - } - - // reallocate the nodesarray if needed - if (nodesarraycount >= nodesarraycapacity) - { - K_UpdateNodesArrayBaseSize(nodesarraycapacity * 2); - nodesarraycapacity = K_GetNodesArrayBaseSize(); - nodesarray = Z_Realloc(nodesarray, nodesarraycapacity * sizeof(pathfindnode_t), PU_STATIC, NULL); - - if (nodesarray == NULL) - { - I_Error("K_WaypointAStar: Out of memory"); - } - } - - // There is currently no node created for this waypoint, so make one - newnode = &nodesarray[nodesarraycount]; - newnode->camefrom = currentnode; - newnode->heapindex = SIZE_MAX; - newnode->gscore = tentativegscore; - newnode->hscore = K_DistanceBetweenWaypoints(checkwaypoints[i], destinationwaypoint); - newnode->waypoint = checkwaypoints[i]; - nodesarraycount++; - - // because there was no node for the waypoint, it's also not in the openset, add it - K_BHeapPush(&openset, newnode, K_GetNodeFScore(newnode), K_NodeUpdateHeapIndex); - } - } - } - - // Clean up the memory - K_BHeapFree(&openset); - Z_Free(closedset); - Z_Free(nodesarray); + waypoint_t *waypoint = (waypoint_t *)data; + traversable = (K_GetWaypointIsEnabled(waypoint) == true); } - return pathfindsuccess; + return traversable; +} + +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; } /*-------------------------------------------------- @@ -962,7 +630,38 @@ boolean K_PathfindToWaypoint( } else { - pathfound = K_WaypointAStar(sourcewaypoint, destinationwaypoint, returnpath, useshortcuts, huntbackwards); + 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; @@ -1025,22 +724,53 @@ waypoint_t *K_GetNextWaypointToDestination( } else { - path_t pathtowaypoint; - boolean pathfindsuccess = - K_WaypointAStar(sourcewaypoint, destinationwaypoint, &pathtowaypoint, useshortcuts, huntbackwards); + 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 = pathtowaypoint.array[1].waypoint; + 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 = pathtowaypoint.array[0].waypoint; + nextwaypoint = (waypoint_t*)pathtowaypoint.array[0].nodedata; } Z_Free(pathtowaypoint.array); @@ -1257,7 +987,6 @@ searchwaypointstart: { // No next waypoints, this function will be returned from } - } } } diff --git a/src/k_waypoint.h b/src/k_waypoint.h index f93df802f..7e5dffc1a 100644 --- a/src/k_waypoint.h +++ b/src/k_waypoint.h @@ -1,8 +1,9 @@ #ifndef __K_WAYPOINT__ #define __K_WAYPOINT__ -#include "doomdef.h" +#include "doomtype.h" #include "p_mobj.h" +#include "k_pathfind.h" typedef struct waypoint_s { @@ -16,20 +17,6 @@ typedef struct waypoint_s size_t numprevwaypoints; } waypoint_t; -typedef struct pathfindnode_s { - size_t heapindex; // The index in the openset binary heap. Only valid while the node is in the openset. - waypoint_t *waypoint; - struct pathfindnode_s *camefrom; // should eventually be the most efficient predecessor node - UINT32 gscore; // The accumulated distance from the start to this node - UINT32 hscore; // The heuristic from this node to the goal, saved to avoid expensive recalculation -} pathfindnode_t; - -typedef struct path_s { - size_t numnodes; - struct pathfindnode_s *array; - UINT32 totaldist; -} path_t; - // AVAILABLE FOR LUA From 4dfd81568ddf81a85b089c677b86b08df3386edd Mon Sep 17 00:00:00 2001 From: Sryder Date: Fri, 14 Jun 2019 23:55:10 +0100 Subject: [PATCH 020/105] Get distance to finish line using waypoints/pathfinding. Update player position using distance from finish line. Minor Bug: At the start of a race for a split second the leading players will be last. Proposed Fix: Start on lap 0 and Force a lap count update when crossing the finish line --- src/d_player.h | 1 + src/k_kart.c | 220 +++++++++++++++++++++++++++++++++---------------- src/p_saveg.c | 4 + 3 files changed, 154 insertions(+), 71 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index d2ed296ce..388b578a0 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -432,6 +432,7 @@ typedef struct player_s angle_t frameangle; // for the player add the ability to have the sprite only face other angles INT16 lturn_max[MAXPREDICTTICS]; // What's the expected turn value for full-left for a number of frames back (to account for netgame latency)? INT16 rturn_max[MAXPREDICTTICS]; // Ditto but for full-right + UINT32 distancetofinish; // Bit flags. // See pflags_t, above. diff --git a/src/k_kart.c b/src/k_kart.c index 9422170a6..4cb1f5995 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -23,6 +23,8 @@ #include "lua_hud.h" // For Lua hud checks #include "lua_hook.h" // For MobjDamage and ShouldDamage +#include "k_waypoint.h" + // SOME IMPORTANT VARIABLES DEFINED IN DOOMDEF.H: // gamespeed is cc (0 for easy, 1 for normal, 2 for hard) // franticitems is Frantic Mode items, bool @@ -1638,7 +1640,7 @@ static void K_DrawDraftCombiring(player_t *player, player_t *victim, fixed_t cur curx += stepx; cury += stepy; curz += stepz; - + offset = abs(offset-1) % 3; n--; } @@ -3415,7 +3417,7 @@ void K_SpawnDraftDust(mobj_t *mo) ang = mo->player->frameangle; - if (mo->player->kartstuff[k_drift] != 0) + if (mo->player->kartstuff[k_drift] != 0) { drifting = true; ang += (mo->player->kartstuff[k_drift] * ((ANGLE_270 + ANGLE_22h) / 5)); // -112.5 doesn't work. I fucking HATE SRB2 angles @@ -5346,6 +5348,10 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) void K_KartPlayerAfterThink(player_t *player) { + // Moved to afterthink, as at this point the players have had their distances to the finish line updated + // and this will correctly account for all players + K_KartUpdatePosition(player); + if (player->kartstuff[k_curshield] || player->kartstuff[k_invincibilitytimer] || (player->kartstuff[k_growshrinktimer] != 0 && player->kartstuff[k_growshrinktimer] % 5 == 4)) // 4 instead of 0 because this is afterthink! @@ -5666,6 +5672,130 @@ static void K_KartDrift(player_t *player, boolean onground) else player->kartstuff[k_brakedrift] = 0; } + +static void K_UpdateDistanceFromFinishLine(player_t *player) +{ + if ((player != NULL) && (player->mo != NULL)) + { + mobj_t *wpmobj; + mobj_t *closestwpmobj = NULL; + fixed_t wpdist = INT32_MAX; + fixed_t closestdist = INT32_MAX; + waypoint_t *waypoint = NULL; + waypoint_t *bestwaypoint = NULL; + waypoint_t *finishline = K_GetFinishLineWaypoint(); + + // Find the closest waypoint mobj to the player + for (wpmobj = waypointcap; wpmobj; wpmobj = wpmobj->tracer) + { + wpdist = P_AproxDistance(wpmobj->x - player->mo->x, wpmobj->y - player->mo->y); + wpdist = P_AproxDistance(wpdist, wpmobj->z - player->mo->z); + + if (wpdist < closestdist) + { + closestdist = wpdist; + closestwpmobj = wpmobj; + } + } + + waypoint = K_SearchWaypointGraphForMobj(closestwpmobj); + bestwaypoint = waypoint; + + // check the waypoint's location in relation to the player + // If it's generally in front, it's fine, otherwise, use the best next waypoint. + if (waypoint != NULL) + { + angle_t playerangle = player->mo->angle; + angle_t angletowaypoint = + R_PointToAngle2(player->mo->x, player->mo->y, waypoint->mobj->x, waypoint->mobj->y); + angle_t angledelta = playerangle - angletowaypoint; + + if (angledelta > ANGLE_180) + { + angledelta = InvAngle(angletowaypoint); + } + + if (angledelta > ANGLE_90) + { + angle_t nextbestdelta = angledelta; + size_t i = 0U; + + if ((waypoint->nextwaypoints != NULL) && (waypoint->numnextwaypoints > 0U)) + { + for (i = 0U; i < waypoint->numnextwaypoints; i++) + { + angletowaypoint = R_PointToAngle2( + player->mo->x, player->mo->y, + waypoint->nextwaypoints[i]->mobj->x, waypoint->nextwaypoints[i]->mobj->y); + angledelta = playerangle - angletowaypoint; + + if (angledelta > ANGLE_180) + { + angledelta = InvAngle(angledelta); + } + + if (angledelta < nextbestdelta) + { + bestwaypoint = waypoint->nextwaypoints[i]; + nextbestdelta = angledelta; + } + } + } + + if ((waypoint->prevwaypoints != NULL) && (waypoint->numprevwaypoints > 0U)) + { + for (i = 0U; i < waypoint->numprevwaypoints; i++) + { + angletowaypoint = R_PointToAngle2( + player->mo->x, player->mo->y, + waypoint->prevwaypoints[i]->mobj->x, waypoint->prevwaypoints[i]->mobj->y); + angledelta = playerangle - angletowaypoint; + + if (angledelta > ANGLE_180) + { + angledelta = InvAngle(angledelta); + } + + if (angledelta < nextbestdelta) + { + bestwaypoint = waypoint->prevwaypoints[i]; + nextbestdelta = angledelta; + } + } + } + } + } + + // bestwaypoint is now the waypoint that is in front of us + if ((bestwaypoint != NULL) && (finishline != NULL)) + { + const boolean useshortcuts = false; + const boolean huntbackwards = false; + boolean pathfindsuccess = false; + path_t pathtofinish = {}; + + pathfindsuccess = + K_PathfindToWaypoint(bestwaypoint, finishline, &pathtofinish, useshortcuts, huntbackwards); + + // Update the player's distance to the finish line if a path was found. + // Using shortcuts won't find a path, so the distance won't be updated until the player gets back on track + if (pathfindsuccess == true) + { + // Add euclidean distance to the next waypoint to the distancetofinish + UINT32 adddist; + fixed_t disttowaypoint = + P_AproxDistance(player->mo->x - bestwaypoint->mobj->x, player->mo->y - bestwaypoint->mobj->y); + disttowaypoint = P_AproxDistance(disttowaypoint, player->mo->z - bestwaypoint->mobj->z); + + adddist = ((UINT32)disttowaypoint) >> FRACBITS; + + player->distancetofinish = pathtofinish.totaldist + adddist; + Z_Free(pathtofinish.array); + } + } + } +} + // // K_KartUpdatePosition // @@ -5673,11 +5803,7 @@ void K_KartUpdatePosition(player_t *player) { fixed_t position = 1; fixed_t oldposition = player->kartstuff[k_position]; - fixed_t i, ppcd, pncd, ipcd, incd; - fixed_t pmo, imo; - mobj_t *mo; - - return; + fixed_t i; if (player->spectator || !player->mo) return; @@ -5689,70 +5815,12 @@ void K_KartUpdatePosition(player_t *player) if (G_RaceGametype()) { - if ((((players[i].starpostnum) + (numstarposts + 1) * players[i].laps) > - ((player->starpostnum) + (numstarposts + 1) * player->laps))) - position++; - else if (((players[i].starpostnum) + (numstarposts+1)*players[i].laps) == - ((player->starpostnum) + (numstarposts+1)*player->laps)) + // I'm a lap behind this player OR + // My distance to the finish line is higher, so I'm behind + if ((players[i].laps > player->laps) + || (players[i].distancetofinish < player->distancetofinish)) { - ppcd = pncd = ipcd = incd = 0; - - player->kartstuff[k_prevcheck] = players[i].kartstuff[k_prevcheck] = 0; - player->kartstuff[k_nextcheck] = players[i].kartstuff[k_nextcheck] = 0; - - // This checks every thing on the map, and looks for MT_BOSS3WAYPOINT (the thing we're using for checkpoint wp's, for now) - for (mo = waypointcap; mo != NULL; mo = mo->tracer) - { - pmo = P_AproxDistance(P_AproxDistance( mo->x - player->mo->x, - mo->y - player->mo->y), - mo->z - player->mo->z) / FRACUNIT; - imo = P_AproxDistance(P_AproxDistance( mo->x - players[i].mo->x, - mo->y - players[i].mo->y), - mo->z - players[i].mo->z) / FRACUNIT; - - if (mo->health == player->starpostnum && (!mo->movecount || mo->movecount == player->laps+1)) - { - player->kartstuff[k_prevcheck] += pmo; - ppcd++; - } - if (mo->health == (player->starpostnum + 1) && (!mo->movecount || mo->movecount == player->laps+1)) - { - player->kartstuff[k_nextcheck] += pmo; - pncd++; - } - if (mo->health == players[i].starpostnum && (!mo->movecount || mo->movecount == players[i].laps+1)) - { - players[i].kartstuff[k_prevcheck] += imo; - ipcd++; - } - if (mo->health == (players[i].starpostnum + 1) && (!mo->movecount || mo->movecount == players[i].laps+1)) - { - players[i].kartstuff[k_nextcheck] += imo; - incd++; - } - } - - if (ppcd > 1) player->kartstuff[k_prevcheck] /= ppcd; - if (pncd > 1) player->kartstuff[k_nextcheck] /= pncd; - if (ipcd > 1) players[i].kartstuff[k_prevcheck] /= ipcd; - if (incd > 1) players[i].kartstuff[k_nextcheck] /= incd; - - if ((players[i].kartstuff[k_nextcheck] > 0 || player->kartstuff[k_nextcheck] > 0) && !player->exiting) - { - if ((players[i].kartstuff[k_nextcheck] - players[i].kartstuff[k_prevcheck]) < - (player->kartstuff[k_nextcheck] - player->kartstuff[k_prevcheck])) - position++; - } - else if (!player->exiting) - { - if (players[i].kartstuff[k_prevcheck] > player->kartstuff[k_prevcheck]) - position++; - } - else - { - if (players[i].starposttime < player->starposttime) - position++; - } + position++; } } else if (G_BattleGametype()) @@ -5899,7 +5967,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) boolean HOLDING_ITEM = (player->kartstuff[k_itemheld] || player->kartstuff[k_eggmanheld]); boolean NO_HYUDORO = (player->kartstuff[k_stolentimer] == 0 && player->kartstuff[k_stealingtimer] == 0); - K_KartUpdatePosition(player); + K_UpdateDistanceFromFinishLine(player); if (!player->exiting) { @@ -9694,6 +9762,14 @@ static void K_drawCheckpointDebugger(void) V_DrawString(8, 192, 0, va("Waypoint dist: Prev %d, Next %d", stplyr->kartstuff[k_prevcheck], stplyr->kartstuff[k_nextcheck])); } +static void K_DrawWaypointDebugger(void) +{ + if ((cv_kartdebugwaypoints.value != 0) && (stplyr == &players[displayplayers[0]])) + { + V_DrawString(8, 176, 0, va("Finishline Distance: %d", stplyr->distancetofinish)); + } +} + void K_drawKartHUD(void) { boolean isfreeplay = false; @@ -9925,6 +10001,8 @@ void K_drawKartHUD(void) } } } + + K_DrawWaypointDebugger(); } //} diff --git a/src/p_saveg.c b/src/p_saveg.c index 7d2e9a307..d8f4351a4 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -276,6 +276,8 @@ static void P_NetArchivePlayers(void) WRITEINT16(save_p, players[i].lturn_max[j]); WRITEINT16(save_p, players[i].rturn_max[j]); } + + WRITEUINT32(save_p, players[i].distancetofinish); } } @@ -444,6 +446,8 @@ static void P_NetUnArchivePlayers(void) players[i].lturn_max[j] = READINT16(save_p); players[i].rturn_max[j] = READINT16(save_p); } + + players[i].distancetofinish = READUINT32(save_p); } } From 7f48bfc19a0c26ad8dd975a3b7473ac628012e41 Mon Sep 17 00:00:00 2001 From: Sryder Date: Sat, 15 Jun 2019 13:11:45 +0100 Subject: [PATCH 021/105] Slight refactor of getting distance to finish line for players. --- src/k_kart.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 4cb1f5995..03981a59c 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -5673,8 +5673,9 @@ static void K_KartDrift(player_t *player, boolean onground) player->kartstuff[k_brakedrift] = 0; } -static void K_UpdateDistanceFromFinishLine(player_t *player) +static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) { + waypoint_t *bestwaypoint = NULL; if ((player != NULL) && (player->mo != NULL)) { mobj_t *wpmobj; @@ -5682,8 +5683,6 @@ static void K_UpdateDistanceFromFinishLine(player_t *player) fixed_t wpdist = INT32_MAX; fixed_t closestdist = INT32_MAX; waypoint_t *waypoint = NULL; - waypoint_t *bestwaypoint = NULL; - waypoint_t *finishline = K_GetFinishLineWaypoint(); // Find the closest waypoint mobj to the player for (wpmobj = waypointcap; wpmobj; wpmobj = wpmobj->tracer) @@ -5702,7 +5701,7 @@ static void K_UpdateDistanceFromFinishLine(player_t *player) bestwaypoint = waypoint; // check the waypoint's location in relation to the player - // If it's generally in front, it's fine, otherwise, use the best next waypoint. + // If it's generally in front, it's fine, otherwise, use the best next/previous waypoint. if (waypoint != NULL) { angle_t playerangle = player->mo->angle; @@ -5765,6 +5764,17 @@ static void K_UpdateDistanceFromFinishLine(player_t *player) } } } + } + + return bestwaypoint; +} + +static void K_UpdateDistanceFromFinishLine(player_t *player) +{ + if ((player != NULL) && (player->mo != NULL)) + { + waypoint_t *bestwaypoint = K_GetPlayerNextWaypoint(player); + waypoint_t *finishline = K_GetFinishLineWaypoint(); // bestwaypoint is now the waypoint that is in front of us if ((bestwaypoint != NULL) && (finishline != NULL)) From d8f81ab4b9333724a175b97f8bc7dc61d5315e7b Mon Sep 17 00:00:00 2001 From: Sryder Date: Sat, 15 Jun 2019 15:47:23 +0100 Subject: [PATCH 022/105] Add function header comments to my new functions. --- src/k_kart.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/k_kart.c b/src/k_kart.c index 03981a59c..024431478 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -5673,6 +5673,18 @@ static void K_KartDrift(player_t *player, boolean onground) player->kartstuff[k_brakedrift] = 0; } +/*-------------------------------------------------- + static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) + + Gets the next waypoint of a player, by finding their closest waypoint, then checking which of itself and next or + previous waypoints are infront of the player. + + Input Arguments:- + player - The player the next waypoint is being found for + + Return:- + The waypoint that is the player's next waypoint +--------------------------------------------------*/ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) { waypoint_t *bestwaypoint = NULL; @@ -5769,6 +5781,17 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) return bestwaypoint; } +/*-------------------------------------------------- + static void K_UpdateDistanceFromFinishLine(player_t *player) + + Updates the distance a player has to the finish line. + + Input Arguments:- + player - The player the distance is being updated for + + Return:- + None +--------------------------------------------------*/ static void K_UpdateDistanceFromFinishLine(player_t *player) { if ((player != NULL) && (player->mo != NULL)) From 5ad7db9d6418712be9ad4a02bbe58defc723ce27 Mon Sep 17 00:00:00 2001 From: Sryder Date: Sat, 15 Jun 2019 15:50:04 +0100 Subject: [PATCH 023/105] Revert to needing to touch every checkpoint to complete a lap. --- src/p_spec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/p_spec.c b/src/p_spec.c index a0b2f8626..de7f37f0d 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -4225,12 +4225,12 @@ DoneSection2: case 10: // Finish Line // SRB2kart - 150117 - if (G_RaceGametype() && (player->starpostnum >= (numstarposts - (numstarposts/2)) || player->exiting)) + if (G_RaceGametype() && ((player->starpostnum == numstarposts) || player->exiting)) player->kartstuff[k_starpostwp] = player->kartstuff[k_waypoint] = 0; // if (G_RaceGametype() && !player->exiting) { - if (player->starpostnum >= (numstarposts - (numstarposts/2))) // srb2kart: must have touched *enough* starposts (was originally "(player->starpostnum == numstarposts)") + if (player->starpostnum == numstarposts) { UINT8 nump = 0; From 530214aa873cf06e0ea9a0cabc4ba11c6254cb4c Mon Sep 17 00:00:00 2001 From: Sryder Date: Sat, 15 Jun 2019 17:04:16 +0100 Subject: [PATCH 024/105] Remove now unused waypoint player variables. --- src/d_player.h | 4 ---- src/g_game.c | 5 ----- src/k_kart.c | 5 ++--- src/p_inter.c | 10 ++-------- src/p_mobj.c | 2 -- src/p_spec.c | 2 -- 6 files changed, 4 insertions(+), 24 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index 388b578a0..cdc4fed30 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -240,10 +240,6 @@ typedef enum k_position, // Used for Kart positions, mostly for deterministic stuff k_oldposition, // Used for taunting when you pass someone k_positiondelay, // Used for position number, so it can grow when passing/being passed - k_prevcheck, // Previous checkpoint distance; for p_user.c (was "pw_pcd") - k_nextcheck, // Next checkpoint distance; for p_user.c (was "pw_ncd") - k_waypoint, // Waypoints. - k_starpostwp, // Temporarily stores player waypoint for... some reason. Used when respawning and finishing. k_starpostflip, // the last starpost we hit requires flipping? k_respawn, // Timer for the DEZ laser respawn effect k_dropdash, // Charge up for respawn Drop Dash diff --git a/src/g_game.c b/src/g_game.c index 72bbe6f69..297f4a198 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2571,7 +2571,6 @@ void G_PlayerReborn(INT32 player) SINT8 pity; // SRB2kart - INT32 starpostwp; INT32 itemtype; INT32 itemamount; INT32 itemroulette; @@ -2635,12 +2634,9 @@ void G_PlayerReborn(INT32 player) rings = (G_BattleGametype() ? 0 : 5); comebackpoints = 0; wanted = 0; - starpostwp = 0; } else { - starpostwp = players[player].kartstuff[k_starpostwp]; - itemroulette = (players[player].kartstuff[k_itemroulette] > 0 ? 1 : 0); roulettetype = players[player].kartstuff[k_roulettetype]; @@ -2707,7 +2703,6 @@ void G_PlayerReborn(INT32 player) p->pity = pity; // SRB2kart - p->kartstuff[k_starpostwp] = starpostwp; // TODO: get these out of kartstuff, it causes desync (Does it...?) p->kartstuff[k_itemroulette] = itemroulette; p->kartstuff[k_roulettetype] = roulettetype; p->kartstuff[k_itemtype] = itemtype; diff --git a/src/k_kart.c b/src/k_kart.c index 024431478..f0bc7149b 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -9788,11 +9788,10 @@ static void K_drawCheckpointDebugger(void) if (stplyr != &players[displayplayers[0]]) // only for p1 return; - if (stplyr->starpostnum >= (numstarposts - (numstarposts/2))) + if (stplyr->starpostnum == numstarposts) V_DrawString(8, 184, 0, va("Checkpoint: %d / %d (Can finish)", stplyr->starpostnum, numstarposts)); else - V_DrawString(8, 184, 0, va("Checkpoint: %d / %d (Skip: %d)", stplyr->starpostnum, numstarposts, ((numstarposts/2) + stplyr->starpostnum))); - V_DrawString(8, 192, 0, va("Waypoint dist: Prev %d, Next %d", stplyr->kartstuff[k_prevcheck], stplyr->kartstuff[k_nextcheck])); + V_DrawString(8, 184, 0, va("Checkpoint: %d / %d", stplyr->starpostnum, numstarposts)); } static void K_DrawWaypointDebugger(void) diff --git a/src/p_inter.c b/src/p_inter.c index de14b3db9..4ea169a83 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1461,12 +1461,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) case MT_STARPOST: if (player->bot) return; - // SRB2kart - 150117 - if (player->exiting) //STOP MESSING UP MY STATS FASDFASDF - { - player->kartstuff[k_starpostwp] = player->kartstuff[k_waypoint]; - return; - } // // SRB2kart: make sure the player will have enough checkpoints to touch if (circuitmap && special->health >= ((numstarposts/2) + player->starpostnum)) @@ -3306,12 +3300,12 @@ void P_PlayerRingBurst(player_t *player, INT32 num_rings) // 20 is the ring cap in kart if (num_rings > 20) - num_rings = 20; + num_rings = 20; else if (num_rings <= 0) return; // Cap the maximum loss automatically to 2 in ring debt - if (player->kartstuff[k_rings] <= 0 && num_rings > 2) + if (player->kartstuff[k_rings] <= 0 && num_rings > 2) num_rings = 2; P_GivePlayerRings(player, -num_rings); diff --git a/src/p_mobj.c b/src/p_mobj.c index ffac8e678..d211bbc11 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -11205,8 +11205,6 @@ void P_MovePlayerToStarpost(INT32 playernum) mobj->angle = p->starpostangle; - p->kartstuff[k_waypoint] = p->kartstuff[k_starpostwp]; // SRB2kart - P_AfterPlayerSpawn(playernum); //if (!(netgame || multiplayer)) diff --git a/src/p_spec.c b/src/p_spec.c index de7f37f0d..209aa1a18 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -4225,8 +4225,6 @@ DoneSection2: case 10: // Finish Line // SRB2kart - 150117 - if (G_RaceGametype() && ((player->starpostnum == numstarposts) || player->exiting)) - player->kartstuff[k_starpostwp] = player->kartstuff[k_waypoint] = 0; // if (G_RaceGametype() && !player->exiting) { From 49a8b0ac388e20a9a3532b4c35530f83499dcc9e Mon Sep 17 00:00:00 2001 From: Sryder Date: Sun, 16 Jun 2019 17:58:28 +0100 Subject: [PATCH 025/105] Add player's nextwaypoint to the player struct Network synchronised(?) nextwaypoint in player struct Make the waypointheap actually a heap and not allocate memory for every individual waypoint. No need to store id in the waypoint struct, since it can be gotten from the waypointheap now. --- src/d_player.h | 4 ++ src/dehacked.c | 4 -- src/k_kart.c | 12 +++-- src/k_waypoint.c | 126 ++++++++++++++++++++++------------------------- src/k_waypoint.h | 30 ++++++++++- src/p_saveg.c | 11 +++++ 6 files changed, 111 insertions(+), 76 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index cdc4fed30..1eb63770c 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -29,6 +29,9 @@ // as commands per game tick. #include "d_ticcmd.h" +// the player struct stores a waypoint for racing +#include "k_waypoint.h" + // Extra abilities/settings for skins (combinable stuff) typedef enum { @@ -429,6 +432,7 @@ typedef struct player_s INT16 lturn_max[MAXPREDICTTICS]; // What's the expected turn value for full-left for a number of frames back (to account for netgame latency)? INT16 rturn_max[MAXPREDICTTICS]; // Ditto but for full-right UINT32 distancetofinish; + waypoint_t *nextwaypoint; // Bit flags. // See pflags_t, above. diff --git a/src/dehacked.c b/src/dehacked.c index be45f3f0f..2162bcbf5 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -8392,10 +8392,6 @@ static const char *const KARTSTUFF_LIST[] = { "POSITION", "OLDPOSITION", "POSITIONDELAY", - "PREVCHECK", - "NEXTCHECK", - "WAYPOINT", - "STARPOSTWP", "STARPOSTFLIP", "RESPAWN", "DROPDASH", diff --git a/src/k_kart.c b/src/k_kart.c index f0bc7149b..07d44909b 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -5796,11 +5796,11 @@ static void K_UpdateDistanceFromFinishLine(player_t *player) { if ((player != NULL) && (player->mo != NULL)) { - waypoint_t *bestwaypoint = K_GetPlayerNextWaypoint(player); waypoint_t *finishline = K_GetFinishLineWaypoint(); + player->nextwaypoint = K_GetPlayerNextWaypoint(player); // bestwaypoint is now the waypoint that is in front of us - if ((bestwaypoint != NULL) && (finishline != NULL)) + if ((player->nextwaypoint != NULL) && (finishline != NULL)) { const boolean useshortcuts = false; const boolean huntbackwards = false; @@ -5808,7 +5808,7 @@ static void K_UpdateDistanceFromFinishLine(player_t *player) path_t pathtofinish = {}; pathfindsuccess = - K_PathfindToWaypoint(bestwaypoint, finishline, &pathtofinish, useshortcuts, huntbackwards); + K_PathfindToWaypoint(player->nextwaypoint, finishline, &pathtofinish, useshortcuts, huntbackwards); // Update the player's distance to the finish line if a path was found. // Using shortcuts won't find a path, so the distance won't be updated until the player gets back on track @@ -5817,8 +5817,10 @@ static void K_UpdateDistanceFromFinishLine(player_t *player) // Add euclidean distance to the next waypoint to the distancetofinish UINT32 adddist; fixed_t disttowaypoint = - P_AproxDistance(player->mo->x - bestwaypoint->mobj->x, player->mo->y - bestwaypoint->mobj->y); - disttowaypoint = P_AproxDistance(disttowaypoint, player->mo->z - bestwaypoint->mobj->z); + P_AproxDistance( + player->mo->x - player->nextwaypoint->mobj->x, + player->mo->y - player->nextwaypoint->mobj->y); + disttowaypoint = P_AproxDistance(disttowaypoint, player->mo->z - player->nextwaypoint->mobj->z); adddist = ((UINT32)disttowaypoint) >> FRACBITS; diff --git a/src/k_waypoint.c b/src/k_waypoint.c index 79017b4dd..570fa8a51 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -14,7 +14,7 @@ static const size_t OPENSET_BASE_SIZE = 16U; static const size_t CLOSEDSET_BASE_SIZE = 256U; static const size_t NODESARRAY_BASE_SIZE = 256U; -static waypoint_t **waypointheap = NULL; +static waypoint_t *waypointheap = NULL; static waypoint_t *firstwaypoint = NULL; static waypoint_t *finishline = NULL; @@ -160,6 +160,49 @@ INT32 K_GetWaypointID(waypoint_t *waypoint) return waypointid; } + +/*-------------------------------------------------- + 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; +} + /*-------------------------------------------------- void K_DebugWaypointsSpawnLine(waypoint_t *const waypoint1, waypoint_t *const waypoint2) @@ -940,11 +983,12 @@ searchwaypointstart: } else { + size_t waypointindex = K_GetWaypointHeapIndex(waypoint); // If we've already visited this waypoint, we've already checked the next waypoints, no point continuing - if (visitedarray[waypoint->id] != true) + if ((waypointindex != SIZE_MAX) && (visitedarray[waypointindex] != true)) { // Mark this waypoint as being visited - visitedarray[waypoint->id] = true; + visitedarray[waypointindex] = true; if (conditionalfunc(waypoint, condition) == true) { @@ -1105,9 +1149,9 @@ static waypoint_t *K_SearchWaypointHeap( // waypoints setup in the heap while numwaypointmobjs ends up being the capacity for (i = 0; i < numwaypoints; i++) { - if (conditionalfunc(waypointheap[i], condition) == true) + if (conditionalfunc(&waypointheap[i], condition) == true) { - foundwaypoint = waypointheap[i]; + foundwaypoint = &waypointheap[i]; break; } } @@ -1188,49 +1232,6 @@ static void K_AddPrevToWaypoint(waypoint_t *const waypoint, waypoint_t *const pr } } -/*-------------------------------------------------- - static waypoint_t *K_NewWaypoint(mobj_t *mobj) - - Creates memory for a new waypoint - - Input Arguments:- - mobj - The map object that this waypoint is represented by - - Return:- - Pointer to waypoint_t for the rest of the waypoint data to be placed into ---------------------------------------------------*/ -static waypoint_t *K_NewWaypoint(mobj_t *const mobj) -{ - waypoint_t *waypoint = NULL; - - // Error conditions - if (mobj == NULL || P_MobjWasRemoved(mobj)) - { - CONS_Debug(DBG_SETUP, "NULL mobj in K_NewWaypoint.\n"); - } - else if (waypointheap == NULL) - { - CONS_Debug(DBG_SETUP, "NULL waypointheap in K_NewWaypoint.\n"); - } - else - { - // Each made waypoint is placed directly into the waypoint heap to be able to search it during creation - waypointheap[numwaypoints] = Z_Calloc(sizeof(waypoint_t), PU_LEVEL, NULL); - waypoint = waypointheap[numwaypoints]; - // numwaypoints is incremented later when waypoint->id is set - - if (waypoint == NULL) - { - I_Error("K_NewWaypoint: Failed to allocate memory for waypoint."); - } - - P_SetTarget(&waypoint->mobj, mobj); - waypoint->id = numwaypoints++; - } - - return waypoint; -} - /*-------------------------------------------------- static waypoint_t *K_MakeWaypoint(mobj_t *const mobj) @@ -1257,9 +1258,17 @@ static waypoint_t *K_MakeWaypoint(mobj_t *const mobj) { CONS_Debug(DBG_SETUP, "K_MakeWaypoint called with NULL waypointcap.\n"); } + else if (numwaypoints >= numwaypointmobjs) + { + CONS_Debug(DBG_SETUP, "K_MakeWaypoint called with max waypoint capacity reached.\n"); + } else { - madewaypoint = K_NewWaypoint(mobj); + // numwaypoints is incremented later in K_SetupWaypoint + madewaypoint = &waypointheap[numwaypoints]; + numwaypoints++; + + P_SetTarget(&madewaypoint->mobj, mobj); // 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) @@ -1340,8 +1349,7 @@ static waypoint_t *K_SetupWaypoint(mobj_t *const mobj) if (thiswaypoint != NULL) { - // Temporarily set the first waypoint to be the first waypoint we setup, this is so that we can search - // through them as they're made and added to the linked list + // Set the first waypoint if it isn't already if (firstwaypoint == NULL) { firstwaypoint = thiswaypoint; @@ -1447,7 +1455,7 @@ static boolean K_AllocateWaypointHeap(void) { // 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); + waypointheap = Z_Calloc(numwaypointmobjs * sizeof(waypoint_t), PU_LEVEL, NULL); if (waypointheap == NULL) { @@ -1478,20 +1486,6 @@ static void K_FreeWaypoints(void) { if (waypointheap != NULL) { - // Free each waypoint if it's not already - UINT32 i; - for (i = 0; i < numwaypoints; i++) - { - if (waypointheap[i] != NULL) - { - Z_Free(waypointheap[i]); - } - else - { - CONS_Debug(DBG_SETUP, "NULL waypoint %d attempted to be freed.\n", i); - } - } - // Free the waypointheap Z_Free(waypointheap); } diff --git a/src/k_waypoint.h b/src/k_waypoint.h index 7e5dffc1a..35f21570a 100644 --- a/src/k_waypoint.h +++ b/src/k_waypoint.h @@ -8,7 +8,6 @@ typedef struct waypoint_s { mobj_t *mobj; - size_t id; struct waypoint_s **nextwaypoints; struct waypoint_s **prevwaypoints; UINT32 *nextwaypointdistances; @@ -200,9 +199,38 @@ waypoint_t *K_SearchWaypointGraphForMobj(mobj_t * const mobj); waypoint_t *K_SearchWaypointHeapForMobj(mobj_t * const mobj); + // NOT AVAILABLE FOR LUA +/*-------------------------------------------------- + size_t K_GetWaypointHeapIndex(waypoint_t *waypoint) + + Returns the waypoint's index in the waypoint heap. + + Input Arguments:- + waypoint - The waypoint to return the index of. + + Return:- + The waypoint heap index, SIZE_MAX if there's an issue with the waypoint. +--------------------------------------------------*/ +size_t K_GetWaypointHeapIndex(waypoint_t *waypoint); + + +/*-------------------------------------------------- + waypoint_t *K_GetWaypointFromIndex(size_t waypointindex) + + Returns the waypoint from an index to the heap. + + Input Arguments:- + waypointindex - The index of the waypoint to get + + Return:- + The waypoint from the heap index, NULL if the index if too high +--------------------------------------------------*/ +waypoint_t *K_GetWaypointFromIndex(size_t waypointindex); + + /*-------------------------------------------------- void K_DebugWaypointsVisualise() diff --git a/src/p_saveg.c b/src/p_saveg.c index d8f4351a4..1d7dbeef4 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -278,6 +278,7 @@ static void P_NetArchivePlayers(void) } WRITEUINT32(save_p, players[i].distancetofinish); + WRITEUINT32(save_p, K_GetWaypointHeapIndex(players[i].nextwaypoint)); } } @@ -448,6 +449,7 @@ static void P_NetUnArchivePlayers(void) } players[i].distancetofinish = READUINT32(save_p); + players[i].nextwaypoint = (waypoint_t *)(size_t)READUINT32(save_p); } } @@ -3062,6 +3064,15 @@ static void P_RelinkPointers(void) if (!P_SetTarget(&mobj->player->awayviewmobj, P_FindNewPosition(temp))) CONS_Debug(DBG_GAMELOGIC, "awayviewmobj not found on %d\n", mobj->type); } + if (mobj->player && mobj->player->nextwaypoint) + { + temp = (UINT32)(size_t)mobj->player->nextwaypoint; + mobj->player->nextwaypoint = K_GetWaypointFromIndex(temp); + if (mobj->player->nextwaypoint == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "nextwaypoint not found on %d\n", mobj->type); + } + } } } } From 6bcc283d3b47a93b7165dcbba78d1870d47fcfba Mon Sep 17 00:00:00 2001 From: Sryder Date: Sun, 16 Jun 2019 22:03:26 +0100 Subject: [PATCH 026/105] Reimplement system for specials that activate when crossing a linedef from DOOM Used for the finish line, crossing it the correct way increments the lap count, the wrong way decrements it Remove usability of the sector special for the finish line Undo another check of numstarposts to force all of them to need passing to complete the stage player laps start from 0 now, it goes to lap 1 when you initially cross the start line. --- src/k_kart.c | 18 +-- src/p_inter.c | 2 +- src/p_map.c | 151 +++++++++++++++++++++++++ src/p_spec.c | 293 ++++++++++++++++++++++++++++++------------------- src/p_spec.h | 2 + src/p_user.c | 2 +- src/st_stuff.c | 2 +- 7 files changed, 345 insertions(+), 125 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 07d44909b..33f985746 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6959,7 +6959,7 @@ void K_CheckSpectateStatus(void) continue; if (leveltime > (starttime + 20*TICRATE)) // DON'T allow if the match is 20 seconds in return; - if (G_RaceGametype() && players[i].laps) // DON'T allow if the race is at 2 laps + if (G_RaceGametype() && players[i].laps >= 2) // DON'T allow if the race is at 2 laps return; continue; } @@ -8197,7 +8197,7 @@ static void K_DrawKartPositionNum(INT32 num) { if (win) // 1st place winner? You get rainbows!! localpatch = kp_winnernum[(leveltime % (NUMWINFRAMES*3)) / 3]; - else if (stplyr->laps+1 >= cv_numlaps.value || stplyr->exiting) // Check for the final lap, or won + else if (stplyr->laps >= cv_numlaps.value || stplyr->exiting) // Check for the final lap, or won { // Alternate frame every three frames switch (leveltime % 9) @@ -8555,8 +8555,8 @@ static void K_drawKartLapsAndRings(void) if (cv_numlaps.value >= 10) { UINT8 ln[2]; - ln[0] = ((abs(stplyr->laps+1) / 10) % 10); - ln[1] = (abs(stplyr->laps+1) % 10); + ln[0] = ((abs(stplyr->laps) / 10) % 10); + ln[1] = (abs(stplyr->laps) % 10); V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, pingnum[ln[0]]); V_DrawScaledPatch(fx+17, fy, V_HUDTRANS|splitflags, pingnum[ln[1]]); @@ -8569,7 +8569,7 @@ static void K_drawKartLapsAndRings(void) } else { - V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, kp_facenum[(stplyr->laps+1) % 10]); + V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, kp_facenum[(stplyr->laps) % 10]); V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, kp_facenum[(cv_numlaps.value) % 10]); } @@ -8611,7 +8611,7 @@ static void K_drawKartLapsAndRings(void) if (stplyr->exiting) V_DrawKartString(LAPS_X+33, LAPS_Y+3, V_HUDTRANS|splitflags, "FIN"); else - V_DrawKartString(LAPS_X+33, LAPS_Y+3, V_HUDTRANS|splitflags, va("%d/%d", stplyr->laps+1, cv_numlaps.value)); + V_DrawKartString(LAPS_X+33, LAPS_Y+3, V_HUDTRANS|splitflags, va("%d/%d", stplyr->laps, cv_numlaps.value)); // Rings if (netgame) @@ -9617,7 +9617,7 @@ static void K_drawLapStartAnim(void) kp_lapanim_hand[stplyr->karthud[khud_laphand]-1], NULL); } - if (stplyr->laps == (UINT8)(cv_numlaps.value - 1)) + if (stplyr->laps == (UINT8)(cv_numlaps.value)) { V_DrawFixedPatch((62 - (32*max(0, progress-76)))*FRACUNIT, // 27 30*FRACUNIT, // 24 @@ -9644,14 +9644,14 @@ static void K_drawLapStartAnim(void) V_DrawFixedPatch((188 + (32*max(0, progress-76)))*FRACUNIT, // 194 30*FRACUNIT, // 24 FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, - kp_lapanim_number[(((UINT32)stplyr->laps+1) / 10)][min(progress/2-8, 2)], NULL); + kp_lapanim_number[(((UINT32)stplyr->laps) / 10)][min(progress/2-8, 2)], NULL); if (progress/2-10 >= 0) { V_DrawFixedPatch((208 + (32*max(0, progress-76)))*FRACUNIT, // 221 30*FRACUNIT, // 24 FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, - kp_lapanim_number[(((UINT32)stplyr->laps+1) % 10)][min(progress/2-10, 2)], NULL); + kp_lapanim_number[(((UINT32)stplyr->laps) % 10)][min(progress/2-10, 2)], NULL); } } } diff --git a/src/p_inter.c b/src/p_inter.c index 4ea169a83..2d4571e11 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1463,7 +1463,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) return; // // SRB2kart: make sure the player will have enough checkpoints to touch - if (circuitmap && special->health >= ((numstarposts/2) + player->starpostnum)) + if (circuitmap && special->health >= player->starpostnum) { // blatant reuse of a variable that's normally unused in circuit if (!player->tossdelay) diff --git a/src/p_map.c b/src/p_map.c index d99105005..1d1b03e57 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -68,6 +68,20 @@ line_t *ceilingline; // that is, for any line which is 'solid' line_t *blockingline; +// Mostly re-ported from DOOM Legacy +// Keep track of special lines as they are hit, process them when the move is valid +static size_t *spechit = NULL; +static size_t spechit_max = 0U; +static size_t numspechit = 0U; + +// Need a intermediate buffer for P_TryMove because it performs multiple moves +// the lines put into spechit will be moved into here after each checkposition, +// then and duplicates will be removed before processing +static size_t *spechitint = NULL; +static size_t spechitint_max = 0U; +static size_t numspechitint = 0U; + + msecnode_t *sector_list = NULL; mprecipsecnode_t *precipsector_list = NULL; camera_t *mapcampointer; @@ -81,6 +95,8 @@ camera_t *mapcampointer; // boolean P_TeleportMove(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z) { + numspechit = 0U; + // the move is ok, // so link the thing into its new position P_UnsetThingPosition(thing); @@ -113,6 +129,100 @@ boolean P_TeleportMove(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z) // MOVEMENT ITERATOR FUNCTIONS // ========================================================================= +// For our intermediate buffer, remove any duplicate entries by adding each one to +// a temprary buffer if it's not already in there, copy the temporary buffer back over the intermediate afterwards +static void spechitint_removedups(void) +{ + // Only needs to be run if there's more than 1 line crossed + if (numspechitint > 1U) + { + boolean valueintemp = false; + size_t i = 0U, j = 0U; + size_t numspechittemp = 0U; + size_t *spechittemp = Z_Calloc(numspechitint * sizeof(size_t), PU_STATIC, NULL); + + // Fill the hashtable + for (i = 0U; i < numspechitint; i++) + { + valueintemp = false; + for (j = 0; j < numspechittemp; j++) + { + if (spechitint[i] == spechittemp[j]) + { + valueintemp = true; + break; + } + } + + if (!valueintemp) + { + spechittemp[numspechittemp] = spechitint[i]; + numspechittemp++; + } + } + + // The hash table now IS the result we want to send back + // easiest way to handle this is a memcpy + if (numspechittemp != numspechitint) + { + memcpy(spechitint, spechittemp, numspechittemp * sizeof(size_t)); + numspechitint = numspechittemp; + } + + Z_Free(spechittemp); + } +} + +// copy the contents of spechit into the end of spechitint +static void spechitint_copyinto(void) +{ + if (numspechit > 0U) + { + if (numspechitint + numspechit >= spechitint_max) + { + spechitint_max = spechitint_max + numspechit; + spechitint = Z_Realloc(spechitint, spechitint_max * sizeof(size_t), PU_STATIC, NULL); + } + + memcpy(&spechitint[numspechitint], spechit, numspechit * sizeof(size_t)); + numspechitint += numspechit; + } +} + +static void add_spechit(line_t *ld) +{ + if (numspechit >= spechit_max) + { + spechit_max = spechit_max ? spechit_max * 2U : 16U; + spechit = Z_Realloc(spechit, spechit_max * sizeof(size_t), PU_STATIC, NULL); + } + + spechit[numspechit] = ld - lines; + numspechit++; +} + +static boolean P_SpecialIsLinedefCrossType(UINT16 ldspecial) +{ + boolean linedefcrossspecial = false; + + switch (ldspecial) + { + case 2001: // Finish line + { + linedefcrossspecial = true; + } + break; + + default: + { + linedefcrossspecial = false; + } + break; + } + + return linedefcrossspecial; +} + boolean P_DoSpring(mobj_t *spring, mobj_t *object) { //INT32 pflags; @@ -1994,6 +2104,12 @@ if (tmthing->flags & MF_PAPERCOLLISION) // Caution! Turning whilst up against a if (lowfloor < tmdropoffz) tmdropoffz = lowfloor; + // we've crossed the line + if (P_SpecialIsLinedefCrossType(ld->special)) + { + add_spechit(ld); + } + return true; } @@ -2268,6 +2384,9 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y) validcount++; + // reset special lines + numspechit = 0U; + if (tmflags & MF_NOCLIP) return true; @@ -2707,6 +2826,8 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) { fixed_t tryx = thing->x; fixed_t tryy = thing->y; + fixed_t oldx = tryx; + fixed_t oldy = tryy; fixed_t radius = thing->radius; fixed_t thingtop = thing->z + thing->height; #ifdef ESLOPE @@ -2714,6 +2835,9 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) #endif floatok = false; + // reset this to 0 at the start of each trymove call as it's only used here + numspechitint = 0U; + if (radius < MAXRADIUS/2) radius = MAXRADIUS/2; @@ -2739,6 +2863,9 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) if (!P_CheckPosition(thing, tryx, tryy)) return false; // solid wall or thing + // copy into the spechitint buffer from spechit + spechitint_copyinto(); + if (!(thing->flags & MF_NOCLIP)) { //All things are affected by their scale. @@ -2915,6 +3042,30 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) thing->eflags |= MFE_ONGROUND; P_SetThingPosition(thing); + + // remove any duplicates that may be in spechitint + spechitint_removedups(); + + // handle any of the special lines that were crossed + if (!(thing->flags & (MF_NOCLIP))) + { + line_t *ld = NULL; + INT32 side = 0, oldside = 0; + while (numspechitint--) + { + ld = &lines[spechitint[numspechitint]]; + side = P_PointOnLineSide(thing->x, thing->y, ld); + oldside = P_PointOnLineSide(oldx, oldy, ld); + if (side != oldside) + { + if (ld->special) + { + P_CrossSpecialLine(ld, oldside, thing); + } + } + } + } + return true; } diff --git a/src/p_spec.c b/src/p_spec.c index 209aa1a18..eb1190007 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -2200,6 +2200,178 @@ void P_SwitchWeather(INT32 weathernum) } } +// Passed over the finish line forwards +static void K_HandleLapIncrement(player_t *player) +{ + if (player) + { + if ((player->starpostnum == numstarposts) || (player->laps == 0)) + { + UINT8 i = 0; + UINT8 nump = 0; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator) + continue; + nump++; + } + + player->laps++; + + // Set up lap animation vars + if (player->laps > 1) + { + if (nump > 1) + { + if (K_IsPlayerLosing(player)) + player->karthud[khud_laphand] = 3; + else + { + if (nump > 2 && player->kartstuff[k_position] == 1) // 1st place in 1v1 uses thumbs up + player->karthud[khud_laphand] = 1; + else + player->karthud[khud_laphand] = 2; + } + } + else + player->karthud[khud_laphand] = 0; // No hands in FREE PLAY + + player->karthud[khud_lapanimation] = 80; + } + + if (netgame && player->laps >= (UINT8)cv_numlaps.value) + CON_LogMessage(va(M_GetText("%s has finished the race.\n"), player_names[player-players])); + + // SRB2Kart: save best lap for record attack + if (player == &players[consoleplayer]) + { + if (curlap < bestlap || bestlap == 0) + bestlap = curlap; + curlap = 0; + } + + player->starposttime = player->realtime; + player->starpostnum = 0; + + if (mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE) + { + // SRB2Kart 281118 + // Save the player's time and position. + player->starpostx = player->mo->x>>FRACBITS; + player->starposty = player->mo->y>>FRACBITS; + player->starpostz = player->mo->floorz>>FRACBITS; + player->kartstuff[k_starpostflip] = player->mo->flags2 & MF2_OBJECTFLIP; // store flipping + player->starpostangle = player->mo->angle; //R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); torn; a momentum-based guess is less likely to be wrong in general, but when it IS wrong, it fucks you over entirely... + } + else + { + // SRB2kart 200117 + // Reset starposts (checkpoints) info + player->starpostangle = player->starpostx = player->starposty = player->starpostz = player->kartstuff[k_starpostflip] = 0; + } + + if (P_IsDisplayPlayer(player)) + { + if (player->laps == (UINT8)(cv_numlaps.value)) // final lap + S_StartSound(NULL, sfx_s3k68); + else if ((player->laps > 1) && (player->laps < (UINT8)(cv_numlaps.value))) // non-final lap + S_StartSound(NULL, sfx_s221); + else if (player->laps > (UINT8)(cv_numlaps.value)) + { + // finished + S_StartSound(NULL, sfx_s3k6a); + } + + } + else + { + if ((player->laps > (UINT8)(cv_numlaps.value)) && (player->kartstuff[k_position] == 1)) + { + // opponent finished + S_StartSound(NULL, sfx_s253); + } + } + + // finished race exit setup + if (player->laps > (unsigned)cv_numlaps.value) + { + P_DoPlayerExit(player); + P_SetupSignExit(player); + } + + //player->starpostangle = player->starposttime = player->starpostnum = 0; + //player->starpostx = player->starposty = player->starpostz = 0; + + // Play the starpost sound for 'consistency' + // S_StartSound(player->mo, sfx_strpst); + + // Figure out how many are playing on the last lap, to prevent spectate griefing + if (!nospectategrief && player->laps > (UINT8)(cv_numlaps.value)) + nospectategrief = nump; + + thwompsactive = true; // Lap 2 effects + } + else if (player->starpostnum) + { + S_StartSound(player->mo, sfx_s26d); + } + } +} + +// player went backwards over the line +static void K_HandleLapDecrement(player_t *player) +{ + if (player) + { + if (player->laps > 0) + { + player->starpostnum = numstarposts; + player->laps--; + } + } +} + +// +// P_CrossSpecialLine - TRIGGER +// Called every time a thing origin is about +// to cross a line with specific specials +// Kart - Only used for the finish line currently +// +void P_CrossSpecialLine(line_t *line, INT32 side, mobj_t *thing) +{ + // only used for the players currently + if (thing && thing->player) + { + player_t *player = thing->player; + switch (line->special) + { + case 2001: // Finish Line + { + if (G_RaceGametype() && !(player->exiting)) + { + if (((line->flags & (ML_NOCLIMB)) && (side == 1)) + || (!(line->flags & (ML_NOCLIMB)) && (side == 0))) // crossed from behind to infront + { + K_HandleLapIncrement(player); + } + else + { + K_HandleLapDecrement(player); + } + } + } + break; + + default: + { + // Do nothing + } + break; + } + } +} + /** Gets an object. * * \param type Object type to look for. @@ -4223,115 +4395,8 @@ DoneSection2: } break; - case 10: // Finish Line - // SRB2kart - 150117 - // - if (G_RaceGametype() && !player->exiting) - { - if (player->starpostnum == numstarposts) - { - UINT8 nump = 0; - - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator) - continue; - nump++; - } - - player->laps++; - - // Set up lap animation vars - if (nump > 1) - { - if (K_IsPlayerLosing(player)) - player->karthud[khud_laphand] = 3; - else - { - if (nump > 2 && player->kartstuff[k_position] == 1) // 1st place in 1v1 uses thumbs up - player->karthud[khud_laphand] = 1; - else - player->karthud[khud_laphand] = 2; - } - } - else - player->karthud[khud_laphand] = 0; // No hands in FREE PLAY - - player->karthud[khud_lapanimation] = 80; - - if (player->pflags & PF_NIGHTSMODE) - player->drillmeter += 48*20; - - if (netgame && player->laps >= (UINT8)cv_numlaps.value) - CON_LogMessage(va(M_GetText("%s has finished the race.\n"), player_names[player-players])); - - // SRB2Kart: save best lap for record attack - if (player == &players[consoleplayer]) - { - if (curlap < bestlap || bestlap == 0) - bestlap = curlap; - curlap = 0; - } - - player->starposttime = player->realtime; - player->starpostnum = 0; - - if (mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE) - { - // SRB2Kart 281118 - // Save the player's time and position. - player->starpostx = player->mo->x>>FRACBITS; - player->starposty = player->mo->y>>FRACBITS; - player->starpostz = player->mo->floorz>>FRACBITS; - player->kartstuff[k_starpostflip] = player->mo->flags2 & MF2_OBJECTFLIP; // store flipping - player->starpostangle = player->mo->angle; //R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); torn; a momentum-based guess is less likely to be wrong in general, but when it IS wrong, it fucks you over entirely... - } - else - { - // SRB2kart 200117 - // Reset starposts (checkpoints) info - player->starpostangle = player->starpostx = player->starposty = player->starpostz = player->kartstuff[k_starpostflip] = 0; - } - - if (P_IsDisplayPlayer(player)) - { - if (player->laps == (UINT8)(cv_numlaps.value - 1)) - S_StartSound(NULL, sfx_s3k68); - else if (player->laps < (UINT8)(cv_numlaps.value - 1)) - S_StartSound(NULL, sfx_s221); - } - - //player->starpostangle = player->starposttime = player->starpostnum = 0; - //player->starpostx = player->starposty = player->starpostz = 0; - - // Play the starpost sound for 'consistency' - // S_StartSound(player->mo, sfx_strpst); - - // Figure out how many are playing on the last lap, to prevent spectate griefing - if (!nospectategrief && player->laps >= (UINT8)(cv_numlaps.value - 1)) - nospectategrief = nump; - - thwompsactive = true; // Lap 2 effects - } - else if (player->starpostnum) - { - // blatant reuse of a variable that's normally unused in circuit - if (!player->tossdelay) - S_StartSound(player->mo, sfx_s26d); - player->tossdelay = 3; - } - - if (player->laps >= (unsigned)cv_numlaps.value) - { - if (P_IsDisplayPlayer(player)) - S_StartSound(NULL, sfx_s3k6a); - else if (player->kartstuff[k_position] == 1) - S_StartSound(NULL, sfx_s253); - - P_DoPlayerExit(player); - P_SetupSignExit(player); - } - } + case 10: // Finish Line (Unused) + // SRB2Kart 20190616 - Is now a linedef type that activates by crossing over it break; case 11: // Rope hang @@ -4884,7 +4949,7 @@ static void P_RunSpecialSectorCheck(player_t *player, sector_t *sector) case 6: // Super Sonic Transform case 8: // Zoom Tube Start case 9: // Zoom Tube End - case 10: // Finish line + case 10: // Finish line (Unused) nofloorneeded = true; break; } @@ -5758,9 +5823,7 @@ void P_SpawnSpecials(INT32 fromnetsave) // Process Section 4 switch(GETSECSPECIAL(sector->special, 4)) { - case 10: // Circuit finish line - if (G_RaceGametype()) - circuitmap = true; + case 10: // Circuit finish line (Unused) break; } } @@ -6701,6 +6764,10 @@ void P_SpawnSpecials(INT32 fromnetsave) case 2000: // Waypoint Parameters break; + case 2001: // Finish Line + if (G_RaceGametype()) + circuitmap = true; + break; default: break; } diff --git a/src/p_spec.h b/src/p_spec.h index b604ac951..2763a34ad 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -56,6 +56,8 @@ INT32 P_FindSpecialLineFromTag(INT16 special, INT16 tag, INT32 start); INT32 P_FindMinSurroundingLight(sector_t *sector, INT32 max); +void P_CrossSpecialLine(line_t *ld, INT32 side, mobj_t *thing); + void P_SetupSignExit(player_t *player); boolean P_IsFlagAtBase(mobjtype_t flag); diff --git a/src/p_user.c b/src/p_user.c index 46478d7d9..275ad1b69 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1253,7 +1253,7 @@ void P_RestoreMusic(player_t *player) #if 0 // Event - Final Lap // Still works for GME, but disabled for consistency - if (G_RaceGametype() && player->laps >= (UINT8)(cv_numlaps.value - 1)) + if (G_RaceGametype() && player->laps >= (UINT8)(cv_numlaps.value)) S_SpeedMusic(1.2f); #endif S_ChangeMusicEx(mapmusname, mapmusflags, true, mapmusposition, 0, 0); diff --git a/src/st_stuff.c b/src/st_stuff.c index e59846aed..c55724359 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -1514,7 +1514,7 @@ static inline void ST_drawRaceHUD(void) // SRB2kart - unused. if (stplyr->exiting) V_DrawString(hudinfo[HUD_LAP].x, STRINGY(hudinfo[HUD_LAP].y), V_YELLOWMAP, "FINISHED!"); else - V_DrawString(hudinfo[HUD_LAP].x, STRINGY(hudinfo[HUD_LAP].y), 0, va("Lap: %u/%d", stplyr->laps+1, cv_numlaps.value)); + V_DrawString(hudinfo[HUD_LAP].x, STRINGY(hudinfo[HUD_LAP].y), 0, va("Lap: %u/%d", stplyr->laps, cv_numlaps.value)); } } */ From 991cef91638c798fd310fa65eeb85ea3f7379096 Mon Sep 17 00:00:00 2001 From: Sryder Date: Sun, 16 Jun 2019 22:15:36 +0100 Subject: [PATCH 027/105] Get distance to first by using the distanceofinish variable for roulette --- src/k_kart.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 33f985746..793fcc2b5 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1088,13 +1088,12 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) for (i = 0; i < MAXPLAYERS; i++) { - if (playeringame[i] && !players[i].spectator && players[i].mo - && players[i].kartstuff[k_position] < player->kartstuff[k_position]) - pdis += P_AproxDistance(P_AproxDistance(players[i].mo->x - player->mo->x, - players[i].mo->y - player->mo->y), - players[i].mo->z - player->mo->z) / mapobjectscale - * (pingame - players[i].kartstuff[k_position]) - / max(1, ((pingame - 1) * (pingame + 1) / 3)); + if (players[i].kartstuff[k_position] == 1) + { + // This player is first! Yay! + pdis = player->distancetofinish - players[i].distancetofinish; + break; + } } if (franticitems) // Frantic items make the distances between everyone artifically higher, for crazier items From fbd72c149b5dc93df57739d4b9376a3882b6c1c1 Mon Sep 17 00:00:00 2001 From: Sryder Date: Sun, 16 Jun 2019 22:25:58 +0100 Subject: [PATCH 028/105] Use correct formatting for size_t in Printf --- src/k_waypoint.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_waypoint.c b/src/k_waypoint.c index 570fa8a51..1cd26e557 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -1526,7 +1526,7 @@ boolean K_SetupWaypointList(void) } else { - CONS_Debug(DBG_SETUP, "Successfully setup %zu waypoints.\n", numwaypoints); + CONS_Debug(DBG_SETUP, "Successfully setup %s waypoints.\n", sizeu1(numwaypoints)); if (finishline == NULL) { CONS_Alert( From d96c90acc8dc78022517dcf8a441d71270ad2762 Mon Sep 17 00:00:00 2001 From: Sryder Date: Sun, 16 Jun 2019 22:43:20 +0100 Subject: [PATCH 029/105] Actually revert the checkpoints properly --- src/p_inter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_inter.c b/src/p_inter.c index 2d4571e11..ceef7accb 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1463,7 +1463,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) return; // // SRB2kart: make sure the player will have enough checkpoints to touch - if (circuitmap && special->health >= player->starpostnum) + if (circuitmap && special->health - player->starpostnum > 1) { // blatant reuse of a variable that's normally unused in circuit if (!player->tossdelay) From b2d5521d34c25a710a56c0c71761d57c076995ce Mon Sep 17 00:00:00 2001 From: Sryder Date: Sun, 16 Jun 2019 23:30:39 +0100 Subject: [PATCH 030/105] Finish Line linedef needs condition flipped for correct crossing of it. --- src/p_spec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/p_spec.c b/src/p_spec.c index eb1190007..8522fdf9b 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -2350,8 +2350,8 @@ void P_CrossSpecialLine(line_t *line, INT32 side, mobj_t *thing) { if (G_RaceGametype() && !(player->exiting)) { - if (((line->flags & (ML_NOCLIMB)) && (side == 1)) - || (!(line->flags & (ML_NOCLIMB)) && (side == 0))) // crossed from behind to infront + if (((line->flags & (ML_NOCLIMB)) && (side == 0)) + || (!(line->flags & (ML_NOCLIMB)) && (side == 1))) // crossed from behind to infront { K_HandleLapIncrement(player); } From 04966f561386355e2db4f0066b492602511ab0d5 Mon Sep 17 00:00:00 2001 From: Sryder Date: Fri, 28 Jun 2019 09:59:19 +0100 Subject: [PATCH 031/105] Add missing function header comments to k_waypoint.c --- src/k_waypoint.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/src/k_waypoint.c b/src/k_waypoint.c index 1cd26e557..0db35aa2a 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -495,6 +495,18 @@ static UINT32 K_DistanceBetweenWaypoints(waypoint_t *const waypoint1, waypoint_t return finaldist; } +/*-------------------------------------------------- + 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; @@ -517,6 +529,18 @@ static void **K_WaypointPathfindGetNext(void *data, size_t *numconnections) 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; @@ -539,6 +563,17 @@ static void **K_WaypointPathfindGetPrev(void *data, size_t *numconnections) 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; @@ -556,6 +591,17 @@ static UINT32 *K_WaypointPathfindGetNextCosts(void* data) 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; @@ -573,6 +619,18 @@ static UINT32 *K_WaypointPathfindGetPrevCosts(void* data) 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; @@ -596,6 +654,18 @@ static UINT32 K_WaypointPathfindGetHeuristic(void *data1, void *data2) 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; @@ -613,6 +683,18 @@ static boolean K_WaypointPathfindTraversableAllEnabled(void *data) 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; From 10adecb4a6d8b3e274536819dfd0189a7d22765b Mon Sep 17 00:00:00 2001 From: Sryder Date: Fri, 28 Jun 2019 21:11:26 +0100 Subject: [PATCH 032/105] Improvements to player finish distances Figure out the circuit length on map load player->distancetofinish is now a total distance on circuit maps some hacky alterations around circuit finish lines to correctly calculate distancetofinish --- src/k_kart.c | 205 ++++++++++++++++++++++++++++++++++++++--------- src/k_waypoint.c | 74 ++++++++++++++++- src/k_waypoint.h | 13 +++ 3 files changed, 252 insertions(+), 40 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 793fcc2b5..68bddc224 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -5673,20 +5673,19 @@ static void K_KartDrift(player_t *player, boolean onground) } /*-------------------------------------------------- - static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) + static waypoint_t *K_GetPlayerClosestWaypoint(player_t *player) - Gets the next waypoint of a player, by finding their closest waypoint, then checking which of itself and next or - previous waypoints are infront of the player. + Gets the closest waypoint of a player. Input Arguments:- - player - The player the next waypoint is being found for + player - The player the closest waypoint is being found for Return:- - The waypoint that is the player's next waypoint + The waypoint that is the player's closest waypoint --------------------------------------------------*/ -static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) +static waypoint_t *K_GetPlayerClosestWaypoint(player_t *player) { - waypoint_t *bestwaypoint = NULL; + waypoint_t *closestwaypoint = NULL; if ((player != NULL) && (player->mo != NULL)) { mobj_t *wpmobj; @@ -5709,23 +5708,62 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) } waypoint = K_SearchWaypointGraphForMobj(closestwpmobj); + closestwaypoint = waypoint; + } + + return closestwaypoint; +} + +/*-------------------------------------------------- + static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) + + Gets the next waypoint of a player, by finding their closest waypoint, then checking which of itself and next or + previous waypoints are infront of the player. + + Input Arguments:- + player - The player the next waypoint is being found for + + Return:- + The waypoint that is the player's next waypoint +--------------------------------------------------*/ +static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) +{ + waypoint_t *bestwaypoint = NULL; + if ((player != NULL) && (player->mo != NULL)) + { + waypoint_t *waypoint = NULL; + + waypoint = K_GetPlayerClosestWaypoint(player); bestwaypoint = waypoint; // check the waypoint's location in relation to the player // If it's generally in front, it's fine, otherwise, use the best next/previous waypoint. + // EXCEPTION: If our closest waypoint is the finishline AND we're facing towards it, don't do this. + // Otherwise it breaks the distance calculations. if (waypoint != NULL) { - angle_t playerangle = player->mo->angle; + boolean finishlinehack = false; + angle_t playerangle = player->mo->angle; angle_t angletowaypoint = R_PointToAngle2(player->mo->x, player->mo->y, waypoint->mobj->x, waypoint->mobj->y); - angle_t angledelta = playerangle - angletowaypoint; + angle_t angledelta = playerangle - angletowaypoint; + if (angledelta > ANGLE_180) { - angledelta = InvAngle(angletowaypoint); + angledelta = InvAngle(angledelta); } - if (angledelta > ANGLE_90) + if (bestwaypoint == K_GetFinishLineWaypoint()) + { + // facing towards the finishline + if (angledelta <= ANGLE_90) + { + finishlinehack = true; + } + } + + if ((angledelta > ANGLE_45) && (finishlinehack == false)) { angle_t nextbestdelta = angledelta; size_t i = 0U; @@ -5780,8 +5818,69 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) return bestwaypoint; } +static boolean K_PlayerCloserToNextWaypoints(waypoint_t *const waypoint, player_t *const player) +{ + boolean nextiscloser = true; + + if ((waypoint != NULL) && (player != NULL) && (player->mo != NULL)) + { + size_t i = 0U; + waypoint_t *currentwpcheck = NULL; + angle_t angletoplayer = ANGLE_MAX; + angle_t currentanglecheck = ANGLE_MAX; + angle_t bestangle = ANGLE_MAX; + + angletoplayer = R_PointToAngle2(waypoint->mobj->x, waypoint->mobj->y, + player->mo->x, player->mo->y); + + for (i = 0U; i < waypoint->numnextwaypoints; i++) + { + currentwpcheck = waypoint->nextwaypoints[i]; + currentanglecheck = R_PointToAngle2( + waypoint->mobj->x, waypoint->mobj->y, currentwpcheck->mobj->x, currentwpcheck->mobj->y); + + // Get delta angle + currentanglecheck = currentanglecheck - angletoplayer; + + if (currentanglecheck > ANGLE_180) + { + currentanglecheck = InvAngle(currentanglecheck); + } + + if (currentanglecheck < bestangle) + { + bestangle = currentanglecheck; + } + } + + for (i = 0U; i < waypoint->numprevwaypoints; i++) + { + currentwpcheck = waypoint->prevwaypoints[i]; + currentanglecheck = R_PointToAngle2( + waypoint->mobj->x, waypoint->mobj->y, currentwpcheck->mobj->x, currentwpcheck->mobj->y); + + // Get delta angle + currentanglecheck = currentanglecheck - angletoplayer; + + if (currentanglecheck > ANGLE_180) + { + currentanglecheck = InvAngle(currentanglecheck); + } + + if (currentanglecheck < bestangle) + { + bestangle = currentanglecheck; + nextiscloser = false; + break; + } + } + } + + return nextiscloser; +} + /*-------------------------------------------------- - static void K_UpdateDistanceFromFinishLine(player_t *player) + static void K_UpdateDistanceFromFinishLine(player_t *const player) Updates the distance a player has to the finish line. @@ -5791,40 +5890,70 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) Return:- None --------------------------------------------------*/ -static void K_UpdateDistanceFromFinishLine(player_t *player) +static void K_UpdateDistanceFromFinishLine(player_t *const player) { if ((player != NULL) && (player->mo != NULL)) { - waypoint_t *finishline = K_GetFinishLineWaypoint(); - player->nextwaypoint = K_GetPlayerNextWaypoint(player); - - // bestwaypoint is now the waypoint that is in front of us - if ((player->nextwaypoint != NULL) && (finishline != NULL)) + if (player->exiting) { - const boolean useshortcuts = false; - const boolean huntbackwards = false; - boolean pathfindsuccess = false; - path_t pathtofinish = {}; + player->nextwaypoint = K_GetFinishLineWaypoint(); + player->distancetofinish = 0U; + } + else + { + waypoint_t *finishline = K_GetFinishLineWaypoint(); + player->nextwaypoint = K_GetPlayerNextWaypoint(player); - pathfindsuccess = - K_PathfindToWaypoint(player->nextwaypoint, finishline, &pathtofinish, useshortcuts, huntbackwards); - - // Update the player's distance to the finish line if a path was found. - // Using shortcuts won't find a path, so the distance won't be updated until the player gets back on track - if (pathfindsuccess == true) + // nextwaypoint is now the waypoint that is in front of us + if ((player->nextwaypoint != NULL) && (finishline != NULL)) { - // Add euclidean distance to the next waypoint to the distancetofinish - UINT32 adddist; - fixed_t disttowaypoint = - P_AproxDistance( - player->mo->x - player->nextwaypoint->mobj->x, - player->mo->y - player->nextwaypoint->mobj->y); - disttowaypoint = P_AproxDistance(disttowaypoint, player->mo->z - player->nextwaypoint->mobj->z); + const boolean useshortcuts = false; + const boolean huntbackwards = false; + boolean pathfindsuccess = false; + path_t pathtofinish = {}; - adddist = ((UINT32)disttowaypoint) >> FRACBITS; + pathfindsuccess = + K_PathfindToWaypoint(player->nextwaypoint, finishline, &pathtofinish, useshortcuts, huntbackwards); - player->distancetofinish = pathtofinish.totaldist + adddist; - Z_Free(pathtofinish.array); + // Update the player's distance to the finish line if a path was found. + // Using shortcuts won't find a path, so distance won't be updated until the player gets back on track + if (pathfindsuccess == true) + { + // Add euclidean distance to the next waypoint to the distancetofinish + UINT32 adddist; + fixed_t disttowaypoint = + P_AproxDistance( + player->mo->x - player->nextwaypoint->mobj->x, + player->mo->y - player->nextwaypoint->mobj->y); + disttowaypoint = P_AproxDistance(disttowaypoint, player->mo->z - player->nextwaypoint->mobj->z); + + adddist = ((UINT32)disttowaypoint) >> FRACBITS; + + player->distancetofinish = pathtofinish.totaldist + adddist; + Z_Free(pathtofinish.array); + + // distancetofinish is currently a flat distance to the finish line, but in order to be fully + // correct we need to add to it the length of the entire circuit multiplied by the number of laps + // left after this one. This will give us the total distance to the finish line, and allow item + // distance calculation to work easily + if ((mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE) == 0U) + { + const UINT8 numfulllapsleft = ((UINT8)cv_numlaps.value - player->laps); + + player->distancetofinish += numfulllapsleft * K_GetCircuitLength(); + + // An additional HACK, to fix looking backwards towards the finish line + // If the player's next waypoint is the finishline and the angle distance from player to + // connectin waypoints implies they're closer to a next waypoint, add a full track distance + if (player->nextwaypoint == finishline) + { + if (K_PlayerCloserToNextWaypoints(player->nextwaypoint, player) == true) + { + player->distancetofinish += K_GetCircuitLength(); + } + } + } + } } } } diff --git a/src/k_waypoint.c b/src/k_waypoint.c index 0db35aa2a..0ef3d9c32 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -4,6 +4,7 @@ #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; @@ -18,6 +19,8 @@ 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; @@ -160,6 +163,15 @@ INT32 K_GetWaypointID(waypoint_t *waypoint) return waypointid; } +/*-------------------------------------------------- + UINT32 K_GetCircuitLength(void) + + See header file for description. +--------------------------------------------------*/ +UINT32 K_GetCircuitLength(void) +{ + return circuitlength; +} /*-------------------------------------------------- size_t K_GetWaypointHeapIndex(waypoint_t *waypoint) @@ -306,6 +318,10 @@ void K_DebugWaypointsVisualise(void) { debugmobj->color = SKINCOLOR_ORANGE; } + else if (waypoint == players[displayplayers[0]].nextwaypoint) + { + debugmobj->color = SKINCOLOR_YELLOW; + } else { debugmobj->color = SKINCOLOR_BLUE; @@ -1267,6 +1283,56 @@ waypoint_t *K_SearchWaypointHeapForMobj(mobj_t *const 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) +{ + if ((firstwaypoint == NULL) || (numwaypoints == 0U)) + { + CONS_Debug(DBG_GAMELOGIC, "K_SetupCircuitLength called with no waypoints.\n"); + } + else if (finishline == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "K_SetupCircuitLength called with no finishline waypoint.\n"); + } + else + { + // 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) @@ -1616,6 +1682,9 @@ boolean K_SetupWaypointList(void) K_GetWaypointID(firstwaypoint)); finishline = firstwaypoint; } + + (void)K_SetupCircuitLength(); + setupsuccessful = true; } } @@ -1634,6 +1703,7 @@ void K_ClearWaypoints(void) waypointheap = NULL; firstwaypoint = NULL; finishline = NULL; - numwaypoints = 0; - numwaypointmobjs = 0; + numwaypoints = 0U; + numwaypointmobjs = 0U; + circuitlength = 0U; } diff --git a/src/k_waypoint.h b/src/k_waypoint.h index 35f21570a..d797d2116 100644 --- a/src/k_waypoint.h +++ b/src/k_waypoint.h @@ -109,6 +109,19 @@ INT32 K_GetWaypointNextID(waypoint_t *waypoint); INT32 K_GetWaypointID(waypoint_t *waypoint); +/*-------------------------------------------------- + UINT32 K_GetCircuitLength(void) + + Returns the circuit length, 0 on sprint maps. + + Input Arguments:- + + Return:- + The circuit length. +--------------------------------------------------*/ +UINT32 K_GetCircuitLength(void); + + /*-------------------------------------------------- boolean K_PathfindToWaypoint( waypoint_t *const sourcewaypoint, From 6eca35aae133eda69f38fb5a8102dcadaba81178 Mon Sep 17 00:00:00 2001 From: Sryder Date: Fri, 28 Jun 2019 22:43:34 +0100 Subject: [PATCH 033/105] SPB Now follows the track. You better watch out, You better watch out, You better watch out! --- src/info.c | 2 +- src/k_kart.c | 46 ++--------------------------------- src/k_waypoint.c | 37 ++++++++++++++++++++++++++++ src/k_waypoint.h | 14 +++++++++++ src/p_enemy.c | 63 +++++++++++++++++++++++++++++++++++++++--------- 5 files changed, 106 insertions(+), 56 deletions(-) diff --git a/src/info.c b/src/info.c index 7fc319043..f4da59d78 100644 --- a/src/info.c +++ b/src/info.c @@ -15870,7 +15870,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_SPB_DEAD, // deathstate S_NULL, // xdeathstate sfx_s3k5d, // deathsound - 64*FRACUNIT, // speed + 96*FRACUNIT, // speed 24*FRACUNIT, // radius 48*FRACUNIT, // height 0, // display offset diff --git a/src/k_kart.c b/src/k_kart.c index 68bddc224..1740aa862 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -5672,48 +5672,6 @@ static void K_KartDrift(player_t *player, boolean onground) player->kartstuff[k_brakedrift] = 0; } -/*-------------------------------------------------- - static waypoint_t *K_GetPlayerClosestWaypoint(player_t *player) - - Gets the closest waypoint of a player. - - Input Arguments:- - player - The player the closest waypoint is being found for - - Return:- - The waypoint that is the player's closest waypoint ---------------------------------------------------*/ -static waypoint_t *K_GetPlayerClosestWaypoint(player_t *player) -{ - waypoint_t *closestwaypoint = NULL; - if ((player != NULL) && (player->mo != NULL)) - { - mobj_t *wpmobj; - mobj_t *closestwpmobj = NULL; - fixed_t wpdist = INT32_MAX; - fixed_t closestdist = INT32_MAX; - waypoint_t *waypoint = NULL; - - // Find the closest waypoint mobj to the player - for (wpmobj = waypointcap; wpmobj; wpmobj = wpmobj->tracer) - { - wpdist = P_AproxDistance(wpmobj->x - player->mo->x, wpmobj->y - player->mo->y); - wpdist = P_AproxDistance(wpdist, wpmobj->z - player->mo->z); - - if (wpdist < closestdist) - { - closestdist = wpdist; - closestwpmobj = wpmobj; - } - } - - waypoint = K_SearchWaypointGraphForMobj(closestwpmobj); - closestwaypoint = waypoint; - } - - return closestwaypoint; -} - /*-------------------------------------------------- static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) @@ -5729,11 +5687,11 @@ static waypoint_t *K_GetPlayerClosestWaypoint(player_t *player) static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) { waypoint_t *bestwaypoint = NULL; - if ((player != NULL) && (player->mo != NULL)) + if ((player != NULL) && (player->mo != NULL) && (P_MobjWasRemoved(player->mo) == false)) { waypoint_t *waypoint = NULL; - waypoint = K_GetPlayerClosestWaypoint(player); + waypoint = K_GetClosestWaypointToMobj(player->mo); bestwaypoint = waypoint; // check the waypoint's location in relation to the player diff --git a/src/k_waypoint.c b/src/k_waypoint.c index 0ef3d9c32..f58d11bf9 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -173,6 +173,43 @@ 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 - checkwaypoint->mobj->x, mobj->y - checkwaypoint->mobj->y); + checkdist = P_AproxDistance(checkdist, mobj->z - checkwaypoint->mobj->z); + + if (checkdist < closestdist) + { + closestwaypoint = checkwaypoint; + closestdist = checkdist; + } + } + } + + return closestwaypoint; +} + /*-------------------------------------------------- size_t K_GetWaypointHeapIndex(waypoint_t *waypoint) diff --git a/src/k_waypoint.h b/src/k_waypoint.h index d797d2116..e0dffdbda 100644 --- a/src/k_waypoint.h +++ b/src/k_waypoint.h @@ -122,6 +122,20 @@ INT32 K_GetWaypointID(waypoint_t *waypoint); UINT32 K_GetCircuitLength(void); +/*-------------------------------------------------- + waypoint_t *K_GetClosestWaypointToMobj(mobj_t *const mobj) + + Returns the closest waypoint to an mobj + + Input Arguments:- + mobj - mobj to get the closest waypoint of. + + Return:- + The closest waypoint to the mobj +--------------------------------------------------*/ +waypoint_t *K_GetClosestWaypointToMobj(mobj_t *const mobj); + + /*-------------------------------------------------- boolean K_PathfindToWaypoint( waypoint_t *const sourcewaypoint, diff --git a/src/p_enemy.c b/src/p_enemy.c index 7baca2adc..2a7a80829 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -24,6 +24,7 @@ #include "i_video.h" #include "lua_hook.h" #include "k_kart.h" // SRB2kart +#include "k_waypoint.h" #ifdef HW3SOUND #include "hardware/hw3sound.h" @@ -8387,6 +8388,20 @@ void A_JawzExplode(mobj_t *actor) return; } +static void SpawnSPBTrailRings(mobj_t *actor) +{ + if (actor != NULL) + { + if (leveltime % 6 == 0) + { + mobj_t *ring = P_SpawnMobj(actor->x - actor->momx, actor->y - actor->momx, + actor->z - actor->momz + (24*mapobjectscale), MT_RING); + ring->threshold = 10; + ring->fuse = 120*TICRATE; + } + } +} + void A_SPBChase(mobj_t *actor) { player_t *player = NULL; @@ -8543,13 +8558,7 @@ void A_SPBChase(mobj_t *actor) actor->momz = FixedMul(zspeed, FINESINE(actor->movedir>>ANGLETOFINESHIFT)); // Spawn a trail of rings behind the SPB! - if (leveltime % 6 == 0) - { - mobj_t *ring = P_SpawnMobj(actor->x - actor->momx, actor->y - actor->momx, - actor->z - actor->momz + (24*mapobjectscale), MT_RING); - ring->threshold = 10; - ring->fuse = 120*TICRATE; - } + SpawnSPBTrailRings(actor); // Red speed lines for when it's gaining on its target. A tell for when you're starting to lose too much speed! if (R_PointToDist2(0, 0, actor->momx, actor->momy) > (actor->tracer->player ? (16*actor->tracer->player->speed)/15 @@ -8610,6 +8619,8 @@ void A_SPBChase(mobj_t *actor) } else // MODE: SEEKING { + waypoint_t *closestwaypoint = NULL; + waypoint_t *nextwaypoint = NULL; actor->lastlook = -1; // Just make sure this is reset if (!player || !player->mo || player->mo->health <= 0 || player->kartstuff[k_respawn]) @@ -8623,16 +8634,43 @@ void A_SPBChase(mobj_t *actor) // Found someone, now get close enough to initiate the slaughter... - // don't hurt players that have nothing to do with this: - actor->flags |= MF_NOCLIPTHING; + // Seeking SPB can now hurt people + actor->flags &= ~MF_NOCLIPTHING; P_SetTarget(&actor->tracer, player->mo); spbplace = bestrank; dist = P_AproxDistance(P_AproxDistance(actor->x-actor->tracer->x, actor->y-actor->tracer->y), actor->z-actor->tracer->z); - hang = R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y); - vang = R_PointToAngle2(0, actor->z, dist, actor->tracer->z); + closestwaypoint = K_GetClosestWaypointToMobj(actor); + if (closestwaypoint != NULL) + { + const boolean huntbackwards = false; + boolean useshortcuts = false; + + // If the player is on a shortcut, use shortcuts. No escape. + if (K_GetWaypointIsShortcut(player->nextwaypoint)) + { + useshortcuts = true; + } + + nextwaypoint = K_GetNextWaypointToDestination( + closestwaypoint, player->nextwaypoint, useshortcuts, huntbackwards); + } + + if (nextwaypoint != NULL) + { + const fixed_t xywaypointdist = P_AproxDistance( + actor->x - nextwaypoint->mobj->x, actor->y - nextwaypoint->mobj->y); + hang = R_PointToAngle2(actor->x, actor->y, nextwaypoint->mobj->x, nextwaypoint->mobj->y); + vang = R_PointToAngle2(0, actor->z, xywaypointdist, nextwaypoint->mobj->z); + } + else + { + // continue straight ahead... Shouldn't happen. + hang = actor->angle; + vang = 0U; + } { // Smoothly rotate horz angle @@ -8670,6 +8708,9 @@ void A_SPBChase(mobj_t *actor) actor->momy = FixedMul(FixedMul(xyspeed, FINESINE(actor->angle>>ANGLETOFINESHIFT)), FINECOSINE(actor->movedir>>ANGLETOFINESHIFT)); actor->momz = FixedMul(zspeed, FINESINE(actor->movedir>>ANGLETOFINESHIFT)); + // Spawn a trail of rings behind the SPB! + SpawnSPBTrailRings(actor); + if (dist <= (3072*actor->tracer->scale)) // Close enough to target? { S_StartSound(actor, actor->info->attacksound); // Siren sound; might not need this anymore, but I'm keeping it for now just for debugging. From 20f8037351dffd30b296f28bbeaa536a26d3a740 Mon Sep 17 00:00:00 2001 From: Sryder Date: Sat, 29 Jun 2019 11:05:35 +0100 Subject: [PATCH 034/105] Respawn at waypoints once first crossing the finish line. --- src/g_game.c | 3 ++- src/k_kart.c | 36 ++++++++++++++++++++++++++++++++++-- src/p_inter.c | 6 ------ src/p_spec.c | 17 ----------------- 4 files changed, 36 insertions(+), 26 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 297f4a198..b9099c5a8 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -3216,7 +3216,8 @@ void G_DoReborn(INT32 playernum) // respawn at the start mobj_t *oldmo = NULL; - if (player->starpostnum || ((mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE) && player->laps)) // SRB2kart + // Now only respawn at the start if you haven't crossed it at all + if (player->laps) // SRB2kart starpost = true; // first dissasociate the corpse diff --git a/src/k_kart.c b/src/k_kart.c index 1740aa862..01cc4b940 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -5859,8 +5859,40 @@ static void K_UpdateDistanceFromFinishLine(player_t *const player) } else { - waypoint_t *finishline = K_GetFinishLineWaypoint(); - player->nextwaypoint = K_GetPlayerNextWaypoint(player); + waypoint_t *finishline = K_GetFinishLineWaypoint(); + waypoint_t *nextwaypoint = K_GetPlayerNextWaypoint(player); + + if ((nextwaypoint != player->nextwaypoint) && + (K_GetWaypointIsShortcut(nextwaypoint) == false) && (K_GetWaypointIsEnabled(nextwaypoint) == true)) + { + size_t i = 0U; + waypoint_t *aimwaypoint = NULL; + player->starpostx = nextwaypoint->mobj->x >> FRACBITS; + player->starposty = nextwaypoint->mobj->y >> FRACBITS; + player->starpostz = nextwaypoint->mobj->z >> FRACBITS; + + // player gravflip determines which way to respawn + player->kartstuff[k_starpostflip] = player->mo->flags2 & MF2_OBJECTFLIP; + + // starpostangle is to the first valid nextwaypoint for simplicity + // if we reach the last waypoint and it's still not valid, just use it anyway. Someone needs to fix + // their map! + for (i = 0U; i < nextwaypoint->numnextwaypoints; i++) + { + aimwaypoint = nextwaypoint->nextwaypoints[i]; + + if ((i == nextwaypoint->numnextwaypoints - 1U) + || ((K_GetWaypointIsShortcut(aimwaypoint) == false) + && (K_GetWaypointIsEnabled(aimwaypoint) == true))) + { + player->starpostangle = R_PointToAngle2( + nextwaypoint->mobj->x, nextwaypoint->mobj->y, aimwaypoint->mobj->x, aimwaypoint->mobj->y); + break; + } + } + } + + player->nextwaypoint = nextwaypoint; // nextwaypoint is now the waypoint that is in front of us if ((player->nextwaypoint != NULL) && (finishline != NULL)) diff --git a/src/p_inter.c b/src/p_inter.c index ceef7accb..63e091828 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1484,13 +1484,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) // Save the player's time and position. player->starposttime = player->realtime; //this makes race mode's timers work correctly whilst not affecting sp -x - //player->starposttime = leveltime; - player->starpostx = toucher->x>>FRACBITS; - player->starposty = toucher->y>>FRACBITS; - player->starpostz = special->z>>FRACBITS; - player->starpostangle = special->angle; player->starpostnum = special->health; - player->kartstuff[k_starpostflip] = special->spawnpoint->options & MTF_OBJECTFLIP; // store flipping //S_StartSound(toucher, special->info->painsound); return; diff --git a/src/p_spec.c b/src/p_spec.c index 8522fdf9b..b83a51cde 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -2254,23 +2254,6 @@ static void K_HandleLapIncrement(player_t *player) player->starposttime = player->realtime; player->starpostnum = 0; - if (mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE) - { - // SRB2Kart 281118 - // Save the player's time and position. - player->starpostx = player->mo->x>>FRACBITS; - player->starposty = player->mo->y>>FRACBITS; - player->starpostz = player->mo->floorz>>FRACBITS; - player->kartstuff[k_starpostflip] = player->mo->flags2 & MF2_OBJECTFLIP; // store flipping - player->starpostangle = player->mo->angle; //R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); torn; a momentum-based guess is less likely to be wrong in general, but when it IS wrong, it fucks you over entirely... - } - else - { - // SRB2kart 200117 - // Reset starposts (checkpoints) info - player->starpostangle = player->starpostx = player->starposty = player->starpostz = player->kartstuff[k_starpostflip] = 0; - } - if (P_IsDisplayPlayer(player)) { if (player->laps == (UINT8)(cv_numlaps.value)) // final lap From 19a296241366a80a7a7d5d2c11812be76673374f Mon Sep 17 00:00:00 2001 From: Sryder Date: Sun, 30 Jun 2019 22:14:20 +0100 Subject: [PATCH 035/105] Just remembered there's supposed to be a flag to determine if a waypoint can be spawned at --- src/k_kart.c | 4 ++-- src/k_waypoint.c | 35 ++++++++++++++++++++++++++++++----- src/k_waypoint.h | 15 +++++++++++++++ 3 files changed, 47 insertions(+), 7 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 01cc4b940..b0f5a86c9 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -5882,8 +5882,8 @@ static void K_UpdateDistanceFromFinishLine(player_t *const player) aimwaypoint = nextwaypoint->nextwaypoints[i]; if ((i == nextwaypoint->numnextwaypoints - 1U) - || ((K_GetWaypointIsShortcut(aimwaypoint) == false) - && (K_GetWaypointIsEnabled(aimwaypoint) == true))) + || ((K_GetWaypointIsEnabled(aimwaypoint) == true) + && (K_GetWaypointIsSpawnpoint(aimwaypoint) == true))) { player->starpostangle = R_PointToAngle2( nextwaypoint->mobj->x, nextwaypoint->mobj->y, aimwaypoint->mobj->x, aimwaypoint->mobj->y); diff --git a/src/k_waypoint.c b/src/k_waypoint.c index f58d11bf9..878367095 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -51,7 +51,7 @@ boolean K_GetWaypointIsFinishline(waypoint_t *waypoint) { CONS_Debug(DBG_GAMELOGIC, "NULL waypoint in K_GetWaypointIsShortcut.\n"); } - else if (waypoint->mobj == NULL) + else if ((waypoint->mobj == NULL) || (P_MobjWasRemoved(waypoint->mobj) == false)) { CONS_Debug(DBG_GAMELOGIC, "NULL waypoint mobj in K_GetWaypointIsShortcut.\n"); } @@ -76,7 +76,7 @@ boolean K_GetWaypointIsShortcut(waypoint_t *waypoint) { CONS_Debug(DBG_GAMELOGIC, "NULL waypoint in K_GetWaypointIsShortcut.\n"); } - else if (waypoint->mobj == NULL) + else if ((waypoint->mobj == NULL) || (P_MobjWasRemoved(waypoint->mobj) == false)) { CONS_Debug(DBG_GAMELOGIC, "NULL waypoint mobj in K_GetWaypointIsShortcut.\n"); } @@ -101,7 +101,7 @@ boolean K_GetWaypointIsEnabled(waypoint_t *waypoint) { CONS_Debug(DBG_GAMELOGIC, "NULL waypoint in K_GetWaypointIsEnabled.\n"); } - else if (waypoint->mobj == NULL) + else if ((waypoint->mobj == NULL) || (P_MobjWasRemoved(waypoint->mobj) == false)) { CONS_Debug(DBG_GAMELOGIC, "NULL waypoint mobj in K_GetWaypointIsEnabled.\n"); } @@ -113,6 +113,31 @@ boolean K_GetWaypointIsEnabled(waypoint_t *waypoint) 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) == false)) + { + 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) @@ -126,7 +151,7 @@ INT32 K_GetWaypointNextID(waypoint_t *waypoint) { CONS_Debug(DBG_GAMELOGIC, "NULL waypoint in K_GetWaypointNextID.\n"); } - else if (waypoint->mobj == NULL) + else if ((waypoint->mobj == NULL) || (P_MobjWasRemoved(waypoint->mobj) == false)) { CONS_Debug(DBG_GAMELOGIC, "NULL waypoint mobj in K_GetWaypointNextID.\n"); } @@ -151,7 +176,7 @@ INT32 K_GetWaypointID(waypoint_t *waypoint) { CONS_Debug(DBG_GAMELOGIC, "NULL waypoint in K_GetWaypointID.\n"); } - else if (waypoint->mobj == NULL) + else if ((waypoint->mobj == NULL) || (P_MobjWasRemoved(waypoint->mobj) == false)) { CONS_Debug(DBG_GAMELOGIC, "NULL waypoint mobj in K_GetWaypointID.\n"); } diff --git a/src/k_waypoint.h b/src/k_waypoint.h index e0dffdbda..d79045119 100644 --- a/src/k_waypoint.h +++ b/src/k_waypoint.h @@ -80,6 +80,21 @@ boolean K_GetWaypointIsShortcut(waypoint_t *waypoint); boolean K_GetWaypointIsEnabled(waypoint_t *waypoint); +/*-------------------------------------------------- + boolean K_GetWaypointIsSpawnpoint(waypoint_t *waypoint) + + Returns whether the waypoint is a spawnpoint. + + Input Arguments:- + waypoint - The waypoint to return spawnpoint status of. + + Return:- + true if the waypoint is a spawnpoint, false if it isn't. +--------------------------------------------------*/ + +boolean K_GetWaypointIsSpawnpoint(waypoint_t *waypoint); + + /*-------------------------------------------------- INT32 K_GetWaypointNextID(waypoint_t *waypoint) From ae344fa2bc6479a789c73b12693c350b883d979f Mon Sep 17 00:00:00 2001 From: Sryder Date: Thu, 4 Jul 2019 17:55:41 +0100 Subject: [PATCH 036/105] Fix silly issue --- src/k_waypoint.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/k_waypoint.c b/src/k_waypoint.c index 878367095..6673cec97 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -49,11 +49,11 @@ boolean K_GetWaypointIsFinishline(waypoint_t *waypoint) if (waypoint == NULL) { - CONS_Debug(DBG_GAMELOGIC, "NULL waypoint in K_GetWaypointIsShortcut.\n"); + CONS_Debug(DBG_GAMELOGIC, "NULL waypoint in K_GetWaypointIsFinishline.\n"); } - else if ((waypoint->mobj == NULL) || (P_MobjWasRemoved(waypoint->mobj) == false)) + else if ((waypoint->mobj == NULL) || (P_MobjWasRemoved(waypoint->mobj) == true)) { - CONS_Debug(DBG_GAMELOGIC, "NULL waypoint mobj in K_GetWaypointIsShortcut.\n"); + CONS_Debug(DBG_GAMELOGIC, "NULL waypoint mobj in K_GetWaypointIsFinishline.\n"); } else { @@ -76,7 +76,7 @@ boolean K_GetWaypointIsShortcut(waypoint_t *waypoint) { CONS_Debug(DBG_GAMELOGIC, "NULL waypoint in K_GetWaypointIsShortcut.\n"); } - else if ((waypoint->mobj == NULL) || (P_MobjWasRemoved(waypoint->mobj) == false)) + else if ((waypoint->mobj == NULL) || (P_MobjWasRemoved(waypoint->mobj) == true)) { CONS_Debug(DBG_GAMELOGIC, "NULL waypoint mobj in K_GetWaypointIsShortcut.\n"); } @@ -101,7 +101,7 @@ boolean K_GetWaypointIsEnabled(waypoint_t *waypoint) { CONS_Debug(DBG_GAMELOGIC, "NULL waypoint in K_GetWaypointIsEnabled.\n"); } - else if ((waypoint->mobj == NULL) || (P_MobjWasRemoved(waypoint->mobj) == false)) + else if ((waypoint->mobj == NULL) || (P_MobjWasRemoved(waypoint->mobj) == true)) { CONS_Debug(DBG_GAMELOGIC, "NULL waypoint mobj in K_GetWaypointIsEnabled.\n"); } @@ -126,7 +126,7 @@ boolean K_GetWaypointIsSpawnpoint(waypoint_t *waypoint) { CONS_Debug(DBG_GAMELOGIC, "NULL waypoint in K_GetWaypointIsEnabled.\n"); } - else if ((waypoint->mobj == NULL) || (P_MobjWasRemoved(waypoint->mobj) == false)) + else if ((waypoint->mobj == NULL) || (P_MobjWasRemoved(waypoint->mobj) == true)) { CONS_Debug(DBG_GAMELOGIC, "NULL waypoint mobj in K_GetWaypointIsEnabled.\n"); } @@ -151,7 +151,7 @@ INT32 K_GetWaypointNextID(waypoint_t *waypoint) { CONS_Debug(DBG_GAMELOGIC, "NULL waypoint in K_GetWaypointNextID.\n"); } - else if ((waypoint->mobj == NULL) || (P_MobjWasRemoved(waypoint->mobj) == false)) + else if ((waypoint->mobj == NULL) || (P_MobjWasRemoved(waypoint->mobj) == true)) { CONS_Debug(DBG_GAMELOGIC, "NULL waypoint mobj in K_GetWaypointNextID.\n"); } @@ -176,7 +176,7 @@ INT32 K_GetWaypointID(waypoint_t *waypoint) { CONS_Debug(DBG_GAMELOGIC, "NULL waypoint in K_GetWaypointID.\n"); } - else if ((waypoint->mobj == NULL) || (P_MobjWasRemoved(waypoint->mobj) == false)) + else if ((waypoint->mobj == NULL) || (P_MobjWasRemoved(waypoint->mobj) == true)) { CONS_Debug(DBG_GAMELOGIC, "NULL waypoint mobj in K_GetWaypointID.\n"); } From bca4f30ee38bed7c2fa0d00ac9a0efe7ce9b9ba0 Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Wed, 25 Sep 2019 07:17:14 -0400 Subject: [PATCH 037/105] Use defines here instead I got a "initializer element is not constant" error when compiling. From a quick Google search, it seems that const-type variables aren't totally considered constants in C. There may be a better way to fix this error. --- src/k_waypoint.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/k_waypoint.c b/src/k_waypoint.c index 6673cec97..4070e61e6 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -11,9 +11,9 @@ 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. -static const size_t OPENSET_BASE_SIZE = 16U; -static const size_t CLOSEDSET_BASE_SIZE = 256U; -static const size_t NODESARRAY_BASE_SIZE = 256U; +#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; From 2c400487a5e1b83ae82f0a944d8651885b1ab5ee Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Fri, 27 Sep 2019 23:11:43 -0400 Subject: [PATCH 038/105] not def 0 --- src/p_map.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_map.c b/src/p_map.c index 719f945e8..e2c7523d9 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -254,7 +254,7 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object) object->eflags |= MFE_SPRUNG; // apply this flag asap! spring->flags &= ~(MF_SOLID|MF_SPECIAL); // De-solidify -#ifdef 0 +#if 0 if (horizspeed && vertispeed) // Mimic SA { object->momx = object->momy = 0; From 9e3b818ccea9841d7df3aaed8bd5c85037f8678a Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Sun, 29 Sep 2019 14:40:23 -0400 Subject: [PATCH 039/105] Fix finish line occasionally failing for small players --- src/p_map.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/p_map.c b/src/p_map.c index e2c7523d9..608f13a2c 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -2080,7 +2080,7 @@ static boolean PIT_CheckLine(line_t *ld) if (P_BoxOnLineSide(tmbbox, ld) != -1) return true; -if (tmthing->flags & MF_PAPERCOLLISION) // Caution! Turning whilst up against a wall will get you stuck. You probably shouldn't give the player this flag. + if (tmthing->flags & MF_PAPERCOLLISION) // Caution! Turning whilst up against a wall will get you stuck. You probably shouldn't give the player this flag. { fixed_t cosradius, sinradius; cosradius = FixedMul(tmthing->radius, FINECOSINE(tmthing->angle>>ANGLETOFINESHIFT)); @@ -2881,9 +2881,6 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) // reset this to 0 at the start of each trymove call as it's only used here numspechitint = 0U; - if (radius < MAXRADIUS/2) - radius = MAXRADIUS/2; - do { if (thing->flags & MF_NOCLIP) { tryx = x; From d1c20c2a5ad420fda551895d1514d727700e416e Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Tue, 15 Oct 2019 21:30:23 -0400 Subject: [PATCH 040/105] Don't allow finish lines to be hit multiple times per tic --- src/d_netcmd.c | 12 +---- src/d_player.h | 2 +- src/dehacked.c | 2 +- src/g_game.c | 2 +- src/p_enemy.c | 10 ----- src/p_local.h | 1 - src/p_map.c | 2 +- src/p_spec.c | 16 +++---- src/p_user.c | 118 +++---------------------------------------------- 9 files changed, 18 insertions(+), 147 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 8bd03dfbf..90ba1b137 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1873,8 +1873,6 @@ void SendWeaponPref(void) buf[0] = 0; if (cv_flipcam.value) buf[0] |= 1; - if (cv_analog.value) - buf[0] |= 2; SendNetXCmd(XD_WEAPONPREF, buf, 1); } @@ -1885,8 +1883,6 @@ void SendWeaponPref2(void) buf[0] = 0; if (cv_flipcam2.value) buf[0] |= 1; - if (cv_analog2.value) - buf[0] |= 2; SendNetXCmd2(XD_WEAPONPREF, buf, 1); } @@ -1897,8 +1893,6 @@ void SendWeaponPref3(void) buf[0] = 0; if (cv_flipcam3.value) buf[0] |= 1; - if (cv_analog3.value) - buf[0] |= 2; SendNetXCmd3(XD_WEAPONPREF, buf, 1); } @@ -1909,8 +1903,6 @@ void SendWeaponPref4(void) buf[0] = 0; if (cv_flipcam4.value) buf[0] |= 1; - if (cv_analog4.value) - buf[0] |= 2; SendNetXCmd4(XD_WEAPONPREF, buf, 1); } @@ -1918,11 +1910,9 @@ static void Got_WeaponPref(UINT8 **cp,INT32 playernum) { UINT8 prefs = READUINT8(*cp); - players[playernum].pflags &= ~(PF_FLIPCAM|PF_ANALOGMODE); + players[playernum].pflags &= ~(PF_FLIPCAM); if (prefs & 1) players[playernum].pflags |= PF_FLIPCAM; - if (prefs & 2) - players[playernum].pflags |= PF_ANALOGMODE; } void D_SendPlayerConfig(void) diff --git a/src/d_player.h b/src/d_player.h index 536f46ea3..1d9f77c60 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -121,7 +121,7 @@ typedef enum /*** misc ***/ PF_FORCESTRAFE = 1<<29, // Turning inputs are translated into strafing inputs - PF_ANALOGMODE = 1<<30, // Analog mode? + PF_HITFINISHLINE = 1<<30, // Already hit the finish line this tic // free: 1<<30 and 1<<31 } pflags_t; diff --git a/src/dehacked.c b/src/dehacked.c index 81ad3c823..355aa86bd 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -8176,7 +8176,7 @@ static const char *const PLAYERFLAG_LIST[] = { /*** misc ***/ "FORCESTRAFE", // Translate turn inputs into strafe inputs - "ANALOGMODE", // Analog mode? + "HITFINISHLINE", // Already hit the finish line this tic NULL // stop loop here. }; diff --git a/src/g_game.c b/src/g_game.c index 69889e803..282a63215 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2593,7 +2593,7 @@ void G_PlayerReborn(INT32 player) jointime = players[player].jointime; splitscreenindex = players[player].splitscreenindex; spectator = players[player].spectator; - pflags = (players[player].pflags & (PF_TIMEOVER|PF_FLIPCAM|PF_TAGIT|PF_TAGGED|PF_ANALOGMODE|PF_WANTSTOJOIN)); + pflags = (players[player].pflags & (PF_TIMEOVER|PF_FLIPCAM|PF_TAGIT|PF_TAGGED|PF_WANTSTOJOIN)); // As long as we're not in multiplayer, carry over cheatcodes from map to map if (!(netgame || multiplayer)) diff --git a/src/p_enemy.c b/src/p_enemy.c index a1bba0092..c3f316225 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -8554,9 +8554,6 @@ void A_SPBChase(mobj_t *actor) fixed_t range = (160*actor->tracer->scale); fixed_t cx = 0, cy =0; - // we're tailing a player, now's a good time to regain our damage properties - actor->flags &= ~MF_NOCLIPTHING; - // Play the intimidating gurgle if (!S_SoundPlaying(actor, actor->info->activesound)) S_StartSound(actor, actor->info->activesound); @@ -8688,9 +8685,6 @@ void A_SPBChase(mobj_t *actor) { actor->momx = actor->momy = actor->momz = 0; // Stoooop - // don't hurt players that have nothing to do with this: - actor->flags |= MF_NOCLIPTHING; - if (actor->lastlook != -1 && playeringame[actor->lastlook] && !players[actor->lastlook].spectator @@ -8729,10 +8723,6 @@ void A_SPBChase(mobj_t *actor) } // Found someone, now get close enough to initiate the slaughter... - - // Seeking SPB can now hurt people - actor->flags &= ~MF_NOCLIPTHING; - P_SetTarget(&actor->tracer, player->mo); spbplace = bestrank; diff --git a/src/p_local.h b/src/p_local.h index d4da9fe29..220ee1c3d 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -180,7 +180,6 @@ boolean P_LookForEnemies(player_t *player); void P_NukeEnemies(mobj_t *inflictor, mobj_t *source, fixed_t radius); void P_HomingAttack(mobj_t *source, mobj_t *enemy); /// \todo doesn't belong in p_user //boolean P_SuperReady(player_t *player); -boolean P_AnalogMove(player_t *player); /*boolean P_TransferToNextMare(player_t *player); UINT8 P_FindLowestMare(void);*/ UINT8 P_FindLowestLap(void); diff --git a/src/p_map.c b/src/p_map.c index 608f13a2c..fe4c160dd 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -1417,7 +1417,7 @@ static boolean PIT_CheckThing(mobj_t *thing) thing->angle = tmthing->angle; - if (!demo.playback || P_AnalogMove(thing->player)) + if (!demo.playback) { if (thing->player == &players[consoleplayer]) localangle[0] = thing->angle; diff --git a/src/p_spec.c b/src/p_spec.c index 66955d0ec..6150aa391 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -2283,12 +2283,6 @@ static void K_HandleLapIncrement(player_t *player) P_SetupSignExit(player); } - //player->starpostangle = player->starposttime = player->starpostnum = 0; - //player->starpostx = player->starposty = player->starpostz = 0; - - // Play the starpost sound for 'consistency' - // S_StartSound(player->mo, sfx_strpst); - // Figure out how many are playing on the last lap, to prevent spectate griefing if (!nospectategrief && player->laps > (UINT8)(cv_numlaps.value)) nospectategrief = nump; @@ -2307,7 +2301,7 @@ static void K_HandleLapDecrement(player_t *player) { if (player) { - if (player->laps > 0) + if ((player->starpostnum == 0) && (player->laps > 0)) { player->starpostnum = numstarposts; player->laps--; @@ -2331,7 +2325,7 @@ void P_CrossSpecialLine(line_t *line, INT32 side, mobj_t *thing) { case 2001: // Finish Line { - if (G_RaceGametype() && !(player->exiting)) + if (G_RaceGametype() && !(player->exiting) && !(player->pflags & PF_HITFINISHLINE)) { if (((line->flags & (ML_NOCLIMB)) && (side == 0)) || (!(line->flags & (ML_NOCLIMB)) && (side == 1))) // crossed from behind to infront @@ -2342,6 +2336,8 @@ void P_CrossSpecialLine(line_t *line, INT32 side, mobj_t *thing) { K_HandleLapDecrement(player); } + + player->pflags |= PF_HITFINISHLINE; } } break; @@ -4027,7 +4023,7 @@ DoneSection2: if (player->mo->scale > mapobjectscale) linespeed = FixedMul(linespeed, mapobjectscale + (player->mo->scale - mapobjectscale)); - if (!demo.playback || P_AnalogMove(player)) + if (!demo.playback) { if (player == &players[consoleplayer]) localangle[0] = player->mo->angle; @@ -7935,7 +7931,7 @@ void T_Pusher(pusher_t *p) thing->player->pflags |= PF_SLIDING; thing->angle = R_PointToAngle2 (0, 0, xspeed<<(FRACBITS-PUSH_FACTOR), yspeed<<(FRACBITS-PUSH_FACTOR)); - if (!demo.playback || P_AnalogMove(thing->player)) + if (!demo.playback) { if (thing->player == &players[consoleplayer]) { diff --git a/src/p_user.c b/src/p_user.c index 434091fd7..cffba20bb 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -3716,11 +3716,6 @@ void P_Telekinesis(player_t *player, fixed_t thrust, fixed_t range) player->pflags |= PF_THOKKED; } -boolean P_AnalogMove(player_t *player) -{ - return player->pflags & PF_ANALOGMODE; -} - // // P_GetPlayerControlDirection // @@ -3763,14 +3758,6 @@ boolean P_AnalogMove(player_t *player) origtempangle = tempangle = 0; // relative to the axis rather than the player! controlplayerdirection = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); } - else if (P_AnalogMove(player) && thiscam->chase) - { - if (player->awayviewtics) - origtempangle = tempangle = player->awayviewmobj->angle; - else - origtempangle = tempangle = thiscam->angle; - controlplayerdirection = player->mo->angle; - } else { origtempangle = tempangle = player->mo->angle; @@ -3994,7 +3981,6 @@ static void P_3dMovement(player_t *player) angle_t dangle; // replaces old quadrants bits //boolean dangleflip = false; // SRB2kart - toaster //fixed_t normalspd = FixedMul(player->normalspeed, player->mo->scale); - boolean analogmove = false; fixed_t oldMagnitude, newMagnitude; #ifdef ESLOPE vector3_t totalthrust; @@ -4006,8 +3992,6 @@ static void P_3dMovement(player_t *player) // Get the old momentum; this will be needed at the end of the function! -SH oldMagnitude = R_PointToDist2(player->mo->momx - player->cmomx, player->mo->momy - player->cmomy, 0, 0); - analogmove = P_AnalogMove(player); - cmd = &player->cmd; if ((player->exiting || mapreset) || player->pflags & PF_STASIS || player->kartstuff[k_spinouttimer]) // pw_introcam? @@ -4020,19 +4004,13 @@ static void P_3dMovement(player_t *player) if (!(player->pflags & PF_FORCESTRAFE) && !player->kartstuff[k_pogospring]) cmd->sidemove = 0; - if (analogmove) - { - movepushangle = (cmd->angleturn<<16 /* not FRACBITS */); - } + if (player->kartstuff[k_drift] != 0) + movepushangle = player->mo->angle-(ANGLE_45/5)*player->kartstuff[k_drift]; + else if (player->kartstuff[k_spinouttimer] || player->kartstuff[k_wipeoutslow]) // if spun out, use the boost angle + movepushangle = (angle_t)player->kartstuff[k_boostangle]; else - { - if (player->kartstuff[k_drift] != 0) - movepushangle = player->mo->angle-(ANGLE_45/5)*player->kartstuff[k_drift]; - else if (player->kartstuff[k_spinouttimer] || player->kartstuff[k_wipeoutslow]) // if spun out, use the boost angle - movepushangle = (angle_t)player->kartstuff[k_boostangle]; - else - movepushangle = player->mo->angle; - } + movepushangle = player->mo->angle; + movepushsideangle = movepushangle-ANGLE_90; // cmomx/cmomy stands for the conveyor belt speed. @@ -6191,69 +6169,6 @@ static void P_MovePlayer(player_t *player) player->pflags &= ~PF_STARTDASH; */ - ////////////////// - //ANALOG CONTROL// - ////////////////// - -#if 0 - // This really looks like it should be moved to P_3dMovement. -Red - if (P_AnalogMove(player) - && (cmd->forwardmove != 0 || cmd->sidemove != 0) && !player->climbing && !twodlevel && !(player->mo->flags2 & MF2_TWOD)) - { - // If travelling slow enough, face the way the controls - // point and not your direction of movement. - if (player->speed < FixedMul(5*FRACUNIT, player->mo->scale) || player->pflags & PF_GLIDING || !onground) - { - angle_t tempangle; - - tempangle = (cmd->angleturn << 16); - -#ifdef REDSANALOG // Ease to it. Chillax. ~Red - tempangle += R_PointToAngle2(0, 0, cmd->forwardmove*FRACUNIT, -cmd->sidemove*FRACUNIT); - { - fixed_t tweenvalue = max(abs(cmd->forwardmove), abs(cmd->sidemove)); - - if (tweenvalue < 10 && (cmd->buttons & (BT_FORWARD|BT_BACKWARD)) == (BT_FORWARD|BT_BACKWARD)) { - tempangle = (cmd->angleturn << 16); - tweenvalue = 16; - } - - tweenvalue *= tweenvalue*tweenvalue*1536; - - //if (player->pflags & PF_GLIDING) - //tweenvalue >>= 1; - - tempangle -= player->mo->angle; - - if (tempangle < ANGLE_180 && tempangle > tweenvalue) - player->mo->angle += tweenvalue; - else if (tempangle >= ANGLE_180 && InvAngle(tempangle) > tweenvalue) - player->mo->angle -= tweenvalue; - else - player->mo->angle += tempangle; - } -#else - // Less math this way ~Red - player->mo->angle = R_PointToAngle2(0, 0, cmd->forwardmove*FRACUNIT, -cmd->sidemove*FRACUNIT)+tempangle; -#endif - } - // Otherwise, face the direction you're travelling. - else if (player->panim == PA_WALK || player->panim == PA_RUN || player->panim == PA_ROLL - /*|| ((player->mo->state >= &states[S_PLAY_ABL1] && player->mo->state <= &states[S_PLAY_SPC4]) && player->charability == CA_FLY)*/) // SRB2kart - idk - player->mo->angle = R_PointToAngle2(0, 0, player->rmomx, player->rmomy); - - // Update the local angle control. - if (player == &players[consoleplayer]) - localangle[0] = player->mo->angle; - else if (player == &players[displayplayers[1]]) - localangle[1] = player->mo->angle; - else if (player == &players[displayplayers[2]]) - localangle[2] = player->mo->angle; - else if (player == &players[displayplayers[3]]) - localangle[3] = player->mo->angle; - } -#endif - /////////////////////////// //BOMB SHIELD ACTIVATION,// //HOMING, AND OTHER COOL // @@ -8373,26 +8288,7 @@ void P_PlayerThink(player_t *player) player->mo->reactiontime--; else if (player->mo->tracer && player->mo->tracer->type == MT_TUBEWAYPOINT) { - // SRB2kart - don't need no rope hangin' - //if (player->pflags & PF_ROPEHANG) - //{ - // if (!P_AnalogMove(player)) - // player->mo->angle = (cmd->angleturn<<16 /* not FRACBITS */); - - // ticruned++; - // if ((cmd->angleturn & TICCMD_RECEIVED) == 0) - // ticmiss++; - - // P_DoRopeHang(player); - // P_SetPlayerMobjState(player->mo, S_PLAY_CARRY); - // P_DoJumpStuff(player, &player->cmd); - //} - //else - { - P_DoZoomTube(player); - //if (!(player->panim == PA_ROLL) && player->charability2 == CA2_SPINDASH) // SRB2kart - // P_SetPlayerMobjState(player->mo, S_PLAY_ATK1); - } + P_DoZoomTube(player); player->rmomx = player->rmomy = 0; // no actual momentum from your controls P_ResetScore(player); } From e6dc29358e6f12af83cd21bf5fe8c71ed34bacb1 Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Tue, 15 Oct 2019 21:46:45 -0400 Subject: [PATCH 041/105] Reduced SPB target distance to 1024 units (previously 3072 units) --- src/p_enemy.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/p_enemy.c b/src/p_enemy.c index e185add81..26e757e41 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -8791,9 +8791,9 @@ void A_SPBChase(mobj_t *actor) // Spawn a trail of rings behind the SPB! SpawnSPBTrailRings(actor); - if (dist <= (3072*actor->tracer->scale)) // Close enough to target? + if (dist <= (1024*actor->tracer->scale)) // Close enough to target? { - S_StartSound(actor, actor->info->attacksound); // Siren sound; might not need this anymore, but I'm keeping it for now just for debugging. + S_StartSound(actor, actor->info->attacksound); actor->extravalue1 = 1; // TARGET ACQUIRED actor->extravalue2 = 7*TICRATE; actor->cvmem = wspeed; From a37eb798e0a5593d0b9c1367a49fbd2409f56fae Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Tue, 15 Oct 2019 22:23:01 -0400 Subject: [PATCH 042/105] Fix bad merge --- src/p_spec.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/p_spec.c b/src/p_spec.c index e3dea0f17..b71d7508c 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -2283,10 +2283,6 @@ static void K_HandleLapIncrement(player_t *player) P_SetupSignExit(player); } - // Figure out how many are playing on the last lap, to prevent spectate griefing - if (!nospectategrief && player->laps > (UINT8)(cv_numlaps.value)) - nospectategrief = nump; - thwompsactive = true; // Lap 2 effects } else if (player->starpostnum) From 52624cb6debd558a664d5ef71c975bfedce31298 Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Tue, 15 Oct 2019 22:34:22 -0400 Subject: [PATCH 043/105] Properly reset PF_HITFINISHLINE --- src/k_kart.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/k_kart.c b/src/k_kart.c index fc42a925c..00326e057 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6189,6 +6189,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) boolean NO_HYUDORO = (player->kartstuff[k_stolentimer] == 0 && player->kartstuff[k_stealingtimer] == 0); K_UpdateDistanceFromFinishLine(player); + player->pflags &= ~PF_HITFINISHLINE; if (!player->exiting) { From c91986cc97a63acf4c98344416d0e7b3d01afccb Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Tue, 15 Oct 2019 23:37:20 -0400 Subject: [PATCH 044/105] Fix end-of-race standings --- src/k_kart.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 00326e057..89c821975 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6036,26 +6036,37 @@ void K_KartUpdatePosition(player_t *player) if (G_RaceGametype()) { - // I'm a lap behind this player OR - // My distance to the finish line is higher, so I'm behind - if ((players[i].laps > player->laps) - || (players[i].distancetofinish < player->distancetofinish)) + if (player->exiting) // End of match standings { - position++; + // Only time matters + if (players[i].realtime < player->realtime) + position++; + } + else + { + // I'm a lap behind this player OR + // My distance to the finish line is higher, so I'm behind + if ((players[i].laps > player->laps) + || (players[i].distancetofinish < player->distancetofinish)) + { + position++; + } } } else if (G_BattleGametype()) { if (player->exiting) // End of match standings { - if (players[i].marescore > player->marescore) // Only score matters + // Only score matters + if (players[i].marescore > player->marescore) position++; } else { - if (players[i].kartstuff[k_bumper] == player->kartstuff[k_bumper] && players[i].marescore > player->marescore) - position++; - else if (players[i].kartstuff[k_bumper] > player->kartstuff[k_bumper]) + // I have less points than but the same bumpers as this player OR + // I have less bumpers than this player + if ((players[i].kartstuff[k_bumper] == player->kartstuff[k_bumper] && players[i].marescore > player->marescore) + || (players[i].kartstuff[k_bumper] > player->kartstuff[k_bumper])) position++; } } From a94601a7932ee7de9eac494f690fa79436736a16 Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Wed, 16 Oct 2019 00:12:03 -0400 Subject: [PATCH 045/105] fix SPB weirdo distances --- src/k_kart.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 89c821975..5040f4899 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -848,9 +848,7 @@ static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean sp if (first != -1 && second != -1) // calculate 2nd's distance from 1st, for SPB { - secondist = P_AproxDistance(P_AproxDistance(players[first].mo->x - players[second].mo->x, - players[first].mo->y - players[second].mo->y), - players[first].mo->z - players[second].mo->z) / mapobjectscale; + secondist = players[second]->distancetofinish - players[first].distancetofinish; if (franticitems) secondist = (15 * secondist) / 14; secondist = ((28 + (8-pingame)) * secondist) / 28; @@ -1088,7 +1086,8 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) for (i = 0; i < MAXPLAYERS; i++) { - if (players[i].kartstuff[k_position] == 1) + if (playeringame[i] && !players[i].spectator + && players[i].kartstuff[k_position] == 1) { // This player is first! Yay! pdis = player->distancetofinish - players[i].distancetofinish; @@ -9979,13 +9978,13 @@ static void K_drawDistributionDebugger(void) // lovely double loop...... for (i = 0; i < MAXPLAYERS; i++) { - if (playeringame[i] && !players[i].spectator && players[i].mo - && players[i].kartstuff[k_position] < stplyr->kartstuff[k_position]) - pdis += P_AproxDistance(P_AproxDistance(players[i].mo->x - stplyr->mo->x, - players[i].mo->y - stplyr->mo->y), - players[i].mo->z - stplyr->mo->z) / mapobjectscale - * (pingame - players[i].kartstuff[k_position]) - / max(1, ((pingame - 1) * (pingame + 1) / 3)); + if (playeringame[i] && !players[i].spectator + && players[i].kartstuff[k_position] == 1) + { + // This player is first! Yay! + pdis = stplyr->distancetofinish - players[i].distancetofinish; + break; + } } if (franticitems) // Frantic items make the distances between everyone artifically higher, for crazier items From 857afbbb49558e6f2c1e685b405e38b88ad7b255 Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Wed, 16 Oct 2019 00:16:27 -0400 Subject: [PATCH 046/105] increase DISTVAR --- src/k_kart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index 5040f4899..1b75d523c 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -709,7 +709,7 @@ static INT32 K_KartItemOddsBattle[NUMKARTRESULTS][6] = /*Jawz x2*/ { 0, 0, 1, 2, 4, 2 } // Jawz x2 }; -#define DISTVAR (64*14) +#define DISTVAR (1024) // Magic number distance for use with item roulette tiers /** \brief Item Roulette for Kart From 07fac259e08aff44d9d640e16cb3eaaa760306a2 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 15 Oct 2019 21:20:28 -0700 Subject: [PATCH 047/105] Not a pointer --- src/k_kart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index 1b75d523c..4752617d0 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -848,7 +848,7 @@ static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean sp if (first != -1 && second != -1) // calculate 2nd's distance from 1st, for SPB { - secondist = players[second]->distancetofinish - players[first].distancetofinish; + secondist = players[second].distancetofinish - players[first].distancetofinish; if (franticitems) secondist = (15 * secondist) / 14; secondist = ((28 + (8-pingame)) * secondist) / 28; From 4b04423bbe5256e7796026329bd53a1b24e9bb58 Mon Sep 17 00:00:00 2001 From: James Date: Tue, 22 Oct 2019 08:46:14 -0400 Subject: [PATCH 048/105] Bring this back, but more lenient (not tested) --- src/p_map.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/p_map.c b/src/p_map.c index fe4c160dd..9585849f7 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -2881,6 +2881,11 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) // reset this to 0 at the start of each trymove call as it's only used here numspechitint = 0U; + // This makes sure that there are no freezes from computing extremely small movements. + // Originally was MAXRADIUS/2, but that causes some inconsistencies for small players. + if (radius < mapobjectscale) + radius = mapobjectscale; + do { if (thing->flags & MF_NOCLIP) { tryx = x; From 0fdcc22d939c90dd34dc6dc74f25f6136d24bd0b Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Tue, 22 Oct 2019 16:44:56 -0400 Subject: [PATCH 049/105] - Only update to waypoints you're in the radius of (applies to players & spb) - Player direction value uses momentum angle rather than facing angle (fixes weird nextwaypoint/position flickering while drifting) - Raise default radius to 384 --- src/k_kart.c | 30 ++++++++++++++++----- src/k_waypoint.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++ src/k_waypoint.h | 14 ++++++++++ src/p_enemy.c | 48 +++++++++++++++++++++++++++------ src/p_mobj.c | 2 +- 5 files changed, 148 insertions(+), 16 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 4752617d0..b376ade41 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -5488,28 +5488,33 @@ void K_KartPlayerAfterThink(player_t *player) Input Arguments:- player - The player the next waypoint is being found for + closest - Use closest waypoint algorithm, instead of best touching Return:- The waypoint that is the player's next waypoint --------------------------------------------------*/ -static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) +static waypoint_t *K_GetPlayerNextWaypoint(player_t *player, boolean closest) { waypoint_t *bestwaypoint = NULL; if ((player != NULL) && (player->mo != NULL) && (P_MobjWasRemoved(player->mo) == false)) { waypoint_t *waypoint = NULL; - waypoint = K_GetClosestWaypointToMobj(player->mo); + if (closest) + waypoint = K_GetClosestWaypointToMobj(player->mo); + else + waypoint = K_GetBestWaypointTouchingMobj(player->mo); + bestwaypoint = waypoint; // check the waypoint's location in relation to the player // If it's generally in front, it's fine, otherwise, use the best next/previous waypoint. - // EXCEPTION: If our closest waypoint is the finishline AND we're facing towards it, don't do this. + // EXCEPTION: If our best waypoint is the finishline AND we're facing towards it, don't do this. // Otherwise it breaks the distance calculations. if (waypoint != NULL) { boolean finishlinehack = false; - angle_t playerangle = player->mo->angle; + angle_t playerangle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); //player->mo->angle angle_t angletowaypoint = R_PointToAngle2(player->mo->x, player->mo->y, waypoint->mobj->x, waypoint->mobj->y); angle_t angledelta = playerangle - angletowaypoint; @@ -5667,9 +5672,16 @@ static void K_UpdateDistanceFromFinishLine(player_t *const player) else { waypoint_t *finishline = K_GetFinishLineWaypoint(); - waypoint_t *nextwaypoint = K_GetPlayerNextWaypoint(player); + waypoint_t *nextwaypoint = K_GetPlayerNextWaypoint(player, false); - if ((nextwaypoint != player->nextwaypoint) && + if ((nextwaypoint == NULL) && (player->nextwaypoint == NULL)) + { + // Special case: if player nextwaypoint is still NULL, we want to fix that as soon as possible, so use the closest waypoint instead. + nextwaypoint = K_GetPlayerNextWaypoint(player, true); + } + + if ((nextwaypoint != NULL) && + (nextwaypoint != player->nextwaypoint) && (K_GetWaypointIsShortcut(nextwaypoint) == false) && (K_GetWaypointIsEnabled(nextwaypoint) == true)) { size_t i = 0U; @@ -5699,7 +5711,11 @@ static void K_UpdateDistanceFromFinishLine(player_t *const player) } } - player->nextwaypoint = nextwaypoint; + if (nextwaypoint != NULL) + { + // At this point, we don't want to update the waypoint until we touch another. + player->nextwaypoint = nextwaypoint; + } // nextwaypoint is now the waypoint that is in front of us if ((player->nextwaypoint != NULL) && (finishline != NULL)) diff --git a/src/k_waypoint.c b/src/k_waypoint.c index 4070e61e6..a73487e14 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -235,6 +235,76 @@ waypoint_t *K_GetClosestWaypointToMobj(mobj_t *const mobj) return closestwaypoint; } +/*-------------------------------------------------- + waypoint_t *K_GetBestWaypointTouchingMobj(mobj_t *const mobj) + + See header file for description. +--------------------------------------------------*/ +waypoint_t *K_GetBestWaypointTouchingMobj(mobj_t *const mobj) +{ + waypoint_t *bestwaypoint = NULL; + + if ((mobj == NULL) || P_MobjWasRemoved(mobj)) + { + CONS_Debug(DBG_GAMELOGIC, "NULL mobj in K_GetBestWaypointTouchingMobj.\n"); + } + else + { + size_t i = 0U; + waypoint_t *checkwaypoint = NULL; + fixed_t bestdist = INT32_MAX; + fixed_t checkdist = INT32_MAX; + + for (i = 0; i < numwaypoints; i++) + { + checkwaypoint = &waypointheap[i]; + checkdist = P_AproxDistance(mobj->x - checkwaypoint->mobj->x, mobj->y - checkwaypoint->mobj->y); + checkdist = P_AproxDistance(checkdist, mobj->z - checkwaypoint->mobj->z); + + // The mobj has to be touching this waypoint to update to it. + if (checkdist <= checkwaypoint->mobj->radius) + { +#if 0 + // This kind of algorithm may or may not be more reliable than what's below. + // But it's a little heavier, computation-wise. + // We'll see if simple closer checks work fine in netgame testing, or if it needs this. + boolean success = false; + path_t pathtofinish = {}; + success = K_PathfindToWaypoint(checkwaypoint, finishline, &pathtofinish, false, false); + + // If you're touching more than 1 waypoint, then we use the closest one to the finish line. + if (success == true) + { + // Add euclidean distance to the next waypoint to the distancetofinish + UINT32 distancetofinish; + UINT32 adddist; + + adddist = ((UINT32)checkdist) >> FRACBITS; + + distancetofinish = pathtofinish.totaldist + adddist; + Z_Free(pathtofinish.array); + + if (distancetofinish < bestdist) + { + bestwaypoint = checkwaypoint; + bestdist = checkdist; + } + } +#else + // Simple closest check + if (checkdist < bestdist) + { + bestwaypoint = checkwaypoint; + bestdist = checkdist; + } +#endif + } + } + } + + return bestwaypoint; +} + /*-------------------------------------------------- size_t K_GetWaypointHeapIndex(waypoint_t *waypoint) diff --git a/src/k_waypoint.h b/src/k_waypoint.h index d79045119..af4ac71a5 100644 --- a/src/k_waypoint.h +++ b/src/k_waypoint.h @@ -151,6 +151,20 @@ UINT32 K_GetCircuitLength(void); waypoint_t *K_GetClosestWaypointToMobj(mobj_t *const mobj); +/*-------------------------------------------------- + waypoint_t *K_GetBestWaypointTouchingMobj(mobj_t *const mobj) + + Returns the waypoint closest to the finish line that an mobj is touching + + Input Arguments:- + mobj - mobj to get the waypoint for. + + Return:- + The best waypoint for the mobj +--------------------------------------------------*/ +waypoint_t *K_GetBestWaypointTouchingMobj(mobj_t *const mobj); + + /*-------------------------------------------------- boolean K_PathfindToWaypoint( waypoint_t *const sourcewaypoint, diff --git a/src/p_enemy.c b/src/p_enemy.c index 26e757e41..a4684b40b 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -8511,9 +8511,9 @@ void A_SPBChase(mobj_t *actor) if (actor->threshold) // Just fired, go straight. { actor->lastlook = -1; + actor->cusval = -1; spbplace = -1; P_InstaThrust(actor, actor->angle, wspeed); - actor->flags &= ~MF_NOCLIPTHING; // just in case. return; } @@ -8539,11 +8539,18 @@ void A_SPBChase(mobj_t *actor) } } + // lastlook = last player num targetted + // cvmem = stored speed + // cusval = next waypoint heap index + // extravalue1 = SPB movement mode + // extravalue2 = mode misc option + if (actor->extravalue1 == 1) // MODE: TARGETING { + actor->cusval = -1; // Reset waypoint + if (actor->tracer && actor->tracer->health) { - fixed_t defspeed = wspeed; fixed_t range = (160*actor->tracer->scale); fixed_t cx = 0, cy =0; @@ -8678,6 +8685,7 @@ void A_SPBChase(mobj_t *actor) else if (actor->extravalue1 == 2) // MODE: WAIT... { actor->momx = actor->momy = actor->momz = 0; // Stoooop + actor->cusval = -1; // Reset waypoint if (actor->lastlook != -1 && playeringame[actor->lastlook] @@ -8703,8 +8711,10 @@ void A_SPBChase(mobj_t *actor) } else // MODE: SEEKING { - waypoint_t *closestwaypoint = NULL; - waypoint_t *nextwaypoint = NULL; + waypoint_t *lastwaypoint = NULL; + waypoint_t *bestwaypoint = NULL; + waypoint_t *nextwaypoint = NULL; + actor->lastlook = -1; // Just make sure this is reset if (!player || !player->mo || player->mo->health <= 0 || player->kartstuff[k_respawn]) @@ -8719,11 +8729,24 @@ void A_SPBChase(mobj_t *actor) // Found someone, now get close enough to initiate the slaughter... P_SetTarget(&actor->tracer, player->mo); spbplace = bestrank; - dist = P_AproxDistance(P_AproxDistance(actor->x-actor->tracer->x, actor->y-actor->tracer->y), actor->z-actor->tracer->z); - closestwaypoint = K_GetClosestWaypointToMobj(actor); - if (closestwaypoint != NULL) + // Move along the waypoints until you get close enough + if (actor->cusval > -1) + { + // Previously set nextwaypoint + lastwaypoint = K_GetWaypointFromIndex((size_t)actor->cusval); + } + + bestwaypoint = K_GetBestWaypointTouchingMobj(actor); + + if (bestwaypoint == NULL && lastwaypoint == NULL) + { + // We have invalid waypoints all around, so use closest to try and make it non-NULL. + bestwaypoint = K_GetClosestWaypointToMobj(actor); + } + + if (bestwaypoint != NULL) { const boolean huntbackwards = false; boolean useshortcuts = false; @@ -8735,15 +8758,24 @@ void A_SPBChase(mobj_t *actor) } nextwaypoint = K_GetNextWaypointToDestination( - closestwaypoint, player->nextwaypoint, useshortcuts, huntbackwards); + bestwaypoint, player->nextwaypoint, useshortcuts, huntbackwards); + } + + if (nextwaypoint == NULL && lastwaypoint != NULL) + { + // Restore to the last nextwaypoint + nextwaypoint = lastwaypoint; } if (nextwaypoint != NULL) { const fixed_t xywaypointdist = P_AproxDistance( actor->x - nextwaypoint->mobj->x, actor->y - nextwaypoint->mobj->y); + hang = R_PointToAngle2(actor->x, actor->y, nextwaypoint->mobj->x, nextwaypoint->mobj->y); vang = R_PointToAngle2(0, actor->z, xywaypointdist, nextwaypoint->mobj->z); + + actor->cusval = (INT32)K_GetWaypointHeapIndex(nextwaypoint); } else { diff --git a/src/p_mobj.c b/src/p_mobj.c index cbe7238b3..b0e378649 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -12289,7 +12289,7 @@ ML_NOCLIMB : Direction not controllable case MT_WAYPOINT: { size_t line; - mobj->radius = 256*FRACUNIT; + mobj->radius = 384*FRACUNIT; // Same reason as for MT_SPINMACEPOINT we can't use the function to find the linedef for (line = 0; line < numlines; line++) { From add1fb87941c20c4c9a71c58f80dab16bf76d116 Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Tue, 22 Oct 2019 17:05:46 -0400 Subject: [PATCH 050/105] Reduce precision of distance checks Fixes huge maps, such as Dark Race --- src/k_kart.c | 8 ++++---- src/k_waypoint.c | 12 ++++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index b376ade41..492b94c72 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -5736,11 +5736,11 @@ static void K_UpdateDistanceFromFinishLine(player_t *const player) UINT32 adddist; fixed_t disttowaypoint = P_AproxDistance( - player->mo->x - player->nextwaypoint->mobj->x, - player->mo->y - player->nextwaypoint->mobj->y); - disttowaypoint = P_AproxDistance(disttowaypoint, player->mo->z - player->nextwaypoint->mobj->z); + (player->mo->x >> FRACBITS) - (player->nextwaypoint->mobj->x >> FRACBITS), + (player->mo->y >> FRACBITS) - (player->nextwaypoint->mobj->y >> FRACBITS)); + disttowaypoint = P_AproxDistance(disttowaypoint, (player->mo->z >> FRACBITS) - (player->nextwaypoint->mobj->z >> FRACBITS)); - adddist = ((UINT32)disttowaypoint) >> FRACBITS; + adddist = (UINT32)disttowaypoint; player->distancetofinish = pathtofinish.totaldist + adddist; Z_Free(pathtofinish.array); diff --git a/src/k_waypoint.c b/src/k_waypoint.c index a73487e14..9ddf713a0 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -221,8 +221,10 @@ waypoint_t *K_GetClosestWaypointToMobj(mobj_t *const mobj) for (i = 0; i < numwaypoints; i++) { checkwaypoint = &waypointheap[i]; - checkdist = P_AproxDistance(mobj->x - checkwaypoint->mobj->x, mobj->y - checkwaypoint->mobj->y); - checkdist = P_AproxDistance(checkdist, mobj->z - checkwaypoint->mobj->z); + checkdist = P_AproxDistance( + (mobj->x >> FRACBITS) - (checkwaypoint->mobj->x >> FRACBITS), + (mobj->y >> FRACBITS) - (checkwaypoint->mobj->y >> FRACBITS)); + checkdist = P_AproxDistance(checkdist, (mobj->z >> FRACBITS) - (checkwaypoint->mobj->z >> FRACBITS)); if (checkdist < closestdist) { @@ -258,8 +260,10 @@ waypoint_t *K_GetBestWaypointTouchingMobj(mobj_t *const mobj) for (i = 0; i < numwaypoints; i++) { checkwaypoint = &waypointheap[i]; - checkdist = P_AproxDistance(mobj->x - checkwaypoint->mobj->x, mobj->y - checkwaypoint->mobj->y); - checkdist = P_AproxDistance(checkdist, mobj->z - checkwaypoint->mobj->z); + checkdist = P_AproxDistance( + (mobj->x >> FRACBITS) - (checkwaypoint->mobj->x >> FRACBITS), + (mobj->y >> FRACBITS) - (checkwaypoint->mobj->y >> FRACBITS)); + checkdist = P_AproxDistance(checkdist, (mobj->z >> FRACBITS) - (checkwaypoint->mobj->z >> FRACBITS)); // The mobj has to be touching this waypoint to update to it. if (checkdist <= checkwaypoint->mobj->radius) From 3c407d971ede2590c849e3f212f3f18fd866b996 Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Tue, 22 Oct 2019 17:27:06 -0400 Subject: [PATCH 051/105] Rudimentary wrong way --- src/d_player.h | 1 + src/dehacked.c | 3 ++- src/k_kart.c | 7 +++++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/d_player.h b/src/d_player.h index 163e9b7cf..07e0f7a97 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -330,6 +330,7 @@ typedef enum k_springstars, // Spawn stars around a player when they hit a spring k_springcolor, // Color of spring stars k_killfield, // How long have you been in the kill field, stay in too long and lose a bumper + k_wrongway, // Display WRONG WAY on screen NUMKARTSTUFF } kartstufftype_t; diff --git a/src/dehacked.c b/src/dehacked.c index 94b009803..fb6e7ff4f 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -8501,7 +8501,8 @@ static const char *const KARTSTUFF_LIST[] = { "TIREGREASE", "SPRINGSTARS", "SPRINGCOLOR", - "KILLFIELD" + "KILLFIELD", + "WRONGWAY" }; #endif diff --git a/src/k_kart.c b/src/k_kart.c index 492b94c72..363c762b5 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -5556,6 +5556,8 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player, boolean closest) { bestwaypoint = waypoint->nextwaypoints[i]; nextbestdelta = angledelta; + + player->kartstuff[k_wrongway] = 0; } } } @@ -5578,6 +5580,8 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player, boolean closest) { bestwaypoint = waypoint->prevwaypoints[i]; nextbestdelta = angledelta; + + player->kartstuff[k_wrongway] = 1; } } } @@ -10277,6 +10281,9 @@ void K_drawKartHUD(void) K_drawKartFreePlay(leveltime); } + if (stplyr->kartstuff[k_wrongway] && ((leveltime / 8) & 1)) + V_DrawCenteredString(BASEVIDWIDTH>>1, 176, V_REDMAP, "WRONG WAY"); + if (cv_kartdebugdistribution.value) K_drawDistributionDebugger(); From 3137e6e54a033ef076b0f83a7b1f354f9c3d441f Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Tue, 22 Oct 2019 17:41:00 -0400 Subject: [PATCH 052/105] Parathesis for defines Also just caught the ;, which could've mess up the define(?) --- src/k_waypoint.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/k_waypoint.c b/src/k_waypoint.c index 9ddf713a0..6acaadb21 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -11,9 +11,9 @@ 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; +#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; From a9a7f9c1ec5017f33028fb3639ee9ca7d496a83e Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Thu, 24 Oct 2019 08:16:25 -0400 Subject: [PATCH 053/105] Quadruple DISTVAR I picked 4096 because it's around the distance of that end slope in Green Hills, which is about the length I imagine a item block "switch" being. I'm being a little conservative though, and it might need to be even higher -- we'll have to play it by ear. Also changed the types of some of the roulette variables to better match what they are used for. --- src/k_kart.c | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 363c762b5..0b9eecd3b 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -709,7 +709,7 @@ static INT32 K_KartItemOddsBattle[NUMKARTRESULTS][6] = /*Jawz x2*/ { 0, 0, 1, 2, 4, 2 } // Jawz x2 }; -#define DISTVAR (1024) // Magic number distance for use with item roulette tiers +#define DISTVAR (4096) // Magic number distance for use with item roulette tiers /** \brief Item Roulette for Kart @@ -926,13 +926,13 @@ static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean sp return newodds; } -//{ SRB2kart Roulette Code - Distance Based, no waypoints +//{ SRB2kart Roulette Code - Distance Based, yes waypoints -static INT32 K_FindUseodds(player_t *player, fixed_t mashed, INT32 pdis, INT32 bestbumper, boolean spbrush) +static UINT8 K_FindUseodds(player_t *player, fixed_t mashed, UINT32 pdis, UINT8 bestbumper, boolean spbrush) { INT32 i; INT32 n = 0; - INT32 useodds = 0; + UINT8 useodds = 0; UINT8 disttable[14]; UINT8 totallen = 0; UINT8 distlen = 0; @@ -1001,9 +1001,9 @@ static INT32 K_FindUseodds(player_t *player, fixed_t mashed, INT32 pdis, INT32 b SETUPDISTTABLE(6,3); SETUPDISTTABLE(7,1); - if (pdis <= 0) // (64*14) * 0 = 0 + if (pdis == 0) useodds = disttable[0]; - else if (pdis > DISTVAR * ((12 * distlen) / 14)) // (64*14) * 12 = 10752 + else if (pdis > DISTVAR * ((12 * distlen) / 14)) useodds = disttable[distlen-1]; else { @@ -1028,11 +1028,11 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) INT32 i; UINT8 pingame = 0; UINT8 roulettestop; - INT32 pdis = 0; - INT32 useodds = 0; + UINT32 pdis = 0; + UINT8 useodds = 0; INT32 spawnchance[NUMKARTRESULTS]; INT32 totalspawnchance = 0; - INT32 bestbumper = 0; + UINT8 bestbumper = 0; fixed_t mashed = 0; boolean dontforcespb = false; boolean spbrush = false; @@ -6046,7 +6046,12 @@ void K_KartUpdatePosition(player_t *player) fixed_t i; if (player->spectator || !player->mo) + { + // Ensure these are reset for spectators + player->kartstuff[k_position] = 0; + player->kartstuff[k_positiondelay] = 0; return; + } for (i = 0; i < MAXPLAYERS; i++) { @@ -9975,9 +9980,9 @@ static void K_drawDistributionDebugger(void) kp_orbinaut[4], kp_jawz[1] }; - INT32 useodds = 0; - INT32 pingame = 0, bestbumper = 0; - INT32 pdis = 0; + UINT8 useodds = 0; + UINT8 pingame = 0, bestbumper = 0; + UINT32 pdis = 0; INT32 i; INT32 x = -9, y = -9; boolean spbrush = false; From fc03012749b3d93fd6c430232a92b4796353fae9 Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Thu, 24 Oct 2019 08:21:45 -0400 Subject: [PATCH 054/105] Use facing angle while still Also comment some of my additions a little better --- src/k_kart.c | 22 ++++++++++++++++++---- src/k_waypoint.c | 41 +++++------------------------------------ 2 files changed, 23 insertions(+), 40 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 0b9eecd3b..3e3532561 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -5500,7 +5500,7 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player, boolean closest) { waypoint_t *waypoint = NULL; - if (closest) + if (closest == true) waypoint = K_GetClosestWaypointToMobj(player->mo); else waypoint = K_GetBestWaypointTouchingMobj(player->mo); @@ -5514,10 +5514,18 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player, boolean closest) if (waypoint != NULL) { boolean finishlinehack = false; - angle_t playerangle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); //player->mo->angle + angle_t playerangle = player->mo->angle; angle_t angletowaypoint = R_PointToAngle2(player->mo->x, player->mo->y, waypoint->mobj->x, waypoint->mobj->y); - angle_t angledelta = playerangle - angletowaypoint; + angle_t angledelta = ANGLE_MAX; + + if (player->mo->momx != 0 || player->mo->momy != 0) + { + // Default to facing angle if you're not moving, but use momentum angle otherwise. + playerangle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); + } + + angledelta = playerangle - angletowaypoint; if (angledelta > ANGLE_180) { @@ -5533,6 +5541,7 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player, boolean closest) } } + // The wrong way flag will use its previous value if we're facing sideways if ((angledelta > ANGLE_45) && (finishlinehack == false)) { angle_t nextbestdelta = angledelta; @@ -5557,6 +5566,7 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player, boolean closest) bestwaypoint = waypoint->nextwaypoints[i]; nextbestdelta = angledelta; + // Remove wrong way flag if we're using nextwaypoints player->kartstuff[k_wrongway] = 0; } } @@ -5581,6 +5591,7 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player, boolean closest) bestwaypoint = waypoint->prevwaypoints[i]; nextbestdelta = angledelta; + // Set wrong way flag if we're using prevwaypoints player->kartstuff[k_wrongway] = 1; } } @@ -5681,6 +5692,7 @@ static void K_UpdateDistanceFromFinishLine(player_t *const player) if ((nextwaypoint == NULL) && (player->nextwaypoint == NULL)) { // Special case: if player nextwaypoint is still NULL, we want to fix that as soon as possible, so use the closest waypoint instead. + // This will most likely only happen on map load or player spawn. nextwaypoint = K_GetPlayerNextWaypoint(player, true); } @@ -5695,6 +5707,7 @@ static void K_UpdateDistanceFromFinishLine(player_t *const player) player->starpostz = nextwaypoint->mobj->z >> FRACBITS; // player gravflip determines which way to respawn + // (should waypoints have a flip option?) player->kartstuff[k_starpostflip] = player->mo->flags2 & MF2_OBJECTFLIP; // starpostangle is to the first valid nextwaypoint for simplicity @@ -5717,7 +5730,8 @@ static void K_UpdateDistanceFromFinishLine(player_t *const player) if (nextwaypoint != NULL) { - // At this point, we don't want to update the waypoint until we touch another. + // If nextwaypoint is NULL, it means we don't want to update the waypoint until we touch another one. + // player->nextwaypoint will keep its previous value in this case. player->nextwaypoint = nextwaypoint; } diff --git a/src/k_waypoint.c b/src/k_waypoint.c index 6acaadb21..695d47201 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -265,43 +265,12 @@ waypoint_t *K_GetBestWaypointTouchingMobj(mobj_t *const mobj) (mobj->y >> FRACBITS) - (checkwaypoint->mobj->y >> FRACBITS)); checkdist = P_AproxDistance(checkdist, (mobj->z >> FRACBITS) - (checkwaypoint->mobj->z >> FRACBITS)); - // The mobj has to be touching this waypoint to update to it. - if (checkdist <= checkwaypoint->mobj->radius) + // The mobj has to be touching this waypoint to use it. + if ((checkdist <= checkwaypoint->mobj->radius) + && (checkdist < bestdist)) { -#if 0 - // This kind of algorithm may or may not be more reliable than what's below. - // But it's a little heavier, computation-wise. - // We'll see if simple closer checks work fine in netgame testing, or if it needs this. - boolean success = false; - path_t pathtofinish = {}; - success = K_PathfindToWaypoint(checkwaypoint, finishline, &pathtofinish, false, false); - - // If you're touching more than 1 waypoint, then we use the closest one to the finish line. - if (success == true) - { - // Add euclidean distance to the next waypoint to the distancetofinish - UINT32 distancetofinish; - UINT32 adddist; - - adddist = ((UINT32)checkdist) >> FRACBITS; - - distancetofinish = pathtofinish.totaldist + adddist; - Z_Free(pathtofinish.array); - - if (distancetofinish < bestdist) - { - bestwaypoint = checkwaypoint; - bestdist = checkdist; - } - } -#else - // Simple closest check - if (checkdist < bestdist) - { - bestwaypoint = checkwaypoint; - bestdist = checkdist; - } -#endif + bestwaypoint = checkwaypoint; + bestdist = checkdist; } } } From acb8d61fb627d4b5eb28ff09e1d1143769d4a1e4 Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Thu, 24 Oct 2019 08:56:03 -0400 Subject: [PATCH 055/105] These don't need to be INT32s either --- src/k_kart.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 3e3532561..eb6d68a59 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -930,8 +930,8 @@ static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean sp static UINT8 K_FindUseodds(player_t *player, fixed_t mashed, UINT32 pdis, UINT8 bestbumper, boolean spbrush) { - INT32 i; - INT32 n = 0; + UINT8 i; + UINT8 n = 0; UINT8 useodds = 0; UINT8 disttable[14]; UINT8 totallen = 0; @@ -940,7 +940,7 @@ static UINT8 K_FindUseodds(player_t *player, fixed_t mashed, UINT32 pdis, UINT8 for (i = 0; i < 8; i++) { - INT32 j; + UINT8 j; boolean available = false; if (G_BattleGametype() && i > 5) From ae72a365f36484c89c00d0ef2879c976eb0b369c Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Thu, 24 Oct 2019 20:50:27 +0200 Subject: [PATCH 056/105] Fix SPB pathing --- src/p_enemy.c | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/p_enemy.c b/src/p_enemy.c index a4684b40b..f64acbfd6 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -8714,6 +8714,7 @@ void A_SPBChase(mobj_t *actor) waypoint_t *lastwaypoint = NULL; waypoint_t *bestwaypoint = NULL; waypoint_t *nextwaypoint = NULL; + waypoint_t *tempwaypoint = NULL; actor->lastlook = -1; // Just make sure this is reset @@ -8732,13 +8733,22 @@ void A_SPBChase(mobj_t *actor) dist = P_AproxDistance(P_AproxDistance(actor->x-actor->tracer->x, actor->y-actor->tracer->y), actor->z-actor->tracer->z); // Move along the waypoints until you get close enough - if (actor->cusval > -1) + if (actor->cusval > -1 && actor->extravalue2 > 0) { // Previously set nextwaypoint lastwaypoint = K_GetWaypointFromIndex((size_t)actor->cusval); - } + tempwaypoint = K_GetBestWaypointTouchingMobj(actor); + // check if the tempwaypoint corresponds to lastwaypoint's next ID at least; + // This is to avoid situations where the SPB decides to suicide jump down a bridge because it found a COMPLETELY unrelated waypoint down there. - bestwaypoint = K_GetBestWaypointTouchingMobj(actor); + if (K_GetWaypointID(tempwaypoint) == K_GetWaypointNextID(lastwaypoint) || K_GetWaypointID(tempwaypoint) == K_GetWaypointID(lastwaypoint)) + // either our previous or curr waypoint ID, sure, take it + bestwaypoint = tempwaypoint; + else + bestwaypoint = K_GetWaypointFromIndex((size_t)actor->extravalue2); // keep going from the PREVIOUS wp. + } + else + bestwaypoint = K_GetBestWaypointTouchingMobj(actor); if (bestwaypoint == NULL && lastwaypoint == NULL) { @@ -8761,12 +8771,6 @@ void A_SPBChase(mobj_t *actor) bestwaypoint, player->nextwaypoint, useshortcuts, huntbackwards); } - if (nextwaypoint == NULL && lastwaypoint != NULL) - { - // Restore to the last nextwaypoint - nextwaypoint = lastwaypoint; - } - if (nextwaypoint != NULL) { const fixed_t xywaypointdist = P_AproxDistance( @@ -8776,6 +8780,7 @@ void A_SPBChase(mobj_t *actor) vang = R_PointToAngle2(0, actor->z, xywaypointdist, nextwaypoint->mobj->z); actor->cusval = (INT32)K_GetWaypointHeapIndex(nextwaypoint); + actor->extravalue2 = (INT32)K_GetWaypointHeapIndex(bestwaypoint); // save our last best, used above. } else { @@ -8832,6 +8837,13 @@ void A_SPBChase(mobj_t *actor) } } + // Finally, no matter what, the spb should not be able to be under the ground, or above the ceiling; + if (actor->z < actor->floorz + 8*mapobjectscale) + actor->z = actor->floorz + 8*mapobjectscale; + else if (actor->z > actor->ceilingz - 8*mapobjectscale) + actor->z = actor->ceilingz - 8*mapobjectscale; + + return; } From 08ae2c926edf4696bea11e0256ac3e0200b5ad6a Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Thu, 24 Oct 2019 21:00:58 +0200 Subject: [PATCH 057/105] Accidentally removed a bit of code I shouldn't have removed --- src/p_enemy.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/p_enemy.c b/src/p_enemy.c index f64acbfd6..65a66fdb5 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -8771,6 +8771,13 @@ void A_SPBChase(mobj_t *actor) bestwaypoint, player->nextwaypoint, useshortcuts, huntbackwards); } + if (nextwaypoint == NULL && lastwaypoint != NULL) + { + // Restore to the last nextwaypoint + nextwaypoint = lastwaypoint; + } + + if (nextwaypoint != NULL) { const fixed_t xywaypointdist = P_AproxDistance( From b69d105af3d446e1a537c5beac2aeff650c61153 Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Thu, 24 Oct 2019 16:35:39 -0400 Subject: [PATCH 058/105] Add respawn animation This commit also fixes MF_NOCLIP not working properly on players. Sink now spawns an SPB explosion on successful hit, since dying is no longer a harsh punishment. --- src/d_netcmd.c | 14 ++-- src/dehacked.c | 5 ++ src/g_game.c | 5 +- src/info.c | 7 +- src/info.h | 5 ++ src/k_kart.c | 185 +++++++++++++++++++++++++++++++++++++++++-------- src/k_kart.h | 1 + src/m_cheat.c | 9 +++ src/p_enemy.c | 4 +- src/p_map.c | 26 +++++-- src/p_mobj.c | 8 ++- src/p_spec.c | 12 ++-- src/p_user.c | 6 -- 13 files changed, 227 insertions(+), 60 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index c19f8e391..39434928b 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2842,13 +2842,15 @@ static void Got_Respawn(UINT8 **cp, INT32 playernum) return; } - // incase the above checks were modified to allow sending a respawn on these occasions: - if (players[respawnplayer].mo && !P_IsObjectOnGround(players[respawnplayer].mo)) - return; - if (players[respawnplayer].mo) - P_DamageMobj(players[respawnplayer].mo, NULL, NULL, 10000); - demo_extradata[playernum] |= DXD_RESPAWN; + { + // incase the above checks were modified to allow sending a respawn on these occasions: + if (!P_IsObjectOnGround(players[respawnplayer].mo)) + return; + + K_DoIngameRespawn(&players[respawnplayer]); + demo_extradata[playernum] |= DXD_RESPAWN; + } } /** Deals with an ::XD_RANDOMSEED message in a netgame. diff --git a/src/dehacked.c b/src/dehacked.c index fb6e7ff4f..6b99eeb7c 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -6661,6 +6661,11 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit // DEZ respawn laser "S_DEZLASER", + "S_DEZLASER_TRAIL1", + "S_DEZLASER_TRAIL2", + "S_DEZLASER_TRAIL3", + "S_DEZLASER_TRAIL4", + "S_DEZLASER_TRAIL5", // Audience Members "S_RANDOMAUDIENCE", diff --git a/src/g_game.c b/src/g_game.c index 0b58855a0..ad41f13af 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -4915,7 +4915,10 @@ void G_ReadDemoExtraData(void) if (extradata & DXD_RESPAWN) { if (players[p].mo) - P_DamageMobj(players[p].mo, NULL, NULL, 10000); // Is this how this should work..? + { + // Is this how this should work..? + K_DoIngameRespawn(&players[p]); + } } if (extradata & DXD_SKIN) { diff --git a/src/info.c b/src/info.c index 60ffbc626..943c0bbb9 100644 --- a/src/info.c +++ b/src/info.c @@ -2875,7 +2875,12 @@ state_t states[NUMSTATES] = {SPR_KBLN, FF_FULLBRIGHT|1, -1, {NULL}, 0, 0, S_BATTLEBUMPER2}, // S_BATTLEBUMPER2 {SPR_KBLN, FF_FULLBRIGHT|2, -1, {NULL}, 0, 0, S_BATTLEBUMPER3}, // S_BATTLEBUMPER3 - {SPR_DEZL, FF_FULLBRIGHT|FF_PAPERSPRITE, 8, {NULL}, 0, 0, S_NULL}, // S_DEZLASER + {SPR_DEZL, FF_FULLBRIGHT|FF_PAPERSPRITE, 8, {NULL}, 0, 0, S_NULL}, // S_DEZLASER + {SPR_DEZL, FF_FULLBRIGHT|1, 2, {NULL}, 0, 0, S_DEZLASER_TRAIL2}, // S_DEZLASER_TRAIL1 + {SPR_DEZL, FF_FULLBRIGHT|2, 2, {NULL}, 0, 0, S_DEZLASER_TRAIL3}, // S_DEZLASER_TRAIL2 + {SPR_DEZL, FF_FULLBRIGHT|FF_PAPERSPRITE|3, 4, {NULL}, 0, 0, S_DEZLASER_TRAIL4}, // S_DEZLASER_TRAIL3 + {SPR_DEZL, FF_FULLBRIGHT|2, 2, {NULL}, 0, 0, S_DEZLASER_TRAIL5}, // S_DEZLASER_TRAIL4 + {SPR_DEZL, FF_FULLBRIGHT|1, 2, {NULL}, 0, 0, S_NULL}, // S_DEZLASER_TRAIL5 {SPR_NULL, 0, 1, {A_RandomStateRange}, S_AUDIENCE_CHAO_CHEER1, S_AUDIENCE_CHAO_CHEER2, S_RANDOMAUDIENCE}, // S_RANDOMAUDIENCE diff --git a/src/info.h b/src/info.h index 27488d916..f47a4a151 100644 --- a/src/info.h +++ b/src/info.h @@ -3550,6 +3550,11 @@ typedef enum state // DEZ Laser respawn S_DEZLASER, + S_DEZLASER_TRAIL1, + S_DEZLASER_TRAIL2, + S_DEZLASER_TRAIL3, + S_DEZLASER_TRAIL4, + S_DEZLASER_TRAIL5, // Audience Members S_RANDOMAUDIENCE, diff --git a/src/k_kart.c b/src/k_kart.c index eb6d68a59..936259d8f 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1919,6 +1919,29 @@ static void K_SpawnBrakeDriftSparks(player_t *player) // Be sure to update the m sparks->flags2 |= MF2_DONTDRAW; } +/** \brief Preps a player to respawn + + \param player player to respawn + + \return void +*/ +void K_DoIngameRespawn(player_t *player) +{ + if (!player->mo || P_MobjWasRemoved(player->mo)) + return; + + if (player->kartstuff[k_respawn]) + return; + + if (leveltime <= starttime) + return; + + player->mo->flags &= ~(MF_SOLID|MF_SHOOTABLE); + player->mo->flags |= MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING|MF_NOGRAVITY; + player->mo->flags2 &= ~MF2_DONTDRAW; + player->kartstuff[k_respawn] = 48; +} + /** \brief Calculates the respawn timer and drop-boosting \param player player object passed from K_KartPlayerThink @@ -1934,39 +1957,136 @@ void K_RespawnChecker(player_t *player) if (player->kartstuff[k_respawn] > 1) { - player->kartstuff[k_respawn]--; - player->mo->momz = 0; + fixed_t destx = 0, desty = 0, destz = 0; + + player->mo->momx = player->mo->momy = player->mo->momz = 0; player->powers[pw_flashing] = 2; player->powers[pw_nocontrol] = 2; - if (leveltime % 8 == 0) + + if (leveltime % 8 == 0 && !mapreset) + S_StartSound(player->mo, sfx_s3kcas); + + destx = (player->starpostx << FRACBITS); + desty = (player->starposty << FRACBITS); + destz = (player->starpostz << FRACBITS); + + if (player->kartstuff[k_starpostflip]) + destz -= (128 * mapobjectscale) + (player->mo->height); + else + destz += (128 * mapobjectscale); + + if (player->mo->x != destx || player->mo->y != desty || player->mo->z != destz) { - INT32 i; - if (!mapreset) - S_StartSound(player->mo, sfx_s3kcas); + fixed_t step = 64*mapobjectscale; + fixed_t dist = P_AproxDistance(P_AproxDistance(player->mo->x - destx, player->mo->y - desty), player->mo->z - destz); - for (i = 0; i < 8; i++) + if (dist <= step) // You're ready to respawn { - mobj_t *mo; - angle_t newangle; - fixed_t newx, newy, newz; + P_TryMove(player->mo, destx, desty, true); + player->mo->z = destz; + } + else + { + fixed_t stepx = 0, stepy = 0, stepz = 0; + angle_t stepha = R_PointToAngle2(player->mo->x, player->mo->y, destx, desty); + angle_t stepva = R_PointToAngle2(0, player->mo->z, P_AproxDistance(player->mo->x - destx, player->mo->y - desty), destz); + fixed_t laserx = 0, lasery = 0, laserz = 0; + UINT8 lasersteps = 4; - newangle = FixedAngle(((360/8)*i)*FRACUNIT); - newx = player->mo->x + P_ReturnThrustX(player->mo, newangle, 31<mo->y + P_ReturnThrustY(player->mo, newangle, 31<mo->eflags & MFE_VERTICALFLIP) - newz = player->mo->z + player->mo->height; - else - newz = player->mo->z; + // Move toward the respawn point + stepx = FixedMul(FixedMul(FINECOSINE(stepha >> ANGLETOFINESHIFT), step), FINECOSINE(stepva >> ANGLETOFINESHIFT)); + stepy = FixedMul(FixedMul(FINESINE(stepha >> ANGLETOFINESHIFT), step), FINECOSINE(stepva >> ANGLETOFINESHIFT)); + stepz = FixedMul(FINESINE(stepva >> ANGLETOFINESHIFT), 2*step); - mo = P_SpawnMobj(newx, newy, newz, MT_DEZLASER); - if (mo) + P_TryMove(player->mo, player->mo->x + stepx, player->mo->y + stepy, true); + player->mo->z += stepz; + + // Spawn lasers along the path + laserx = player->mo->x + (stepx / 2); + lasery = player->mo->y + (stepy / 2); + laserz = player->mo->z + (stepz / 2); + dist = P_AproxDistance(P_AproxDistance(laserx - destx, lasery - desty), laserz - destz); + + if (dist > step/2) { + while (lasersteps) + { + + stepha = R_PointToAngle2(laserx, lasery, destx, desty); + stepva = R_PointToAngle2(0, laserz, P_AproxDistance(laserx - destx, lasery - desty), destz); + + stepx = FixedMul(FixedMul(FINECOSINE(stepha >> ANGLETOFINESHIFT), step), FINECOSINE(stepva >> ANGLETOFINESHIFT)); + stepy = FixedMul(FixedMul(FINESINE(stepha >> ANGLETOFINESHIFT), step), FINECOSINE(stepva >> ANGLETOFINESHIFT)); + stepz = FixedMul(FINESINE(stepva >> ANGLETOFINESHIFT), 2*step); + + laserx += stepx; + lasery += stepy; + laserz += stepz; + dist = P_AproxDistance(P_AproxDistance(laserx - destx, lasery - desty), laserz - destz); + + if (dist <= step/2) + break; + + lasersteps--; + } + } + + if (lasersteps == 0) // Don't spawn them beyond the respawn point. + { + mobj_t *laser; + + laser = P_SpawnMobj(laserx, lasery, laserz + (player->mo->height / 2), MT_DEZLASER); + + if (laser && !P_MobjWasRemoved(laser)) + { + P_SetMobjState(laser, S_DEZLASER_TRAIL1); + if (player->mo->eflags & MFE_VERTICALFLIP) + laser->eflags |= MFE_VERTICALFLIP; + P_SetTarget(&laser->target, player->mo); + laser->angle = stepha + ANGLE_90; + P_SetScale(laser, (laser->destscale = FRACUNIT)); + } + } + } + } + else + { + player->kartstuff[k_respawn]--; + + player->mo->flags |= MF_SOLID|MF_SHOOTABLE; + player->mo->flags &= ~(MF_NOCLIPHEIGHT|MF_NOCLIPTHING|MF_NOGRAVITY); + if (!(player->pflags & PF_NOCLIP)) + player->mo->flags &= ~MF_NOCLIP; + + if (leveltime % 8 == 0) + { + INT32 i; + + for (i = 0; i < 8; i++) + { + mobj_t *laser; + angle_t newangle; + fixed_t newx, newy, newz; + + newangle = FixedAngle(((360/8)*i)*FRACUNIT); + newx = player->mo->x + P_ReturnThrustX(player->mo, newangle, 31<mo->y + P_ReturnThrustY(player->mo, newangle, 31<mo->eflags & MFE_VERTICALFLIP) - mo->eflags |= MFE_VERTICALFLIP; - P_SetTarget(&mo->target, player->mo); - mo->angle = newangle+ANGLE_90; - mo->momz = (8<mo); - P_SetScale(mo, (mo->destscale = FRACUNIT)); + newz = player->mo->z + player->mo->height; + else + newz = player->mo->z; + + laser = P_SpawnMobj(newx, newy, newz, MT_DEZLASER); + + if (laser && !P_MobjWasRemoved(laser)) + { + if (player->mo->eflags & MFE_VERTICALFLIP) + laser->eflags |= MFE_VERTICALFLIP; + P_SetTarget(&laser->target, player->mo); + laser->angle = newangle+ANGLE_90; + laser->momz = (8<mo); + P_SetScale(laser, (laser->destscale = FRACUNIT)); + } } } } @@ -5313,8 +5433,15 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) player->kartstuff[k_stolentimer]--; if (player->kartstuff[k_squishedtimer]) + { player->kartstuff[k_squishedtimer]--; + if ((player->kartstuff[k_squishedtimer] == 0) && !(player->pflags & PF_NOCLIP)) + { + player->mo->flags &= ~MF_NOCLIP; + } + } + if (player->kartstuff[k_justbumped]) player->kartstuff[k_justbumped]--; @@ -5698,10 +5825,12 @@ static void K_UpdateDistanceFromFinishLine(player_t *const player) if ((nextwaypoint != NULL) && (nextwaypoint != player->nextwaypoint) && + (player->kartstuff[k_respawn] == 0) && (K_GetWaypointIsShortcut(nextwaypoint) == false) && (K_GetWaypointIsEnabled(nextwaypoint) == true)) { size_t i = 0U; waypoint_t *aimwaypoint = NULL; + player->starpostx = nextwaypoint->mobj->x >> FRACBITS; player->starposty = nextwaypoint->mobj->y >> FRACBITS; player->starpostz = nextwaypoint->mobj->z >> FRACBITS; @@ -6895,13 +7024,9 @@ void K_MoveKartPlayer(player_t *player, boolean onground) // Squishing // If a Grow player or a sector crushes you, get flattened instead of being killed. - if (player->kartstuff[k_squishedtimer] <= 0) + if (player->kartstuff[k_squishedtimer] > 0) { - player->mo->flags &= ~MF_NOCLIP; - } - else - { - player->mo->flags |= MF_NOCLIP; + //player->mo->flags |= MF_NOCLIP; player->mo->momx = 0; player->mo->momy = 0; } diff --git a/src/k_kart.h b/src/k_kart.h index b91e8c8a1..ff6f6fafc 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -27,6 +27,7 @@ void K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean solid) void K_KartPainEnergyFling(player_t *player); void K_FlipFromObject(mobj_t *mo, mobj_t *master); void K_MatchGenericExtraFlags(mobj_t *mo, mobj_t *master); +void K_DoIngameRespawn(player_t *player); void K_RespawnChecker(player_t *player); void K_KartMoveAnimation(player_t *player); void K_KartPlayerHUDUpdate(player_t *player); diff --git a/src/m_cheat.c b/src/m_cheat.c index c24a8014b..d61d22cd0 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -292,9 +292,18 @@ void Command_CheatNoClip_f(void) REQUIRE_NOULTIMATE; plyr = &players[consoleplayer]; + + if (!plyr->mo || P_MobjWasRemoved(plyr->mo)) + return; + plyr->pflags ^= PF_NOCLIP; CONS_Printf(M_GetText("No Clipping %s\n"), plyr->pflags & PF_NOCLIP ? M_GetText("On") : M_GetText("Off")); + if (plyr->pflags & PF_NOCLIP) + plyr->mo->flags |= MF_NOCLIP; + else + plyr->mo->flags &= ~MF_NOCLIP; + G_SetGameModified(multiplayer, true); } diff --git a/src/p_enemy.c b/src/p_enemy.c index a4684b40b..86e615ee8 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -10673,8 +10673,8 @@ void A_RemoteDamage(mobj_t *actor) if (locvar2 == 1) // Kill mobj! { - if (target->player) // players die using P_DamageMobj instead for some reason - P_DamageMobj(target, source, source, 10000); + if (target->player) + K_DoIngameRespawn(target->player); else P_KillMobj(target, source, source); } diff --git a/src/p_map.c b/src/p_map.c index 9585849f7..52a19e1c0 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -1005,12 +1005,20 @@ static boolean PIT_CheckThing(mobj_t *thing) if (thing->type == MT_PLAYER) { - S_StartSound(NULL, sfx_bsnipe); //let all players hear it. + mobj_t *explosion; + + S_StartSound(NULL, sfx_bsnipe); // let all players hear it. + HU_SetCEchoFlags(0); HU_SetCEchoDuration(5); HU_DoCEcho(va("%s\\was hit by a kitchen sink.\\\\\\\\", player_names[thing->player-players])); I_OutputMsg("%s was hit by a kitchen sink.\n", player_names[thing->player-players]); - P_DamageMobj(thing, tmthing, tmthing->target, 10000); + + explosion = P_SpawnMobj(thing->x, thing->y, thing->z, MT_SPBEXPLOSION); + explosion->extravalue1 = 1; // Tell K_ExplodePlayer to use extra knockback + if (tmthing->target && !P_MobjWasRemoved(tmthing->target)) + P_SetTarget(&explosion->target, tmthing->target); + P_KillMobj(tmthing, thing, thing); } @@ -1269,15 +1277,23 @@ static boolean PIT_CheckThing(mobj_t *thing) } else if (thing->type == MT_SINK) { + mobj_t *explosion; + if ((thing->target == tmthing) && (thing->threshold > 0)) return true; - S_StartSound(NULL, sfx_cgot); //let all players hear it. + S_StartSound(NULL, sfx_bsnipe); // let all players hear it. + HU_SetCEchoFlags(0); HU_SetCEchoDuration(5); HU_DoCEcho(va("%s\\was hit by a kitchen sink.\\\\\\\\", player_names[tmthing->player-players])); I_OutputMsg("%s was hit by a kitchen sink.\n", player_names[tmthing->player-players]); - P_DamageMobj(tmthing, thing, thing->target, 10000); + + explosion = P_SpawnMobj(tmthing->x, tmthing->y, tmthing->z, MT_SPBEXPLOSION); + explosion->extravalue1 = 1; // Tell K_ExplodePlayer to use extra knockback + if (thing->target && !P_MobjWasRemoved(thing->target)) + P_SetTarget(&explosion->target, thing->target); + P_KillMobj(thing, tmthing, tmthing); } @@ -1662,7 +1678,7 @@ static boolean PIT_CheckThing(mobj_t *thing) { // Objects kill you if it falls from above. if (thing != tmthing->target) - P_DamageMobj(thing, tmthing, tmthing->target, 10000); + K_DoIngameRespawn(thing->player); tmthing->momz = -tmthing->momz/2; // Bounce, just for fun! // The tmthing->target allows the pusher of the object diff --git a/src/p_mobj.c b/src/p_mobj.c index b0e378649..b04bf717a 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -11475,6 +11475,9 @@ void P_SpawnPlayer(INT32 playernum) // Spawn with a pity shield if necessary. //P_DoPityCheck(p); + if (p->kartstuff[k_respawn] != 0) + p->mo->flags |= MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING|MF_NOGRAVITY; + if (G_BattleGametype()) // SRB2kart { mobj_t *overheadarrow = P_SpawnMobj(mobj->x, mobj->y, mobj->z + P_GetPlayerHeight(p)+16*FRACUNIT, MT_PLAYERARROW); @@ -11691,11 +11694,10 @@ void P_MovePlayerToStarpost(INT32 playernum) sector->ceilingheight; if (mobj->player->kartstuff[k_starpostflip]) - z = (p->starpostz<height; + z = (p->starpostz<height; else - z = (p->starpostz<starpostz<starpostz + 128) << FRACBITS; // reverse gravity exists, pls mobj->player->kartstuff[k_starpostflip] = 0; if (z < floor) diff --git a/src/p_spec.c b/src/p_spec.c index b71d7508c..f83ca79a7 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -3777,10 +3777,10 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers case 6: // Death Pit (Camera Mod) case 7: // Death Pit (No Camera Mod) if (roversector || P_MobjReadyToTrigger(player->mo, sector)) - P_DamageMobj(player->mo, NULL, NULL, 10000); + K_DoIngameRespawn(player); break; case 8: // Instant Kill - P_DamageMobj(player->mo, NULL, NULL, 10000); + K_DoIngameRespawn(player); break; case 9: // Ring Drainer (Floor Touch) case 10: // Ring Drainer (No Floor Touch) @@ -6954,8 +6954,8 @@ void T_Scroll(scroll_t *s) height = P_GetSpecialBottomZ(thing, sec, psec); - if (!(thing->flags & MF_NOCLIP)) // Thing must be clipped - if (!(thing->flags & MF_NOGRAVITY || thing->z+thing->height != height)) // Thing must a) be non-floating and have z+height == height + if (!(thing->flags & MF_NOCLIP) && // Thing must be clipped + (!(thing->flags & MF_NOGRAVITY || thing->z+thing->height != height))) // Thing must a) be non-floating and have z+height == height { // Move objects only if on floor // non-floating, and clipped. @@ -7030,8 +7030,8 @@ void T_Scroll(scroll_t *s) height = P_GetSpecialTopZ(thing, sec, psec); - if (!(thing->flags & MF_NOCLIP)) // Thing must be clipped - if (!(thing->flags & MF_NOGRAVITY || thing->z != height))// Thing must a) be non-floating and have z == height + if (!(thing->flags & MF_NOCLIP) && // Thing must be clipped + (!(thing->flags & MF_NOGRAVITY || thing->z != height))) // Thing must a) be non-floating and have z == height { // Move objects only if on floor or underwater, // non-floating, and clipped. diff --git a/src/p_user.c b/src/p_user.c index 16aeeb054..6267559e0 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8047,12 +8047,6 @@ void P_PlayerThink(player_t *player) // The timer might've reached zero, but we'll run the remote view camera anyway by setting it to -1. } - /// \note do this in the cheat code - if (player->pflags & PF_NOCLIP) - player->mo->flags |= MF_NOCLIP; - else - player->mo->flags &= ~MF_NOCLIP; - cmd = &player->cmd; // SRB2kart From ed007f34ccb183559e44752b1c2b51e567f0750f Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Thu, 24 Oct 2019 16:55:13 -0400 Subject: [PATCH 059/105] change colors, since orange & yellow are pretty indistinguishable --- src/k_waypoint.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/k_waypoint.c b/src/k_waypoint.c index 695d47201..ab3bd8be9 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -421,11 +421,11 @@ void K_DebugWaypointsVisualise(void) } else if (waypoint->numnextwaypoints == 0 || waypoint->numprevwaypoints == 0) { - debugmobj->color = SKINCOLOR_ORANGE; + debugmobj->color = SKINCOLOR_YELLOW; } else if (waypoint == players[displayplayers[0]].nextwaypoint) { - debugmobj->color = SKINCOLOR_YELLOW; + debugmobj->color = SKINCOLOR_GREEN; } else { From 3686445f2f378be529acd43eecbe360a5ba7c9f1 Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Thu, 24 Oct 2019 17:11:27 -0400 Subject: [PATCH 060/105] Snap to bottom on WRONG WAY --- src/k_kart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index 936259d8f..ccc9ff447 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -10426,7 +10426,7 @@ void K_drawKartHUD(void) } if (stplyr->kartstuff[k_wrongway] && ((leveltime / 8) & 1)) - V_DrawCenteredString(BASEVIDWIDTH>>1, 176, V_REDMAP, "WRONG WAY"); + V_DrawCenteredString(BASEVIDWIDTH>>1, 176, V_REDMAP|V_SNAPTOBOTTOM, "WRONG WAY"); if (cv_kartdebugdistribution.value) K_drawDistributionDebugger(); From 6fbaa72e824426356a77e4ce3b0503d116e3b487 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Thu, 24 Oct 2019 23:35:05 +0200 Subject: [PATCH 061/105] Lower SPB speed, add SPB on minimap, add slight targetting on SPB against non-1st --- src/info.c | 2 +- src/k_kart.c | 17 ++++++++++++++--- src/p_enemy.c | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/src/info.c b/src/info.c index 60ffbc626..19deea636 100644 --- a/src/info.c +++ b/src/info.c @@ -15998,7 +15998,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_SPB_DEAD, // deathstate S_NULL, // xdeathstate sfx_s3k5d, // deathsound - 96*FRACUNIT, // speed + 64*FRACUNIT, // speed 24*FRACUNIT, // radius 48*FRACUNIT, // height 0, // display offset diff --git a/src/k_kart.c b/src/k_kart.c index eb6d68a59..02214a832 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -4985,6 +4985,9 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) K_UpdateDraft(player); K_UpdateEngineSounds(player, cmd); // Thanks, VAda! + if (spbplace == -1) // no spb + player->axis1 = NULL; // remove this + // update boost angle if not spun out if (!player->kartstuff[k_spinouttimer] && !player->kartstuff[k_wipeoutslow]) player->kartstuff[k_boostangle] = (INT32)player->mo->angle; @@ -5519,7 +5522,7 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player, boolean closest) R_PointToAngle2(player->mo->x, player->mo->y, waypoint->mobj->x, waypoint->mobj->y); angle_t angledelta = ANGLE_MAX; - if (player->mo->momx != 0 || player->mo->momy != 0) + if (player->mo->momx != 0 || player->mo->momy != 0) { // Default to facing angle if you're not moving, but use momentum angle otherwise. playerangle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); @@ -9417,7 +9420,11 @@ static void K_drawKartMinimap(void) // Target reticule if ((G_RaceGametype() && players[i].kartstuff[k_position] == spbplace) || (G_BattleGametype() && K_IsPlayerWanted(&players[i]))) - K_drawKartMinimapIcon(players[i].mo->x, players[i].mo->y, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic); + K_drawKartMinimapIcon(players[i].mo->x, players[i].mo->y, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic); + + if (players[i].axis1 && !P_MobjWasRemoved(players[i].axis1)) // SPB after the player? + K_drawKartMinimapIcon(players[i].axis1->x, players[i].axis1->y, x, y, splitflags, kp_ringspblocksmall[14 + leveltime%4 /2], NULL, AutomapPic); + } } @@ -9450,7 +9457,11 @@ static void K_drawKartMinimap(void) // Target reticule if ((G_RaceGametype() && players[localplayers[i]].kartstuff[k_position] == spbplace) || (G_BattleGametype() && K_IsPlayerWanted(&players[localplayers[i]]))) - K_drawKartMinimapIcon(players[localplayers[i]].mo->x, players[localplayers[i]].mo->y, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic); + K_drawKartMinimapIcon(players[localplayers[i]].mo->x, players[localplayers[i]].mo->y, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic); + + if (players[localplayers[i]].axis1 && !P_MobjWasRemoved(players[localplayers[i]].axis1)) // SPB after the player? + K_drawKartMinimapIcon(players[localplayers[i]].axis1->x, players[localplayers[i]].axis1->y, x, y, splitflags, kp_ringspblocksmall[14 + leveltime%4 /2], NULL, AutomapPic); + } } diff --git a/src/p_enemy.c b/src/p_enemy.c index 65a66fdb5..d896aa485 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -8495,11 +8495,15 @@ static void SpawnSPBTrailRings(mobj_t *actor) void A_SPBChase(mobj_t *actor) { player_t *player = NULL; + player_t *scplayer = NULL; // secondary target for seeking UINT8 i; UINT8 bestrank = UINT8_MAX; fixed_t dist; angle_t hang, vang; fixed_t wspeed, xyspeed, zspeed; + fixed_t pdist = 1536<axis1, actor); // axis is the SPB trailing the player. + // lastlook = last player num targetted // cvmem = stored speed // cusval = next waypoint heap index @@ -8828,10 +8835,36 @@ void A_SPBChase(mobj_t *actor) actor->movedir += input; } + actor->momx = FixedMul(FixedMul(xyspeed, FINECOSINE(actor->angle>>ANGLETOFINESHIFT)), FINECOSINE(actor->movedir>>ANGLETOFINESHIFT)); actor->momy = FixedMul(FixedMul(xyspeed, FINESINE(actor->angle>>ANGLETOFINESHIFT)), FINECOSINE(actor->movedir>>ANGLETOFINESHIFT)); actor->momz = FixedMul(zspeed, FINESINE(actor->movedir>>ANGLETOFINESHIFT)); + // see if a player is near us, if they are, try to hit them by slightly thrusting towards them, otherwise, bleh! + for (i=0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator || players[i].exiting) + continue; // not in-game + + if (R_PointToDist2(actor->x, actor->y, players[i].mo->x, players[i].mo->y) < pdist) + { + pdist = R_PointToDist2(actor->x, actor->y, players[i].mo->x, players[i].mo->y); + scplayer = &players[i]; // it doesn't matter if we override this guy now. + } + } + + // different player from our main target, try and ram into em~! + if (scplayer && scplayer != player) + { + pangle = actor->angle - R_PointToAngle2(actor->x, actor->y,scplayer->mo->x, scplayer->mo->y); + // check if the angle wouldn't make us LOSE speed... + if ((INT32)pangle/ANG1 >= -80 && (INT32)pangle/ANG1 <= 80) // allow for around 80 degrees + { + // Thrust us towards the guy, try to screw em up! + P_Thrust(actor, R_PointToAngle2(actor->x, actor->y, scplayer->mo->x, scplayer->mo->y), actor->movefactor/4); // not too fast though. + } + } + // Spawn a trail of rings behind the SPB! SpawnSPBTrailRings(actor); From 151e1c9dfe837edd68b02e7a05f5f834d52bc5b4 Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Thu, 24 Oct 2019 19:15:22 -0400 Subject: [PATCH 062/105] Reduce DISTVAR, don't give SPB toward the end of the track --- src/k_kart.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index ccc9ff447..5d903cbc8 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -709,7 +709,7 @@ static INT32 K_KartItemOddsBattle[NUMKARTRESULTS][6] = /*Jawz x2*/ { 0, 0, 1, 2, 4, 2 } // Jawz x2 }; -#define DISTVAR (4096) // Magic number distance for use with item roulette tiers +#define DISTVAR (2048) // Magic number distance for use with item roulette tiers /** \brief Item Roulette for Kart @@ -897,7 +897,8 @@ static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean sp POWERITEMODDS(newodds); break; case KITEM_SPB: - if ((indirectitemcooldown > 0) || (pexiting > 0) || (secondist/DISTVAR < 3)) + if ((indirectitemcooldown > 0) || (secondist/DISTVAR < 3) + || (first != -1 && players[first].distancetofinish > 8*DISTVAR)) // No SPB near the end of the race newodds = 0; else newodds *= min((secondist/DISTVAR)-4, 3); // POWERITEMODDS(newodds); From 55c2fe31bebcc00e9ca6d4c8ea12b12f3d4fc2b5 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Sat, 26 Oct 2019 13:27:43 +0200 Subject: [PATCH 063/105] SPB on floor --- src/p_enemy.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/p_enemy.c b/src/p_enemy.c index 901589dd9..e18f585be 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -8878,10 +8878,10 @@ void A_SPBChase(mobj_t *actor) } // Finally, no matter what, the spb should not be able to be under the ground, or above the ceiling; - if (actor->z < actor->floorz + 8*mapobjectscale) - actor->z = actor->floorz + 8*mapobjectscale; - else if (actor->z > actor->ceilingz - 8*mapobjectscale) - actor->z = actor->ceilingz - 8*mapobjectscale; + if (actor->z < actor->floorz) + actor->z = actor->floorz; + else if (actor->z > actor->ceilingz - actor->height) + actor->z = actor->ceilingz - actor->height; return; From 58bed7d9231dbc85accd8e1f14f83b8585672b12 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Sat, 26 Oct 2019 17:08:22 +0200 Subject: [PATCH 064/105] New item list + shrink destroys items --- src/k_kart.c | 66 +++++++++++++++++++++++++++++++++++---------- src/p_enemy.c | 3 --- src/p_mobj.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/p_mobj.h | 9 +++++++ src/p_saveg.c | 28 ++++++++++++++++--- src/p_tick.c | 4 +++ 6 files changed, 164 insertions(+), 21 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index ef71558a2..113012a5d 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2011,7 +2011,7 @@ void K_RespawnChecker(player_t *player) { while (lasersteps) { - + stepha = R_PointToAngle2(laserx, lasery, destx, desty); stepva = R_PointToAngle2(0, laserz, P_AproxDistance(laserx - destx, lasery - desty), destz); @@ -4109,6 +4109,7 @@ void K_DoSneaker(player_t *player, INT32 type) static void K_DoShrink(player_t *user) { INT32 i; + mobj_t *mobj, *next; S_StartSound(user->mo, sfx_kc46); // Sound the BANG! user->pflags |= PF_ATTACKDOWN; @@ -4146,6 +4147,43 @@ static void K_DoShrink(player_t *user) } } } + + // kill everything in the kitem list while we're at it: + for (mobj = kitemcap; mobj; mobj = next) + { + next = mobj->itnext; + + // check if the item is being held by a player behind us before removing it. + // check if the item is a "shield" first, bc i'm p sure thrown items keep the player that threw em as target anyway + + if (mobj->type == MT_BANANA_SHIELD || mobj->type == MT_JAWZ_SHIELD || + mobj->type == MT_SSMINE_SHIELD || mobj->type == MT_EGGMANITEM_SHIELD || + mobj->type == MT_SINK_SHIELD || mobj->type == MT_ORBINAUT_SHIELD) + { + if (mobj->target && mobj->target->player) + { + if (mobj->target->player->kartstuff[k_position] > user->kartstuff[k_position]) + continue; // this guy's behind us, don't take his stuff away! + } + } + + // @TODO: This should probably go into the P_KillMobj code for items? + + if (mobj->eflags & MFE_VERTICALFLIP) + mobj->z -= mobj->height; + else + mobj->z += mobj->height; + + S_StartSound(mobj, mobj->info->deathsound); + P_KillMobj(mobj, user->mo, user->mo); + + P_SetObjectMomZ(mobj, 8*FRACUNIT, false); + P_InstaThrust(mobj, (angle_t)P_RandomRange(0, 359)*ANG1, 16*FRACUNIT); + + if (mobj->type == MT_SPB) + spbplace = -1; + + } } @@ -4318,6 +4356,7 @@ void K_DropHnextList(player_t *player) dropwork = P_SpawnMobj(work->x, work->y, work->z, type); P_SetTarget(&dropwork->target, player->mo); + P_AddKartItem(dropwork); // needs to be called here so shrink can bust items off players in front of the user. dropwork->angle = work->angle; dropwork->flags2 = work->flags2; dropwork->flags |= MF_NOCLIPTHING; @@ -5105,9 +5144,6 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) K_UpdateDraft(player); K_UpdateEngineSounds(player, cmd); // Thanks, VAda! - if (spbplace == -1) // no spb - player->axis1 = NULL; // remove this - // update boost angle if not spun out if (!player->kartstuff[k_spinouttimer] && !player->kartstuff[k_wipeoutslow]) player->kartstuff[k_boostangle] = (INT32)player->mo->angle; @@ -9394,7 +9430,8 @@ static void K_drawKartMinimap(void) UINT8 skin = 0; UINT8 *colormap = NULL; SINT8 localplayers[4]; - SINT8 numlocalplayers = 0; + SINT8 numlocalplayers = 0; + mobj_t *mobj, *next; // for SPB drawing (or any other item(s) we may wanna draw, I dunno!) // Draw the HUD only when playing in a level. // hu_stuff needs this, unlike st_stuff. @@ -9545,11 +9582,7 @@ static void K_drawKartMinimap(void) // Target reticule if ((G_RaceGametype() && players[i].kartstuff[k_position] == spbplace) || (G_BattleGametype() && K_IsPlayerWanted(&players[i]))) - K_drawKartMinimapIcon(players[i].mo->x, players[i].mo->y, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic); - - if (players[i].axis1 && !P_MobjWasRemoved(players[i].axis1)) // SPB after the player? - K_drawKartMinimapIcon(players[i].axis1->x, players[i].axis1->y, x, y, splitflags, kp_ringspblocksmall[14 + leveltime%4 /2], NULL, AutomapPic); - + K_drawKartMinimapIcon(players[i].mo->x, players[i].mo->y, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic); } } @@ -9582,12 +9615,17 @@ static void K_drawKartMinimap(void) // Target reticule if ((G_RaceGametype() && players[localplayers[i]].kartstuff[k_position] == spbplace) || (G_BattleGametype() && K_IsPlayerWanted(&players[localplayers[i]]))) - K_drawKartMinimapIcon(players[localplayers[i]].mo->x, players[localplayers[i]].mo->y, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic); + K_drawKartMinimapIcon(players[localplayers[i]].mo->x, players[localplayers[i]].mo->y, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic); + } - if (players[localplayers[i]].axis1 && !P_MobjWasRemoved(players[localplayers[i]].axis1)) // SPB after the player? - K_drawKartMinimapIcon(players[localplayers[i]].axis1->x, players[localplayers[i]].axis1->y, x, y, splitflags, kp_ringspblocksmall[14 + leveltime%4 /2], NULL, AutomapPic); + // draw SPB(s?) + for (mobj = kitemcap; mobj; mobj = next) + { + next = mobj->itnext; + if (mobj->type == MT_SPB) + K_drawKartMinimapIcon(mobj->x, mobj->y, x, y, splitflags, kp_ringspblocksmall[14 + leveltime%4 /2], NULL, AutomapPic); + } - } } static void K_drawKartStartCountdown(void) diff --git a/src/p_enemy.c b/src/p_enemy.c index e18f585be..ff57b0ffa 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -8543,9 +8543,6 @@ void A_SPBChase(mobj_t *actor) } } - if (player) - P_SetTarget(&player->axis1, actor); // axis is the SPB trailing the player. - // lastlook = last player num targetted // cvmem = stored speed // cusval = next waypoint heap index diff --git a/src/p_mobj.c b/src/p_mobj.c index b04bf717a..5ed7b7447 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -48,6 +48,7 @@ actioncache_t actioncachehead; static mobj_t *overlaycap = NULL; static mobj_t *shadowcap = NULL; +mobj_t *kitemcap = NULL; // Used for Kart offensive items (the ones that can get removed by sizedown) mobj_t *waypointcap = NULL; void P_InitCachedActions(void) @@ -6101,6 +6102,71 @@ static boolean P_AddShield(mobj_t *thing) return true; }*/ + +// Kartitem stuff. +boolean P_IsKartItem(INT32 type) +{ + if (type == MT_EGGMANITEM || type == MT_EGGMANITEM_SHIELD || + type == MT_BANANA || type == MT_BANANA_SHIELD || + type == MT_ORBINAUT || type == MT_ORBINAUT_SHIELD || + type == MT_JAWZ || type == MT_JAWZ_DUD || type == MT_JAWZ_SHIELD || + type == MT_SSMINE || type == MT_SSMINE_SHIELD || + type == MT_SINK || type == MT_SINK_SHIELD || + type == MT_FLOATINGITEM || type == MT_SPB) + return true; + else + return false; +} + +// Called when a kart item "thinks" +void P_AddKartItem(mobj_t *thing) +{ + I_Assert(thing != NULL); + + if (kitemcap == NULL) + P_SetTarget(&kitemcap, thing); + else { + mobj_t *mo; + for (mo = kitemcap; mo && mo->itnext; mo = mo->itnext) + ; + + I_Assert(mo != NULL); + I_Assert(mo->itnext == NULL); + + P_SetTarget(&mo->itnext, thing); + } + P_SetTarget(&thing->itnext, NULL); +} + +// Called only when a kart item is removed +// Keeps the hnext list from corrupting. +static void P_RemoveKartItem(mobj_t *thing) +{ + mobj_t *mo; + for (mo = kitemcap; mo; mo = mo->itnext) + if (mo->itnext == thing) + { + P_SetTarget(&mo->itnext, thing->itnext); + P_SetTarget(&thing->itnext, NULL); + return; + } +} + +// Doesn't actually do anything since items have their own thinkers, +// but this is necessary for the sole purpose of updating kitemcap +void P_RunKartItems(void) +{ + mobj_t *mobj, *next; + + for (mobj = kitemcap; mobj; mobj = next) + { + next = mobj->itnext; + P_SetTarget(&mobj->itnext, NULL); + } + P_SetTarget(&kitemcap, NULL); +} + + void P_RunOverlays(void) { // run overlays @@ -9961,6 +10027,12 @@ for (i = ((mobj->flags2 & MF2_STRONGBOX) ? strongboxamt : weakboxamt); i; --i) s } } + if (P_MobjWasRemoved(mobj)) + return; // obligatory paranoia check + + if (P_IsKartItem(mobj->type)) // mobj is a kart item we want on the list: + P_AddKartItem(mobj); // add to kitem list + // Can end up here if a player dies. if (mobj->player) P_CyclePlayerMobjState(mobj); @@ -10887,6 +10959,9 @@ void P_RemoveMobj(mobj_t *mobj) if (mobj->type == MT_SPB) spbplace = -1; + if (P_IsKartItem(mobj->type)) + P_RemoveKartItem(mobj); + mobj->health = 0; // Just because // unlink from sector and block lists diff --git a/src/p_mobj.h b/src/p_mobj.h index aec2ed951..3ade78aa4 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -321,6 +321,9 @@ typedef struct mobj_s struct mobj_s *hnext; struct mobj_s *hprev; + // One last pointer for kart item lists + struct mobj_s *itnext; + mobjtype_t type; const mobjinfo_t *info; // &mobjinfo[mobj->type] @@ -436,12 +439,18 @@ typedef struct actioncache_s extern actioncache_t actioncachehead; +extern mobj_t *kitemcap; extern mobj_t *waypointcap; void P_InitCachedActions(void); void P_RunCachedActions(void); void P_AddCachedAction(mobj_t *mobj, INT32 statenum); +// kartitem stuff: Returns true if the specified 'type' is one of the kart item constants we want in the kitemcap list +boolean P_IsKartItem(INT32 type); +void P_AddKartItem(mobj_t *thing); // needs to be called in k_kart.c +void P_RunKartItems(void); + // check mobj against water content, before movement code void P_MobjCheckWater(mobj_t *mobj); diff --git a/src/p_saveg.c b/src/p_saveg.c index d51240521..4767b09d1 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -957,9 +957,11 @@ typedef enum MD2_HNEXT = 1<<7, MD2_HPREV = 1<<8, MD2_COLORIZED = 1<<9, - MD2_WAYPOINTCAP = 1<<10 + MD2_WAYPOINTCAP = 1<<10, + MD2_KITEMCAP = 1<<11, + MD2_ITNEXT = 1<<12 #ifdef ESLOPE - , MD2_SLOPE = 1<<11 + , MD2_SLOPE = 1<<13 #endif } mobj_diff2_t; @@ -1151,6 +1153,8 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type) diff2 |= MD2_HNEXT; if (mobj->hprev) diff2 |= MD2_HPREV; + if (mobj->itnext) + diff2 |= MD2_ITNEXT; #ifdef ESLOPE if (mobj->standingslope) diff2 |= MD2_SLOPE; @@ -1159,6 +1163,8 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type) diff2 |= MD2_COLORIZED; if (mobj == waypointcap) diff2 |= MD2_WAYPOINTCAP; + if (mobj == kitemcap) + diff2 |= MD2_KITEMCAP; if (diff2 != 0) diff |= MD_MORE; @@ -1274,6 +1280,8 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type) WRITEUINT32(save_p, mobj->hnext->mobjnum); if (diff2 & MD2_HPREV) WRITEUINT32(save_p, mobj->hprev->mobjnum); + if (diff2 & MD2_ITNEXT) + WRITEUINT32(save_p, mobj->itnext->mobjnum); #ifdef ESLOPE if (diff2 & MD2_SLOPE) WRITEUINT16(save_p, mobj->standingslope->id); @@ -2151,6 +2159,8 @@ static void LoadMobjThinker(actionf_p1 thinker) mobj->hnext = (mobj_t *)(size_t)READUINT32(save_p); if (diff2 & MD2_HPREV) mobj->hprev = (mobj_t *)(size_t)READUINT32(save_p); + if (diff2 & MD2_ITNEXT) + mobj->itnext = (mobj_t *)(size_t)READUINT32(save_p); #ifdef ESLOPE if (diff2 & MD2_SLOPE) { @@ -2192,6 +2202,9 @@ static void LoadMobjThinker(actionf_p1 thinker) if (diff2 & MD2_WAYPOINTCAP) P_SetTarget(&waypointcap, mobj); + if (diff2 & MD2_KITEMCAP) + P_SetTarget(&kitemcap, mobj); + mobj->info = (mobjinfo_t *)next; // temporarily, set when leave this function } @@ -3044,6 +3057,13 @@ static void P_RelinkPointers(void) if (!(mobj->hprev = P_FindNewPosition(temp))) CONS_Debug(DBG_GAMELOGIC, "hprev not found on %d\n", mobj->type); } + if (mobj->itnext) + { + temp = (UINT32)(size_t)mobj->itnext; + mobj->itnext = NULL; + if (!(mobj->itnext = P_FindNewPosition(temp))) + CONS_Debug(DBG_GAMELOGIC, "itnext not found on %d\n", mobj->type); + } if (mobj->player && mobj->player->capsule) { temp = (UINT32)(size_t)mobj->player->capsule; @@ -3324,7 +3344,7 @@ static void P_NetArchiveMisc(void) WRITEUINT32(save_p, hyubgone); WRITEUINT32(save_p, mapreset); - for (i = 0; i < MAXPLAYERS; i++) + for (i = 0; i < MAXPLAYERS; i++) WRITEINT16(save_p, nospectategrief[i]); WRITEUINT8(save_p, thwompsactive); @@ -3447,7 +3467,7 @@ static inline boolean P_NetUnArchiveMisc(void) hyubgone = READUINT32(save_p); mapreset = READUINT32(save_p); - for (i = 0; i < MAXPLAYERS; i++) + for (i = 0; i < MAXPLAYERS; i++) nospectategrief[i] = READINT16(save_p); thwompsactive = (boolean)READUINT8(save_p); diff --git a/src/p_tick.c b/src/p_tick.c index 2e3b17b69..33cc32602 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -183,6 +183,7 @@ void P_InitThinkers(void) { thinkercap.prev = thinkercap.next = &thinkercap; waypointcap = NULL; + kitemcap = NULL; } // @@ -639,6 +640,9 @@ void P_Ticker(boolean run) if (runemeraldmanager) P_EmeraldManager(); // Power stone mode*/ + // formality so kitemcap gets updated properly each frame. + P_RunKartItems(); + if (run) { P_RunThinkers(); From 2ddb6cd082f9a2e23418ec1c3632075089894fa2 Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Sat, 26 Oct 2019 23:02:45 -0400 Subject: [PATCH 065/105] set starpost xyz in battle maps --- src/k_kart.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/src/k_kart.c b/src/k_kart.c index 5d903cbc8..b74b516bc 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -12,6 +12,7 @@ #include "m_random.h" #include "p_local.h" #include "p_slopes.h" +#include "p_setup.h" #include "r_draw.h" #include "r_local.h" #include "s_sound.h" @@ -1937,9 +1938,101 @@ void K_DoIngameRespawn(player_t *player) if (leveltime <= starttime) return; + if (player->nextwaypoint == NULL) // Starpost xyz not initalized(?) + { + UINT32 bestdist = UINT32_MAX; + mapthing_t *beststart = NULL; + UINT8 numstarts = 0; + + if (G_RaceGametype()) + { + numstarts = numcoopstarts; + } + else if (G_BattleGametype()) + { + numstarts = numdmstarts; + } + + if (numstarts > 0) + { + UINT8 i = 0; + + for (i = 0; i < numstarts; i++) + { + UINT32 dist = UINT32_MAX; + mapthing_t *checkstart = NULL; + + if (G_RaceGametype()) + { + checkstart = playerstarts[i]; + } + else if (G_BattleGametype()) + { + checkstart = deathmatchstarts[i]; + } + else + { + break; + } + + dist = (UINT32)P_AproxDistance((player->mo->x >> FRACBITS) - checkstart->x, + (player->mo->y >> FRACBITS) - checkstart->y); + + if (dist < bestdist) + { + beststart = checkstart; + bestdist = dist; + } + } + } + + if (beststart == NULL) + { + CONS_Alert(CONS_WARNING, "No respawn points!\n"); + } + else + { + sector_t *s; + fixed_t z = (beststart->options >> ZSHIFT); + + player->starpostx = beststart->x; + player->starposty = beststart->y; + s = R_PointInSubsector(beststart->x << FRACBITS, beststart->y << FRACBITS)->sector; + + if (beststart->options & MTF_OBJECTFLIP) + { + player->starpostz = ( +#ifdef ESLOPE + s->c_slope ? P_GetZAt(s->c_slope, beststart->x << FRACBITS, beststart->y << FRACBITS) : +#endif + s->ceilingheight) >> FRACBITS; + + if (z) + player->starpostz -= z; + + player->starpostz -= mobjinfo[MT_PLAYER].height; + player->kartstuff[k_starpostflip] = 1; + } + else + { + player->starpostz = ( +#ifdef ESLOPE + s->f_slope ? P_GetZAt(s->f_slope, beststart->x << FRACBITS, beststart->y << FRACBITS) : +#endif + s->floorheight) >> FRACBITS; + + if (z) + player->starpostz += z; + + player->kartstuff[k_starpostflip] = 0; + } + } + } + player->mo->flags &= ~(MF_SOLID|MF_SHOOTABLE); player->mo->flags |= MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING|MF_NOGRAVITY; player->mo->flags2 &= ~MF2_DONTDRAW; + player->kartstuff[k_respawn] = 48; } From 54516aeee97b18ee307024672fe91fc60d55fd30 Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Sun, 27 Oct 2019 16:44:41 -0400 Subject: [PATCH 066/105] Don't force SPB in 1v1 --- src/k_kart.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/k_kart.c b/src/k_kart.c index b74b516bc..42067f333 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1057,6 +1057,10 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) bestbumper = players[i].kartstuff[k_bumper]; } + // No forced SPB in 1v1s, it has to be randomly rolled + if (pingame <= 2) + dontforcespb = true; + // This makes the roulette produce the random noises. if ((player->kartstuff[k_itemroulette] % 3) == 1 && P_IsDisplayPlayer(player)) { From 34e0b4b9919e3dc218820287ee25f934e5bc5de0 Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Sun, 27 Oct 2019 17:18:22 -0400 Subject: [PATCH 067/105] Radius was not made more coarse when I did the big-maps fix, so you were considered in every waypoint, meaning it worked exactly before I made this change in the first place https://youtu.be/csnS0nszyuA --- src/k_waypoint.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_waypoint.c b/src/k_waypoint.c index ab3bd8be9..68b21520e 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -266,7 +266,7 @@ waypoint_t *K_GetBestWaypointTouchingMobj(mobj_t *const mobj) checkdist = P_AproxDistance(checkdist, (mobj->z >> FRACBITS) - (checkwaypoint->mobj->z >> FRACBITS)); // The mobj has to be touching this waypoint to use it. - if ((checkdist <= checkwaypoint->mobj->radius) + if ((checkdist <= (checkwaypoint->mobj->radius >> FRACBITS)) && (checkdist < bestdist)) { bestwaypoint = checkwaypoint; From 0afc901d1febfb135e4c804230c3e1683ad81b9d Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Sun, 27 Oct 2019 20:15:27 -0400 Subject: [PATCH 068/105] Shrinking animation --- src/k_kart.c | 28 +++++++--------------------- src/p_mobj.c | 36 +++++++++++++++++++----------------- 2 files changed, 26 insertions(+), 38 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 5c9dd7d8d..3269582e8 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -4225,10 +4225,7 @@ static void K_DoShrink(player_t *user) // Grow should get taken away. if (players[i].kartstuff[k_growshrinktimer] > 0) K_RemoveGrowShrink(&players[i]); - // Don't hit while invulnerable! - else if (!players[i].kartstuff[k_invincibilitytimer] - && players[i].kartstuff[k_growshrinktimer] <= 0 - && !players[i].kartstuff[k_hyudorotimer]) + else { // Start shrinking! K_DropItems(&players[i]); @@ -4261,26 +4258,15 @@ static void K_DoShrink(player_t *user) if (mobj->target && mobj->target->player) { if (mobj->target->player->kartstuff[k_position] > user->kartstuff[k_position]) - continue; // this guy's behind us, don't take his stuff away! + continue; // this guy's behind us, don't take his stuff away! } } - // @TODO: This should probably go into the P_KillMobj code for items? - - if (mobj->eflags & MFE_VERTICALFLIP) - mobj->z -= mobj->height; - else - mobj->z += mobj->height; - - S_StartSound(mobj, mobj->info->deathsound); - P_KillMobj(mobj, user->mo, user->mo); - - P_SetObjectMomZ(mobj, 8*FRACUNIT, false); - P_InstaThrust(mobj, (angle_t)P_RandomRange(0, 359)*ANG1, 16*FRACUNIT); + mobj->destscale = 0; + mobj->flags |= (MF_NOTHINK|MF_NOCLIPTHING); if (mobj->type == MT_SPB) spbplace = -1; - } } @@ -9342,17 +9328,17 @@ static void K_drawKartWanted(void) return; // set X/Y coords depending on splitscreen. - if (splitscreen < 3) // 1P and 2P use the same code. + if (splitscreen < 3) // 1P and 2P use the same code. { basex = WANT_X; basey = WANT_Y; if (splitscreen == 2) { - basey += 16; // slight adjust for 3P + basey += 16; // slight adjust for 3P basex -= 6; } } - else if (splitscreen == 3) // 4P splitscreen... + else if (splitscreen == 3) // 4P splitscreen... { basex = BASEVIDWIDTH/2 - (SHORT(kp_wantedsplit->width)/2); // center on screen basey = BASEVIDHEIGHT - 55; diff --git a/src/p_mobj.c b/src/p_mobj.c index 5ed7b7447..2cdb257d6 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -1658,18 +1658,11 @@ void P_XYMovement(mobj_t *mo) { mo->health--; if (mo->health == 0) - mo->destscale = 1; - } - else - { - if (mo->scale < mapobjectscale/16) - { - P_RemoveMobj(mo); - return; - } + mo->destscale = 0; } } //} + if (!P_TryMove(mo, mo->x + xmove, mo->y + ymove, true) && !(mo->eflags & MFE_SPRUNG)) { // blocked move @@ -6112,7 +6105,7 @@ boolean P_IsKartItem(INT32 type) type == MT_JAWZ || type == MT_JAWZ_DUD || type == MT_JAWZ_SHIELD || type == MT_SSMINE || type == MT_SSMINE_SHIELD || type == MT_SINK || type == MT_SINK_SHIELD || - type == MT_FLOATINGITEM || type == MT_SPB) + type == MT_SPB) return true; else return false; @@ -6881,16 +6874,25 @@ void P_MobjThinker(mobj_t *mobj) mobj->z -= mobj->height - oldheight; if (mobj->scale == mobj->destscale) + { /// \todo Lua hook for "reached destscale"? - switch(mobj->type) + + if (mobj->scale == 0) { - case MT_EGGMOBILE_FIRE: - mobj->destscale = FRACUNIT; - mobj->scalespeed = FRACUNIT>>4; - break; - default: - break; + P_RemoveMobj(mobj); + return; } + + switch (mobj->type) + { + case MT_EGGMOBILE_FIRE: + mobj->destscale = FRACUNIT; + mobj->scalespeed = FRACUNIT>>4; + break; + default: + break; + } + } } if (mobj->type == MT_GHOST && mobj->fuse > 0 // Not guaranteed to be MF_SCENERY or not MF_SCENERY! From 7676dfa7137fcd8ecc50370a439bc4942dae43fb Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Sun, 27 Oct 2019 20:54:00 -0400 Subject: [PATCH 069/105] SPB minimap proper order --- src/k_kart.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 3269582e8..1fc525378 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -7538,6 +7538,8 @@ static patch_t *kp_winnernum[NUMPOSFRAMES]; static patch_t *kp_facenum[MAXPLAYERS+1]; static patch_t *kp_facehighlight[8]; +static patch_t *kp_spbminimap; + static patch_t *kp_ringsticker[2]; static patch_t *kp_ringstickersplit[4]; static patch_t *kp_ring[6]; @@ -7690,6 +7692,8 @@ void K_LoadKartHUDGraphics(void) kp_facehighlight[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); } + kp_spbminimap = W_CachePatchName("SPBMMAP", PU_HUDGFX); + // Rings & Lives kp_ringsticker[0] = W_CachePatchName("RNGBACKA", PU_HUDGFX); kp_ringsticker[1] = W_CachePatchName("RNGBACKB", PU_HUDGFX); @@ -9669,6 +9673,14 @@ static void K_drawKartMinimap(void) K_drawKartMinimapIcon(players[i].mo->x, players[i].mo->y, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic); } } + + // draw SPB(s?) + for (mobj = kitemcap; mobj; mobj = next) + { + next = mobj->itnext; + if (mobj->type == MT_SPB) + K_drawKartMinimapIcon(mobj->x, mobj->y, x, y, splitflags, kp_spbminimap, NULL, AutomapPic); + } // draw our local players here, opaque. splitflags &= ~V_HUDTRANSHALF; @@ -9700,16 +9712,7 @@ static void K_drawKartMinimap(void) if ((G_RaceGametype() && players[localplayers[i]].kartstuff[k_position] == spbplace) || (G_BattleGametype() && K_IsPlayerWanted(&players[localplayers[i]]))) K_drawKartMinimapIcon(players[localplayers[i]].mo->x, players[localplayers[i]].mo->y, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic); - } - - // draw SPB(s?) - for (mobj = kitemcap; mobj; mobj = next) - { - next = mobj->itnext; - if (mobj->type == MT_SPB) - K_drawKartMinimapIcon(mobj->x, mobj->y, x, y, splitflags, kp_ringspblocksmall[14 + leveltime%4 /2], NULL, AutomapPic); - } - + } } static void K_drawKartStartCountdown(void) From 66bef945e82ab4de0233a45876c8c389b54b7d19 Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Sun, 27 Oct 2019 21:01:27 -0400 Subject: [PATCH 070/105] Can't do nothink --- src/k_kart.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index 1fc525378..2587813c5 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -4263,7 +4263,8 @@ static void K_DoShrink(player_t *user) } mobj->destscale = 0; - mobj->flags |= (MF_NOTHINK|MF_NOCLIPTHING); + mobj->flags &= ~(MF_SOLID|MF_SHOOTABLE|MF_SPECIAL); + mobj->flags |= MF_NOCLIPTHING; // Just for safety if (mobj->type == MT_SPB) spbplace = -1; From e2bc8a0d9733493d0f70bd372e127a839d36e617 Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Sun, 27 Oct 2019 21:25:16 -0400 Subject: [PATCH 071/105] color spb icon with who threw it --- src/k_kart.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index 2587813c5..dedc6f79a 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -9680,7 +9680,19 @@ static void K_drawKartMinimap(void) { next = mobj->itnext; if (mobj->type == MT_SPB) - K_drawKartMinimapIcon(mobj->x, mobj->y, x, y, splitflags, kp_spbminimap, NULL, AutomapPic); + { + UINT8 *colormap = NULL; + + if (mobj->target && !P_MobjWasRemoved(mobj->target)) + { + if (mobj->player && mobj->player->skincolor) + colormap = R_GetTranslationColormap(TC_RAINBOW, mobj->player->skincolor, GTC_CACHE); + else if (mobj->color) + colormap = R_GetTranslationColormap(TC_RAINBOW, mobj->color, GTC_CACHE); + } + + K_drawKartMinimapIcon(mobj->x, mobj->y, x, y, splitflags, kp_spbminimap, colormap, AutomapPic); + } } // draw our local players here, opaque. From 6acf3e2337979758c171b0b902e4a145ae1df9e7 Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Sun, 27 Oct 2019 21:35:27 -0400 Subject: [PATCH 072/105] dont overshadow --- src/k_kart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index dedc6f79a..4aca67aef 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -9681,7 +9681,7 @@ static void K_drawKartMinimap(void) next = mobj->itnext; if (mobj->type == MT_SPB) { - UINT8 *colormap = NULL; + colormap = NULL; if (mobj->target && !P_MobjWasRemoved(mobj->target)) { From 377a155cda1de5a5ebc63c2d7989ad50f38a1ec1 Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Mon, 28 Oct 2019 14:20:09 -0400 Subject: [PATCH 073/105] Update Race Lap linedef executors since they stopped working --- src/p_spec.c | 98 ++++++++++++++++++++++++++++------------------------ 1 file changed, 53 insertions(+), 45 deletions(-) diff --git a/src/p_spec.c b/src/p_spec.c index f83ca79a7..b4f5a9d62 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -1705,41 +1705,6 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller if (!(ALL7EMERALDS(emeralds))) return false; } - else if (GETSECSPECIAL(caller->special, 2) == 7) // SRB2Kart: reusing for Race Lap executor - { - UINT8 lap; - - if (actor && actor->player && triggerline->flags & ML_EFFECT4) - { - /*if (maptol & TOL_NIGHTS) - lap = actor->player->mare; - else*/ - lap = actor->player->laps; - } - else - { - /*if (maptol & TOL_NIGHTS) - lap = P_FindLowestMare(); - else*/ - lap = P_FindLowestLap(); - } - - if (triggerline->flags & ML_NOCLIMB) // Need higher than or equal to - { - if (lap < (sides[triggerline->sidenum[0]].textureoffset >> FRACBITS)) - return false; - } - else if (triggerline->flags & ML_BLOCKMONSTERS) // Need lower than or equal to - { - if (lap > (sides[triggerline->sidenum[0]].textureoffset >> FRACBITS)) - return false; - } - else // Need equal to - { - if (lap != (sides[triggerline->sidenum[0]].textureoffset >> FRACBITS)) - return false; - } - } // If we were not triggered by a sector type especially for the purpose, // a Linedef Executor linedef trigger is not handling sector triggers properly, return. @@ -1937,15 +1902,17 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller else // These special types work only once if (specialtype == 302 // Once - || specialtype == 304 // Ring count - Once - || specialtype == 307 // Character ability - Once - || specialtype == 308 // Race only - Once - || specialtype == 315 // No of pushables - Once - || specialtype == 318 // Unlockable trigger - Once - || specialtype == 320 // Unlockable - Once - || specialtype == 321 || specialtype == 322 // Trigger on X calls - Continuous + Each Time - || specialtype == 328 // Encore Load - || specialtype == 399) // Level Load + || specialtype == 304 // Ring count - Once + || specialtype == 307 // Character ability - Once + || specialtype == 308 // Race only - Once + || specialtype == 315 // No of pushables - Once + || specialtype == 318 // Unlockable trigger - Once + || specialtype == 320 // Unlockable - Once + || specialtype == 321 || specialtype == 322 // Trigger on X calls - Continuous + Each Time + || specialtype == 328 // Encore Load + || specialtype == 399 // Level Load + || specialtype == 2002 // SRB2Kart Race Lap + ) triggerline->special = 0; // Clear it out return true; @@ -1981,6 +1948,7 @@ void P_LinedefExecute(INT16 tag, mobj_t *actor, sector_t *caller) if (lines[masterline].special == 313 || lines[masterline].special == 399 || lines[masterline].special == 328 + || lines[masterline].special == 2002 // SRB2Kart race lap trigger // Each-time executors handle themselves, too || lines[masterline].special == 301 // Each time || lines[masterline].special == 306 // Character ability - Each time @@ -2207,7 +2175,7 @@ static void K_HandleLapIncrement(player_t *player) { if ((player->starpostnum == numstarposts) || (player->laps == 0)) { - UINT8 i = 0; + size_t i = 0; UINT8 nump = 0; for (i = 0; i < MAXPLAYERS; i++) @@ -2284,6 +2252,41 @@ static void K_HandleLapIncrement(player_t *player) } thwompsactive = true; // Lap 2 effects + + for (i = 0; i < numlines; i++) + { + if (lines[i].special == 2002) // Race lap trigger + { + UINT8 lap; + + if (lines[i].flags & ML_EFFECT4) + { + lap = player->laps; + } + else + { + lap = P_FindLowestLap(); + } + + if (lines[i].flags & ML_NOCLIMB) // Need higher than or equal to + { + if (lap < (sides[lines[i].sidenum[0]].textureoffset >> FRACBITS)) + continue; + } + else if (lines[i].flags & ML_BLOCKMONSTERS) // Need lower than or equal to + { + if (lap > (sides[lines[i].sidenum[0]].textureoffset >> FRACBITS)) + continue; + } + else // Need equal to + { + if (lap != (sides[lines[i].sidenum[0]].textureoffset >> FRACBITS)) + continue; + } + + P_RunTriggerLinedef(&lines[i], player->mo, NULL); + } + } } else if (player->starpostnum) { @@ -6728,12 +6731,17 @@ void P_SpawnSpecials(INT32 fromnetsave) sectors[s].midmap = lines[i].frontsector->midmap; break; + // SRB2Kart case 2000: // Waypoint Parameters break; case 2001: // Finish Line if (G_RaceGametype()) circuitmap = true; break; + case 2002: // Linedef Trigger: Race Lap + break; + case 2003: // Linedef Executor: Enable/Disable Waypoint + break; default: break; } From fe3d19d15dabf1be5162f90ac4143270da9279dc Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Mon, 28 Oct 2019 14:42:45 -0400 Subject: [PATCH 074/105] Remove this for now --- src/p_spec.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/p_spec.c b/src/p_spec.c index b4f5a9d62..0af99f163 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -6740,8 +6740,6 @@ void P_SpawnSpecials(INT32 fromnetsave) break; case 2002: // Linedef Trigger: Race Lap break; - case 2003: // Linedef Executor: Enable/Disable Waypoint - break; default: break; } From d413b3bcd41e224a3588aec8bb5ddb27fd2e0724 Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Mon, 28 Oct 2019 15:52:27 -0400 Subject: [PATCH 075/105] Use both facing & momentum angle for using next/prev waypoints, only update respawn to nextwaypoints --- src/k_kart.c | 121 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 75 insertions(+), 46 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 4aca67aef..b3297527e 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -5749,7 +5749,8 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player, boolean closest) waypoint_t *bestwaypoint = NULL; if ((player != NULL) && (player->mo != NULL) && (P_MobjWasRemoved(player->mo) == false)) { - waypoint_t *waypoint = NULL; + waypoint_t *waypoint = NULL; + boolean updaterespawn = false; if (closest == true) waypoint = K_GetClosestWaypointToMobj(player->mo); @@ -5766,23 +5767,30 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player, boolean closest) { boolean finishlinehack = false; angle_t playerangle = player->mo->angle; + angle_t momangle = player->mo->angle; angle_t angletowaypoint = R_PointToAngle2(player->mo->x, player->mo->y, waypoint->mobj->x, waypoint->mobj->y); angle_t angledelta = ANGLE_MAX; + angle_t momdelta = ANGLE_MAX; if (player->mo->momx != 0 || player->mo->momy != 0) { - // Default to facing angle if you're not moving, but use momentum angle otherwise. - playerangle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); + // Defaults to facing angle if you're not moving. + momangle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); } angledelta = playerangle - angletowaypoint; - if (angledelta > ANGLE_180) { angledelta = InvAngle(angledelta); } + momdelta = momangle - angletowaypoint; + if (momdelta > ANGLE_180) + { + momdelta = InvAngle(momdelta); + } + if (bestwaypoint == K_GetFinishLineWaypoint()) { // facing towards the finishline @@ -5792,10 +5800,14 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player, boolean closest) } } - // The wrong way flag will use its previous value if we're facing sideways - if ((angledelta > ANGLE_45) && (finishlinehack == false)) + // We're using a lot of angle calculations here, because only using facing angle or only using momentum angle both have downsides. + // nextwaypoints will be picked if you're facing OR moving forward. + // prevwaypoints will be picked if you're facing AND moving backward. + if ((angledelta > ANGLE_45 || momdelta > ANGLE_45) + && (finishlinehack == false)) { angle_t nextbestdelta = angledelta; + angle_t nextbestmomdelta = momdelta; size_t i = 0U; if ((waypoint->nextwaypoints != NULL) && (waypoint->numnextwaypoints > 0U)) @@ -5805,20 +5817,29 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player, boolean closest) angletowaypoint = R_PointToAngle2( player->mo->x, player->mo->y, waypoint->nextwaypoints[i]->mobj->x, waypoint->nextwaypoints[i]->mobj->y); - angledelta = playerangle - angletowaypoint; + angledelta = playerangle - angletowaypoint; if (angledelta > ANGLE_180) { angledelta = InvAngle(angledelta); } - if (angledelta < nextbestdelta) + momdelta = momangle - angletowaypoint; + if (momdelta > ANGLE_180) + { + momdelta = InvAngle(momdelta); + } + + if (angledelta < nextbestdelta || momdelta < nextbestmomdelta) { bestwaypoint = waypoint->nextwaypoints[i]; + nextbestdelta = angledelta; + nextbestmomdelta = momdelta; // Remove wrong way flag if we're using nextwaypoints player->kartstuff[k_wrongway] = 0; + updaterespawn = true; } } } @@ -5830,25 +5851,67 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player, boolean closest) angletowaypoint = R_PointToAngle2( player->mo->x, player->mo->y, waypoint->prevwaypoints[i]->mobj->x, waypoint->prevwaypoints[i]->mobj->y); - angledelta = playerangle - angletowaypoint; + angledelta = playerangle - angletowaypoint; if (angledelta > ANGLE_180) { angledelta = InvAngle(angledelta); } - if (angledelta < nextbestdelta) + momdelta = momangle - angletowaypoint; + if (momdelta > ANGLE_180) + { + momdelta = InvAngle(momdelta); + } + + if (angledelta < nextbestdelta && momdelta < nextbestmomdelta) { bestwaypoint = waypoint->prevwaypoints[i]; - nextbestdelta = angledelta; - // Set wrong way flag if we're using prevwaypoints + nextbestdelta = angledelta; + nextbestmomdelta = momdelta; + + // Ser wrong way flag if we're using prevwaypoints player->kartstuff[k_wrongway] = 1; + updaterespawn = false; } } } } } + + // Respawn point should only be updated when we're going to a nextwaypoint + if ((updaterespawn) && + (bestwaypoint != NULL) && + (bestwaypoint != player->nextwaypoint) && + (player->kartstuff[k_respawn] == 0) && + (K_GetWaypointIsShortcut(bestwaypoint) == false) && (K_GetWaypointIsEnabled(bestwaypoint) == true)) + { + size_t i = 0U; + waypoint_t *aimwaypoint = NULL; + + player->starpostx = bestwaypoint->mobj->x >> FRACBITS; + player->starposty = bestwaypoint->mobj->y >> FRACBITS; + player->starpostz = bestwaypoint->mobj->z >> FRACBITS; + player->kartstuff[k_starpostflip] = (bestwaypoint->mobj->flags2 & MF2_OBJECTFLIP); + + // starpostangle is to the first valid nextwaypoint for simplicity + // if we reach the last waypoint and it's still not valid, just use it anyway. Someone needs to fix + // their map! + for (i = 0U; i < bestwaypoint->numnextwaypoints; i++) + { + aimwaypoint = bestwaypoint->nextwaypoints[i]; + + if ((i == bestwaypoint->numnextwaypoints - 1U) + || ((K_GetWaypointIsEnabled(aimwaypoint) == true) + && (K_GetWaypointIsSpawnpoint(aimwaypoint) == true))) + { + player->starpostangle = R_PointToAngle2( + bestwaypoint->mobj->x, bestwaypoint->mobj->y, aimwaypoint->mobj->x, aimwaypoint->mobj->y); + break; + } + } + } } return bestwaypoint; @@ -5947,40 +6010,6 @@ static void K_UpdateDistanceFromFinishLine(player_t *const player) nextwaypoint = K_GetPlayerNextWaypoint(player, true); } - if ((nextwaypoint != NULL) && - (nextwaypoint != player->nextwaypoint) && - (player->kartstuff[k_respawn] == 0) && - (K_GetWaypointIsShortcut(nextwaypoint) == false) && (K_GetWaypointIsEnabled(nextwaypoint) == true)) - { - size_t i = 0U; - waypoint_t *aimwaypoint = NULL; - - player->starpostx = nextwaypoint->mobj->x >> FRACBITS; - player->starposty = nextwaypoint->mobj->y >> FRACBITS; - player->starpostz = nextwaypoint->mobj->z >> FRACBITS; - - // player gravflip determines which way to respawn - // (should waypoints have a flip option?) - player->kartstuff[k_starpostflip] = player->mo->flags2 & MF2_OBJECTFLIP; - - // starpostangle is to the first valid nextwaypoint for simplicity - // if we reach the last waypoint and it's still not valid, just use it anyway. Someone needs to fix - // their map! - for (i = 0U; i < nextwaypoint->numnextwaypoints; i++) - { - aimwaypoint = nextwaypoint->nextwaypoints[i]; - - if ((i == nextwaypoint->numnextwaypoints - 1U) - || ((K_GetWaypointIsEnabled(aimwaypoint) == true) - && (K_GetWaypointIsSpawnpoint(aimwaypoint) == true))) - { - player->starpostangle = R_PointToAngle2( - nextwaypoint->mobj->x, nextwaypoint->mobj->y, aimwaypoint->mobj->x, aimwaypoint->mobj->y); - break; - } - } - } - if (nextwaypoint != NULL) { // If nextwaypoint is NULL, it means we don't want to update the waypoint until we touch another one. From 41f69b3c16b5250a93b1391394f22a36f006a19d Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Mon, 28 Oct 2019 16:25:38 -0400 Subject: [PATCH 076/105] Break SPBChase into a few more functions Not really easy to read still, but it's a tiny bit better than before --- src/p_enemy.c | 311 ++++++++++++++++++++++++-------------------------- 1 file changed, 150 insertions(+), 161 deletions(-) diff --git a/src/p_enemy.c b/src/p_enemy.c index ff57b0ffa..9e31206be 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -8480,11 +8480,11 @@ void A_JawzExplode(mobj_t *actor) static void SpawnSPBTrailRings(mobj_t *actor) { - if (actor != NULL) + if (actor != NULL && !P_MobjWasRemoved(actor)) { if (leveltime % 6 == 0) { - mobj_t *ring = P_SpawnMobj(actor->x - actor->momx, actor->y - actor->momx, + mobj_t *ring = P_SpawnMobj(actor->x - actor->momx, actor->y - actor->momy, actor->z - actor->momz + (24*mapobjectscale), MT_RING); ring->threshold = 10; ring->fuse = 120*TICRATE; @@ -8492,72 +8492,64 @@ static void SpawnSPBTrailRings(mobj_t *actor) } } -void A_SPBChase(mobj_t *actor) +static void ModifySPBSpeedForAngle(mobj_t *actor, angle_t *hang, angle_t *vang, fixed_t *xyspeed, fixed_t *zspeed) { - player_t *player = NULL; - player_t *scplayer = NULL; // secondary target for seeking - UINT8 i; - UINT8 bestrank = UINT8_MAX; - fixed_t dist; - angle_t hang, vang; - fixed_t wspeed, xyspeed, zspeed; - fixed_t pdist = 1536<movefactor; - - if (actor->threshold) // Just fired, go straight. + if (actor != NULL && !P_MobjWasRemoved(actor)) { - actor->lastlook = -1; - actor->cusval = -1; - spbplace = -1; - P_InstaThrust(actor, actor->angle, wspeed); - return; + // Smoothly rotate horz angle + angle_t input = hang - actor->angle; + boolean invert = (input > ANGLE_180); + if (invert) + input = InvAngle(input); + + // Slow down when turning; it looks better and makes U-turns not unfair + xyspeed = FixedMul(actor->cvmem, max(0, (((180<angle += input; + + // Smoothly rotate vert angle + input = vang - actor->movedir; + invert = (input > ANGLE_180); + if (invert) + input = InvAngle(input); + + // Slow down when turning; might as well do it for momz, since we do it above too + zspeed = FixedMul(actor->cvmem, max(0, (((180<movedir += input; } +} - // Find the player with the best rank - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator || players[i].exiting) - continue; // not in-game - - /*if (!players[i].mo) - continue; // no mobj - - if (players[i].mo->health <= 0) - continue; // dead - - if (players[i].kartstuff[k_respawn]) - continue;*/ // respawning - - if (players[i].kartstuff[k_position] < bestrank) - { - bestrank = players[i].kartstuff[k_position]; - player = &players[i]; - } - } - - // lastlook = last player num targetted - // cvmem = stored speed - // cusval = next waypoint heap index - // extravalue1 = SPB movement mode - // extravalue2 = mode misc option - - if (actor->extravalue1 == 1) // MODE: TARGETING +static void SPBTargettingChase(mobj_t *actor, fixed_t wspeed, UINT8 bestrank) +{ + if (actor != NULL && !P_MobjWasRemoved(actor)) { actor->cusval = -1; // Reset waypoint - if (actor->tracer && actor->tracer->health) + if (actor->tracer == NULL || P_MobjWasRemoved(actor->tracer) || !actor->tracer->health) { + // Target's gone, return to SEEKING + P_SetTarget(&actor->tracer, NULL); + actor->extravalue1 = 2; // WAIT... + actor->extravalue2 = 52; // Slightly over the respawn timer length + return; + } + else + { + fixed_t xyspeed, zspeed; + angle_t hang, vang; + fixed_t dist; fixed_t defspeed = wspeed; fixed_t range = (160*actor->tracer->scale); - fixed_t cx = 0, cy =0; + fixed_t cx = 0, cy = 0; // Play the intimidating gurgle if (!S_SoundPlaying(actor, actor->info->activesound)) @@ -8608,7 +8600,6 @@ void A_SPBChase(mobj_t *actor) wspeed = 20*actor->tracer->scale; if (actor->tracer->player->pflags & PF_SLIDING) wspeed = actor->tracer->player->speed/2; - // ^^^^ current section: These are annoying, and grand metropolis in particular needs this. hang = R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y); vang = R_PointToAngle2(0, actor->z, dist, actor->tracer->z); @@ -8619,37 +8610,7 @@ void A_SPBChase(mobj_t *actor) else actor->cvmem = wspeed; - { - // Smoothly rotate horz angle - angle_t input = hang - actor->angle; - boolean invert = (input > ANGLE_180); - if (invert) - input = InvAngle(input); - - // Slow down when turning; it looks better and makes U-turns not unfair - xyspeed = FixedMul(actor->cvmem, max(0, (((180<angle += input; - - // Smoothly rotate vert angle - input = vang - actor->movedir; - invert = (input > ANGLE_180); - if (invert) - input = InvAngle(input); - - // Slow down when turning; might as well do it for momz, since we do it above too - zspeed = FixedMul(actor->cvmem, max(0, (((180<movedir += input; - } + ModifySPBSpeedForAngle(actor, &hang, &vang, &xyspeed, &zspeed); actor->momx = cx + FixedMul(FixedMul(xyspeed, FINECOSINE(actor->angle>>ANGLETOFINESHIFT)), FINECOSINE(actor->movedir>>ANGLETOFINESHIFT)); actor->momy = cy + FixedMul(FixedMul(xyspeed, FINESINE(actor->angle>>ANGLETOFINESHIFT)), FINECOSINE(actor->movedir>>ANGLETOFINESHIFT)); @@ -8675,46 +8636,21 @@ void A_SPBChase(mobj_t *actor) fast->colorized = true; K_MatchGenericExtraFlags(fast, actor); } - - return; - } - else // Target's gone, return to SEEKING - { - P_SetTarget(&actor->tracer, NULL); - actor->extravalue1 = 2; // WAIT... - actor->extravalue2 = 52; // Slightly over the respawn timer length - return; } } - else if (actor->extravalue1 == 2) // MODE: WAIT... - { - actor->momx = actor->momy = actor->momz = 0; // Stoooop - actor->cusval = -1; // Reset waypoint +} - if (actor->lastlook != -1 - && playeringame[actor->lastlook] - && !players[actor->lastlook].spectator - && !players[actor->lastlook].exiting) - { - spbplace = players[actor->lastlook].kartstuff[k_position]; - players[actor->lastlook].kartstuff[k_ringlock] = 1; - if (actor->extravalue2-- <= 0 && players[actor->lastlook].mo) - { - P_SetTarget(&actor->tracer, players[actor->lastlook].mo); - actor->extravalue1 = 1; // TARGET ACQUIRED - actor->extravalue2 = 7*TICRATE; - actor->cvmem = wspeed; - } - } - else - { - actor->extravalue1 = 0; // SEEKING - actor->extravalue2 = 0; - spbplace = -1; - } - } - else // MODE: SEEKING +static void SPBSeekingChase(mobj_t *actor, player_t *player, fixed_t wspeed, UINT8 bestrank) +{ + if (actor != NULL && !P_MobjWasRemoved(actor)) { +#define TARGETDIST 1536 + fixed_t xyspeed, zspeed; + angle_t hang, vang; + fixed_t dist; + player_t *scplayer = NULL; // secondary target for swerving + fixed_t pdist = TARGETDIST<angle; - boolean invert = (input > ANGLE_180); - if (invert) - input = InvAngle(input); - - // Slow down when turning; it looks better and makes U-turns not unfair - xyspeed = FixedMul(wspeed, max(0, (((180<angle += input; - - // Smoothly rotate vert angle - input = vang - actor->movedir; - invert = (input > ANGLE_180); - if (invert) - input = InvAngle(input); - - // Slow down when turning; might as well do it for momz, since we do it above too - zspeed = FixedMul(wspeed, max(0, (((180<movedir += input; - } - + actor->cvmem = wspeed; + ModifySPBSpeedForAngle(actor, &hang, &vang, &xyspeed, &zspeed); actor->momx = FixedMul(FixedMul(xyspeed, FINECOSINE(actor->angle>>ANGLETOFINESHIFT)), FINECOSINE(actor->movedir>>ANGLETOFINESHIFT)); actor->momy = FixedMul(FixedMul(xyspeed, FINESINE(actor->angle>>ANGLETOFINESHIFT)), FINECOSINE(actor->movedir>>ANGLETOFINESHIFT)); @@ -8846,7 +8752,7 @@ void A_SPBChase(mobj_t *actor) if (R_PointToDist2(actor->x, actor->y, players[i].mo->x, players[i].mo->y) < pdist) { pdist = R_PointToDist2(actor->x, actor->y, players[i].mo->x, players[i].mo->y); - scplayer = &players[i]; // it doesn't matter if we override this guy now. + scplayer = &players[i]; } } @@ -8865,13 +8771,97 @@ void A_SPBChase(mobj_t *actor) // Spawn a trail of rings behind the SPB! SpawnSPBTrailRings(actor); - if (dist <= (1024*actor->tracer->scale)) // Close enough to target? + if (dist <= (TARGETDIST * actor->tracer->scale)) // Close enough to target? { S_StartSound(actor, actor->info->attacksound); actor->extravalue1 = 1; // TARGET ACQUIRED actor->extravalue2 = 7*TICRATE; actor->cvmem = wspeed; } + +#undef TARGETDIST + } +} + +void A_SPBChase(mobj_t *actor) +{ + UINT8 i; + UINT8 bestrank = UINT8_MAX; + player_t *bestplayer = NULL; + fixed_t wspeed; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_SPBChase", actor)) + return; +#endif + + // Default speed + wspeed = actor->movefactor; + + if (actor->threshold) // Just fired, go straight. + { + actor->lastlook = -1; + actor->cusval = -1; + spbplace = -1; + P_InstaThrust(actor, actor->angle, wspeed); + return; + } + + // Find the player with the best rank + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator || players[i].exiting) + continue; // not in-game + + if (players[i].kartstuff[k_position] < bestrank) + { + bestrank = players[i].kartstuff[k_position]; + bestplayer = &players[i]; + } + } + + // lastlook = last player num targetted + // cvmem = stored speed + // cusval = next waypoint heap index + // extravalue1 = SPB movement mode + // extravalue2 = mode misc option + + switch (actor->extravalue1) + { + case 1: // MODE: TARGETING + SPBTargettingChase(actor, wspeed, bestrank); + break; + + case 2: // MODE: WAIT... + actor->momx = actor->momy = actor->momz = 0; // Stoooop + actor->cusval = -1; // Reset waypoint + + if (actor->lastlook != -1 + && playeringame[actor->lastlook] + && !players[actor->lastlook].spectator + && !players[actor->lastlook].exiting) + { + spbplace = players[actor->lastlook].kartstuff[k_position]; + players[actor->lastlook].kartstuff[k_ringlock] = 1; + if (actor->extravalue2-- <= 0 && players[actor->lastlook].mo) + { + P_SetTarget(&actor->tracer, players[actor->lastlook].mo); + actor->extravalue1 = 1; // TARGET ACQUIRED + actor->extravalue2 = 7*TICRATE; + actor->cvmem = wspeed; + } + } + else + { + actor->extravalue1 = 0; // SEEKING + actor->extravalue2 = 0; + spbplace = -1; + } + break; + + case 0: + SPBSeekingChase(actor, bestplayer, wspeed, bestrank); + break; } // Finally, no matter what, the spb should not be able to be under the ground, or above the ceiling; @@ -8880,7 +8870,6 @@ void A_SPBChase(mobj_t *actor) else if (actor->z > actor->ceilingz - actor->height) actor->z = actor->ceilingz - actor->height; - return; } From 8704d684fc643187d1a28869af02ccc18be668a1 Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Mon, 28 Oct 2019 16:27:52 -0400 Subject: [PATCH 077/105] Revert "Break SPBChase into a few more functions" This reverts commit 41f69b3c16b5250a93b1391394f22a36f006a19d. --- src/p_enemy.c | 313 ++++++++++++++++++++++++++------------------------ 1 file changed, 162 insertions(+), 151 deletions(-) diff --git a/src/p_enemy.c b/src/p_enemy.c index 9e31206be..ff57b0ffa 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -8480,11 +8480,11 @@ void A_JawzExplode(mobj_t *actor) static void SpawnSPBTrailRings(mobj_t *actor) { - if (actor != NULL && !P_MobjWasRemoved(actor)) + if (actor != NULL) { if (leveltime % 6 == 0) { - mobj_t *ring = P_SpawnMobj(actor->x - actor->momx, actor->y - actor->momy, + mobj_t *ring = P_SpawnMobj(actor->x - actor->momx, actor->y - actor->momx, actor->z - actor->momz + (24*mapobjectscale), MT_RING); ring->threshold = 10; ring->fuse = 120*TICRATE; @@ -8492,64 +8492,72 @@ static void SpawnSPBTrailRings(mobj_t *actor) } } -static void ModifySPBSpeedForAngle(mobj_t *actor, angle_t *hang, angle_t *vang, fixed_t *xyspeed, fixed_t *zspeed) +void A_SPBChase(mobj_t *actor) { - if (actor != NULL && !P_MobjWasRemoved(actor)) + player_t *player = NULL; + player_t *scplayer = NULL; // secondary target for seeking + UINT8 i; + UINT8 bestrank = UINT8_MAX; + fixed_t dist; + angle_t hang, vang; + fixed_t wspeed, xyspeed, zspeed; + fixed_t pdist = 1536<movefactor; + + if (actor->threshold) // Just fired, go straight. { - // Smoothly rotate horz angle - angle_t input = hang - actor->angle; - boolean invert = (input > ANGLE_180); - if (invert) - input = InvAngle(input); - - // Slow down when turning; it looks better and makes U-turns not unfair - xyspeed = FixedMul(actor->cvmem, max(0, (((180<angle += input; - - // Smoothly rotate vert angle - input = vang - actor->movedir; - invert = (input > ANGLE_180); - if (invert) - input = InvAngle(input); - - // Slow down when turning; might as well do it for momz, since we do it above too - zspeed = FixedMul(actor->cvmem, max(0, (((180<movedir += input; + actor->lastlook = -1; + actor->cusval = -1; + spbplace = -1; + P_InstaThrust(actor, actor->angle, wspeed); + return; } -} -static void SPBTargettingChase(mobj_t *actor, fixed_t wspeed, UINT8 bestrank) -{ - if (actor != NULL && !P_MobjWasRemoved(actor)) + // Find the player with the best rank + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator || players[i].exiting) + continue; // not in-game + + /*if (!players[i].mo) + continue; // no mobj + + if (players[i].mo->health <= 0) + continue; // dead + + if (players[i].kartstuff[k_respawn]) + continue;*/ // respawning + + if (players[i].kartstuff[k_position] < bestrank) + { + bestrank = players[i].kartstuff[k_position]; + player = &players[i]; + } + } + + // lastlook = last player num targetted + // cvmem = stored speed + // cusval = next waypoint heap index + // extravalue1 = SPB movement mode + // extravalue2 = mode misc option + + if (actor->extravalue1 == 1) // MODE: TARGETING { actor->cusval = -1; // Reset waypoint - if (actor->tracer == NULL || P_MobjWasRemoved(actor->tracer) || !actor->tracer->health) + if (actor->tracer && actor->tracer->health) { - // Target's gone, return to SEEKING - P_SetTarget(&actor->tracer, NULL); - actor->extravalue1 = 2; // WAIT... - actor->extravalue2 = 52; // Slightly over the respawn timer length - return; - } - else - { - fixed_t xyspeed, zspeed; - angle_t hang, vang; - fixed_t dist; fixed_t defspeed = wspeed; fixed_t range = (160*actor->tracer->scale); - fixed_t cx = 0, cy = 0; + fixed_t cx = 0, cy =0; // Play the intimidating gurgle if (!S_SoundPlaying(actor, actor->info->activesound)) @@ -8600,6 +8608,7 @@ static void SPBTargettingChase(mobj_t *actor, fixed_t wspeed, UINT8 bestrank) wspeed = 20*actor->tracer->scale; if (actor->tracer->player->pflags & PF_SLIDING) wspeed = actor->tracer->player->speed/2; + // ^^^^ current section: These are annoying, and grand metropolis in particular needs this. hang = R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y); vang = R_PointToAngle2(0, actor->z, dist, actor->tracer->z); @@ -8610,7 +8619,37 @@ static void SPBTargettingChase(mobj_t *actor, fixed_t wspeed, UINT8 bestrank) else actor->cvmem = wspeed; - ModifySPBSpeedForAngle(actor, &hang, &vang, &xyspeed, &zspeed); + { + // Smoothly rotate horz angle + angle_t input = hang - actor->angle; + boolean invert = (input > ANGLE_180); + if (invert) + input = InvAngle(input); + + // Slow down when turning; it looks better and makes U-turns not unfair + xyspeed = FixedMul(actor->cvmem, max(0, (((180<angle += input; + + // Smoothly rotate vert angle + input = vang - actor->movedir; + invert = (input > ANGLE_180); + if (invert) + input = InvAngle(input); + + // Slow down when turning; might as well do it for momz, since we do it above too + zspeed = FixedMul(actor->cvmem, max(0, (((180<movedir += input; + } actor->momx = cx + FixedMul(FixedMul(xyspeed, FINECOSINE(actor->angle>>ANGLETOFINESHIFT)), FINECOSINE(actor->movedir>>ANGLETOFINESHIFT)); actor->momy = cy + FixedMul(FixedMul(xyspeed, FINESINE(actor->angle>>ANGLETOFINESHIFT)), FINECOSINE(actor->movedir>>ANGLETOFINESHIFT)); @@ -8636,21 +8675,46 @@ static void SPBTargettingChase(mobj_t *actor, fixed_t wspeed, UINT8 bestrank) fast->colorized = true; K_MatchGenericExtraFlags(fast, actor); } + + return; + } + else // Target's gone, return to SEEKING + { + P_SetTarget(&actor->tracer, NULL); + actor->extravalue1 = 2; // WAIT... + actor->extravalue2 = 52; // Slightly over the respawn timer length + return; } } -} - -static void SPBSeekingChase(mobj_t *actor, player_t *player, fixed_t wspeed, UINT8 bestrank) -{ - if (actor != NULL && !P_MobjWasRemoved(actor)) + else if (actor->extravalue1 == 2) // MODE: WAIT... + { + actor->momx = actor->momy = actor->momz = 0; // Stoooop + actor->cusval = -1; // Reset waypoint + + if (actor->lastlook != -1 + && playeringame[actor->lastlook] + && !players[actor->lastlook].spectator + && !players[actor->lastlook].exiting) + { + spbplace = players[actor->lastlook].kartstuff[k_position]; + players[actor->lastlook].kartstuff[k_ringlock] = 1; + if (actor->extravalue2-- <= 0 && players[actor->lastlook].mo) + { + P_SetTarget(&actor->tracer, players[actor->lastlook].mo); + actor->extravalue1 = 1; // TARGET ACQUIRED + actor->extravalue2 = 7*TICRATE; + actor->cvmem = wspeed; + } + } + else + { + actor->extravalue1 = 0; // SEEKING + actor->extravalue2 = 0; + spbplace = -1; + } + } + else // MODE: SEEKING { -#define TARGETDIST 1536 - fixed_t xyspeed, zspeed; - angle_t hang, vang; - fixed_t dist; - player_t *scplayer = NULL; // secondary target for swerving - fixed_t pdist = TARGETDIST<cvmem = wspeed; - ModifySPBSpeedForAngle(actor, &hang, &vang, &xyspeed, &zspeed); + { + // Smoothly rotate horz angle + angle_t input = hang - actor->angle; + boolean invert = (input > ANGLE_180); + if (invert) + input = InvAngle(input); + + // Slow down when turning; it looks better and makes U-turns not unfair + xyspeed = FixedMul(wspeed, max(0, (((180<angle += input; + + // Smoothly rotate vert angle + input = vang - actor->movedir; + invert = (input > ANGLE_180); + if (invert) + input = InvAngle(input); + + // Slow down when turning; might as well do it for momz, since we do it above too + zspeed = FixedMul(wspeed, max(0, (((180<movedir += input; + } + actor->momx = FixedMul(FixedMul(xyspeed, FINECOSINE(actor->angle>>ANGLETOFINESHIFT)), FINECOSINE(actor->movedir>>ANGLETOFINESHIFT)); actor->momy = FixedMul(FixedMul(xyspeed, FINESINE(actor->angle>>ANGLETOFINESHIFT)), FINECOSINE(actor->movedir>>ANGLETOFINESHIFT)); @@ -8752,7 +8846,7 @@ static void SPBSeekingChase(mobj_t *actor, player_t *player, fixed_t wspeed, UIN if (R_PointToDist2(actor->x, actor->y, players[i].mo->x, players[i].mo->y) < pdist) { pdist = R_PointToDist2(actor->x, actor->y, players[i].mo->x, players[i].mo->y); - scplayer = &players[i]; + scplayer = &players[i]; // it doesn't matter if we override this guy now. } } @@ -8771,97 +8865,13 @@ static void SPBSeekingChase(mobj_t *actor, player_t *player, fixed_t wspeed, UIN // Spawn a trail of rings behind the SPB! SpawnSPBTrailRings(actor); - if (dist <= (TARGETDIST * actor->tracer->scale)) // Close enough to target? + if (dist <= (1024*actor->tracer->scale)) // Close enough to target? { S_StartSound(actor, actor->info->attacksound); actor->extravalue1 = 1; // TARGET ACQUIRED actor->extravalue2 = 7*TICRATE; actor->cvmem = wspeed; } - -#undef TARGETDIST - } -} - -void A_SPBChase(mobj_t *actor) -{ - UINT8 i; - UINT8 bestrank = UINT8_MAX; - player_t *bestplayer = NULL; - fixed_t wspeed; - -#ifdef HAVE_BLUA - if (LUA_CallAction("A_SPBChase", actor)) - return; -#endif - - // Default speed - wspeed = actor->movefactor; - - if (actor->threshold) // Just fired, go straight. - { - actor->lastlook = -1; - actor->cusval = -1; - spbplace = -1; - P_InstaThrust(actor, actor->angle, wspeed); - return; - } - - // Find the player with the best rank - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator || players[i].exiting) - continue; // not in-game - - if (players[i].kartstuff[k_position] < bestrank) - { - bestrank = players[i].kartstuff[k_position]; - bestplayer = &players[i]; - } - } - - // lastlook = last player num targetted - // cvmem = stored speed - // cusval = next waypoint heap index - // extravalue1 = SPB movement mode - // extravalue2 = mode misc option - - switch (actor->extravalue1) - { - case 1: // MODE: TARGETING - SPBTargettingChase(actor, wspeed, bestrank); - break; - - case 2: // MODE: WAIT... - actor->momx = actor->momy = actor->momz = 0; // Stoooop - actor->cusval = -1; // Reset waypoint - - if (actor->lastlook != -1 - && playeringame[actor->lastlook] - && !players[actor->lastlook].spectator - && !players[actor->lastlook].exiting) - { - spbplace = players[actor->lastlook].kartstuff[k_position]; - players[actor->lastlook].kartstuff[k_ringlock] = 1; - if (actor->extravalue2-- <= 0 && players[actor->lastlook].mo) - { - P_SetTarget(&actor->tracer, players[actor->lastlook].mo); - actor->extravalue1 = 1; // TARGET ACQUIRED - actor->extravalue2 = 7*TICRATE; - actor->cvmem = wspeed; - } - } - else - { - actor->extravalue1 = 0; // SEEKING - actor->extravalue2 = 0; - spbplace = -1; - } - break; - - case 0: - SPBSeekingChase(actor, bestplayer, wspeed, bestrank); - break; } // Finally, no matter what, the spb should not be able to be under the ground, or above the ceiling; @@ -8870,6 +8880,7 @@ void A_SPBChase(mobj_t *actor) else if (actor->z > actor->ceilingz - actor->height) actor->z = actor->ceilingz - actor->height; + return; } From 607d177512b984aee8a2aed919d90e409fcba171 Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Mon, 28 Oct 2019 16:28:56 -0400 Subject: [PATCH 078/105] Fix copypaste typo --- src/p_enemy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_enemy.c b/src/p_enemy.c index ff57b0ffa..6d313f566 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -8484,7 +8484,7 @@ static void SpawnSPBTrailRings(mobj_t *actor) { if (leveltime % 6 == 0) { - mobj_t *ring = P_SpawnMobj(actor->x - actor->momx, actor->y - actor->momx, + mobj_t *ring = P_SpawnMobj(actor->x - actor->momx, actor->y - actor->momy, actor->z - actor->momz + (24*mapobjectscale), MT_RING); ring->threshold = 10; ring->fuse = 120*TICRATE; From d91e8205dc992576766ebf06f51ff52e7174daae Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Tue, 3 Mar 2020 14:22:52 -0500 Subject: [PATCH 079/105] Don't update respawn point when mid-air --- src/k_kart.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/k_kart.c b/src/k_kart.c index b3297527e..c37fb6502 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -5880,6 +5880,11 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player, boolean closest) } } + if (P_IsObjectOnGround(player->mo)) + { + updaterespawn = false; + } + // Respawn point should only be updated when we're going to a nextwaypoint if ((updaterespawn) && (bestwaypoint != NULL) && From 597e6820b6a028c9aaae9e2784a8db592d2b94cf Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Tue, 3 Mar 2020 14:47:41 -0500 Subject: [PATCH 080/105] Let's try out going back to closest check again --- src/k_kart.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index c37fb6502..2597f6138 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6006,14 +6006,16 @@ static void K_UpdateDistanceFromFinishLine(player_t *const player) else { waypoint_t *finishline = K_GetFinishLineWaypoint(); - waypoint_t *nextwaypoint = K_GetPlayerNextWaypoint(player, false); + waypoint_t *nextwaypoint = K_GetPlayerNextWaypoint(player, true); //false + /* if ((nextwaypoint == NULL) && (player->nextwaypoint == NULL)) { // Special case: if player nextwaypoint is still NULL, we want to fix that as soon as possible, so use the closest waypoint instead. // This will most likely only happen on map load or player spawn. nextwaypoint = K_GetPlayerNextWaypoint(player, true); } + */ if (nextwaypoint != NULL) { From 8a19f6f208f9be03260d0cad4cbdbd1cbee7ad0d Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Tue, 3 Mar 2020 15:41:03 -0500 Subject: [PATCH 081/105] Wrong sign here --- src/k_kart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index 2597f6138..789e271d8 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -5880,7 +5880,7 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player, boolean closest) } } - if (P_IsObjectOnGround(player->mo)) + if (!P_IsObjectOnGround(player->mo)) { updaterespawn = false; } From 1cbb4bea6b2f456031879d946963757e6b9424cc Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Tue, 3 Mar 2020 15:47:02 -0500 Subject: [PATCH 082/105] Limited vertical range on player waypoints --- src/k_kart.c | 22 ++++------------------ src/k_waypoint.c | 21 ++++++++++++++------- 2 files changed, 18 insertions(+), 25 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 789e271d8..dd86fe201 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -5739,24 +5739,19 @@ void K_KartPlayerAfterThink(player_t *player) Input Arguments:- player - The player the next waypoint is being found for - closest - Use closest waypoint algorithm, instead of best touching Return:- The waypoint that is the player's next waypoint --------------------------------------------------*/ -static waypoint_t *K_GetPlayerNextWaypoint(player_t *player, boolean closest) +static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) { waypoint_t *bestwaypoint = NULL; + if ((player != NULL) && (player->mo != NULL) && (P_MobjWasRemoved(player->mo) == false)) { - waypoint_t *waypoint = NULL; + waypoint_t *waypoint = K_GetClosestWaypointToMobj(player->mo); boolean updaterespawn = false; - if (closest == true) - waypoint = K_GetClosestWaypointToMobj(player->mo); - else - waypoint = K_GetBestWaypointTouchingMobj(player->mo); - bestwaypoint = waypoint; // check the waypoint's location in relation to the player @@ -6006,16 +6001,7 @@ static void K_UpdateDistanceFromFinishLine(player_t *const player) else { waypoint_t *finishline = K_GetFinishLineWaypoint(); - waypoint_t *nextwaypoint = K_GetPlayerNextWaypoint(player, true); //false - - /* - if ((nextwaypoint == NULL) && (player->nextwaypoint == NULL)) - { - // Special case: if player nextwaypoint is still NULL, we want to fix that as soon as possible, so use the closest waypoint instead. - // This will most likely only happen on map load or player spawn. - nextwaypoint = K_GetPlayerNextWaypoint(player, true); - } - */ + waypoint_t *nextwaypoint = K_GetPlayerNextWaypoint(player); if (nextwaypoint != NULL) { diff --git a/src/k_waypoint.c b/src/k_waypoint.c index 68b21520e..d66076630 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -221,15 +221,22 @@ waypoint_t *K_GetClosestWaypointToMobj(mobj_t *const mobj) for (i = 0; i < numwaypoints; i++) { checkwaypoint = &waypointheap[i]; - checkdist = P_AproxDistance( - (mobj->x >> FRACBITS) - (checkwaypoint->mobj->x >> FRACBITS), - (mobj->y >> FRACBITS) - (checkwaypoint->mobj->y >> FRACBITS)); - checkdist = P_AproxDistance(checkdist, (mobj->z >> FRACBITS) - (checkwaypoint->mobj->z >> FRACBITS)); - if (checkdist < closestdist) + checkdist = abs((mobj->z >> FRACBITS) - (checkwaypoint->mobj->z >> FRACBITS)); + + // TODO: Keep the old version of this function, + // make this 128 check a separate function. + if (checkdist <= 128) { - closestwaypoint = checkwaypoint; - closestdist = checkdist; + checkdist = P_AproxDistance( + (mobj->x >> FRACBITS) - (checkwaypoint->mobj->x >> FRACBITS), + (mobj->y >> FRACBITS) - (checkwaypoint->mobj->y >> FRACBITS)); + + if (checkdist < closestdist) + { + closestwaypoint = checkwaypoint; + closestdist = checkdist; + } } } } From 04b443d58c738b25577773effd6d7901e221ffc5 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Tue, 3 Mar 2020 15:49:10 -0500 Subject: [PATCH 083/105] 2.2 sight checks backport --- src/p_sight.c | 161 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 142 insertions(+), 19 deletions(-) diff --git a/src/p_sight.c b/src/p_sight.c index 626f8bbef..d607fc9e9 100644 --- a/src/p_sight.c +++ b/src/p_sight.c @@ -2,7 +2,7 @@ //----------------------------------------------------------------------------- // Copyright (C) 1993-1996 by id Software, Inc. // Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2018 by Sonic Team Junior. +// Copyright (C) 1999-2020 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -14,6 +14,7 @@ #include "doomdef.h" #include "doomstat.h" #include "p_local.h" +#include "p_slopes.h" #include "r_main.h" #include "r_state.h" @@ -103,12 +104,20 @@ static fixed_t P_InterceptVector2(divline_t *v2, divline_t *v1) static boolean P_CrossSubsecPolyObj(polyobj_t *po, register los_t *los) { size_t i; + sector_t *polysec; + + if (!(po->flags & POF_RENDERALL)) + return true; // the polyobject isn't visible, so we can ignore it + + polysec = po->lines[0]->backsector; for (i = 0; i < po->numLines; ++i) { line_t *line = po->lines[i]; divline_t divl; const vertex_t *v1,*v2; + fixed_t frac; + fixed_t topslope, bottomslope; // already checked other side? if (line->validcount == validcount) @@ -140,7 +149,22 @@ static boolean P_CrossSubsecPolyObj(polyobj_t *po, register los_t *los) continue; // stop because it is not two sided - return false; + //if (!(po->flags & POF_TESTHEIGHT)) + //return false; + + frac = P_InterceptVector2(&los->strace, &divl); + + // get slopes of top and bottom of this polyobject line + topslope = FixedDiv(polysec->ceilingheight - los->sightzstart , frac); + bottomslope = FixedDiv(polysec->floorheight - los->sightzstart , frac); + + if (topslope >= los->topslope && bottomslope <= los->bottomslope) + return false; // view completely blocked + + // TODO: figure out if it's worth considering partially blocked cases or not? + // maybe to adjust los's top/bottom slopes if needed + //if (los->topslope <= los->bottomslope) + //return false; } return true; @@ -193,6 +217,15 @@ static boolean P_CrossSubsector(size_t num, register los_t *los) const sector_t *front, *back; const vertex_t *v1,*v2; fixed_t frac; + fixed_t frontf, backf, frontc, backc; +#ifdef ESLOPE + fixed_t fracx, fracy; +#endif + + /* SRB2Kart doesn't have this? + if (seg->glseg) + continue; + */ // already checked other side? if (line->validcount == validcount) @@ -227,36 +260,51 @@ static boolean P_CrossSubsector(size_t num, register los_t *los) if (!(line->flags & ML_TWOSIDED)) return false; + // calculate fractional intercept (how far along we are divided by how far we are from t2) + frac = P_InterceptVector2(&los->strace, &divl); + + front = seg->frontsector; + back = seg->backsector; +#ifdef ESLOPE + // calculate position at intercept + fracx = los->strace.x + FixedMul(los->strace.dx, frac); + fracy = los->strace.y + FixedMul(los->strace.dy, frac); + // calculate sector heights + frontf = (front->f_slope) ? P_GetZAt(front->f_slope, fracx, fracy) : front->floorheight; + frontc = (front->c_slope) ? P_GetZAt(front->c_slope, fracx, fracy) : front->ceilingheight; + backf = (back->f_slope) ? P_GetZAt(back->f_slope, fracx, fracy) : back->floorheight; + backc = (back->c_slope) ? P_GetZAt(back->c_slope, fracx, fracy) : back->ceilingheight; +#else + frontf = front->floorheight; + frontc = front->ceilingheight; + backf = back->floorheight; + backc = back->ceilingheight; +#endif // crosses a two sided line // no wall to block sight with? - if ((front = seg->frontsector)->floorheight == - (back = seg->backsector)->floorheight && - front->ceilingheight == back->ceilingheight) + if (frontf == backf && frontc == backc + && !front->ffloors & !back->ffloors) // (and no FOFs) continue; // possible occluder // because of ceiling height differences - popentop = front->ceilingheight < back->ceilingheight ? - front->ceilingheight : back->ceilingheight ; + popentop = min(frontc, backc); // because of floor height differences - popenbottom = front->floorheight > back->floorheight ? - front->floorheight : back->floorheight ; + popenbottom = max(frontf, backf); // quick test for totally closed doors if (popenbottom >= popentop) return false; - frac = P_InterceptVector2(&los->strace, &divl); - - if (front->floorheight != back->floorheight) + if (frontf != backf) { fixed_t slope = FixedDiv(popenbottom - los->sightzstart , frac); if (slope > los->bottomslope) los->bottomslope = slope; } - if (front->ceilingheight != back->ceilingheight) + if (frontc != backc) { fixed_t slope = FixedDiv(popentop - los->sightzstart , frac); if (slope < los->topslope) @@ -265,6 +313,58 @@ static boolean P_CrossSubsector(size_t num, register los_t *los) if (los->topslope <= los->bottomslope) return false; + + // Monster Iestyn: check FOFs! + if (front->ffloors || back->ffloors) + { + ffloor_t *rover; + fixed_t topslope, bottomslope; + fixed_t topz, bottomz; + // check front sector's FOFs first + for (rover = front->ffloors; rover; rover = rover->next) + { + if (!(rover->flags & FF_EXISTS) + || !(rover->flags & FF_RENDERSIDES) || rover->flags & FF_TRANSLUCENT) + { + continue; + } + +#ifdef ESLOPE + topz = (*rover->t_slope) ? P_GetZAt(*rover->t_slope, fracx, fracy) : *rover->topheight; + bottomz = (*rover->b_slope) ? P_GetZAt(*rover->b_slope, fracx, fracy) : *rover->bottomheight; +#else + topz = *rover->topheight; + bottomz = *rover->bottomheight; +#endif + topslope = FixedDiv(topz - los->sightzstart , frac); + bottomslope = FixedDiv(bottomz - los->sightzstart , frac); + if (topslope >= los->topslope && bottomslope <= los->bottomslope) + return false; // view completely blocked + } + // check back sector's FOFs as well + for (rover = back->ffloors; rover; rover = rover->next) + { + if (!(rover->flags & FF_EXISTS) + || !(rover->flags & FF_RENDERSIDES) || rover->flags & FF_TRANSLUCENT) + { + continue; + } + +#ifdef ESLOPE + topz = (*rover->t_slope) ? P_GetZAt(*rover->t_slope, fracx, fracy) : *rover->topheight; + bottomz = (*rover->b_slope) ? P_GetZAt(*rover->b_slope, fracx, fracy) : *rover->bottomheight; +#else + topz = *rover->topheight; + bottomz = *rover->bottomheight; +#endif + topslope = FixedDiv(topz - los->sightzstart , frac); + bottomslope = FixedDiv(bottomz - los->sightzstart , frac); + if (topslope >= los->topslope && bottomslope <= los->bottomslope) + return false; // view completely blocked + } + // TODO: figure out if it's worth considering partially blocked cases or not? + // maybe to adjust los's top/bottom slopes if needed + } } // passed the subsector ok @@ -375,6 +475,8 @@ boolean P_CheckSight(mobj_t *t1, mobj_t *t2) if (s1 == s2) // Both sectors are the same. { ffloor_t *rover; + fixed_t topz1, bottomz1; // top, bottom heights at t1's position + fixed_t topz2, bottomz2; // likewise but for t2 for (rover = s1->ffloors; rover; rover = rover->next) { @@ -387,9 +489,30 @@ boolean P_CheckSight(mobj_t *t1, mobj_t *t2) continue; } +#ifdef ESLOPE + if (*rover->t_slope) + { + topz1 = P_GetZAt(*rover->t_slope, t1->x, t1->y); + topz2 = P_GetZAt(*rover->t_slope, t2->x, t2->y); + } + else + topz1 = topz2 = *rover->topheight; + + if (*rover->b_slope) + { + bottomz1 = P_GetZAt(*rover->b_slope, t1->x, t1->y); + bottomz2 = P_GetZAt(*rover->b_slope, t2->x, t2->y); + } + else + bottomz1 = bottomz2 = *rover->bottomheight; +#else + topz1 = topz2 = *rover->topheight; + bottomz1 = bottomz2 = *rover->bottomheight; +#endif + // Check for blocking floors here. - if ((los.sightzstart < *rover->bottomheight && t2->z >= *rover->topheight) - || (los.sightzstart >= *rover->topheight && t2->z + t2->height < *rover->bottomheight)) + if ((los.sightzstart < bottomz1 && t2->z >= topz2) + || (los.sightzstart >= topz1 && t2->z + t2->height < bottomz2)) { // no way to see through that return false; @@ -400,19 +523,19 @@ boolean P_CheckSight(mobj_t *t1, mobj_t *t2) if (!(rover->flags & FF_INVERTPLANES)) { - if (los.sightzstart >= *rover->topheight && t2->z + t2->height < *rover->topheight) + if (los.sightzstart >= topz1 && t2->z + t2->height < topz2) return false; // blocked by upper outside plane - if (los.sightzstart < *rover->bottomheight && t2->z >= *rover->bottomheight) + if (los.sightzstart < bottomz1 && t2->z >= bottomz2) return false; // blocked by lower outside plane } if (rover->flags & FF_INVERTPLANES || rover->flags & FF_BOTHPLANES) { - if (los.sightzstart < *rover->topheight && t2->z >= *rover->topheight) + if (los.sightzstart < topz1 && t2->z >= topz2) return false; // blocked by upper inside plane - if (los.sightzstart >= *rover->bottomheight && t2->z + t2->height < *rover->bottomheight) + if (los.sightzstart >= bottomz1 && t2->z + t2->height < bottomz2) return false; // blocked by lower inside plane } } From f068539bad47b4081ee3defe4e8967b9c897c671 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Tue, 3 Mar 2020 16:12:21 -0500 Subject: [PATCH 084/105] Don't sight check through transparent FOFs Maybe this should be a option somehow, like a distinction between "sight" and "ray-tracing", but I'm not sure how to go about that --- src/p_sight.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_sight.c b/src/p_sight.c index d607fc9e9..f230f40f6 100644 --- a/src/p_sight.c +++ b/src/p_sight.c @@ -484,7 +484,7 @@ boolean P_CheckSight(mobj_t *t1, mobj_t *t2) /// \todo Improve by checking fog density/translucency /// and setting a sight limit. if (!(rover->flags & FF_EXISTS) - || !(rover->flags & FF_RENDERPLANES) || rover->flags & FF_TRANSLUCENT) + || !(rover->flags & FF_RENDERPLANES) /*|| (rover->flags & FF_TRANSLUCENT)*/) { continue; } From ac1d40d6c525387fe45019dd63bce081c2c0e31e Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Tue, 3 Mar 2020 16:14:19 -0500 Subject: [PATCH 085/105] Sight check for player waypoints --- src/k_waypoint.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/k_waypoint.c b/src/k_waypoint.c index d66076630..1913af0a6 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -222,10 +222,10 @@ waypoint_t *K_GetClosestWaypointToMobj(mobj_t *const mobj) { checkwaypoint = &waypointheap[i]; + // TODO: Keep the old version of this function, + // make this 128 check & sight checks a separate function. checkdist = abs((mobj->z >> FRACBITS) - (checkwaypoint->mobj->z >> FRACBITS)); - // TODO: Keep the old version of this function, - // make this 128 check a separate function. if (checkdist <= 128) { checkdist = P_AproxDistance( @@ -234,6 +234,12 @@ waypoint_t *K_GetClosestWaypointToMobj(mobj_t *const mobj) 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; + } + closestwaypoint = checkwaypoint; closestdist = checkdist; } From 7fff21acd4aa7a7f34f8c7e9a14962c08cb0e9c5 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Tue, 3 Mar 2020 16:58:09 -0500 Subject: [PATCH 086/105] Instead of vertical cap, quadruple z axis distance Far more natural results! --- src/k_waypoint.c | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/k_waypoint.c b/src/k_waypoint.c index 1913af0a6..8d822c3d6 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -223,26 +223,22 @@ waypoint_t *K_GetClosestWaypointToMobj(mobj_t *const mobj) checkwaypoint = &waypointheap[i]; // TODO: Keep the old version of this function, - // make this 128 check & sight checks a separate function. - checkdist = abs((mobj->z >> FRACBITS) - (checkwaypoint->mobj->z >> FRACBITS)); + // make the vertical axis faking & sight checks a separate function. + checkdist = P_AproxDistance( + (mobj->x >> FRACBITS) - (checkwaypoint->mobj->x >> FRACBITS), + (mobj->y >> FRACBITS) - (checkwaypoint->mobj->y >> FRACBITS)); + checkdist = P_AproxDistance(checkdist, ((mobj->z >> FRACBITS) - (checkwaypoint->mobj->z >> FRACBITS)) << 2); - if (checkdist <= 128) + if (checkdist < closestdist) { - checkdist = P_AproxDistance( - (mobj->x >> FRACBITS) - (checkwaypoint->mobj->x >> FRACBITS), - (mobj->y >> FRACBITS) - (checkwaypoint->mobj->y >> FRACBITS)); - - if (checkdist < closestdist) + if (!P_CheckSight(mobj, checkwaypoint->mobj)) { - if (!P_CheckSight(mobj, checkwaypoint->mobj)) - { - // Save sight checks for the end, so we only do it if we have to - continue; - } - - closestwaypoint = checkwaypoint; - closestdist = checkdist; + // Save sight checks for the end, so we only do it if we have to + continue; } + + closestwaypoint = checkwaypoint; + closestdist = checkdist; } } } From 721fb369fd5920646b363a4890271f1aadebe7a9 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Tue, 3 Mar 2020 18:34:17 -0500 Subject: [PATCH 087/105] Move changes to K_GetBestWaypointForMobj so that K_GetClosestWaypointToMobj can stay the same --- src/k_kart.c | 2 +- src/k_waypoint.c | 85 +++++++++++++++++++++++------------------------- src/k_waypoint.h | 10 +++--- src/p_enemy.c | 5 ++- 4 files changed, 50 insertions(+), 52 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index dd86fe201..0cb8b9d20 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -5749,7 +5749,7 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) if ((player != NULL) && (player->mo != NULL) && (P_MobjWasRemoved(player->mo) == false)) { - waypoint_t *waypoint = K_GetClosestWaypointToMobj(player->mo); + waypoint_t *waypoint = K_GetBestWaypointForMobj(player->mo); boolean updaterespawn = false; bestwaypoint = waypoint; diff --git a/src/k_waypoint.c b/src/k_waypoint.c index 8d822c3d6..dfa868204 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -222,8 +222,46 @@ waypoint_t *K_GetClosestWaypointToMobj(mobj_t *const mobj) { checkwaypoint = &waypointheap[i]; - // TODO: Keep the old version of this function, - // make the vertical axis faking & sight checks a separate function. + checkdist = P_AproxDistance( + (mobj->x >> FRACBITS) - (checkwaypoint->mobj->x >> FRACBITS), + (mobj->y >> FRACBITS) - (checkwaypoint->mobj->y >> FRACBITS)); + checkdist = P_AproxDistance(checkdist, (mobj->z >> FRACBITS) - (checkwaypoint->mobj->z >> FRACBITS)); + + 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 >> FRACBITS) - (checkwaypoint->mobj->x >> FRACBITS), (mobj->y >> FRACBITS) - (checkwaypoint->mobj->y >> FRACBITS)); @@ -237,49 +275,8 @@ waypoint_t *K_GetClosestWaypointToMobj(mobj_t *const mobj) continue; } - closestwaypoint = checkwaypoint; - closestdist = checkdist; - } - } - } - - return closestwaypoint; -} - -/*-------------------------------------------------- - waypoint_t *K_GetBestWaypointTouchingMobj(mobj_t *const mobj) - - See header file for description. ---------------------------------------------------*/ -waypoint_t *K_GetBestWaypointTouchingMobj(mobj_t *const mobj) -{ - waypoint_t *bestwaypoint = NULL; - - if ((mobj == NULL) || P_MobjWasRemoved(mobj)) - { - CONS_Debug(DBG_GAMELOGIC, "NULL mobj in K_GetBestWaypointTouchingMobj.\n"); - } - else - { - size_t i = 0U; - waypoint_t *checkwaypoint = NULL; - fixed_t bestdist = INT32_MAX; - fixed_t checkdist = INT32_MAX; - - for (i = 0; i < numwaypoints; i++) - { - checkwaypoint = &waypointheap[i]; - checkdist = P_AproxDistance( - (mobj->x >> FRACBITS) - (checkwaypoint->mobj->x >> FRACBITS), - (mobj->y >> FRACBITS) - (checkwaypoint->mobj->y >> FRACBITS)); - checkdist = P_AproxDistance(checkdist, (mobj->z >> FRACBITS) - (checkwaypoint->mobj->z >> FRACBITS)); - - // The mobj has to be touching this waypoint to use it. - if ((checkdist <= (checkwaypoint->mobj->radius >> FRACBITS)) - && (checkdist < bestdist)) - { bestwaypoint = checkwaypoint; - bestdist = checkdist; + closestdist = checkdist; } } } diff --git a/src/k_waypoint.h b/src/k_waypoint.h index af4ac71a5..058ff6882 100644 --- a/src/k_waypoint.h +++ b/src/k_waypoint.h @@ -152,17 +152,19 @@ waypoint_t *K_GetClosestWaypointToMobj(mobj_t *const mobj); /*-------------------------------------------------- - waypoint_t *K_GetBestWaypointTouchingMobj(mobj_t *const mobj) + waypoint_t *K_GetBestWaypointForMobj(mobj_t *const mobj) - Returns the waypoint closest to the finish line that an mobj is touching + Similar to K_GetClosestWaypointToMobj, but prioritizes horizontal distance over vertical distance, and + sight checks to ensure that the waypoint and mobj are the in same area. Can potentially return NULL if + there are no visible waypoints. Input Arguments:- mobj - mobj to get the waypoint for. Return:- - The best waypoint for the mobj + The best waypoint for the mobj, or NULL if there were no matches --------------------------------------------------*/ -waypoint_t *K_GetBestWaypointTouchingMobj(mobj_t *const mobj); +waypoint_t *K_GetBestWaypointForMobj(mobj_t *const mobj); /*-------------------------------------------------- diff --git a/src/p_enemy.c b/src/p_enemy.c index 6d313f566..69058db23 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -8741,7 +8741,7 @@ void A_SPBChase(mobj_t *actor) { // Previously set nextwaypoint lastwaypoint = K_GetWaypointFromIndex((size_t)actor->cusval); - tempwaypoint = K_GetBestWaypointTouchingMobj(actor); + tempwaypoint = K_GetBestWaypointForMobj(actor); // check if the tempwaypoint corresponds to lastwaypoint's next ID at least; // This is to avoid situations where the SPB decides to suicide jump down a bridge because it found a COMPLETELY unrelated waypoint down there. @@ -8752,7 +8752,7 @@ void A_SPBChase(mobj_t *actor) bestwaypoint = K_GetWaypointFromIndex((size_t)actor->extravalue2); // keep going from the PREVIOUS wp. } else - bestwaypoint = K_GetBestWaypointTouchingMobj(actor); + bestwaypoint = K_GetBestWaypointForMobj(actor); if (bestwaypoint == NULL && lastwaypoint == NULL) { @@ -8781,7 +8781,6 @@ void A_SPBChase(mobj_t *actor) nextwaypoint = lastwaypoint; } - if (nextwaypoint != NULL) { const fixed_t xywaypointdist = P_AproxDistance( From 2b6ca4a38471fa91efd1ac4b6d559d9d1d9a1982 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Tue, 3 Mar 2020 20:56:57 -0500 Subject: [PATCH 088/105] Increase SPB speed & turn --- src/info.c | 2 +- src/p_enemy.c | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/info.c b/src/info.c index 839debb3b..444d79196 100644 --- a/src/info.c +++ b/src/info.c @@ -16003,7 +16003,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_SPB_DEAD, // deathstate S_NULL, // xdeathstate sfx_s3k5d, // deathsound - 64*FRACUNIT, // speed + 80*FRACUNIT, // speed 24*FRACUNIT, // radius 48*FRACUNIT, // height 0, // display offset diff --git a/src/p_enemy.c b/src/p_enemy.c index 69058db23..03eea951b 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -8806,13 +8806,13 @@ void A_SPBChase(mobj_t *actor) if (invert) input = InvAngle(input); + input = FixedAngle(AngleFixed(input)/8); + // Slow down when turning; it looks better and makes U-turns not unfair xyspeed = FixedMul(wspeed, max(0, (((180<angle += input; // Smoothly rotate vert angle @@ -8821,17 +8821,16 @@ void A_SPBChase(mobj_t *actor) if (invert) input = InvAngle(input); + input = FixedAngle(AngleFixed(input)/8); + // Slow down when turning; might as well do it for momz, since we do it above too zspeed = FixedMul(wspeed, max(0, (((180<movedir += input; } - actor->momx = FixedMul(FixedMul(xyspeed, FINECOSINE(actor->angle>>ANGLETOFINESHIFT)), FINECOSINE(actor->movedir>>ANGLETOFINESHIFT)); actor->momy = FixedMul(FixedMul(xyspeed, FINESINE(actor->angle>>ANGLETOFINESHIFT)), FINECOSINE(actor->movedir>>ANGLETOFINESHIFT)); actor->momz = FixedMul(zspeed, FINESINE(actor->movedir>>ANGLETOFINESHIFT)); @@ -8879,7 +8878,6 @@ void A_SPBChase(mobj_t *actor) else if (actor->z > actor->ceilingz - actor->height) actor->z = actor->ceilingz - actor->height; - return; } From d8e0bf61f64328e2a8853381821126b77ac1e045 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sun, 8 Mar 2020 00:26:33 -0500 Subject: [PATCH 089/105] Scale item distances with map scale --- src/k_kart.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/k_kart.c b/src/k_kart.c index 0cb8b9d20..853f50a93 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1101,6 +1101,9 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) } } + if (mapobjectscale != FRACUNIT) + pdis = FixedDiv(pdis, mapobjectscale); + if (franticitems) // Frantic items make the distances between everyone artifically higher, for crazier items pdis = (15 * pdis) / 14; From e3d50862c0d2bcf98d1d5e95c3b1baa865a2fa02 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sun, 15 Mar 2020 18:38:25 -0400 Subject: [PATCH 090/105] Using sparks in air experiment --- src/k_kart.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 9792c5a3c..b53f388e8 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -5783,36 +5783,48 @@ static void K_KartDrift(player_t *player, boolean onground) // Holding the Jump button will enable drifting. // Drift Release (Moved here so you can't "chain" drifts) - if ((player->kartstuff[k_drift] != -5 && player->kartstuff[k_drift] != 5) - // || (player->kartstuff[k_drift] >= 1 && player->kartstuff[k_turndir] != 1) || (player->kartstuff[k_drift] <= -1 && player->kartstuff[k_turndir] != -1)) - && onground) + if (player->kartstuff[k_drift] != -5 && player->kartstuff[k_drift] != 5) { if (player->kartstuff[k_driftcharge] < 0 || player->kartstuff[k_driftcharge] >= dsone) { + angle_t pushdir = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); + S_StartSound(player->mo, sfx_s23c); //K_SpawnDashDustRelease(player); if (player->kartstuff[k_driftcharge] < 0) { // Stage 0: Yellow sparks + if (!onground) + P_Thrust(player->mo, pushdir, player->speed / 8); + if (player->kartstuff[k_driftboost] < 15) player->kartstuff[k_driftboost] = 15; } else if (player->kartstuff[k_driftcharge] >= dsone && player->kartstuff[k_driftcharge] < dstwo) { // Stage 1: Red sparks + if (!onground) + P_Thrust(player->mo, pushdir, player->speed / 4); + if (player->kartstuff[k_driftboost] < 20) player->kartstuff[k_driftboost] = 20; } else if (player->kartstuff[k_driftcharge] < dsthree) { // Stage 2: Blue sparks + if (!onground) + P_Thrust(player->mo, pushdir, player->speed / 3); + if (player->kartstuff[k_driftboost] < 50) player->kartstuff[k_driftboost] = 50; } else if (player->kartstuff[k_driftcharge] >= dsthree) { // Stage 3: Rainbow sparks + if (!onground) + P_Thrust(player->mo, pushdir, player->speed / 2); + if (player->kartstuff[k_driftboost] < 125) player->kartstuff[k_driftboost] = 125; } From 084901a8b1107a697ea0289818875ade14d50007 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sun, 15 Mar 2020 19:21:06 -0400 Subject: [PATCH 091/105] Add warnings for old map setups We probably don't want to keep these for release, but we need reminders to get rid of them ourselves :V --- src/p_mobj.c | 4 ++++ src/p_spec.c | 2 ++ 2 files changed, 6 insertions(+) diff --git a/src/p_mobj.c b/src/p_mobj.c index 565ef6fbd..325229de8 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -10650,6 +10650,10 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) } } break; + case MT_BOSS3WAYPOINT: + // Remove before release + CONS_Alert(CONS_WARNING, "Boss waypoints are deprecated. Did you forget to remove the old checkpoints, too?\n"); + break; default: break; } diff --git a/src/p_spec.c b/src/p_spec.c index 0af99f163..8e368d710 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -5801,6 +5801,8 @@ void P_SpawnSpecials(INT32 fromnetsave) switch(GETSECSPECIAL(sector->special, 4)) { case 10: // Circuit finish line (Unused) + // Remove before release + CONS_Alert(CONS_WARNING, "Finish line sector type is deprecated.\n"); break; } } From 5ef433f9c73525128b9125f4e28516b3289321f4 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Tue, 17 Mar 2020 19:54:32 -0400 Subject: [PATCH 092/105] Remove bumper when you respawn in Battle again (This stuff really should be its own function...) --- src/k_kart.c | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index e3b674f33..27a4b1e1f 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2217,12 +2217,45 @@ void K_RespawnChecker(player_t *player) S_StartSound(player->mo, sfx_s23c); player->kartstuff[k_startboost] = 50; K_SpawnDashDustRelease(player); - } + } + player->mo->colorized = false; player->kartstuff[k_dropdash] = 0; - player->kartstuff[k_respawn] = 0; + player->kartstuff[k_respawn] = 0; + //P_PlayRinglossSound(player->mo); - P_PlayerRingBurst(player, 3); + P_PlayerRingBurst(player, 3); + + if (G_BattleGametype()) + { + if (player->kartstuff[k_bumper] > 0) + { + if (player->kartstuff[k_bumper] == 1) + { + mobj_t *karmahitbox = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_KARMAHITBOX); // Player hitbox is too small!! + P_SetTarget(&karmahitbox->target, player->mo); + karmahitbox->destscale = player->mo->scale; + P_SetScale(karmahitbox, player->mo->scale); + CONS_Printf(M_GetText("%s lost all of their bumpers!\n"), player_names[player-players]); + } + player->kartstuff[k_bumper]--; + if (K_IsPlayerWanted(player)) + K_CalculateBattleWanted(); + } + + if (!player->kartstuff[k_bumper]) + { + player->kartstuff[k_comebacktimer] = comebacktime; + if (player->kartstuff[k_comebackmode] == 2) + { + mobj_t *poof = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_EXPLODE); + S_StartSound(poof, mobjinfo[MT_KARMAHITBOX].seesound); + player->kartstuff[k_comebackmode] = 0; + } + } + + K_CheckBumpers(); + } } } } From b959c01349ba98ac62ff72964346ee2ae5f33206 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Fri, 20 Mar 2020 11:04:26 +0100 Subject: [PATCH 093/105] Fix antigrav respawn, no respawn flag, and remove spinout when respawning --- src/k_kart.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/k_kart.c b/src/k_kart.c index 27a4b1e1f..edfcf6991 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2048,6 +2048,8 @@ void K_RespawnChecker(player_t *player) fixed_t destx = 0, desty = 0, destz = 0; player->mo->momx = player->mo->momy = player->mo->momz = 0; + player->kartstuff[k_spinouttimer] = 0; + player->kartstuff[k_wipeoutslow] = 0; // Don't spinout anymore player->powers[pw_flashing] = 2; player->powers[pw_nocontrol] = 2; @@ -2059,9 +2061,20 @@ void K_RespawnChecker(player_t *player) destz = (player->starpostz << FRACBITS); if (player->kartstuff[k_starpostflip]) + { + // This variable is set from the settings of the best waypoint, thus this waypoint is FLIPPED as well. + // So we should flip the player in advance for it as well. + player->mo->flags2 |= MF2_OBJECTFLIP; + player->mo->eflags |= MFE_VERTICALFLIP; destz -= (128 * mapobjectscale) + (player->mo->height); + } else + { + // Ditto, but this waypoint isn't flipped, so make sure the player also isn't flipped! + player->mo->flags2 &= ~MF2_OBJECTFLIP; + player->mo->eflags &= ~MFE_VERTICALFLIP; destz += (128 * mapobjectscale); + } if (player->mo->x != destx || player->mo->y != desty || player->mo->z != destz) { @@ -6068,6 +6081,7 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) (bestwaypoint != NULL) && (bestwaypoint != player->nextwaypoint) && (player->kartstuff[k_respawn] == 0) && + (!(bestwaypoint->mobj->spawnpoint->options & MTF_AMBUSH)) && // Don't try to respawn on waypoints with the MTF_AMBUSH (No respawn) flag! (K_GetWaypointIsShortcut(bestwaypoint) == false) && (K_GetWaypointIsEnabled(bestwaypoint) == true)) { size_t i = 0U; From 263ef8f92cc0f9bdf4d651c7f96aa7d86db0fd82 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Fri, 20 Mar 2020 19:33:41 -0400 Subject: [PATCH 094/105] Use divide/multiply for waypoint closest calculations --- src/k_waypoint.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/k_waypoint.c b/src/k_waypoint.c index dfa868204..849c0e5c7 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -223,9 +223,9 @@ waypoint_t *K_GetClosestWaypointToMobj(mobj_t *const mobj) checkwaypoint = &waypointheap[i]; checkdist = P_AproxDistance( - (mobj->x >> FRACBITS) - (checkwaypoint->mobj->x >> FRACBITS), - (mobj->y >> FRACBITS) - (checkwaypoint->mobj->y >> FRACBITS)); - checkdist = P_AproxDistance(checkdist, (mobj->z >> FRACBITS) - (checkwaypoint->mobj->z >> FRACBITS)); + (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) { @@ -263,9 +263,9 @@ waypoint_t *K_GetBestWaypointForMobj(mobj_t *const mobj) checkwaypoint = &waypointheap[i]; checkdist = P_AproxDistance( - (mobj->x >> FRACBITS) - (checkwaypoint->mobj->x >> FRACBITS), - (mobj->y >> FRACBITS) - (checkwaypoint->mobj->y >> FRACBITS)); - checkdist = P_AproxDistance(checkdist, ((mobj->z >> FRACBITS) - (checkwaypoint->mobj->z >> FRACBITS)) << 2); + (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) { From 3d3b8b6bc674189cd5afe9321305aca0aa5fac49 Mon Sep 17 00:00:00 2001 From: Sryder Date: Sat, 21 Mar 2020 19:00:00 +0000 Subject: [PATCH 095/105] Replace many cases of if else if statements with I_Asserts. This is done specifically on static functions that aren't used as callbacks. This is done to keep the code cleaner, and the I_Asserts get compiled out in Release builds. Logic is left in the "public" functions to avoid crashes as a result of the modules. --- src/k_bheap.c | 278 ++++++++--------------- src/k_pathfind.c | 85 +++---- src/k_waypoint.c | 571 +++++++++++++++++++---------------------------- 3 files changed, 357 insertions(+), 577 deletions(-) diff --git a/src/k_bheap.c b/src/k_bheap.c index 527af1bfc..cf6848f68 100644 --- a/src/k_bheap.c +++ b/src/k_bheap.c @@ -18,20 +18,12 @@ static boolean K_BHeapItemValidate(bheap_t *heap, bheapitem_t *item) { boolean heapitemvalid = false; - if (heap == NULL) + I_Assert(heap != NULL); + I_Assert(item != NULL); + + if ((item->data != NULL) && (item->heapindex < SIZE_MAX / 2) && (item->owner == heap)) { - CONS_Debug(DBG_GAMELOGIC, "NULL heap in K_BHeapItemValidate.\n"); - } - else if (item == NULL) - { - CONS_Debug(DBG_GAMELOGIC, "NULL item in K_BHeapItemValidate.\n"); - } - else - { - if ((item->data != NULL) && (item->heapindex < SIZE_MAX / 2) && (item->owner == heap)) - { - heapitemvalid = true; - } + heapitemvalid = true; } return heapitemvalid; @@ -53,40 +45,21 @@ static boolean K_BHeapItemValidate(bheap_t *heap, bheapitem_t *item) static bheapitem_t *K_BHeapItemsCompare(bheap_t *heap, bheapitem_t *item1, bheapitem_t *item2) { bheapitem_t *lowervalueitem = NULL; - if (heap == NULL) + + I_Assert(heap != NULL); + I_Assert(K_BHeapValid(heap)); + I_Assert(item1 != NULL); + I_Assert(item2 != NULL); + I_Assert(K_BHeapItemValidate(heap, item1)); + I_Assert(K_BHeapItemValidate(heap, item2)); + + if (item1->value < item2->value) { - CONS_Debug(DBG_GAMELOGIC, "NULL heap in K_BHeapItemsCompare.\n"); - } - else if (K_BHeapValid(heap) == false) - { - CONS_Debug(DBG_GAMELOGIC, "Invalid heap in K_BHeapSwapItems.\n"); - } - else if (item1 == NULL) - { - CONS_Debug(DBG_GAMELOGIC, "NULL item1 in K_BHeapItemsCompare.\n"); - } - else if (item2 == NULL) - { - CONS_Debug(DBG_GAMELOGIC, "NULL item2 in K_BHeapItemsCompare.\n"); - } - else if (K_BHeapItemValidate(heap, item1) == false) - { - CONS_Debug(DBG_GAMELOGIC, "Invalid item1 in K_BHeapItemsCompare.\n"); - } - else if (K_BHeapItemValidate(heap, item2) == false) - { - CONS_Debug(DBG_GAMELOGIC, "Invalid item2 in K_BHeapItemsCompare.\n"); + lowervalueitem = item1; } else { - if (item1->value < item2->value) - { - lowervalueitem = item1; - } - else - { - lowervalueitem = item2; - } + lowervalueitem = item2; } return lowervalueitem; @@ -107,31 +80,13 @@ static bheapitem_t *K_BHeapItemsCompare(bheap_t *heap, bheapitem_t *item1, bheap --------------------------------------------------*/ static void K_BHeapSwapItems(bheap_t *heap, bheapitem_t *item1, bheapitem_t *item2) { - if (heap == NULL) - { - CONS_Debug(DBG_GAMELOGIC, "NULL heap in K_BHeapSwapItems.\n"); - } - else if (K_BHeapValid(heap) == false) - { - CONS_Debug(DBG_GAMELOGIC, "Invalid heap in K_BHeapSwapItems.\n"); - } - else if (item1 == NULL) - { - CONS_Debug(DBG_GAMELOGIC, "NULL item1 in K_BHeapSwapItems.\n"); - } - else if (item2 == NULL) - { - CONS_Debug(DBG_GAMELOGIC, "NULL item2 in K_BHeapSwapItems.\n"); - } - else if (K_BHeapItemValidate(heap, item1) == false) - { - CONS_Debug(DBG_GAMELOGIC, "Invalid item1 in K_BHeapSwapItems.\n"); - } - else if (K_BHeapItemValidate(heap, item2) == false) - { - CONS_Debug(DBG_GAMELOGIC, "Invalid item2 in K_BHeapSwapItems.\n"); - } - else + I_Assert(heap != NULL); + I_Assert(K_BHeapValid(heap)); + I_Assert(item1 != NULL); + I_Assert(item2 != NULL); + I_Assert(K_BHeapItemValidate(heap, item1)); + I_Assert(K_BHeapItemValidate(heap, item2)); + { size_t tempitemindex = item1->heapindex; bheapitem_t tempitemstore = *item1; @@ -170,18 +125,10 @@ static size_t K_BHeapItemGetParentIndex(bheapitem_t *item) { size_t parentindex = SIZE_MAX; - if (item == NULL) - { - CONS_Debug(DBG_GAMELOGIC, "NULL item in K_BHeapItemGetParentIndex.\n"); - } - else if (item->heapindex >= (SIZE_MAX / 2)) - { - CONS_Debug(DBG_GAMELOGIC, "Bad item heapindex in K_BHeapItemGetParentIndex.\n"); - } - else - { - parentindex = (item->heapindex - 1U) / 2U; - } + I_Assert(item != NULL); + I_Assert(item->heapindex < (SIZE_MAX / 2)); + + parentindex = (item->heapindex - 1U) / 2U; return parentindex; } @@ -201,18 +148,10 @@ static size_t K_BHeapItemGetLeftChildIndex(bheapitem_t *item) { size_t leftchildindex = SIZE_MAX; - if (item == NULL) - { - CONS_Debug(DBG_GAMELOGIC, "NULL item in K_BHeapItemGetLeftChildIndex.\n"); - } - else if (item->heapindex >= (SIZE_MAX / 2)) - { - CONS_Debug(DBG_GAMELOGIC, "Bad item heapindex in K_BHeapItemGetLeftChildIndex.\n"); - } - else - { - leftchildindex = (item->heapindex * 2U) + 1U; - } + I_Assert(item != NULL); + I_Assert(item->heapindex < (SIZE_MAX / 2)); + + leftchildindex = (item->heapindex * 2U) + 1U; return leftchildindex; } @@ -232,18 +171,10 @@ static size_t K_BHeapItemGetRightChildIndex(bheapitem_t *item) { size_t rightchildindex = SIZE_MAX; - if (item == NULL) - { - CONS_Debug(DBG_GAMELOGIC, "NULL item in K_BHeapItemGetRightChildIndex.\n"); - } - else if (item->heapindex >= (SIZE_MAX / 2)) - { - CONS_Debug(DBG_GAMELOGIC, "Bad item heapindex in K_BHeapItemGetRightChildIndex.\n"); - } - else - { - rightchildindex = (item->heapindex * 2U) + 2U; - } + I_Assert(item != NULL); + I_Assert(item->heapindex < (SIZE_MAX / 2)); + + rightchildindex = (item->heapindex * 2U) + 2U; return rightchildindex; } @@ -262,38 +193,27 @@ static size_t K_BHeapItemGetRightChildIndex(bheapitem_t *item) --------------------------------------------------*/ static void K_BHeapSortUp(bheap_t *heap, bheapitem_t *item) { - if (heap == NULL) - { - CONS_Debug(DBG_GAMELOGIC, "NULL heap in K_BHeapSortUp.\n"); - } - else if (K_BHeapValid(heap) == false) - { - CONS_Debug(DBG_GAMELOGIC, "Invalid heap in K_BHeapSortUp.\n"); - } - else if (item == NULL) - { - CONS_Debug(DBG_GAMELOGIC, "NULL item in K_BHeapSortUp.\n"); - } - else - { - if (item->heapindex > 0U) - { - size_t parentindex = SIZE_MAX; - do - { - parentindex = K_BHeapItemGetParentIndex(item); + I_Assert(heap != NULL); + I_Assert(K_BHeapValid(heap)); + I_Assert(item != NULL); - // Swap the nodes if the parent has a higher value - if (K_BHeapItemsCompare(heap, item, &heap->array[parentindex]) == item) - { - K_BHeapSwapItems(heap, item, &heap->array[parentindex]); - } - else - { - break; - } - } while (parentindex > 0U); - } + if (item->heapindex > 0U) + { + size_t parentindex = SIZE_MAX; + do + { + parentindex = K_BHeapItemGetParentIndex(item); + + // Swap the nodes if the parent has a higher value + if (K_BHeapItemsCompare(heap, item, &heap->array[parentindex]) == item) + { + K_BHeapSwapItems(heap, item, &heap->array[parentindex]); + } + else + { + break; + } + } while (parentindex > 0U); } } @@ -311,71 +231,59 @@ static void K_BHeapSortUp(bheap_t *heap, bheapitem_t *item) --------------------------------------------------*/ static void K_BHeapSortDown(bheap_t *heap, bheapitem_t *item) { - if (heap == NULL) + I_Assert(heap != NULL); + I_Assert(K_BHeapValid(heap)); + I_Assert(item != NULL); + + if (heap->count > 0U) { - CONS_Debug(DBG_GAMELOGIC, "NULL heap in K_BHeapSortDown.\n"); - } - else if (K_BHeapValid(heap) == false) - { - CONS_Debug(DBG_GAMELOGIC, "Invalid heap in K_BHeapSortDown.\n"); - } - else if (item == NULL) - { - CONS_Debug(DBG_GAMELOGIC, "NULL item in K_BHeapSortDown.\n"); - } - else - { - if (heap->count > 0U) + size_t leftchildindex = SIZE_MAX; + size_t rightchildindex = SIZE_MAX; + bheapitem_t *leftchild = NULL; + bheapitem_t *rightchild = NULL; + bheapitem_t *swapchild = NULL; + boolean noswapneeded = false; + + do { - size_t leftchildindex = SIZE_MAX; - size_t rightchildindex = SIZE_MAX; - bheapitem_t *leftchild = NULL; - bheapitem_t *rightchild = NULL; - bheapitem_t *swapchild = NULL; - boolean noswapneeded = false; + leftchildindex = K_BHeapItemGetLeftChildIndex(item); + rightchildindex = K_BHeapItemGetRightChildIndex(item); - do + if (leftchildindex < heap->count) { - leftchildindex = K_BHeapItemGetLeftChildIndex(item); - rightchildindex = K_BHeapItemGetRightChildIndex(item); - - if (leftchildindex < heap->count) + leftchild = &heap->array[leftchildindex]; + swapchild = leftchild; + if (rightchildindex < heap->count) { - leftchild = &heap->array[leftchildindex]; - swapchild = leftchild; - if (rightchildindex < heap->count) + rightchild = &heap->array[rightchildindex]; + // Choose the lower child node to swap with + if (K_BHeapItemsCompare(heap, leftchild, rightchild) == rightchild) { - rightchild = &heap->array[rightchildindex]; - // Choose the lower child node to swap with - if (K_BHeapItemsCompare(heap, leftchild, rightchild) == rightchild) - { - swapchild = rightchild; - } - } - - // Swap with the lower child, if it's lower than item - if (K_BHeapItemsCompare(heap, swapchild, item) == swapchild) - { - K_BHeapSwapItems(heap, item, swapchild); - } - else - { - noswapneeded = true; + swapchild = rightchild; } + } + // Swap with the lower child, if it's lower than item + if (K_BHeapItemsCompare(heap, swapchild, item) == swapchild) + { + K_BHeapSwapItems(heap, item, swapchild); } else { noswapneeded = true; } + } + else + { + noswapneeded = true; + } - if (noswapneeded) - { - break; - } + if (noswapneeded) + { + break; + } - } while (item->heapindex < (heap->count - 1U)); - } + } while (item->heapindex < (heap->count - 1U)); } } diff --git a/src/k_pathfind.c b/src/k_pathfind.c index d80f20f32..ae05930f0 100644 --- a/src/k_pathfind.c +++ b/src/k_pathfind.c @@ -23,14 +23,10 @@ static const size_t DEFAULT_CLOSEDSET_CAPACITY = 8U; static UINT32 K_NodeGetFScore(const pathfindnode_t *const node) { UINT32 fscore = UINT32_MAX; - if (node == NULL) - { - CONS_Debug(DBG_GAMELOGIC, "NULL node in K_PathfindNodeGetFScore."); - } - else - { - fscore = node->gscore + node->hscore; - } + + I_Assert(node != NULL); + + fscore = node->gscore + node->hscore; return fscore; } @@ -52,7 +48,7 @@ static void K_NodeUpdateHeapIndex(void *const node, const size_t newheapindex) { if (node == NULL) { - CONS_Debug(DBG_GAMELOGIC, "NULL node in K_PathfindNodeUpdateHeapIndex.\n"); + CONS_Debug(DBG_GAMELOGIC, "NULL node in K_NodeUpdateHeapIndex.\n"); } else { @@ -84,28 +80,20 @@ static pathfindnode_t *K_NodesArrayContainsNodeData( size_t nodesarraycount) { pathfindnode_t *foundnode = NULL; + size_t i = 0U; - if (nodesarray == NULL) + I_Assert(nodesarray != NULL); + I_Assert(nodedata != NULL); + + // It is more likely that we'll find the node we are looking for from the end of the array + // Yes, the for loop looks weird, remember that size_t is unsigned and we want to check 0, after it hits 0 it + // will loop back up to SIZE_MAX + for (i = nodesarraycount - 1U; i < nodesarraycount; i--) { - CONS_Debug(DBG_GAMELOGIC, "NULL nodesarray in K_NodesArrayContainsWaypoint.\n"); - } - else if (nodedata == NULL) - { - CONS_Debug(DBG_GAMELOGIC, "NULL nodedata in K_NodesArrayContainsWaypoint.\n"); - } - else - { - size_t i; - // It is more likely that we'll find the node we are looking for from the end of the array - // Yes, the for loop looks weird, remember that size_t is unsigned and we want to check 0, after it hits 0 it - // will loop back up to SIZE_MAX - for (i = nodesarraycount - 1U; i < nodesarraycount; i--) + if (nodesarray[i].nodedata == nodedata) { - if (nodesarray[i].nodedata == nodedata) - { - foundnode = &nodesarray[i]; - break; - } + foundnode = &nodesarray[i]; + break; } } return foundnode; @@ -127,28 +115,19 @@ static pathfindnode_t *K_NodesArrayContainsNodeData( static boolean K_ClosedsetContainsNode(pathfindnode_t **closedset, pathfindnode_t *node, size_t closedsetcount) { boolean nodeisinclosedset = false; + size_t i = 0U; - if (closedset == NULL) + I_Assert(closedset != NULL); + I_Assert(node != NULL); + // It is more likely that we'll find the node we are looking for from the end of the array + // Yes, the for loop looks weird, remember that size_t is unsigned and we want to check 0, after it hits 0 it + // will loop back up to SIZE_MAX + for (i = closedsetcount - 1U; i < closedsetcount; i--) { - CONS_Debug(DBG_GAMELOGIC, "NULL closedset in K_PathfindClosedsetContainsNode.\n"); - } - else if (node == NULL) - { - CONS_Debug(DBG_GAMELOGIC, "NULL node in K_PathfindClosedsetContainsNode.\n"); - } - else - { - size_t i; - // It is more likely that we'll find the node we are looking for from the end of the array - // Yes, the for loop looks weird, remember that size_t is unsigned and we want to check 0, after it hits 0 it - // will loop back up to SIZE_MAX - for (i = closedsetcount - 1U; i < closedsetcount; i--) + if (closedset[i] == node) { - if (closedset[i] == node) - { - nodeisinclosedset = true; - break; - } + nodeisinclosedset = true; + break; } } return nodeisinclosedset; @@ -227,15 +206,9 @@ static boolean K_ReconstructPath(path_t *const path, pathfindnode_t *const desti { boolean reconstructsuccess = false; - if (path == NULL) - { - CONS_Debug(DBG_GAMELOGIC, "NULL path in K_ReconstructPath.\n"); - } - else if (destinationnode == NULL) - { - CONS_Debug(DBG_GAMELOGIC, "NULL destinationnode in K_ReconstructPath.\n"); - } - else + I_Assert(path != NULL); + I_Assert(destinationnode != NULL); + { size_t numnodes = 0U; pathfindnode_t *thisnode = destinationnode; diff --git a/src/k_waypoint.c b/src/k_waypoint.c index 849c0e5c7..cbda77f31 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -345,21 +345,11 @@ static void K_DebugWaypointsSpawnLine(waypoint_t *const waypoint1, waypoint_t *c UINT32 numofframes = 1; // If this was 0 it could divide by 0 // Error conditions - if (waypoint1 == NULL || waypoint2 == NULL) - { - CONS_Debug(DBG_GAMELOGIC, "NULL waypoint in K_DebugWaypointsSpawnLine.\n"); - return; - } - if (waypoint1->mobj == NULL || waypoint2->mobj == NULL) - { - CONS_Debug(DBG_GAMELOGIC, "NULL mobj on waypoint in K_DebugWaypointsSpawnLine.\n"); - return; - } - if (cv_kartdebugwaypoints.value == 0) - { - CONS_Debug(DBG_GAMELOGIC, "In K_DebugWaypointsSpawnLine when kartdebugwaypoints is off.\n"); - return; - } + I_Assert(waypoint1 != NULL); + I_Assert(waypoint1->mobj != NULL); + I_Assert(waypoint2 != NULL); + I_Assert(waypoint2->mobj != NULL); + I_Assert(cv_kartdebugwaypoints.value != 0); waypointmobj1 = waypoint1->mobj; waypointmobj2 = waypoint2->mobj; @@ -603,15 +593,9 @@ static void K_UpdateNodesArrayBaseSize(size_t newnodesarraysize) static UINT32 K_DistanceBetweenWaypoints(waypoint_t *const waypoint1, waypoint_t *const waypoint2) { UINT32 finaldist = UINT32_MAX; - if (waypoint1 == NULL) - { - CONS_Debug(DBG_GAMELOGIC, "NULL waypoint1 in K_DistanceBetweenWaypoints.\n"); - } - else if (waypoint2 == NULL) - { - CONS_Debug(DBG_GAMELOGIC, "NULL waypoint2 in K_DistanceBetweenWaypoints.\n"); - } - else + 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); @@ -1104,21 +1088,12 @@ static boolean K_CheckWaypointForMobj(waypoint_t *const waypoint, void *const mo boolean mobjsmatch = false; // Error Conditions - if (waypoint == NULL) + I_Assert(waypoint != NULL); + I_Assert(waypoint->mobj != NULL); + I_Assert(mobjpointer != NULL); + { - CONS_Debug(DBG_GAMELOGIC, "NULL waypoint in K_CheckWaypointForMobj.\n"); - } - else if (waypoint->mobj == NULL) - { - CONS_Debug(DBG_GAMELOGIC, "Waypoint has NULL mobj in K_CheckWaypointForMobj.\n"); - } - else if (mobjpointer == NULL) - { - CONS_Debug(DBG_GAMELOGIC, "NULL mobjpointer in K_CheckWaypointForMobj.\n"); - } - else - { - mobj_t *mobj = (mobj_t *)mobjpointer; + mobj_t *const mobj = (mobj_t *)mobjpointer; if (P_MobjWasRemoved(mobj)) { @@ -1170,76 +1145,61 @@ static waypoint_t *K_TraverseWaypoints( waypoint_t *foundwaypoint = NULL; // Error conditions - if (condition == NULL) - { - CONS_Debug(DBG_GAMELOGIC, "NULL condition in K_TraverseWaypoints.\n"); - } - else if (conditionalfunc == NULL) - { - CONS_Debug(DBG_GAMELOGIC, "NULL conditionalfunc in K_TraverseWaypoints.\n"); - } - else if (visitedarray == NULL) - { - CONS_Debug(DBG_GAMELOGIC, "NULL visitedarray in K_TraverseWaypoints.\n"); - } - else - { + I_Assert(condition != NULL); + I_Assert(conditionalfunc != NULL); + I_Assert(visitedarray != NULL); searchwaypointstart: - if (waypoint == NULL) + 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)) { - CONS_Debug(DBG_GAMELOGIC, "NULL waypoint in K_TraverseWaypoints.\n"); - } - else - { - 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) { - // 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) { - foundwaypoint = waypoint; + waypoint = waypoint->nextwaypoints[0]; + goto searchwaypointstart; } - else + else if (waypoint->numnextwaypoints != 0) { - // 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) + // 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++) { - 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) { - if (waypoint->nextwaypoints[i] != NULL) - { - foundwaypoint = K_TraverseWaypoints(waypoint->nextwaypoints[i], conditionalfunc, - condition, visitedarray); + foundwaypoint = K_TraverseWaypoints(waypoint->nextwaypoints[i], conditionalfunc, + condition, visitedarray); - if (foundwaypoint != NULL) - { - break; - } + if (foundwaypoint != NULL) + { + break; } } } - else - { - // No next waypoints, this function will be returned from - } + } + else + { + // No next waypoints, this function will be returned from } } } @@ -1270,24 +1230,13 @@ static waypoint_t *K_SearchWaypointGraph( waypoint_t *foundwaypoint = NULL; // Error conditions - if (condition == NULL) - { - CONS_Debug(DBG_GAMELOGIC, "NULL condition in K_SearchWaypointGraph.\n"); - } - else if (conditionalfunc == NULL) - { - CONS_Debug(DBG_GAMELOGIC, "NULL conditionalfunc in K_SearchWaypointGraph.\n"); - } - else if (firstwaypoint == NULL) - { - CONS_Debug(DBG_GAMELOGIC, "K_SearchWaypointsForMobj called when no first waypoint.\n"); - } - else - { - visitedarray = Z_Calloc(numwaypoints * sizeof(boolean), PU_STATIC, NULL); - foundwaypoint = K_TraverseWaypoints(firstwaypoint, conditionalfunc, condition, visitedarray); - Z_Free(visitedarray); - } + 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; } @@ -1339,30 +1288,19 @@ static waypoint_t *K_SearchWaypointHeap( waypoint_t *foundwaypoint = NULL; // Error conditions - if (condition == NULL) + 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++) { - CONS_Debug(DBG_GAMELOGIC, "NULL condition in K_SearchWaypointHeap.\n"); - } - else if (conditionalfunc == NULL) - { - CONS_Debug(DBG_GAMELOGIC, "NULL conditionalfunc in K_SearchWaypointHeap.\n"); - } - else if (waypointheap == NULL) - { - CONS_Debug(DBG_GAMELOGIC, "K_SearchWaypointHeap called when no waypointheap.\n"); - } - else - { - // 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) { - if (conditionalfunc(&waypointheap[i], condition) == true) - { - foundwaypoint = &waypointheap[i]; - break; - } + foundwaypoint = &waypointheap[i]; + break; } } @@ -1408,37 +1346,30 @@ waypoint_t *K_SearchWaypointHeapForMobj(mobj_t *const mobj) --------------------------------------------------*/ static UINT32 K_SetupCircuitLength(void) { - if ((firstwaypoint == NULL) || (numwaypoints == 0U)) + 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) { - CONS_Debug(DBG_GAMELOGIC, "K_SetupCircuitLength called with no waypoints.\n"); - } - else if (finishline == NULL) - { - CONS_Debug(DBG_GAMELOGIC, "K_SetupCircuitLength called with no finishline waypoint.\n"); + circuitlength = 0U; } else { - // 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; + // 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); + K_PathfindToWaypoint(&fakefinishline, finishline, &bestcircuitpath, useshortcuts, huntbackwards); - circuitlength = bestcircuitpath.totaldist; + circuitlength = bestcircuitpath.totaldist; - Z_Free(bestcircuitpath.array); - } + Z_Free(bestcircuitpath.array); } return circuitlength; @@ -1460,35 +1391,27 @@ static UINT32 K_SetupCircuitLength(void) static void K_AddPrevToWaypoint(waypoint_t *const waypoint, waypoint_t *const prevwaypoint) { // Error conditions - if (waypoint == NULL) + 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) { - CONS_Debug(DBG_SETUP, "NULL waypoint in K_AddPrevToWaypoint.\n"); + I_Error("K_AddPrevToWaypoint: Failed to reallocate memory for previous waypoints."); } - else if (prevwaypoint == NULL) + + waypoint->prevwaypointdistances = + Z_Realloc(waypoint->prevwaypointdistances, waypoint->numprevwaypoints * sizeof(fixed_t), PU_LEVEL, NULL); + + if (!waypoint->prevwaypointdistances) { - CONS_Debug(DBG_SETUP, "NULL prevwaypoint in K_AddPrevToWaypoint.\n"); + I_Error("K_AddPrevToWaypoint: Failed to reallocate memory for previous waypoint distances."); } - else - { - 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; - } + waypoint->prevwaypoints[waypoint->numprevwaypoints - 1] = prevwaypoint; } /*-------------------------------------------------- @@ -1509,52 +1432,42 @@ static waypoint_t *K_MakeWaypoint(mobj_t *const mobj) mobj_t *otherwaypointmobj = NULL; // Error conditions - if (mobj == NULL || P_MobjWasRemoved(mobj)) - { - CONS_Debug(DBG_SETUP, "NULL mobj in K_MakeWaypoint.\n"); - } - else if (waypointcap == NULL) - { - CONS_Debug(DBG_SETUP, "K_MakeWaypoint called with NULL waypointcap.\n"); - } - else if (numwaypoints >= numwaypointmobjs) - { - CONS_Debug(DBG_SETUP, "K_MakeWaypoint called with max waypoint capacity reached.\n"); - } - else - { - // numwaypoints is incremented later in K_SetupWaypoint - madewaypoint = &waypointheap[numwaypoints]; - numwaypoints++; + 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 - P_SetTarget(&madewaypoint->mobj, mobj); + // numwaypoints is incremented later in K_SetupWaypoint + madewaypoint = &waypointheap[numwaypoints]; + numwaypoints++; - // 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) + P_SetTarget(&madewaypoint->mobj, mobj); + + // 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) { - // threshold = next waypoint id, movecount = my id - if (mobj->threshold == otherwaypointmobj->movecount) - { - madewaypoint->numnextwaypoints++; - } + madewaypoint->numnextwaypoints++; } + } - // No next waypoints - if (madewaypoint->numnextwaypoints != 0) + // 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) { - // 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."); - } + 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."); } } @@ -1578,93 +1491,83 @@ static waypoint_t *K_SetupWaypoint(mobj_t *const mobj) waypoint_t *thiswaypoint = NULL; // Error conditions - if (mobj == NULL || P_MobjWasRemoved(mobj)) + 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) { - CONS_Debug(DBG_SETUP, "NULL mobj in K_SetupWaypoint.\n"); + thiswaypoint = K_SearchWaypointHeapForMobj(mobj); } - else if (mobj->type != MT_WAYPOINT) + + // The waypoint hasn't already been made, so make it + if (thiswaypoint == NULL) { - CONS_Debug(DBG_SETUP, "Non MT_WAYPOINT mobj in K_SetupWaypoint. Type=%d.\n", mobj->type); - } - else if (waypointcap == NULL) - { - CONS_Debug(DBG_SETUP, "K_SetupWaypoint called with NULL waypointcap.\n"); - } - else - { - // If we have waypoints already created, search through them first to see if this mobj is already added. - if (firstwaypoint != NULL) + mobj_t *otherwaypointmobj = NULL; + UINT32 nextwaypointindex = 0; + + thiswaypoint = K_MakeWaypoint(mobj); + + if (thiswaypoint != 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) { - // Set the first waypoint if it isn't already - if (firstwaypoint == NULL) - { - firstwaypoint = thiswaypoint; - } + 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 + if (K_GetWaypointIsFinishline(thiswaypoint)) + { + if (finishline != NULL) { + const INT32 oldfinishlineid = K_GetWaypointID(finishline); + const INT32 thiswaypointid = K_GetWaypointID(thiswaypoint); CONS_Alert( - CONS_WARNING, "Waypoint with ID %d has no next waypoint.\n", K_GetWaypointID(thiswaypoint)); + 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_Debug(DBG_SETUP, "K_SetupWaypoint failed to make new waypoint with ID %d.\n", mobj->movecount); + 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; @@ -1684,51 +1587,43 @@ static boolean K_AllocateWaypointHeap(void) boolean allocationsuccessful = false; // Error conditions - if (waypointheap != NULL) + 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) { - CONS_Debug(DBG_SETUP, "K_AllocateWaypointHeap called when waypointheap is already allocated.\n"); + 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++; } - else if (waypointcap == NULL) + + if (numwaypointmobjs > 0) { - CONS_Debug(DBG_SETUP, "K_AllocateWaypointHeap called with NULL waypointcap.\n"); + // 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 { - // 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"); - } + CONS_Debug(DBG_SETUP, "No waypoint mobjs in waypointcap.\n"); } return allocationsuccessful; @@ -1794,7 +1689,11 @@ boolean K_SetupWaypointList(void) finishline = firstwaypoint; } - (void)K_SetupCircuitLength(); + 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; } From 738d1f194008957ebf1b375ac2f67d9a0b2bec78 Mon Sep 17 00:00:00 2001 From: Sryder Date: Sat, 21 Mar 2020 21:32:40 +0000 Subject: [PATCH 096/105] Make kartdebugwaypoints far more clear to use for the debugging lines Fix memory reallocation issue that I don't think could be seen currently --- src/k_pathfind.c | 2 +- src/k_waypoint.c | 110 +++++++++++++++++++++++++++++------------------ 2 files changed, 70 insertions(+), 42 deletions(-) diff --git a/src/k_pathfind.c b/src/k_pathfind.c index ae05930f0..563456de1 100644 --- a/src/k_pathfind.c +++ b/src/k_pathfind.c @@ -461,7 +461,7 @@ boolean K_PathfindAStar(path_t *const path, pathfindsetup_t *const pathfindsetup if (nodesarraycount >= pathfindsetup->nodesarraycapacity) { pathfindsetup->nodesarraycapacity = pathfindsetup->nodesarraycapacity * 2; - nodesarray = Z_Realloc(nodesarray, pathfindsetup->nodesarraycapacity, PU_STATIC, NULL); + nodesarray = Z_Realloc(nodesarray, pathfindsetup->nodesarraycapacity * sizeof(pathfindnode_t), PU_STATIC, NULL); if (nodesarray == NULL) { diff --git a/src/k_waypoint.c b/src/k_waypoint.c index cbda77f31..4631ed87e 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -326,6 +326,34 @@ waypoint_t *K_GetWaypointFromIndex(size_t 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) @@ -341,8 +369,21 @@ static void K_DebugWaypointsSpawnLine(waypoint_t *const waypoint1, waypoint_t *c mobj_t *spawnedmobj; fixed_t stepx, stepy, stepz; fixed_t x, y, z; + UINT32 waypointdist; INT32 n; - UINT32 numofframes = 1; // If this was 0 it could divide by 0 + 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); @@ -351,13 +392,18 @@ static void K_DebugWaypointsSpawnLine(waypoint_t *const waypoint1, waypoint_t *c 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; - numofframes = S_SPRK16 - S_SPRK1; - // Draw the sparkles + // 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; @@ -366,10 +412,17 @@ static void K_DebugWaypointsSpawnLine(waypoint_t *const waypoint1, waypoint_t *c z = waypointmobj1->z; do { - spawnedmobj = P_SpawnMobj(x, y, z, MT_SPARK); - P_SetMobjState(spawnedmobj, S_SPRK1 + ((leveltime + n) % (numofframes + 1))); - spawnedmobj->state->nextstate = S_NULL; - spawnedmobj->state->tics = 1; + 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; @@ -578,34 +631,6 @@ static void K_UpdateNodesArrayBaseSize(size_t newnodesarraysize) } } -/*-------------------------------------------------- - 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; -} - /*-------------------------------------------------- static void **K_WaypointPathfindGetNext(void *data, size_t *numconnections) @@ -1443,13 +1468,16 @@ static waypoint_t *K_MakeWaypoint(mobj_t *const mobj) P_SetTarget(&madewaypoint->mobj, mobj); - // 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) + // 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) { - madewaypoint->numnextwaypoints++; + // threshold = next waypoint id, movecount = my id + if (mobj->threshold == otherwaypointmobj->movecount) + { + madewaypoint->numnextwaypoints++; + } } } From e874af3e43443568c82d9bc02eb4bee119b9a63e Mon Sep 17 00:00:00 2001 From: Sryder Date: Sat, 21 Mar 2020 23:24:04 +0000 Subject: [PATCH 097/105] Very minor fix to WRONG WAY More opaque waypoint markers for kartdebugwaypoints Looks like whitespace got murdered in k_kart.c again --- src/k_kart.c | 695 ++++++++++++++++++++++++----------------------- src/k_waypoint.c | 3 + 2 files changed, 355 insertions(+), 343 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 27a4b1e1f..b405a06ec 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2217,44 +2217,44 @@ void K_RespawnChecker(player_t *player) S_StartSound(player->mo, sfx_s23c); player->kartstuff[k_startboost] = 50; K_SpawnDashDustRelease(player); - } + } player->mo->colorized = false; player->kartstuff[k_dropdash] = 0; - player->kartstuff[k_respawn] = 0; + player->kartstuff[k_respawn] = 0; //P_PlayRinglossSound(player->mo); - P_PlayerRingBurst(player, 3); - - if (G_BattleGametype()) - { - if (player->kartstuff[k_bumper] > 0) - { - if (player->kartstuff[k_bumper] == 1) - { - mobj_t *karmahitbox = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_KARMAHITBOX); // Player hitbox is too small!! - P_SetTarget(&karmahitbox->target, player->mo); - karmahitbox->destscale = player->mo->scale; - P_SetScale(karmahitbox, player->mo->scale); - CONS_Printf(M_GetText("%s lost all of their bumpers!\n"), player_names[player-players]); - } - player->kartstuff[k_bumper]--; - if (K_IsPlayerWanted(player)) - K_CalculateBattleWanted(); - } - - if (!player->kartstuff[k_bumper]) - { - player->kartstuff[k_comebacktimer] = comebacktime; - if (player->kartstuff[k_comebackmode] == 2) - { - mobj_t *poof = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_EXPLODE); - S_StartSound(poof, mobjinfo[MT_KARMAHITBOX].seesound); - player->kartstuff[k_comebackmode] = 0; - } - } - - K_CheckBumpers(); + P_PlayerRingBurst(player, 3); + + if (G_BattleGametype()) + { + if (player->kartstuff[k_bumper] > 0) + { + if (player->kartstuff[k_bumper] == 1) + { + mobj_t *karmahitbox = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_KARMAHITBOX); // Player hitbox is too small!! + P_SetTarget(&karmahitbox->target, player->mo); + karmahitbox->destscale = player->mo->scale; + P_SetScale(karmahitbox, player->mo->scale); + CONS_Printf(M_GetText("%s lost all of their bumpers!\n"), player_names[player-players]); + } + player->kartstuff[k_bumper]--; + if (K_IsPlayerWanted(player)) + K_CalculateBattleWanted(); + } + + if (!player->kartstuff[k_bumper]) + { + player->kartstuff[k_comebacktimer] = comebacktime; + if (player->kartstuff[k_comebackmode] == 2) + { + mobj_t *poof = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_EXPLODE); + S_StartSound(poof, mobjinfo[MT_KARMAHITBOX].seesound); + player->kartstuff[k_comebackmode] = 0; + } + } + + K_CheckBumpers(); } } } @@ -5566,25 +5566,25 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) player->mo->colorized = false; player->mo->color = player->skincolor; } - } - else if (player->kartstuff[k_killfield]) // You're gonna REALLY diiiiie - { - const INT32 flashtime = 4<<(4-(player->kartstuff[k_killfield]/TICRATE)); - if (player->kartstuff[k_killfield] == 1 || (player->kartstuff[k_killfield] % (flashtime/2) != 0)) - { - player->mo->colorized = false; - player->mo->color = player->skincolor; - } - else if (player->kartstuff[k_killfield] % flashtime == 0) - { - player->mo->colorized = true; - player->mo->color = SKINCOLOR_BYZANTIUM; - } - else - { - player->mo->colorized = true; - player->mo->color = SKINCOLOR_RUBY; - } + } + else if (player->kartstuff[k_killfield]) // You're gonna REALLY diiiiie + { + const INT32 flashtime = 4<<(4-(player->kartstuff[k_killfield]/TICRATE)); + if (player->kartstuff[k_killfield] == 1 || (player->kartstuff[k_killfield] % (flashtime/2) != 0)) + { + player->mo->colorized = false; + player->mo->color = player->skincolor; + } + else if (player->kartstuff[k_killfield] % flashtime == 0) + { + player->mo->colorized = true; + player->mo->color = SKINCOLOR_BYZANTIUM; + } + else + { + player->mo->colorized = true; + player->mo->color = SKINCOLOR_RUBY; + } } else if (player->kartstuff[k_ringboost] && (leveltime & 1)) // ring boosting { @@ -5764,27 +5764,27 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) K_KartPlayerHUDUpdate(player); - if (G_BattleGametype() && player->kartstuff[k_bumper] > 0 - && !player->kartstuff[k_spinouttimer] && !player->kartstuff[k_squishedtimer] - && !player->kartstuff[k_respawn] && !player->powers[pw_flashing]) - { - player->kartstuff[k_wanted]++; - if (battleovertime.enabled >= 10*TICRATE) - { - if (P_AproxDistance(player->mo->x - battleovertime.x, player->mo->y - battleovertime.y) > battleovertime.radius) - { - player->kartstuff[k_killfield]++; - if (player->kartstuff[k_killfield] > 4*TICRATE) - { - K_SpinPlayer(player, NULL, 0, NULL, false); - //player->kartstuff[k_killfield] = 1; - } - } - else if (player->kartstuff[k_killfield] > 0) - player->kartstuff[k_killfield]--; - } - } - else if (player->kartstuff[k_killfield] > 0) + if (G_BattleGametype() && player->kartstuff[k_bumper] > 0 + && !player->kartstuff[k_spinouttimer] && !player->kartstuff[k_squishedtimer] + && !player->kartstuff[k_respawn] && !player->powers[pw_flashing]) + { + player->kartstuff[k_wanted]++; + if (battleovertime.enabled >= 10*TICRATE) + { + if (P_AproxDistance(player->mo->x - battleovertime.x, player->mo->y - battleovertime.y) > battleovertime.radius) + { + player->kartstuff[k_killfield]++; + if (player->kartstuff[k_killfield] > 4*TICRATE) + { + K_SpinPlayer(player, NULL, 0, NULL, false); + //player->kartstuff[k_killfield] = 1; + } + } + else if (player->kartstuff[k_killfield] > 0) + player->kartstuff[k_killfield]--; + } + } + else if (player->kartstuff[k_killfield] > 0) player->kartstuff[k_killfield]--; if (P_IsObjectOnGround(player->mo)) @@ -6012,8 +6012,14 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) { bestwaypoint = waypoint->nextwaypoints[i]; - nextbestdelta = angledelta; - nextbestmomdelta = momdelta; + if (angledelta < nextbestdelta) + { + nextbestdelta = angledelta; + } + if (momdelta < nextbestmomdelta) + { + nextbestmomdelta = momdelta; + } // Remove wrong way flag if we're using nextwaypoints player->kartstuff[k_wrongway] = 0; @@ -6049,7 +6055,7 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) nextbestdelta = angledelta; nextbestmomdelta = momdelta; - // Ser wrong way flag if we're using prevwaypoints + // Set wrong way flag if we're using prevwaypoints player->kartstuff[k_wrongway] = 1; updaterespawn = false; } @@ -7811,7 +7817,7 @@ static patch_t *kp_lapanim_number[10][3]; static patch_t *kp_lapanim_emblem[2]; static patch_t *kp_lapanim_hand[3]; -static patch_t *kp_yougotem; +static patch_t *kp_yougotem; static patch_t *kp_itemminimap; static patch_t *kp_alagles[10]; @@ -8117,7 +8123,7 @@ void K_LoadKartHUDGraphics(void) kp_lapanim_hand[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); } - kp_yougotem = (patch_t *) W_CachePatchName("YOUGOTEM", PU_HUDGFX); + kp_yougotem = (patch_t *) W_CachePatchName("YOUGOTEM", PU_HUDGFX); kp_itemminimap = (patch_t *) W_CachePatchName("MMAPITEM", PU_HUDGFX); sprintf(buffer, "ALAGLESx"); @@ -9682,247 +9688,247 @@ static void K_drawKartPlayerCheck(void) V_DrawMappedPatch(x, CHEK_Y, V_HUDTRANS|splitflags, kp_check[pnum], colormap); } } -} +} -static void K_drawKartMinimapIcon(fixed_t objx, fixed_t objy, INT32 hudx, INT32 hudy, INT32 flags, patch_t *icon, UINT8 *colormap, patch_t *AutomapPic) -{ - // amnum xpos & ypos are the icon's speed around the HUD. - // The number being divided by is for how fast it moves. - // The higher the number, the slower it moves. - - // am xpos & ypos are the icon's starting position. Withouht - // it, they wouldn't 'spawn' on the top-right side of the HUD. - - fixed_t amnumxpos, amnumypos; - INT32 amxpos, amypos; - - node_t *bsp = &nodes[numnodes-1]; - fixed_t maxx, minx, maxy, miny; - - fixed_t mapwidth, mapheight; - fixed_t xoffset, yoffset; - fixed_t xscale, yscale, zoom; - - maxx = maxy = INT32_MAX; - minx = miny = INT32_MIN; - minx = bsp->bbox[0][BOXLEFT]; - maxx = bsp->bbox[0][BOXRIGHT]; - miny = bsp->bbox[0][BOXBOTTOM]; - maxy = bsp->bbox[0][BOXTOP]; - - if (bsp->bbox[1][BOXLEFT] < minx) - minx = bsp->bbox[1][BOXLEFT]; - if (bsp->bbox[1][BOXRIGHT] > maxx) - maxx = bsp->bbox[1][BOXRIGHT]; - if (bsp->bbox[1][BOXBOTTOM] < miny) - miny = bsp->bbox[1][BOXBOTTOM]; - if (bsp->bbox[1][BOXTOP] > maxy) - maxy = bsp->bbox[1][BOXTOP]; - - // You might be wondering why these are being bitshift here - // it's because mapwidth and height would otherwise overflow for maps larger than half the size possible... - // map boundaries and sizes will ALWAYS be whole numbers thankfully - // later calculations take into consideration that these are actually not in terms of FRACUNIT though - minx >>= FRACBITS; - maxx >>= FRACBITS; - miny >>= FRACBITS; - maxy >>= FRACBITS; - - mapwidth = maxx - minx; - mapheight = maxy - miny; - - // These should always be small enough to be bitshift back right now - xoffset = (minx + mapwidth/2)<width, mapwidth); - yscale = FixedDiv(AutomapPic->height, mapheight); - zoom = FixedMul(min(xscale, yscale), FRACUNIT-FRACUNIT/20); - - amnumxpos = (FixedMul(objx, zoom) - FixedMul(xoffset, zoom)); - amnumypos = -(FixedMul(objy, zoom) - FixedMul(yoffset, zoom)); - - if (encoremode) - amnumxpos = -amnumxpos; - - amxpos = amnumxpos + ((hudx + AutomapPic->width/2 - (icon->width/2))<height/2 - (icon->height/2))<width/2 + (icon->width/2))<bbox[0][BOXLEFT]; + maxx = bsp->bbox[0][BOXRIGHT]; + miny = bsp->bbox[0][BOXBOTTOM]; + maxy = bsp->bbox[0][BOXTOP]; + + if (bsp->bbox[1][BOXLEFT] < minx) + minx = bsp->bbox[1][BOXLEFT]; + if (bsp->bbox[1][BOXRIGHT] > maxx) + maxx = bsp->bbox[1][BOXRIGHT]; + if (bsp->bbox[1][BOXBOTTOM] < miny) + miny = bsp->bbox[1][BOXBOTTOM]; + if (bsp->bbox[1][BOXTOP] > maxy) + maxy = bsp->bbox[1][BOXTOP]; + + // You might be wondering why these are being bitshift here + // it's because mapwidth and height would otherwise overflow for maps larger than half the size possible... + // map boundaries and sizes will ALWAYS be whole numbers thankfully + // later calculations take into consideration that these are actually not in terms of FRACUNIT though + minx >>= FRACBITS; + maxx >>= FRACBITS; + miny >>= FRACBITS; + maxy >>= FRACBITS; + + mapwidth = maxx - minx; + mapheight = maxy - miny; + + // These should always be small enough to be bitshift back right now + xoffset = (minx + mapwidth/2)<width, mapwidth); + yscale = FixedDiv(AutomapPic->height, mapheight); + zoom = FixedMul(min(xscale, yscale), FRACUNIT-FRACUNIT/20); + + amnumxpos = (FixedMul(objx, zoom) - FixedMul(xoffset, zoom)); + amnumypos = -(FixedMul(objy, zoom) - FixedMul(yoffset, zoom)); + + if (encoremode) + amnumxpos = -amnumxpos; + + amxpos = amnumxpos + ((hudx + AutomapPic->width/2 - (icon->width/2))<height/2 - (icon->height/2))<width/2 + (icon->width/2))<width/2); - y = MINI_Y - (AutomapPic->height/2); - - if (timeinmap > 105) - { - minimaptrans = cv_kartminimap.value; - if (timeinmap <= 113) - minimaptrans = ((((INT32)timeinmap) - 105)*minimaptrans)/(113-105); - if (!minimaptrans) - return; - } - else - return; - - minimaptrans = ((10-minimaptrans)<width), y, splitflags|V_FLIP, AutomapPic); - else - V_DrawScaledPatch(x, y, splitflags, AutomapPic); - - if (!(splitscreen == 2)) - { - splitflags &= ~minimaptrans; - splitflags |= V_HUDTRANSHALF; - } - - // let offsets transfer to the heads, too! - if (encoremode) - x += SHORT(AutomapPic->leftoffset); - else - x -= SHORT(AutomapPic->leftoffset); - y -= SHORT(AutomapPic->topoffset); - - // Draw the super item in Battle - if (G_BattleGametype() && battleovertime.enabled) - { - if (battleovertime.enabled >= 10*TICRATE || (battleovertime.enabled & 1)) - { - const INT32 prevsplitflags = splitflags; - splitflags &= ~V_HUDTRANSHALF; - splitflags |= V_HUDTRANS; - colormap = R_GetTranslationColormap(TC_RAINBOW, (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1))), GTC_CACHE); - K_drawKartMinimapIcon(battleovertime.x, battleovertime.y, x, y, splitflags, kp_itemminimap, colormap, AutomapPic); - splitflags = prevsplitflags; - } - } - - // initialize - for (i = 0; i < 4; i++) - localplayers[i] = -1; - - // Player's tiny icons on the Automap. (drawn opposite direction so player 1 is drawn last in splitscreen) - if (ghosts) - { - demoghost *g = ghosts; - while (g) - { - if (g->mo->skin) - skin = ((skin_t*)g->mo->skin)-skins; - else - skin = 0; - if (g->mo->color) - { - if (g->mo->colorized) - colormap = R_GetTranslationColormap(TC_RAINBOW, g->mo->color, GTC_CACHE); - else - colormap = R_GetTranslationColormap(skin, g->mo->color, GTC_CACHE); - } - else - colormap = NULL; - K_drawKartMinimapIcon(g->mo->x, g->mo->y, x, y, splitflags, facemmapprefix[skin], colormap, AutomapPic); - g = g->next; - } - - if (!stplyr->mo || stplyr->spectator) // do we need the latter..? - return; - - localplayers[numlocalplayers] = stplyr-players; - numlocalplayers++; - } - else - { - for (i = MAXPLAYERS-1; i >= 0; i--) - { - if (!playeringame[i]) - continue; - if (!players[i].mo || players[i].spectator) - continue; - - if (i != displayplayers[0] || splitscreen) - { - if (G_BattleGametype() && players[i].kartstuff[k_bumper] <= 0) - continue; - - if (players[i].kartstuff[k_hyudorotimer] > 0) - { - if (!((players[i].kartstuff[k_hyudorotimer] < 1*TICRATE/2 - || players[i].kartstuff[k_hyudorotimer] > hyudorotime-(1*TICRATE/2)) - && !(leveltime & 1))) - continue; - } - } - - if (i == displayplayers[0] || i == displayplayers[1] || i == displayplayers[2] || i == displayplayers[3]) - { - // Draw display players on top of everything else - localplayers[numlocalplayers] = i; - numlocalplayers++; - continue; - } - - if (players[i].mo->skin) - skin = ((skin_t*)players[i].mo->skin)-skins; - else - skin = 0; - - if (players[i].mo->color) - { - if (players[i].mo->colorized) - colormap = R_GetTranslationColormap(TC_RAINBOW, players[i].mo->color, GTC_CACHE); - else - colormap = R_GetTranslationColormap(skin, players[i].mo->color, GTC_CACHE); - } - else - colormap = NULL; - - K_drawKartMinimapIcon(players[i].mo->x, players[i].mo->y, x, y, splitflags, facemmapprefix[skin], colormap, AutomapPic); + // Maybe move this somewhere else where this won't be a concern? + if (stplyr != &players[displayplayers[0]]) + return; + + lumpnum = W_CheckNumForName(va("%sR", G_BuildMapName(gamemap))); + + if (lumpnum != -1) + AutomapPic = W_CachePatchName(va("%sR", G_BuildMapName(gamemap)), PU_HUDGFX); + else + return; // no pic, just get outta here + + x = MINI_X - (AutomapPic->width/2); + y = MINI_Y - (AutomapPic->height/2); + + if (timeinmap > 105) + { + minimaptrans = cv_kartminimap.value; + if (timeinmap <= 113) + minimaptrans = ((((INT32)timeinmap) - 105)*minimaptrans)/(113-105); + if (!minimaptrans) + return; + } + else + return; + + minimaptrans = ((10-minimaptrans)<width), y, splitflags|V_FLIP, AutomapPic); + else + V_DrawScaledPatch(x, y, splitflags, AutomapPic); + + if (!(splitscreen == 2)) + { + splitflags &= ~minimaptrans; + splitflags |= V_HUDTRANSHALF; + } + + // let offsets transfer to the heads, too! + if (encoremode) + x += SHORT(AutomapPic->leftoffset); + else + x -= SHORT(AutomapPic->leftoffset); + y -= SHORT(AutomapPic->topoffset); + + // Draw the super item in Battle + if (G_BattleGametype() && battleovertime.enabled) + { + if (battleovertime.enabled >= 10*TICRATE || (battleovertime.enabled & 1)) + { + const INT32 prevsplitflags = splitflags; + splitflags &= ~V_HUDTRANSHALF; + splitflags |= V_HUDTRANS; + colormap = R_GetTranslationColormap(TC_RAINBOW, (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1))), GTC_CACHE); + K_drawKartMinimapIcon(battleovertime.x, battleovertime.y, x, y, splitflags, kp_itemminimap, colormap, AutomapPic); + splitflags = prevsplitflags; + } + } + + // initialize + for (i = 0; i < 4; i++) + localplayers[i] = -1; + + // Player's tiny icons on the Automap. (drawn opposite direction so player 1 is drawn last in splitscreen) + if (ghosts) + { + demoghost *g = ghosts; + while (g) + { + if (g->mo->skin) + skin = ((skin_t*)g->mo->skin)-skins; + else + skin = 0; + if (g->mo->color) + { + if (g->mo->colorized) + colormap = R_GetTranslationColormap(TC_RAINBOW, g->mo->color, GTC_CACHE); + else + colormap = R_GetTranslationColormap(skin, g->mo->color, GTC_CACHE); + } + else + colormap = NULL; + K_drawKartMinimapIcon(g->mo->x, g->mo->y, x, y, splitflags, facemmapprefix[skin], colormap, AutomapPic); + g = g->next; + } + + if (!stplyr->mo || stplyr->spectator) // do we need the latter..? + return; + + localplayers[numlocalplayers] = stplyr-players; + numlocalplayers++; + } + else + { + for (i = MAXPLAYERS-1; i >= 0; i--) + { + if (!playeringame[i]) + continue; + if (!players[i].mo || players[i].spectator) + continue; + + if (i != displayplayers[0] || splitscreen) + { + if (G_BattleGametype() && players[i].kartstuff[k_bumper] <= 0) + continue; + + if (players[i].kartstuff[k_hyudorotimer] > 0) + { + if (!((players[i].kartstuff[k_hyudorotimer] < 1*TICRATE/2 + || players[i].kartstuff[k_hyudorotimer] > hyudorotime-(1*TICRATE/2)) + && !(leveltime & 1))) + continue; + } + } + + if (i == displayplayers[0] || i == displayplayers[1] || i == displayplayers[2] || i == displayplayers[3]) + { + // Draw display players on top of everything else + localplayers[numlocalplayers] = i; + numlocalplayers++; + continue; + } + + if (players[i].mo->skin) + skin = ((skin_t*)players[i].mo->skin)-skins; + else + skin = 0; + + if (players[i].mo->color) + { + if (players[i].mo->colorized) + colormap = R_GetTranslationColormap(TC_RAINBOW, players[i].mo->color, GTC_CACHE); + else + colormap = R_GetTranslationColormap(skin, players[i].mo->color, GTC_CACHE); + } + else + colormap = NULL; + + K_drawKartMinimapIcon(players[i].mo->x, players[i].mo->y, x, y, splitflags, facemmapprefix[skin], colormap, AutomapPic); // Target reticule if ((G_RaceGametype() && players[i].kartstuff[k_position] == spbplace) || (G_BattleGametype() && K_IsPlayerWanted(&players[i]))) - K_drawKartMinimapIcon(players[i].mo->x, players[i].mo->y, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic); - } - } + K_drawKartMinimapIcon(players[i].mo->x, players[i].mo->y, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic); + } + } // draw SPB(s?) for (mobj = kitemcap; mobj; mobj = next) @@ -9943,39 +9949,39 @@ static void K_drawKartMinimap(void) K_drawKartMinimapIcon(mobj->x, mobj->y, x, y, splitflags, kp_spbminimap, colormap, AutomapPic); } } - - // draw our local players here, opaque. - splitflags &= ~V_HUDTRANSHALF; - splitflags |= V_HUDTRANS; - - for (i = 0; i < numlocalplayers; i++) - { - if (i == -1) - continue; // this doesn't interest us - - if (players[localplayers[i]].mo->skin) - skin = ((skin_t*)players[localplayers[i]].mo->skin)-skins; - else - skin = 0; - - if (players[localplayers[i]].mo->color) - { - if (players[localplayers[i]].mo->colorized) - colormap = R_GetTranslationColormap(TC_RAINBOW, players[localplayers[i]].mo->color, GTC_CACHE); - else - colormap = R_GetTranslationColormap(skin, players[localplayers[i]].mo->color, GTC_CACHE); - } - else - colormap = NULL; - + + // draw our local players here, opaque. + splitflags &= ~V_HUDTRANSHALF; + splitflags |= V_HUDTRANS; + + for (i = 0; i < numlocalplayers; i++) + { + if (i == -1) + continue; // this doesn't interest us + + if (players[localplayers[i]].mo->skin) + skin = ((skin_t*)players[localplayers[i]].mo->skin)-skins; + else + skin = 0; + + if (players[localplayers[i]].mo->color) + { + if (players[localplayers[i]].mo->colorized) + colormap = R_GetTranslationColormap(TC_RAINBOW, players[localplayers[i]].mo->color, GTC_CACHE); + else + colormap = R_GetTranslationColormap(skin, players[localplayers[i]].mo->color, GTC_CACHE); + } + else + colormap = NULL; + K_drawKartMinimapIcon(players[localplayers[i]].mo->x, players[localplayers[i]].mo->y, x, y, splitflags, facemmapprefix[skin], colormap, AutomapPic); - // Target reticule + // Target reticule if ((G_RaceGametype() && players[localplayers[i]].kartstuff[k_position] == spbplace) - || (G_BattleGametype() && K_IsPlayerWanted(&players[localplayers[i]]))) - K_drawKartMinimapIcon(players[localplayers[i]].mo->x, players[localplayers[i]].mo->y, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic); - } -} + || (G_BattleGametype() && K_IsPlayerWanted(&players[localplayers[i]]))) + K_drawKartMinimapIcon(players[localplayers[i]].mo->x, players[localplayers[i]].mo->y, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic); + } +} static void K_drawKartStartCountdown(void) { @@ -10619,6 +10625,7 @@ static void K_DrawWaypointDebugger(void) { if ((cv_kartdebugwaypoints.value != 0) && (stplyr == &players[displayplayers[0]])) { + V_DrawString(8, 166, 0, va("'Best' Waypoint ID: %d", K_GetWaypointID(stplyr->nextwaypoint))); V_DrawString(8, 176, 0, va("Finishline Distance: %d", stplyr->distancetofinish)); } } @@ -10823,8 +10830,10 @@ void K_drawKartHUD(void) K_drawKartFreePlay(leveltime); } - if (stplyr->kartstuff[k_wrongway] && ((leveltime / 8) & 1)) + if (splitscreen == 0 && stplyr->kartstuff[k_wrongway] && ((leveltime / 8) & 1)) + { V_DrawCenteredString(BASEVIDWIDTH>>1, 176, V_REDMAP|V_SNAPTOBOTTOM, "WRONG WAY"); + } if (cv_kartdebugdistribution.value) K_drawDistributionDebugger(); diff --git a/src/k_waypoint.c b/src/k_waypoint.c index 4631ed87e..fc5aa4590 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -461,6 +461,9 @@ void K_DebugWaypointsVisualise(void) 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) { From 5cd1296505752813f40bb22f3a5edc5ae89bb9aa Mon Sep 17 00:00:00 2001 From: Sryder Date: Sun, 22 Mar 2020 00:16:32 +0000 Subject: [PATCH 098/105] Include license/copyright headers on new files. --- src/k_bheap.c | 12 ++++++++++++ src/k_bheap.h | 12 ++++++++++++ src/k_pathfind.c | 12 ++++++++++++ src/k_pathfind.h | 12 ++++++++++++ src/k_waypoint.c | 13 +++++++++++++ src/k_waypoint.h | 13 +++++++++++++ 6 files changed, 74 insertions(+) diff --git a/src/k_bheap.c b/src/k_bheap.c index cf6848f68..777a62e84 100644 --- a/src/k_bheap.c +++ b/src/k_bheap.c @@ -1,3 +1,15 @@ +// 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_bheap.c +/// \brief Binary Heap implementation for SRB2 code base. + #include "k_bheap.h" #include "z_zone.h" diff --git a/src/k_bheap.h b/src/k_bheap.h index f1c6d2d5c..04e37492c 100644 --- a/src/k_bheap.h +++ b/src/k_bheap.h @@ -1,3 +1,15 @@ +// 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_bheap.h +/// \brief Binary Heap implementation for SRB2 code base. + #ifndef __K_BHEAP__ #define __K_BHEAP__ diff --git a/src/k_pathfind.c b/src/k_pathfind.c index 563456de1..8cccd1e81 100644 --- a/src/k_pathfind.c +++ b/src/k_pathfind.c @@ -1,3 +1,15 @@ +// 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_pathfind.c +/// \brief A* Pathfinding algorithm implementation for SRB2 code base. + #include "k_pathfind.h" #include "doomdef.h" diff --git a/src/k_pathfind.h b/src/k_pathfind.h index dac23373c..ba0e38f47 100644 --- a/src/k_pathfind.h +++ b/src/k_pathfind.h @@ -1,3 +1,15 @@ +// 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_pathfind.h +/// \brief A* Pathfinding algorithm implementation for SRB2 code base. + #ifndef __K_PATHFIND__ #define __K_PATHFIND__ diff --git a/src/k_waypoint.c b/src/k_waypoint.c index fc5aa4590..f77e6d62f 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -1,3 +1,16 @@ +// 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" diff --git a/src/k_waypoint.h b/src/k_waypoint.h index 058ff6882..fb8d37f20 100644 --- a/src/k_waypoint.h +++ b/src/k_waypoint.h @@ -1,3 +1,16 @@ +// 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.h +/// \brief Waypoint handling from the relevant mobjs +/// Setup and interfacing with waypoints for the main game + #ifndef __K_WAYPOINT__ #define __K_WAYPOINT__ From 203aa0602e458095b344664e437db894bfca373f Mon Sep 17 00:00:00 2001 From: Sryder Date: Sun, 22 Mar 2020 00:25:48 +0000 Subject: [PATCH 099/105] Use P_FindSpecialLineFromTag for MT_WAYPOINT, now that it works where we need it This matches how MT_SPINMACEPOINT was changed --- src/p_mobj.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index cdfe8fcb1..7259f46f2 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -12072,16 +12072,11 @@ ML_NOCLIMB : Direction not controllable break; case MT_WAYPOINT: { - size_t line; + // Just like MT_SPINMACEPOINT, this now works here too! + INT32 line = P_FindSpecialLineFromTag(9, mthing->angle, -1); mobj->radius = 384*FRACUNIT; - // Same reason as for MT_SPINMACEPOINT we can't use the function to find the linedef - for (line = 0; line < numlines; line++) - { - if (lines[line].special == 2000 && lines[line].tag == mthing->angle) - break; - } // Set the radius, mobj z, and mthing z to match what the parameters want - if (line < numlines) + if (line != -1) { fixed_t lineradius = sides[lines[line].sidenum[0]].textureoffset; fixed_t linez = sides[lines[line].sidenum[0]].rowoffset; From 2be2277f4b2583fa1d5c9df56f0dadf5d01f5b8e Mon Sep 17 00:00:00 2001 From: Sryder Date: Sun, 22 Mar 2020 00:44:28 +0000 Subject: [PATCH 100/105] Fix the merge confluct I SOMEHOW missed --- src/k_kart.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 3fe2b9b3c..29579ad50 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2157,8 +2157,8 @@ void K_RespawnChecker(player_t *player) fixed_t newx, newy, newz; newangle = FixedAngle(((360/8)*i)*FRACUNIT); - newx = player->mo->x + P_ReturnThrustX(player->mo, newangle, 31 * player->mo->scale); - newy = player->mo->y + P_ReturnThrustY(player->mo, newangle, 31 * player->mo->scale); + newx = player->mo->x + P_ReturnThrustX(player->mo, newangle, 31 * player->mo->scale); + newy = player->mo->y + P_ReturnThrustY(player->mo, newangle, 31 * player->mo->scale); if (player->mo->eflags & MFE_VERTICALFLIP) newz = player->mo->z + player->mo->height; else @@ -2172,8 +2172,8 @@ void K_RespawnChecker(player_t *player) laser->eflags |= MFE_VERTICALFLIP; P_SetTarget(&laser->target, player->mo); laser->angle = newangle+ANGLE_90; - mo->momz = (8 * player->mo->scale) * P_MobjFlip(player->mo); - P_SetScale(mo, (mo->destscale = player->mo->scale)); + laser->momz = (8 * player->mo->scale) * P_MobjFlip(player->mo); + P_SetScale(laser, (laser->destscale = player->mo->scale)); } } } From 9dae62c2c6adc82005f647c6c9f41c8877d00d71 Mon Sep 17 00:00:00 2001 From: Sryder Date: Sun, 22 Mar 2020 00:47:02 +0000 Subject: [PATCH 101/105] Fix me being hyperdumb with copypaste error --- src/p_mobj.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 7259f46f2..57fbaedf3 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -12073,7 +12073,7 @@ ML_NOCLIMB : Direction not controllable case MT_WAYPOINT: { // Just like MT_SPINMACEPOINT, this now works here too! - INT32 line = P_FindSpecialLineFromTag(9, mthing->angle, -1); + INT32 line = P_FindSpecialLineFromTag(2000, mthing->angle, -1); mobj->radius = 384*FRACUNIT; // Set the radius, mobj z, and mthing z to match what the parameters want if (line != -1) From 4975cce3ac41ad69c593d5244aeda538c4019f5a Mon Sep 17 00:00:00 2001 From: Sryder Date: Sun, 22 Mar 2020 00:47:17 +0000 Subject: [PATCH 102/105] Just use an I_Assert on SpawnSPBRingTrail --- src/p_enemy.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/p_enemy.c b/src/p_enemy.c index 521d44b1b..df20cb6f1 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -8447,15 +8447,14 @@ void A_JawzExplode(mobj_t *actor) static void SpawnSPBTrailRings(mobj_t *actor) { - if (actor != NULL) + I_Assert(actor != NULL); + + if (leveltime % 6 == 0) { - if (leveltime % 6 == 0) - { - mobj_t *ring = P_SpawnMobj(actor->x - actor->momx, actor->y - actor->momy, - actor->z - actor->momz + (24*mapobjectscale), MT_RING); - ring->threshold = 10; - ring->fuse = 120*TICRATE; - } + mobj_t *ring = P_SpawnMobj(actor->x - actor->momx, actor->y - actor->momy, + actor->z - actor->momz + (24*mapobjectscale), MT_RING); + ring->threshold = 10; + ring->fuse = 120*TICRATE; } } From 521c4662bf1cb8f74aa76781f55be993e07dd6ed Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sat, 21 Mar 2020 23:09:05 -0400 Subject: [PATCH 103/105] void out heap after done asserting, to fix an unused variable compile error I get --- src/k_bheap.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/k_bheap.c b/src/k_bheap.c index 777a62e84..40c652b5e 100644 --- a/src/k_bheap.c +++ b/src/k_bheap.c @@ -65,6 +65,8 @@ static bheapitem_t *K_BHeapItemsCompare(bheap_t *heap, bheapitem_t *item1, bheap I_Assert(K_BHeapItemValidate(heap, item1)); I_Assert(K_BHeapItemValidate(heap, item2)); + (void)heap; + if (item1->value < item2->value) { lowervalueitem = item1; @@ -99,6 +101,8 @@ static void K_BHeapSwapItems(bheap_t *heap, bheapitem_t *item1, bheapitem_t *ite I_Assert(K_BHeapItemValidate(heap, item1)); I_Assert(K_BHeapItemValidate(heap, item2)); + (void)heap; + { size_t tempitemindex = item1->heapindex; bheapitem_t tempitemstore = *item1; From c75e3a025cd0d5d6ed5ecbb8b6de6abf994c6c38 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sat, 21 Mar 2020 23:11:19 -0400 Subject: [PATCH 104/105] Braces to avoid compile error (Not sure why I never got this until now?) --- src/p_mobj.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/p_mobj.c b/src/p_mobj.c index 50bb4009a..f33d598ac 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -8315,7 +8315,10 @@ void P_MobjThinker(mobj_t *mobj) if (p) { if (p->kartstuff[k_driftboost] > mobj->movecount) + { ; // reset animation + } + mobj->movecount = p->kartstuff[k_driftboost]; } } From 80e04a20350bc385d5f0dcac26cca114f0877a89 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Sun, 22 Mar 2020 11:32:55 +0100 Subject: [PATCH 105/105] Use proper respawn check, don't cancel spinout, but prevent dropdashing when respawning with spinout --- src/k_kart.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index edfcf6991..59760761c 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2048,8 +2048,6 @@ void K_RespawnChecker(player_t *player) fixed_t destx = 0, desty = 0, destz = 0; player->mo->momx = player->mo->momy = player->mo->momz = 0; - player->kartstuff[k_spinouttimer] = 0; - player->kartstuff[k_wipeoutslow] = 0; // Don't spinout anymore player->powers[pw_flashing] = 2; player->powers[pw_nocontrol] = 2; @@ -2209,7 +2207,7 @@ void K_RespawnChecker(player_t *player) // Sal: The old behavior was stupid and prone to accidental usage. // Let's rip off Mania instead, and turn this into a Drop Dash! - if (cmd->buttons & BT_ACCELERATE) + if (cmd->buttons & BT_ACCELERATE && !player->kartstuff[k_spinouttimer]) // Lat: Since we're letting players spin out on respawn, don't let them charge a dropdash in this state. (It wouldn't work anyway) player->kartstuff[k_dropdash]++; else player->kartstuff[k_dropdash] = 0; @@ -6081,7 +6079,7 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) (bestwaypoint != NULL) && (bestwaypoint != player->nextwaypoint) && (player->kartstuff[k_respawn] == 0) && - (!(bestwaypoint->mobj->spawnpoint->options & MTF_AMBUSH)) && // Don't try to respawn on waypoints with the MTF_AMBUSH (No respawn) flag! + (K_GetWaypointIsSpawnpoint(bestwaypoint)) && // Don't try to respawn on waypoints that are marked with no respawn (K_GetWaypointIsShortcut(bestwaypoint) == false) && (K_GetWaypointIsEnabled(bestwaypoint) == true)) { size_t i = 0U;