From 3ae8623ebb98f51ac93517d24fd4444b75e33bfe Mon Sep 17 00:00:00 2001 From: Sryder13 Date: Sun, 12 Nov 2017 01:10:02 +0000 Subject: [PATCH 001/163] 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/163] 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/163] 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/163] 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/163] 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/163] 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/163] 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/163] 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/163] 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/163] 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/163] 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/163] 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/163] 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/163] 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/163] 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/163] 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/163] 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/163] 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/163] 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/163] 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/163] 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/163] 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/163] 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/163] 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/163] 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/163] 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/163] 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/163] 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/163] 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/163] 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/163] 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/163] 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/163] 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/163] 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/163] 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/163] 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/163] 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/163] 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/163] 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/163] 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/163] 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/163] 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/163] 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/163] 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/163] 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/163] 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/163] 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 83c4017993eb3597e13e854d402fcd4652c6c8a7 Mon Sep 17 00:00:00 2001 From: James Date: Mon, 21 Oct 2019 08:35:17 -0400 Subject: [PATCH 048/163] Another round of small color tweaks --- src/d_main.c | 2 +- src/dehacked.c | 6 +++--- src/doomdef.h | 6 +++--- src/g_game.c | 2 +- src/hu_stuff.c | 6 +++--- src/k_kart.c | 22 +++++++++++----------- 6 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index 597eea87c..b5d4245a3 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -525,7 +525,7 @@ static void D_Display(void) } if (demo.rewinding) - V_DrawFadeScreen(TC_RAINBOW, (leveltime & 0x20) ? SKINCOLOR_PASTEL : SKINCOLOR_MOONSLAM); + V_DrawFadeScreen(TC_RAINBOW, (leveltime & 0x20) ? SKINCOLOR_PASTEL : SKINCOLOR_MOONSET); // vid size change is now finished if it was on... vid.recalc = 0; diff --git a/src/dehacked.c b/src/dehacked.c index 5f9249d89..93ff13b73 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -8242,7 +8242,7 @@ static const char *COLOR_ENUMS[] = { // Rejigged for Kart. "SCARLET", // SKINCOLOR_SCARLET "KETCHUP", // SKINCOLOR_KETCHUP "DAWN", // SKINCOLOR_DAWN - "SUNSET", // SKINCOLOR_SUNSET + "SUNSLAM", // SKINCOLOR_SUNSLAM "CREAMSICLE", // SKINCOLOR_CREAMSICLE "ORANGE", // SKINCOLOR_ORANGE "ROSEWOOD", // SKINCOLOR_ROSEWOOD @@ -8302,9 +8302,9 @@ static const char *COLOR_ENUMS[] = { // Rejigged for Kart. "THISTLE", // SKINCOLOR_THISTLE "PURPLE", // SKINCOLOR_PURPLE "PASTEL", // SKINCOLOR_PASTEL - "MOONSLAM", // SKINCOLOR_MOONSLAM + "MOONSET", // SKINCOLOR_MOONSET "DUSK", // SKINCOLOR_DUSK - "BUBBLEGUM", // SKINCOLOR_BUBBLEGUM + "VIOLET", // SKINCOLOR_VIOLET "MAGENTA", // SKINCOLOR_MAGENTA "FUCHSIA", // SKINCOLOR_FUCHSIA "TOXIC", // SKINCOLOR_TOXIC diff --git a/src/doomdef.h b/src/doomdef.h index e1e9ab13d..bb21e3d0f 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -280,7 +280,7 @@ typedef enum SKINCOLOR_SCARLET, SKINCOLOR_KETCHUP, SKINCOLOR_DAWN, - SKINCOLOR_SUNSET, + SKINCOLOR_SUNSLAM, SKINCOLOR_CREAMSICLE, SKINCOLOR_ORANGE, SKINCOLOR_ROSEWOOD, @@ -340,9 +340,9 @@ typedef enum SKINCOLOR_THISTLE, SKINCOLOR_PURPLE, SKINCOLOR_PASTEL, - SKINCOLOR_MOONSLAM, + SKINCOLOR_MOONSET, SKINCOLOR_DUSK, - SKINCOLOR_BUBBLEGUM, + SKINCOLOR_VIOLET, SKINCOLOR_MAGENTA, SKINCOLOR_FUCHSIA, SKINCOLOR_TOXIC, diff --git a/src/g_game.c b/src/g_game.c index d5ed9af7d..7cdb57410 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -6926,7 +6926,7 @@ void G_LoadDemoInfo(menudemo_t *pdemo) (void)extrainfo_p; sprintf(pdemo->winnername, "transrights420"); pdemo->winnerskin = 1; - pdemo->winnercolor = SKINCOLOR_MOONSLAM; + pdemo->winnercolor = SKINCOLOR_MOONSET; pdemo->winnertime = 6666;*/ // Read standings! diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 6126f7bec..3ebf3ee29 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -812,7 +812,6 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) case SKINCOLOR_PINK: case SKINCOLOR_ROSE: case SKINCOLOR_LEMONADE: - case SKINCOLOR_BUBBLEGUM: case SKINCOLOR_LILAC: case SKINCOLOR_TAFFY: cstart = "\x8d"; // V_PINKMAP @@ -828,7 +827,7 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) cstart = "\x85"; // V_REDMAP break; case SKINCOLOR_DAWN: - case SKINCOLOR_SUNSET: + case SKINCOLOR_SUNSLAM: case SKINCOLOR_CREAMSICLE: case SKINCOLOR_ORANGE: case SKINCOLOR_ROSEWOOD: @@ -906,7 +905,8 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) break; case SKINCOLOR_MAGENTA: case SKINCOLOR_FUCHSIA: - case SKINCOLOR_MOONSLAM: + case SKINCOLOR_MOONSET: + case SKINCOLOR_VIOLET: cstart = "\x8c"; // V_MAGENTAMAP break; case SKINCOLOR_DUSK: diff --git a/src/k_kart.c b/src/k_kart.c index 230662d2a..335f3cfeb 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -71,7 +71,7 @@ const char *KartColor_Names[MAXSKINCOLORS] = "Scarlet", // SKINCOLOR_SCARLET "Ketchup", // SKINCOLOR_KETCHUP "Dawn", // SKINCOLOR_DAWN - "Sunset", // SKINCOLOR_SUNSET + "Sunslam", // SKINCOLOR_SUNSLAM "Creamsicle", // SKINCOLOR_CREAMSICLE "Orange", // SKINCOLOR_ORANGE "Rosewood", // SKINCOLOR_ROSEWOOD @@ -131,9 +131,9 @@ const char *KartColor_Names[MAXSKINCOLORS] = "Thistle", // SKINCOLOR_THISTLE "Purple", // SKINCOLOR_PURPLE "Pastel", // SKINCOLOR_PASTEL - "Moonslam", // SKINCOLOR_MOONSLAM + "Moonset", // SKINCOLOR_MOONSET "Dusk", // SKINCOLOR_DUSK - "Bubblegum", // SKINCOLOR_BUBBLEGUM + "Violet", // SKINCOLOR_VIOLET "Magenta", // SKINCOLOR_MAGENTA "Fuchsia", // SKINCOLOR_FUCHSIA "Toxic", // SKINCOLOR_TOXIC @@ -177,7 +177,7 @@ const UINT8 KartColor_Opposite[MAXSKINCOLORS*2] = SKINCOLOR_ALGAE,10, // SKINCOLOR_SCARLET SKINCOLOR_MUSTARD,10, // SKINCOLOR_KETCHUP SKINCOLOR_DUSK,8, // SKINCOLOR_DAWN - SKINCOLOR_MOONSLAM,8, // SKINCOLOR_SUNSET + SKINCOLOR_MOONSET,8, // SKINCOLOR_SUNSLAM SKINCOLOR_PERIWINKLE,8, // SKINCOLOR_CREAMSICLE SKINCOLOR_BLUE,8, // SKINCOLOR_ORANGE SKINCOLOR_BLUEBERRY,6, // SKINCOLOR_ROSEWOOD @@ -192,7 +192,7 @@ const UINT8 KartColor_Opposite[MAXSKINCOLORS*2] = SKINCOLOR_KETCHUP,8, // SKINCOLOR_MUSTARD SKINCOLOR_EMERALD,8, // SKINCOLOR_BANANA SKINCOLOR_TEAL,8, // SKINCOLOR_OLIVE - SKINCOLOR_BUBBLEGUM,8, // SKINCOLOR_CROCODILE + SKINCOLOR_VIOLET,8, // SKINCOLOR_CROCODILE SKINCOLOR_NAVY,6, // SKINCOLOR_PERIDOT SKINCOLOR_ROBOHOOD,8, // SKINCOLOR_VOMIT SKINCOLOR_LAVENDER,6, // SKINCOLOR_GARDEN @@ -237,9 +237,9 @@ const UINT8 KartColor_Opposite[MAXSKINCOLORS*2] = SKINCOLOR_AZURE,8, // SKINCOLOR_THISTLE SKINCOLOR_CARIBBEAN,10, // SKINCOLOR_PURPLE SKINCOLOR_FUCHSIA,11, // SKINCOLOR_PASTEL - SKINCOLOR_SUNSET,10, // SKINCOLOR_MOONSLAM + SKINCOLOR_SUNSLAM,10, // SKINCOLOR_MOONSET SKINCOLOR_DAWN,6, // SKINCOLOR_DUSK - SKINCOLOR_CROCODILE,8, // SKINCOLOR_BUBBLEGUM + SKINCOLOR_CROCODILE,8, // SKINCOLOR_VIOLET SKINCOLOR_TURTLE,8, // SKINCOLOR_MAGENTA SKINCOLOR_PASTEL,11, // SKINCOLOR_FUCHSIA SKINCOLOR_MAROON,8, // SKINCOLOR_TOXIC @@ -258,7 +258,7 @@ UINT8 colortranslations[MAXTRANSLATIONS][16] = { { 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31}, // SKINCOLOR_GREY { 3, 5, 8, 11, 15, 17, 19, 21, 23, 24, 25, 26, 27, 29, 30, 31}, // SKINCOLOR_NICKEL { 4, 7, 11, 15, 20, 22, 24, 27, 28, 28, 28, 29, 29, 30, 30, 31}, // SKINCOLOR_BLACK - { 0, 1, 208, 208, 209, 210, 10, 14, 16, 18, 20, 22, 24, 26, 28, 31}, // SKINCOLOR_FAIRY + { 0, 0, 252, 252, 200, 201, 211, 14, 16, 18, 20, 22, 24, 26, 28, 31}, // SKINCOLOR_FAIRY { 0, 80, 80, 81, 82, 218, 240, 11, 13, 16, 18, 21, 23, 26, 28, 31}, // SKINCOLOR_POPCORN { 80, 88, 89, 98, 99, 91, 12, 14, 16, 18, 20, 22, 24, 26, 28, 31}, // SKINCOLOR_ARTICHOKE { 0, 128, 129, 130, 146, 170, 14, 15, 17, 19, 21, 23, 25, 27, 29, 31}, // SKINCOLOR_PIGEON @@ -281,7 +281,7 @@ UINT8 colortranslations[MAXTRANSLATIONS][16] = { { 48, 49, 50, 51, 53, 34, 36, 38, 184, 185, 168, 168, 169, 169, 254, 31}, // SKINCOLOR_SCARLET { 72, 73, 64, 51, 52, 54, 34, 36, 38, 40, 42, 43, 44, 71, 46, 47}, // SKINCOLOR_KETCHUP { 0, 208, 216, 209, 210, 211, 212, 57, 58, 59, 60, 61, 63, 71, 47, 31}, // SKINCOLOR_DAWN - { 82, 72, 73, 64, 51, 53, 55, 213, 214, 195, 195, 173, 174, 175, 253, 254}, // SKINCOLOR_SUNSET + { 82, 72, 73, 64, 51, 53, 55, 213, 214, 195, 195, 173, 174, 175, 253, 254}, // SKINCOLOR_SUNSLAM { 0, 0, 208, 208, 48, 49, 50, 52, 53, 54, 56, 57, 58, 60, 61, 63}, // SKINCOLOR_CREAMSICLE {208, 48, 49, 50, 51, 52, 53, 54, 55, 57, 59, 60, 62, 44, 71, 47}, // SKINCOLOR_ORANGE { 50, 52, 55, 56, 58, 59, 60, 61, 62, 63, 44, 45, 71, 46, 47, 30}, // SKINCOLOR_ROSEWOOD @@ -341,9 +341,9 @@ UINT8 colortranslations[MAXTRANSLATIONS][16] = { { 0, 0, 0, 252, 252, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 254}, // SKINCOLOR_THISTLE { 0, 252, 160, 161, 162, 163, 164, 165, 166, 167, 168, 168, 169, 169, 253, 254}, // SKINCOLOR_PURPLE { 0, 128, 128, 129, 129, 146, 170, 162, 163, 164, 165, 166, 167, 168, 169, 254}, // SKINCOLOR_PASTEL - { 0, 144, 145, 146, 170, 162, 163, 184, 184, 207, 207, 44, 45, 46, 47, 31}, // SKINCOLOR_MOONSLAM + { 0, 144, 145, 146, 170, 162, 163, 184, 184, 207, 207, 44, 45, 46, 47, 31}, // SKINCOLOR_MOONSET {252, 200, 201, 192, 193, 194, 172, 172, 173, 173, 174, 174, 175, 169, 253, 254}, // SKINCOLOR_DUSK - { 0, 252, 252, 200, 201, 181, 182, 183, 184, 166, 167, 168, 169, 253, 254, 31}, // SKINCOLOR_BUBBLEGUM + {176, 177, 178, 179, 180, 181, 182, 183, 184, 164, 165, 166, 167, 168, 169, 254}, // SKINCOLOR_VIOLET {176, 177, 178, 179, 180, 181, 182, 183, 184, 184, 185, 185, 186, 187, 30, 31}, // SKINCOLOR_MAGENTA {208, 209, 209, 32, 33, 182, 183, 184, 185, 185, 186, 186, 187, 253, 254, 31}, // SKINCOLOR_FUCHSIA { 0, 0, 88, 88, 89, 6, 8, 10, 193, 194, 195, 184, 185, 186, 187, 31}, // SKINCOLOR_TOXIC From f9ea6833129ccf884ab2f0b3d9fb3d9d0e37d44f Mon Sep 17 00:00:00 2001 From: James Date: Tue, 22 Oct 2019 08:45:38 -0400 Subject: [PATCH 049/163] P_PlayerTouchingSectorSpecial turned into P_MobjTouchingSectorSpecial --- src/k_kart.c | 27 ++++++---------------- src/lua_baselib.c | 12 +++++----- src/p_floor.c | 2 +- src/p_inter.c | 2 +- src/p_map.c | 32 ++++++------------------- src/p_mobj.c | 26 +++++---------------- src/p_spec.c | 59 +++++++++++++++++++++++++---------------------- src/p_spec.h | 2 +- 8 files changed, 61 insertions(+), 101 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 230662d2a..6c268269d 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1533,20 +1533,16 @@ void K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean solid) \return boolean */ -static UINT8 K_CheckOffroadCollide(mobj_t *mo, sector_t *sec) +static UINT8 K_CheckOffroadCollide(mobj_t *mo) { UINT8 i; - sector_t *sec2; I_Assert(mo != NULL); I_Assert(!P_MobjWasRemoved(mo)); - sec2 = P_ThingOnSpecial3DFloor(mo); - for (i = 2; i < 5; i++) { - if ((sec2 && GETSECSPECIAL(sec2->special, 1) == i) - || (P_IsObjectOnRealGround(mo, sec) && GETSECSPECIAL(sec->special, 1) == i)) + if (P_MobjTouchingSectorSpecial(mo, 1, i)) return i-1; } @@ -1561,25 +1557,16 @@ static UINT8 K_CheckOffroadCollide(mobj_t *mo, sector_t *sec) */ static void K_UpdateOffroad(player_t *player) { - fixed_t offroad; - sector_t *nextsector = R_PointInSubsector( - player->mo->x + player->mo->momx*2, player->mo->y + player->mo->momy*2)->sector; - UINT8 offroadstrength = K_CheckOffroadCollide(player->mo, nextsector); + fixed_t offroadstrength = (K_CheckOffroadCollide(player->mo) << FRACBITS); // If you are in offroad, a timer starts. if (offroadstrength) { - if (K_CheckOffroadCollide(player->mo, player->mo->subsector->sector) && player->kartstuff[k_offroad] == 0) - player->kartstuff[k_offroad] = TICRATE; + if (player->kartstuff[k_offroad] < offroadstrength) + player->kartstuff[k_offroad] += offroadstrength / TICRATE; - if (player->kartstuff[k_offroad] > 0) - { - offroad = (offroadstrength << FRACBITS) / TICRATE; - player->kartstuff[k_offroad] += offroad; - } - - if (player->kartstuff[k_offroad] > (offroadstrength << FRACBITS)) - player->kartstuff[k_offroad] = (offroadstrength << FRACBITS); + if (player->kartstuff[k_offroad] > offroadstrength) + player->kartstuff[k_offroad] = offroadstrength; } else player->kartstuff[k_offroad] = 0; diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 7464743c3..3d836dad2 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -1313,15 +1313,15 @@ static int lib_pExplodeMissile(lua_State *L) return 0; } -static int lib_pPlayerTouchingSectorSpecial(lua_State *L) +static int lib_pMobjTouchingSectorSpecial(lua_State *L) { - player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); + mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); INT32 section = (INT32)luaL_checkinteger(L, 2); INT32 number = (INT32)luaL_checkinteger(L, 3); //HUDSAFE - if (!player) - return LUA_ErrInvalid(L, "player_t"); - LUA_PushUserdata(L, P_PlayerTouchingSectorSpecial(player, section, number), META_SECTOR); + if (!mo) + return LUA_ErrInvalid(L, "mobj_t"); + LUA_PushUserdata(L, P_MobjTouchingSectorSpecial(mo, section, number), META_SECTOR); return 1; } @@ -2736,7 +2736,7 @@ static luaL_Reg lib[] = { {"P_SetMobjStateNF",lib_pSetMobjStateNF}, {"P_DoSuperTransformation",lib_pDoSuperTransformation}, {"P_ExplodeMissile",lib_pExplodeMissile}, - {"P_PlayerTouchingSectorSpecial",lib_pPlayerTouchingSectorSpecial}, + {"P_MobjTouchingSectorSpecial",lib_pMobjTouchingSectorSpecial}, {"P_FindSpecialLineFromTag",lib_pFindSpecialLineFromTag}, {"P_SwitchWeather",lib_pSwitchWeather}, {"P_LinedefExecute",lib_pLinedefExecute}, diff --git a/src/p_floor.c b/src/p_floor.c index ccbfd6eae..a4e4f2747 100644 --- a/src/p_floor.c +++ b/src/p_floor.c @@ -2275,7 +2275,7 @@ void T_EachTimeThinker(levelspecthink_t *eachtime) continue; if (!(players[i].mo->subsector->sector == sec - || P_PlayerTouchingSectorSpecial(&players[i], 2, (GETSECSPECIAL(sec->special, 2))) == sec)) + || P_MobjTouchingSectorSpecial(players[i].mo, 2, (GETSECSPECIAL(sec->special, 2))) == sec)) continue; if (floortouch == true && P_IsObjectOnRealGround(players[i].mo, sec)) diff --git a/src/p_inter.c b/src/p_inter.c index 7a975951c..a1a44c998 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -925,7 +925,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) special->fuse = 1; special->flags2 |= MF2_JUSTATTACKED; - if (!P_PlayerTouchingSectorSpecial(player, 4, 2 + flagteam)) + if (!P_MobjTouchingSectorSpecial(player->mo, 4, 2 + flagteam)) { CONS_Printf(M_GetText("%s returned the %c%s%c to base.\n"), plname, flagcolor, flagtext, 0x80); diff --git a/src/p_map.c b/src/p_map.c index d4f2a94fa..22e624311 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -2789,25 +2789,12 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) //All things are affected by their scale. fixed_t maxstep = FixedMul(MAXSTEPMOVE, mapobjectscale); - if (thing->player) - { - // If using type Section1:13, double the maxstep. - if (P_PlayerTouchingSectorSpecial(thing->player, 1, 13) - || GETSECSPECIAL(R_PointInSubsector(x, y)->sector->special, 1) == 13) - maxstep <<= 1; - // If using type Section1:12, no maxstep. For ledges you don't want the player to climb! (see: Egg Zeppelin & SMK port walls) - else if (P_PlayerTouchingSectorSpecial(thing->player, 1, 12) - || GETSECSPECIAL(R_PointInSubsector(x, y)->sector->special, 1) == 12) - maxstep = 0; - - // Don't 'step up' while springing, - // Only step up "if needed". - /* // SRB2kart - don't need - if (thing->state == &states[S_PLAY_SPRING] - && P_MobjFlip(thing)*thing->momz > FixedMul(FRACUNIT, thing->scale)) - maxstep = 0; - */ - } + // If using type Section1:13, double the maxstep. + if (P_MobjTouchingSectorSpecial(thing, 1, 13)) + maxstep <<= 1; + // If using type Section1:12, no maxstep. For short walls, like Egg Zeppelin + else if (P_MobjTouchingSectorSpecial(thing, 1, 12)) + maxstep = 0; if (thing->type == MT_SKIM) maxstep = 0; @@ -2830,12 +2817,7 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) return false; // mobj must lower itself to fit // Ramp test - if (maxstep > 0 && !( - thing->player && ( - P_PlayerTouchingSectorSpecial(thing->player, 1, 14) - || GETSECSPECIAL(R_PointInSubsector(x, y)->sector->special, 1) == 14) - ) - ) + if ((maxstep > 0) && !(P_MobjTouchingSectorSpecial(thing, 1, 14))) { // If the floor difference is MAXSTEPMOVE or less, and the sector isn't Section1:14, ALWAYS // step down! Formerly required a Section1:13 sector for the full MAXSTEPMOVE, but no more. diff --git a/src/p_mobj.c b/src/p_mobj.c index f54eca41e..1fd846ff1 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -8355,14 +8355,8 @@ void P_MobjThinker(mobj_t *mobj) P_Thrust(mobj, mobj->angle, thrustamount); - if (grounded) - { - sector_t *sec2 = P_ThingOnSpecial3DFloor(mobj); - if ((sec2 && GETSECSPECIAL(sec2->special, 3) == 1) - || (P_IsObjectOnRealGround(mobj, mobj->subsector->sector) - && GETSECSPECIAL(mobj->subsector->sector->special, 3) == 1)) - K_DoPogoSpring(mobj, 0, 1); - } + if (P_MobjTouchingSectorSpecial(mobj, 3, 1)) + K_DoPogoSpring(mobj, 0, 1); if (mobj->threshold > 0) mobj->threshold--; @@ -8374,7 +8368,6 @@ void P_MobjThinker(mobj_t *mobj) } case MT_JAWZ: { - sector_t *sec2; mobj_t *ghost = P_SpawnGhostMobj(mobj); if (mobj->target && !P_MobjWasRemoved(mobj->target) && mobj->target->player) @@ -8392,10 +8385,7 @@ void P_MobjThinker(mobj_t *mobj) K_DriftDustHandling(mobj); - sec2 = P_ThingOnSpecial3DFloor(mobj); - if ((sec2 && GETSECSPECIAL(sec2->special, 3) == 1) - || (P_IsObjectOnRealGround(mobj, mobj->subsector->sector) - && GETSECSPECIAL(mobj->subsector->sector->special, 3) == 1)) + if (P_MobjTouchingSectorSpecial(mobj, 3, 1)) K_DoPogoSpring(mobj, 0, 1); break; @@ -9602,13 +9592,9 @@ void P_MobjThinker(mobj_t *mobj) break; case MT_BLUEFLAG: case MT_REDFLAG: - { - sector_t *sec2; - sec2 = P_ThingOnSpecial3DFloor(mobj); - if ((sec2 && GETSECSPECIAL(sec2->special, 4) == 2) || (GETSECSPECIAL(mobj->subsector->sector->special, 4) == 2)) - mobj->fuse = 1; // Return to base. - break; - } + if (P_MobjTouchingSectorSpecial(mobj, 4, 2)) + mobj->fuse = 1; // Return to base. + break; case MT_CANNONBALL: #ifdef FLOORSPLATS R_AddFloorSplat(mobj->tracer->subsector, mobj->tracer, "TARGET", mobj->tracer->x, diff --git a/src/p_spec.c b/src/p_spec.c index 93e88e2ce..7786329ec 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -3361,7 +3361,7 @@ boolean P_IsFlagAtBase(mobjtype_t flag) } // -// P_PlayerTouchingSectorSpecial +// P_MobjTouchingSectorSpecial // // Replaces the old player->specialsector. // This allows a player to touch more than @@ -3371,20 +3371,23 @@ boolean P_IsFlagAtBase(mobjtype_t flag) // the particular type that it finds. // Returns NULL if it doesn't find it. // -sector_t *P_PlayerTouchingSectorSpecial(player_t *player, INT32 section, INT32 number) +// Sal: There was absolutely no reason for +// this to be a player_t only function. +// +sector_t *P_MobjTouchingSectorSpecial(mobj_t *mo, INT32 section, INT32 number) { msecnode_t *node; ffloor_t *rover; - if (!player->mo) + if (!mo) return NULL; // Check default case first - if (GETSECSPECIAL(player->mo->subsector->sector->special, section) == number) - return player->mo->subsector->sector; + if (GETSECSPECIAL(mo->subsector->sector->special, section) == number) + return mo->subsector->sector; // Hmm.. maybe there's a FOF that has it... - for (rover = player->mo->subsector->sector->ffloors; rover; rover = rover->next) + for (rover = mo->subsector->sector->ffloors; rover; rover = rover->next) { fixed_t topheight, bottomheight; @@ -3394,37 +3397,38 @@ sector_t *P_PlayerTouchingSectorSpecial(player_t *player, INT32 section, INT32 n if (!(rover->flags & FF_EXISTS)) continue; - topheight = P_GetSpecialTopZ(player->mo, sectors + rover->secnum, player->mo->subsector->sector); - bottomheight = P_GetSpecialBottomZ(player->mo, sectors + rover->secnum, player->mo->subsector->sector); + topheight = P_GetSpecialTopZ(mo, sectors + rover->secnum, mo->subsector->sector); + bottomheight = P_GetSpecialBottomZ(mo, sectors + rover->secnum, mo->subsector->sector); // Check the 3D floor's type... - if (rover->flags & FF_BLOCKPLAYER) + if (((rover->flags & FF_BLOCKPLAYER) && mo->player) + || ((rover->flags & FF_BLOCKOTHERS) && !mo->player)) { // Thing must be on top of the floor to be affected... if ((rover->master->frontsector->flags & SF_FLIPSPECIAL_FLOOR) && !(rover->master->frontsector->flags & SF_FLIPSPECIAL_CEILING)) { - if ((player->mo->eflags & MFE_VERTICALFLIP) || player->mo->z != topheight) + if ((mo->eflags & MFE_VERTICALFLIP) || mo->z != topheight) continue; } else if ((rover->master->frontsector->flags & SF_FLIPSPECIAL_CEILING) && !(rover->master->frontsector->flags & SF_FLIPSPECIAL_FLOOR)) { - if (!(player->mo->eflags & MFE_VERTICALFLIP) - || player->mo->z + player->mo->height != bottomheight) + if (!(mo->eflags & MFE_VERTICALFLIP) + || mo->z + mo->height != bottomheight) continue; } else if (rover->master->frontsector->flags & SF_FLIPSPECIAL_BOTH) { - if (!((player->mo->eflags & MFE_VERTICALFLIP && player->mo->z + player->mo->height == bottomheight) - || (!(player->mo->eflags & MFE_VERTICALFLIP) && player->mo->z == topheight))) + if (!((mo->eflags & MFE_VERTICALFLIP && mo->z + mo->height == bottomheight) + || (!(mo->eflags & MFE_VERTICALFLIP) && mo->z == topheight))) continue; } } else { // Water and DEATH FOG!!! heh - if (player->mo->z > topheight || (player->mo->z + player->mo->height) < bottomheight) + if (mo->z > topheight || (mo->z + mo->height) < bottomheight) continue; } @@ -3432,13 +3436,13 @@ sector_t *P_PlayerTouchingSectorSpecial(player_t *player, INT32 section, INT32 n return rover->master->frontsector; } - for (node = player->mo->touching_sectorlist; node; node = node->m_sectorlist_next) + for (node = mo->touching_sectorlist; node; node = node->m_sectorlist_next) { if (GETSECSPECIAL(node->m_sector->special, section) == number) { // This sector has the special we're looking for, but // are we allowed to touch it? - if (node->m_sector == player->mo->subsector->sector + if (node->m_sector == mo->subsector->sector || (node->m_sector->flags & SF_TRIGGERSPECIAL_TOUCH)) return node->m_sector; } @@ -3454,42 +3458,43 @@ sector_t *P_PlayerTouchingSectorSpecial(player_t *player, INT32 section, INT32 n if (!(rover->flags & FF_EXISTS)) continue; - topheight = P_GetSpecialTopZ(player->mo, sectors + rover->secnum, player->mo->subsector->sector); - bottomheight = P_GetSpecialBottomZ(player->mo, sectors + rover->secnum, player->mo->subsector->sector); + topheight = P_GetSpecialTopZ(mo, sectors + rover->secnum, mo->subsector->sector); + bottomheight = P_GetSpecialBottomZ(mo, sectors + rover->secnum, mo->subsector->sector); // Check the 3D floor's type... - if (rover->flags & FF_BLOCKPLAYER) + if (((rover->flags & FF_BLOCKPLAYER) && mo->player) + || ((rover->flags & FF_BLOCKOTHERS) && !mo->player)) { // Thing must be on top of the floor to be affected... if ((rover->master->frontsector->flags & SF_FLIPSPECIAL_FLOOR) && !(rover->master->frontsector->flags & SF_FLIPSPECIAL_CEILING)) { - if ((player->mo->eflags & MFE_VERTICALFLIP) || player->mo->z != topheight) + if ((mo->eflags & MFE_VERTICALFLIP) || mo->z != topheight) continue; } else if ((rover->master->frontsector->flags & SF_FLIPSPECIAL_CEILING) && !(rover->master->frontsector->flags & SF_FLIPSPECIAL_FLOOR)) { - if (!(player->mo->eflags & MFE_VERTICALFLIP) - || player->mo->z + player->mo->height != bottomheight) + if (!(mo->eflags & MFE_VERTICALFLIP) + || mo->z + mo->height != bottomheight) continue; } else if (rover->master->frontsector->flags & SF_FLIPSPECIAL_BOTH) { - if (!((player->mo->eflags & MFE_VERTICALFLIP && player->mo->z + player->mo->height == bottomheight) - || (!(player->mo->eflags & MFE_VERTICALFLIP) && player->mo->z == topheight))) + if (!((mo->eflags & MFE_VERTICALFLIP && mo->z + mo->height == bottomheight) + || (!(mo->eflags & MFE_VERTICALFLIP) && mo->z == topheight))) continue; } } else { // Water and DEATH FOG!!! heh - if (player->mo->z > topheight || (player->mo->z + player->mo->height) < bottomheight) + if (mo->z > topheight || (mo->z + mo->height) < bottomheight) continue; } // This FOF has the special we're looking for, but are we allowed to touch it? - if (node->m_sector == player->mo->subsector->sector + if (node->m_sector == mo->subsector->sector || (rover->master->frontsector->flags & SF_TRIGGERSPECIAL_TOUCH)) return rover->master->frontsector; } diff --git a/src/p_spec.h b/src/p_spec.h index b604ac951..8431fcc75 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -37,7 +37,7 @@ void P_SpawnSpecials(INT32 fromnetsave); // every tic void P_UpdateSpecials(void); -sector_t *P_PlayerTouchingSectorSpecial(player_t *player, INT32 section, INT32 number); +sector_t *P_MobjTouchingSectorSpecial(mobj_t *mo, INT32 section, INT32 number); void P_PlayerInSpecialSector(player_t *player); void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *roversector); From 4b04423bbe5256e7796026329bd53a1b24e9bb58 Mon Sep 17 00:00:00 2001 From: James Date: Tue, 22 Oct 2019 08:46:14 -0400 Subject: [PATCH 050/163] 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 051/163] - 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 052/163] 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 053/163] 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 054/163] 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 055/163] 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 056/163] 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 057/163] 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 058/163] 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 059/163] 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 060/163] 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 061/163] 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 062/163] 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 063/163] 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 064/163] 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 065/163] 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 066/163] 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 067/163] 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 522959e762711247098764f784f92078f35b5485 Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Sat, 26 Oct 2019 23:11:19 -0400 Subject: [PATCH 068/163] Disable charging drift sparks in bounce pad state --- 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 230662d2a..8db2b0d81 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6609,7 +6609,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) } } - K_KartDrift(player, onground); + K_KartDrift(player, P_IsObjectOnGround(player->mo)); // Not using onground, since we don't want this affected by spring pads // Quick Turning // You can't turn your kart when you're not moving. From 54516aeee97b18ee307024672fe91fc60d55fd30 Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Sun, 27 Oct 2019 16:44:41 -0400 Subject: [PATCH 069/163] 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 070/163] 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 071/163] 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 072/163] 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 073/163] 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 074/163] 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 075/163] 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 076/163] 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 077/163] 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 078/163] 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 079/163] 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 080/163] 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 081/163] 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 88cb623c2a7f719d21c09436eacd8d1f4c69262a Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Fri, 8 Nov 2019 23:25:46 -0500 Subject: [PATCH 082/163] 3D signpost --- src/dehacked.c | 27 +++-------- src/info.c | 58 ++++++++++++++---------- src/info.h | 27 +++-------- src/p_enemy.c | 33 -------------- src/p_mobj.c | 118 +++++++++++++++++++++++++++++++++++++++---------- src/p_spec.c | 97 +++++++++++++++++++++++++++++++--------- 6 files changed, 213 insertions(+), 147 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index 5f9249d89..7687803d3 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -1774,7 +1774,6 @@ static actionpointer_t actionpointers[] = {{A_GrenadeRing}, "A_GRENADERING"}, // SRB2kart {{A_SetSolidSteam}, "A_SETSOLIDSTEAM"}, {{A_UnsetSolidSteam}, "A_UNSETSOLIDSTEAM"}, - {{A_SignPlayer}, "A_SIGNPLAYER"}, {{A_OverlayThink}, "A_OVERLAYTHINK"}, {{A_JetChase}, "A_JETCHASE"}, {{A_JetbThink}, "A_JETBTHINK"}, @@ -4887,27 +4886,10 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_BUBBLES2", // Level End Sign - "S_SIGN1", - "S_SIGN2", - "S_SIGN3", - "S_SIGN4", - "S_SIGN5", - "S_SIGN6", - "S_SIGN7", - "S_SIGN8", - "S_SIGN9", - "S_SIGN10", - "S_SIGN11", - "S_SIGN12", - "S_SIGN13", - "S_SIGN14", - "S_SIGN15", - "S_SIGN16", - "S_SIGN17", - "S_SIGN18", - "S_SIGN19", - "S_SIGN20", - "S_SIGN_END", + "S_SIGN_POLE", + "S_SIGN_BACK", + "S_SIGN_SIDE", + "S_SIGN_FACE", // Steam Riser "S_STEAM1", @@ -7363,6 +7345,7 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s // Interactive Objects "MT_BUBBLES", // Bubble source "MT_SIGN", // Level end sign + "MT_SIGN_PIECE", "MT_SPIKEBALL", // Spike Ball "MT_SPECIALSPIKEBALL", "MT_SPINFIRE", diff --git a/src/info.c b/src/info.c index 4f448cc00..7476a9344 100644 --- a/src/info.c +++ b/src/info.c @@ -193,7 +193,7 @@ state_t states[NUMSTATES] = {SPR_NULL, 0, 18, {NULL}, 0, 4, S_NULL}, // S_PLAY_ICON3 // Level end sign (uses player sprite) - {SPR_PLAY, 18, 1, {NULL}, 0, 24, S_PLAY_SIGN}, // S_PLAY_SIGN S + {SPR_PLAY, 18|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_PLAY_SIGN}, // S_PLAY_SIGN // Blue Crawla {SPR_POSS, 0, 5, {A_Look}, 0, 0, S_POSS_STND}, // S_POSS_STND @@ -1063,27 +1063,10 @@ state_t states[NUMSTATES] = {SPR_BUBL, 1, 8, {A_BubbleCheck}, 0, 0, S_BUBBLES1}, // S_BUBBLES2 // Level End Sign - {SPR_SIGN, 0, 1, {NULL}, 0, 0, S_SIGN2}, // S_SIGN1 - {SPR_SIGN, 1, 1, {NULL}, 0, 0, S_SIGN3}, // S_SIGN2 - {SPR_SIGN, 2, 1, {NULL}, 0, 0, S_SIGN4}, // S_SIGN3 - {SPR_SIGN, 3, 1, {NULL}, 0, 0, S_SIGN5}, // S_SIGN4 - {SPR_SIGN, 0, 1, {NULL}, 0, 0, S_SIGN6}, // S_SIGN5 - {SPR_SIGN, 1, 1, {NULL}, 0, 0, S_SIGN7}, // S_SIGN6 - {SPR_SIGN, 2, 1, {NULL}, 0, 0, S_SIGN8}, // S_SIGN7 - {SPR_SIGN, 4, 1, {NULL}, 0, 0, S_SIGN9}, // S_SIGN8 - {SPR_SIGN, 0, 1, {NULL}, 0, 0, S_SIGN10}, // S_SIGN9 - {SPR_SIGN, 1, 1, {NULL}, 0, 0, S_SIGN11}, // S_SIGN10 - {SPR_SIGN, 2, 1, {NULL}, 0, 0, S_SIGN12}, // S_SIGN11 - {SPR_SIGN, 5, 1, {NULL}, 0, 0, S_SIGN13}, // S_SIGN12 - {SPR_SIGN, 0, 1, {NULL}, 0, 0, S_SIGN14}, // S_SIGN13 - {SPR_SIGN, 1, 1, {NULL}, 0, 0, S_SIGN15}, // S_SIGN14 - {SPR_SIGN, 2, 1, {NULL}, 0, 0, S_SIGN16}, // S_SIGN15 - {SPR_SIGN, 6, 1, {NULL}, 0, 0, S_SIGN17}, // S_SIGN16 - {SPR_SIGN, 0, 1, {NULL}, 0, 0, S_SIGN18}, // S_SIGN17 - {SPR_SIGN, 1, 1, {NULL}, 0, 0, S_SIGN19}, // S_SIGN18 - {SPR_SIGN, 2, 1, {NULL}, 0, 0, S_SIGN20}, // S_SIGN19 - {SPR_SIGN, 7, 1, {NULL}, 0, 0, S_SIGN1}, // S_SIGN20 - {SPR_SIGN, 8, -1, {A_SignPlayer}, 0, 0, S_NULL}, // S_SIGN_END + {SPR_SIGN, 0, -1, {NULL}, 0, 0, S_SIGN_POLE}, // S_SIGN_POLE + {SPR_SIGN, 1|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_SIGN_BACK}, // S_SIGN_BACK + {SPR_SIGN, 2|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_SIGN_SIDE}, // S_SIGN_SIDE + {SPR_SIGN, 3|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_SIGN_FACE}, // S_SIGN_FACE // Steam Riser {SPR_STEM, 0, 2, {A_SetSolidSteam}, 0, 0, S_STEAM2}, // S_STEAM1 @@ -6434,7 +6417,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 501, // doomednum S_INVISIBLE, // spawnstate 1000, // spawnhealth - S_PLAY_SIGN, // seestate + S_NULL, // seestate sfx_s3kb8, // seesound 8, // reactiontime sfx_s3k7e, // attacksound @@ -6448,7 +6431,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = sfx_None, // deathsound 8, // speed 8*FRACUNIT, // radius - 32*FRACUNIT, // height + 48*FRACUNIT, // height 0, // display offset 16, // mass 0, // damage @@ -6457,6 +6440,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_SIGN_PIECE + -1, // doomednum + S_INVISIBLE, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 8, // speed + 8*FRACUNIT, // radius + 48*FRACUNIT, // height + 0, // display offset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_NOTHINK|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, + { // MT_SPIKEBALL -1, // doomednum S_SPIKEBALL1, // spawnstate diff --git a/src/info.h b/src/info.h index 27488d916..f3450ff11 100644 --- a/src/info.h +++ b/src/info.h @@ -63,7 +63,6 @@ void A_ThrownRing(); // Sparkle trail for red ring void A_GrenadeRing(); // SRB2kart void A_SetSolidSteam(); void A_UnsetSolidSteam(); -void A_SignPlayer(); void A_OverlayThink(); void A_JetChase(); void A_JetbThink(); // Jetty-Syn Bomber Thinker @@ -1776,27 +1775,10 @@ typedef enum state S_BUBBLES2, // Level End Sign - S_SIGN1, - S_SIGN2, - S_SIGN3, - S_SIGN4, - S_SIGN5, - S_SIGN6, - S_SIGN7, - S_SIGN8, - S_SIGN9, - S_SIGN10, - S_SIGN11, - S_SIGN12, - S_SIGN13, - S_SIGN14, - S_SIGN15, - S_SIGN16, - S_SIGN17, - S_SIGN18, - S_SIGN19, - S_SIGN20, - S_SIGN_END, + S_SIGN_POLE, + S_SIGN_BACK, + S_SIGN_SIDE, + S_SIGN_FACE, // Steam Riser S_STEAM1, @@ -4284,6 +4266,7 @@ typedef enum mobj_type // Interactive Objects MT_BUBBLES, // Bubble source MT_SIGN, // Level end sign + MT_SIGN_PIECE, MT_SPIKEBALL, // Spike Ball MT_SPECIALSPIKEBALL, MT_SPINFIRE, diff --git a/src/p_enemy.c b/src/p_enemy.c index 76f6b3159..64a3ee12e 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -117,7 +117,6 @@ void A_ThrownRing(mobj_t *actor); void A_GrenadeRing(mobj_t *actor); void A_SetSolidSteam(mobj_t *actor); void A_UnsetSolidSteam(mobj_t *actor); -void A_SignPlayer(mobj_t *actor); void A_OverlayThink(mobj_t *actor); void A_JetChase(mobj_t *actor); void A_JetbThink(mobj_t *actor); @@ -4180,38 +4179,6 @@ void A_UnsetSolidSteam(mobj_t *actor) actor->flags |= MF_NOCLIP; } -// Function: A_SignPlayer -// -// Description: Changes the state of a level end sign to reflect the player that hit it. -// -// var1 = unused -// var2 = unused -// -void A_SignPlayer(mobj_t *actor) -{ - mobj_t *ov; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_SignPlayer", actor)) - return; -#endif - if (!actor->target) - return; - - if (!actor->target->player) - return; - - // Set the sign to be an appropriate background color for this player's skincolor. - actor->color = KartColor_Opposite[actor->target->player->skincolor*2]; - actor->frame += KartColor_Opposite[actor->target->player->skincolor*2+1]; - - // spawn an overlay of the player's face. - ov = P_SpawnMobj(actor->x, actor->y, actor->z, MT_OVERLAY); - P_SetTarget(&ov->target, actor); - ov->color = actor->target->player->skincolor; - ov->skin = &skins[actor->target->player->skin]; - P_SetMobjState(ov, actor->info->seestate); // S_PLAY_SIGN -} - // Function: A_OverlayThink // // Description: Moves the overlay to the position of its target. diff --git a/src/p_mobj.c b/src/p_mobj.c index b220ff4e6..ffdbebf4a 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -1329,9 +1329,6 @@ fixed_t P_GetMobjGravity(mobj_t *mo) case MT_JAWZ_DUD: gravityadd = (5*gravityadd)/2; break; - case MT_SIGN: - gravityadd /= 8; - break; case MT_KARMAFIREWORK: gravityadd /= 3; break; @@ -8957,31 +8954,104 @@ void P_MobjThinker(mobj_t *mobj) } break; case MT_SIGN: // Kart's unique sign behavior - if (mobj->movecount) + if (mobj->movecount != 0) { - if (mobj->z <= mobj->movefactor) + mobj_t *cur = mobj->hnext; + SINT8 newskin = -1; + UINT8 newcolor = SKINCOLOR_NONE; + angle_t endangle = FixedAngle(mobj->extravalue1 << FRACBITS); + + if (mobj->movecount == 1) { - P_SetMobjState(mobj, S_SIGN_END); - if (mobj->info->attacksound) - S_StartSound(mobj, mobj->info->attacksound); - mobj->flags |= MF_NOGRAVITY; // ? - mobj->flags &= ~MF_NOCLIPHEIGHT; - mobj->z = mobj->movefactor; - mobj->movecount = 0; - } - else - { - P_SpawnMobj(mobj->x + (P_RandomRange(-48,48)*mobj->scale), - mobj->y + (P_RandomRange(-48,48)*mobj->scale), - mobj->z + (24*mobj->scale) + (P_RandomRange(-8,8)*mobj->scale), - MT_SIGNSPARKLE); - mobj->flags &= ~MF_NOGRAVITY; - if (abs(mobj->z - mobj->movefactor) <= (512*mobj->scale) && !mobj->cvmem) + if (mobj->z + mobj->momz <= mobj->movefactor) { - if (mobj->info->seesound) - S_StartSound(mobj, mobj->info->seesound); - mobj->cvmem = 1; + if (mobj->info->attacksound) + S_StartSound(mobj, mobj->info->attacksound); + + mobj->z = mobj->movefactor; + mobj->momz = 0; + mobj->movecount = 2; + + newskin = ((skin_t*)mobj->target->skin)-skins; + newcolor = mobj->target->player->skincolor; } + else + { + fixed_t g = (6*mobj->scale); + UINT16 ticstilimpact = abs(mobj->z - mobj->movefactor) / g; + + P_SpawnMobj( + mobj->x + FixedMul(48*mobj->scale, FINECOSINE(mobj->angle >> ANGLETOFINESHIFT)), + mobj->y + FixedMul(48*mobj->scale, FINESINE(mobj->angle >> ANGLETOFINESHIFT)), + mobj->z + ((24 + ((leveltime % 4) * 8)) * mobj->scale), + MT_SIGNSPARKLE + ); + + if (ticstilimpact == (3*TICRATE/2)) + { + if (mobj->info->seesound) + S_StartSound(mobj, mobj->info->seesound); + } + + mobj->angle += ANGLE_45; + mobj->momz = -g; + + if (mobj->angle == endangle + ANGLE_180) + { + if (ticstilimpact <= 8) + { + newskin = ((skin_t*)mobj->target->skin)-skins; + newcolor = mobj->target->player->skincolor; + } + else + { + newskin = leveltime % numskins; + newcolor = skins[newskin].prefcolor; + } + } + } + } + else if (mobj->movecount == 2) + { + if (mobj->angle != endangle) + mobj->angle += ANGLE_11hh; + } + + while (cur && !P_MobjWasRemoved(cur)) + { + fixed_t amt = cur->extravalue1 * mobj->scale; + angle_t dir = mobj->angle + (cur->extravalue2 * ANGLE_90); + fixed_t z = mobj->z + (23*mobj->scale); + + if (cur->state == &states[S_SIGN_FACE]) + { + if (newcolor != SKINCOLOR_NONE) + { + cur->color = KartColor_Opposite[newcolor*2]; + cur->frame = states[S_SIGN_FACE].frame + KartColor_Opposite[newcolor*2+1]; + } + } + else if (cur->state == &states[S_PLAY_SIGN]) + { + z += (5*mobj->scale); + amt += 1; + + if (newskin != -1) + cur->skin = &skins[newskin]; + + if (newcolor != SKINCOLOR_NONE) + cur->color = newcolor; + } + + P_TeleportMove( + cur, + mobj->x + FixedMul(amt, FINECOSINE(dir >> ANGLETOFINESHIFT)), + mobj->y + FixedMul(amt, FINESINE(dir >> ANGLETOFINESHIFT)), + z + ); + cur->angle = dir + ANGLE_90; + + cur = cur->hnext; } } break; diff --git a/src/p_spec.c b/src/p_spec.c index 93e88e2ce..0eb618c45 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -3232,6 +3232,76 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) } } +static void P_SetupSignObject(mobj_t *sign, mobj_t *pmo) +{ + mobj_t *cur = sign, *prev = NULL; + + // Setup the sign itself + P_SetTarget(&sign->target, pmo); + P_SetMobjState(sign, S_SIGN_POLE); + + sign->movefactor = sign->z; + sign->z += (768*sign->scale) * P_MobjFlip(sign); + sign->movecount = 1; + sign->extravalue1 = AngleFixed(sign->angle) >> FRACBITS; + + // Setup the overlay pieces + // Front + cur->hnext = P_SpawnMobj(sign->x, sign->y, sign->z, MT_SIGN_PIECE); + P_SetTarget(&cur->hnext->target, sign); + P_SetMobjState(cur->hnext, S_SIGN_FACE); + cur->hnext->extravalue1 = 6; + cur->hnext->extravalue2 = 2; + + prev = cur; + cur = cur->hnext; + cur->hprev = prev; + + // Player icon + cur->hnext = P_SpawnMobj(sign->x, sign->y, sign->z, MT_SIGN_PIECE); + P_SetTarget(&cur->hnext->target, sign); + cur->hnext->skin = pmo->skin; + P_SetMobjState(cur->hnext, S_PLAY_SIGN); + cur->hnext->extravalue1 = 7; + cur->hnext->extravalue2 = 2; + + prev = cur; + cur = cur->hnext; + cur->hprev = prev; + + // Back + cur->hnext = P_SpawnMobj(sign->x, sign->y, sign->z, MT_SIGN_PIECE); + P_SetTarget(&cur->hnext->target, sign); + P_SetMobjState(cur->hnext, S_SIGN_BACK); + cur->hnext->extravalue1 = 6; + cur->hnext->extravalue2 = 0; + + prev = cur; + cur = cur->hnext; + cur->hprev = prev; + + // Sides + cur->hnext = P_SpawnMobj(sign->x, sign->y, sign->z, MT_SIGN_PIECE); + P_SetTarget(&cur->hnext->target, sign); + P_SetMobjState(cur->hnext, S_SIGN_SIDE); + cur->hnext->extravalue1 = 30; + cur->hnext->extravalue2 = 1; + + prev = cur; + cur = cur->hnext; + cur->hprev = prev; + + cur->hnext = P_SpawnMobj(sign->x, sign->y, sign->z, MT_SIGN_PIECE); + P_SetTarget(&cur->hnext->target, sign); + P_SetMobjState(cur->hnext, S_SIGN_SIDE); + cur->hnext->extravalue1 = 30; + cur->hnext->extravalue2 = 3; + + prev = cur; + cur = cur->hnext; + cur->hprev = prev; +} + // // P_SetupSignExit // @@ -3257,13 +3327,7 @@ void P_SetupSignExit(player_t *player) if (thing->state != &states[thing->info->spawnstate]) continue; - P_SetTarget(&thing->target, player->mo); - P_SetMobjState(thing, S_SIGN1); - - // SRB2Kart: Set sign spinning variables - thing->movefactor = thing->z; - thing->z += (768*thing->scale) * P_MobjFlip(thing); - thing->movecount = 1; + P_SetupSignObject(thing, player->mo); ++numfound; } @@ -3285,14 +3349,7 @@ void P_SetupSignExit(player_t *player) if (thing->state != &states[thing->info->spawnstate]) continue; - P_SetTarget(&thing->target, player->mo); - P_SetMobjState(thing, S_SIGN1); - - // SRB2Kart: Set sign spinning variables - thing->movefactor = thing->z; - thing->z += (768*thing->scale) * P_MobjFlip(thing); - thing->movecount = 1; - + P_SetupSignObject(thing, player->mo); ++numfound; } @@ -3300,14 +3357,10 @@ void P_SetupSignExit(player_t *player) return; // SRB2Kart: FINALLY, add in an alternative if no place is found - if (player->mo) + if (player->mo && !P_MobjWasRemoved(player->mo)) { - mobj_t *sign = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z + (768*mapobjectscale), MT_SIGN); - - P_SetTarget(&sign->target, player->mo); - P_SetMobjState(sign, S_SIGN1); - sign->movefactor = player->mo->floorz; - sign->movecount = 1; + thing = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->floorz, MT_SIGN); + P_SetupSignObject(thing, player->mo); } } From bba8861d2f0c37defca40d2f5289869ddd23c15a Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Fri, 8 Nov 2019 23:33:59 -0500 Subject: [PATCH 083/163] Set angle for ties --- src/p_spec.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/p_spec.c b/src/p_spec.c index 0eb618c45..9f8bc6f4b 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -3360,6 +3360,7 @@ void P_SetupSignExit(player_t *player) if (player->mo && !P_MobjWasRemoved(player->mo)) { thing = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->floorz, MT_SIGN); + thing->angle = player->mo->angle; P_SetupSignObject(thing, player->mo); } } From 80bb59031c4bbf360fd45b875f5c85728feae466 Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Thu, 14 Nov 2019 04:26:41 -0500 Subject: [PATCH 084/163] Blizzard weather option + much more sane workflow for adding new precipitation options "precipprops" lets you create a new precipitation type by picking a mobj type to use, how many random states it has, and flags for effects such as thunder/lightning. Seesound on the mobj type sets an ambient sound (like rain drops) and mass sets the sound's frequency in tics. This could open up the possibility for SOC/Lua Weather options later. --- src/dehacked.c | 12 ++- src/doomstat.h | 35 +++++-- src/g_game.c | 15 ++- src/hardware/hw_main.c | 5 +- src/info.c | 74 ++++++++++---- src/info.h | 7 ++ src/p_floor.c | 1 + src/p_mobj.c | 224 ++++++++++++++++++++--------------------- src/p_mobj.h | 31 +++--- src/p_spec.c | 221 +++++++++------------------------------- src/p_spec.h | 2 +- src/p_tick.c | 10 -- src/r_things.c | 6 +- 13 files changed, 283 insertions(+), 360 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index 5f9249d89..af1ee934e 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -5690,6 +5690,11 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_SNOW2", "S_SNOW3", + // Blizzard Snowball + "S_BLIZZARDSNOW1", + "S_BLIZZARDSNOW2", + "S_BLIZZARDSNOW3", + // Water Splish "S_SPLISH1", "S_SPLISH2", @@ -7589,6 +7594,7 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s // Environmental Effects "MT_RAIN", // Rain "MT_SNOWFLAKE", // Snowflake + "MT_BLIZZARDSNOW", // Blizzard Snowball "MT_SPLISH", // Water splish! "MT_SMOKE", "MT_SMALLBUBBLE", // small bubble @@ -8700,10 +8706,10 @@ struct { // Precipitation {"PRECIP_NONE",PRECIP_NONE}, - {"PRECIP_STORM",PRECIP_STORM}, - {"PRECIP_SNOW",PRECIP_SNOW}, {"PRECIP_RAIN",PRECIP_RAIN}, - {"PRECIP_BLANK",PRECIP_BLANK}, + {"PRECIP_SNOW",PRECIP_SNOW}, + {"PRECIP_BLIZZARD",PRECIP_BLIZZARD}, + {"PRECIP_STORM",PRECIP_STORM}, {"PRECIP_STORM_NORAIN",PRECIP_STORM_NORAIN}, {"PRECIP_STORM_NOSTRIKES",PRECIP_STORM_NOSTRIKES}, diff --git a/src/doomstat.h b/src/doomstat.h index 59e2bd5c4..cdc09cf86 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -41,18 +41,37 @@ extern UINT32 mapmusposition; extern INT16 maptol; extern UINT8 globalweather; -extern INT32 curWeather; +extern UINT8 curWeather; extern INT32 cursaveslot; extern INT16 lastmapsaved; extern boolean gamecomplete; -#define PRECIP_NONE 0 -#define PRECIP_STORM 1 -#define PRECIP_SNOW 2 -#define PRECIP_RAIN 3 -#define PRECIP_BLANK 4 -#define PRECIP_STORM_NORAIN 5 -#define PRECIP_STORM_NOSTRIKES 6 +typedef enum +{ + PRECIP_NONE = 0, + PRECIP_RAIN, + PRECIP_SNOW, + PRECIP_BLIZZARD, + PRECIP_STORM, + PRECIP_STORM_NORAIN, + PRECIP_STORM_NOSTRIKES, + MAXPRECIP +} preciptype_t; + +typedef enum +{ + PRECIPFX_THUNDER = 1, + PRECIPFX_LIGHTNING = 1<<1 +} precipeffect_t; + +typedef struct +{ + mobjtype_t type; + precipeffect_t effects; + UINT8 randomstates; +} precipprops_t; + +extern precipprops_t precipprops[MAXPRECIP]; // Set if homebrew PWAD stuff has been added. extern boolean modifiedgame; diff --git a/src/g_game.c b/src/g_game.c index d4d48f7c2..e15968b3e 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -83,8 +83,21 @@ UINT32 mapmusposition; // Position to jump to INT16 gamemap = 1; INT16 maptol; + UINT8 globalweather = 0; -INT32 curWeather = PRECIP_NONE; +UINT8 curWeather = PRECIP_NONE; + +precipprops_t precipprops[MAXPRECIP] = +{ + {MT_NULL, 0, 0}, // PRECIP_NONE + {MT_RAIN, 0, 0}, // PRECIP_RAIN + {MT_SNOWFLAKE, 0, 2}, // PRECIP_SNOW + {MT_BLIZZARDSNOW, 0, 2}, // PRECIP_BLIZZARD + {MT_RAIN, PRECIPFX_THUNDER|PRECIPFX_LIGHTNING, 0}, // PRECIP_STORM + {MT_NULL, PRECIPFX_THUNDER|PRECIPFX_LIGHTNING, 0}, // PRECIP_STORM_NORAIN + {MT_RAIN, PRECIPFX_THUNDER, 0} // PRECIP_STORM_NOSTRIKES +}; + INT32 cursaveslot = -1; // Auto-save 1p savegame slot INT16 lastmapsaved = 0; // Last map we auto-saved at boolean gamecomplete = false; diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 659af386d..31461283c 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -5882,10 +5882,7 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing) // okay, we can't return now... this is a hack, but weather isn't networked, so it should be ok if (!(thing->precipflags & PCF_THUNK)) { - if (thing->precipflags & PCF_RAIN) - P_RainThinker(thing); - else - P_SnowThinker(thing); + P_PrecipThinker(thing); thing->precipflags |= PCF_THUNK; } diff --git a/src/info.c b/src/info.c index 4f448cc00..4542e6c3a 100644 --- a/src/info.c +++ b/src/info.c @@ -44,17 +44,17 @@ char sprnames[NUMSPRITES + 1][5] = "BSZ7","BSZ8","STLG","DBAL","RCRY","ARMA","ARMF","ARMB","WIND","MAGN", "ELEM","FORC","PITY","IVSP","SSPK","GOAL","BIRD","BUNY","MOUS","CHIC", "COWZ","RBRD","SPVY","SPVR","SPVB","SPVG","SPDY","SPDR","SPDB","SPDG", - "SPHY","SPHR","SPHB","SPHG","RAIN","SNO1","SPLH","SPLA","SMOK","BUBP", - "BUBO","BUBN","BUBM","POPP","TFOG","SEED","PRTL","SCOR","DRWN","TTAG", - "GFLG","RRNG","RNGB","RNGR","RNGI","RNGA","RNGE","RNGS","RNGG","PIKB", - "PIKR","PIKA","PIKE","PIKS","PIKG","TAUT","TGRE","TSCR","COIN","CPRK", - "GOOM","BGOM","FFWR","FBLL","SHLL","PUMA","HAMM","KOOP","BFLM","MAXE", - "MUS1","MUS2","TOAD","NDRN","SUPE","SUPZ","NDRL","NSPK","NBMP","HOOP", - "NSCR","NPRU","CAPS","SUPT","SPRK","BOM1","BOM2","BOM3","BOM4","ROIA", - "ROIB","ROIC","ROID","ROIE","ROIF","ROIG","ROIH","ROII","ROIJ","ROIK", - "ROIL","ROIM","ROIN","ROIO","ROIP","BBAL","GWLG","GWLR","SRBA","SRBB", - "SRBC","SRBD","SRBE","SRBF","SRBG","SRBH","SRBI","SRBJ","SRBK","SRBL", - "SRBM","SRBN","SRBO", + "SPHY","SPHR","SPHB","SPHG","RAIN","SNO1","SNO2","SPLH","SPLA","SMOK", + "BUBP","BUBO","BUBN","BUBM","POPP","TFOG","SEED","PRTL","SCOR","DRWN", + "TTAG","GFLG","RRNG","RNGB","RNGR","RNGI","RNGA","RNGE","RNGS","RNGG", + "PIKB","PIKR","PIKA","PIKE","PIKS","PIKG","TAUT","TGRE","TSCR","COIN", + "CPRK","GOOM","BGOM","FFWR","FBLL","SHLL","PUMA","HAMM","KOOP","BFLM", + "MAXE","MUS1","MUS2","TOAD","NDRN","SUPE","SUPZ","NDRL","NSPK","NBMP", + "HOOP","NSCR","NPRU","CAPS","SUPT","SPRK","BOM1","BOM2","BOM3","BOM4", + "ROIA","ROIB","ROIC","ROID","ROIE","ROIF","ROIG","ROIH","ROII","ROIJ", + "ROIK","ROIL","ROIM","ROIN","ROIO","ROIP","BBAL","GWLG","GWLR","SRBA", + "SRBB","SRBC","SRBD","SRBE","SRBF","SRBG","SRBH","SRBI","SRBJ","SRBK", + "SRBL","SRBM","SRBN","SRBO", //SRB2kart Sprites "RNDM","RPOP","SGNS","FAST","DSHR","BOST","BOSM","KFRE","KINV","KINF", "WIPD","DRIF","BDRF","DUST","RSHE","FITM","BANA","ORBN","JAWZ","SSMN", @@ -1869,13 +1869,18 @@ state_t states[NUMSTATES] = // Rain {SPR_RAIN, FF_TRANS50, -1, {NULL}, 0, 0, S_NULL}, // S_RAIN1 - {SPR_RAIN, FF_TRANS50, 1, {NULL}, 0, 0, S_RAIN1}, // S_RAINRETURN + {SPR_NULL, 0, -1, {NULL}, 0, 0, S_NULL}, // S_RAINRETURN // Snowflake {SPR_SNO1, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SNOW1 {SPR_SNO1, 1, -1, {NULL}, 0, 0, S_NULL}, // S_SNOW2 {SPR_SNO1, 2, -1, {NULL}, 0, 0, S_NULL}, // S_SNOW3 + // Blizzard Snowball + {SPR_SNO2, 0, -1, {NULL}, 0, 0, S_NULL}, // S_BLIZZARDSNOW1 + {SPR_SNO2, 1, -1, {NULL}, 0, 0, S_NULL}, // S_BLIZZARDSNOW2 + {SPR_SNO2, 2, -1, {NULL}, 0, 0, S_NULL}, // S_BLIZZARDSNOW3 + // Water Splish {SPR_SPLH, FF_TRANS50 , 2, {NULL}, 0, 0, S_SPLISH2}, // S_SPLISH1 {SPR_SPLH, FF_TRANS50|1, 2, {NULL}, 0, 0, S_SPLISH3}, // S_SPLISH2 @@ -1888,9 +1893,9 @@ state_t states[NUMSTATES] = {SPR_SPLH, FF_TRANS50|8, 2, {NULL}, 0, 0, S_NULL}, // S_SPLISH9 // Water Splash - {SPR_SPLA, FF_TRANS50 , 3, {NULL}, 0, 0, S_SPLASH2}, // S_SPLASH1 - {SPR_SPLA, FF_TRANS70|1, 3, {NULL}, 0, 0, S_SPLASH3}, // S_SPLASH2 - {SPR_SPLA, FF_TRANS90|2, 3, {NULL}, 0, 0, S_RAINRETURN}, // S_SPLASH3 + {SPR_SPLA, FF_TRANS50 , 3, {NULL}, 0, 0, S_SPLASH2}, // S_SPLASH1 + {SPR_SPLA, FF_TRANS70|1, 3, {NULL}, 0, 0, S_SPLASH3}, // S_SPLASH2 + {SPR_SPLA, FF_TRANS90|2, 3, {NULL}, 0, 0, S_NULL}, // S_SPLASH3 // Smoke {SPR_SMOK, FF_TRANS50 , 4, {NULL}, 0, 0, S_SMOKE2}, // S_SMOKE1 @@ -11438,22 +11443,22 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_RAIN1, // spawnstate 1000, // spawnhealth S_NULL, // seestate - sfx_None, // seesound - 8, // reactiontime + sfx_rainin, // seesound + 0, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate - S_NULL, // deathstate + S_SPLASH1, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound -72*FRACUNIT, // speed -- -24*FRACUNIT originally, srb2kart x3 (nya) 1*FRACUNIT, // radius 8*FRACUNIT, // height 0, // display offset - 4, // mass + 80, // mass 0, // damage sfx_None, // activesound MF_NOBLOCKMAP, // flags @@ -11466,7 +11471,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound - 8, // reactiontime + 0, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance @@ -11480,7 +11485,34 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 4*FRACUNIT, // radius 4*FRACUNIT, // height 0, // display offset - 4, // mass + 0, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP, // flags + S_NULL // raisestate + }, + + { // MT_BLIZZARDSNOW + -1, // doomednum + S_BLIZZARDSNOW1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 0, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + -24*FRACUNIT, // speed + 4*FRACUNIT, // radius + 4*FRACUNIT, // height + 0, // display offset + 0, // mass 0, // damage sfx_None, // activesound MF_NOBLOCKMAP, // flags diff --git a/src/info.h b/src/info.h index 27488d916..cb520252c 100644 --- a/src/info.h +++ b/src/info.h @@ -475,6 +475,7 @@ typedef enum sprite // Environmental Effects SPR_RAIN, // Rain SPR_SNO1, // Snowflake + SPR_SNO2, // Blizzard Snowball SPR_SPLH, // Water Splish SPR_SPLA, // Water Splash SPR_SMOK, @@ -2579,6 +2580,11 @@ typedef enum state S_SNOW2, S_SNOW3, + // Blizzard Snowball + S_BLIZZARDSNOW1, + S_BLIZZARDSNOW2, + S_BLIZZARDSNOW3, + // Water Splish S_SPLISH1, S_SPLISH2, @@ -4510,6 +4516,7 @@ typedef enum mobj_type // Environmental Effects MT_RAIN, // Rain MT_SNOWFLAKE, // Snowflake + MT_BLIZZARDSNOW, // Blizzard Snowball MT_SPLISH, // Water splish! MT_SMOKE, MT_SMALLBUBBLE, // small bubble diff --git a/src/p_floor.c b/src/p_floor.c index ccbfd6eae..666656958 100644 --- a/src/p_floor.c +++ b/src/p_floor.c @@ -1769,6 +1769,7 @@ static mobj_t *SearchMarioNode(msecnode_t *node) case MT_SUPERSPARK: case MT_RAIN: case MT_SNOWFLAKE: + case MT_BLIZZARDSNOW: case MT_SPLISH: case MT_SMOKE: case MT_SMALLBUBBLE: diff --git a/src/p_mobj.c b/src/p_mobj.c index b220ff4e6..87fb7a186 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -3928,51 +3928,60 @@ void P_NullPrecipThinker(precipmobj_t *mobj) mobj->precipflags &= ~PCF_THUNK; } -void P_SnowThinker(precipmobj_t *mobj) +void P_PrecipThinker(precipmobj_t *mobj) { P_CycleStateAnimation((mobj_t *)mobj); - // adjust height - if ((mobj->z += mobj->momz) <= mobj->floorz) - mobj->z = mobj->ceilingz; -} - -void P_RainThinker(precipmobj_t *mobj) -{ - P_CycleStateAnimation((mobj_t *)mobj); - - if (mobj->state != &states[S_RAIN1]) + if (mobj->state == &states[S_RAINRETURN]) { - // cycle through states, - // calling action functions at transitions - if (mobj->tics <= 0) - return; - - if (--mobj->tics) - return; - - if (!P_SetPrecipMobjState(mobj, mobj->state->nextstate)) - return; - - if (mobj->state != &states[S_RAINRETURN]) - return; - + // Reset to ceiling! + P_SetPrecipMobjState(mobj, mobj->info->spawnstate); mobj->z = mobj->ceilingz; - P_SetPrecipMobjState(mobj, S_RAIN1); - - return; + mobj->momz = mobj->info->speed; + mobj->precipflags &= ~PCF_SPLASH; } + if (mobj->tics != -1) + { + if (mobj->tics) + { + mobj->tics--; + } + + if (mobj->tics == 0) + { + if ((mobj->precipflags & PCF_SPLASH) && (mobj->state->nextstate == S_NULL)) + { + // HACK: sprite changes are 1 tic late, so you would see splashes on the ceiling if not for this state. + // We need to use the settings from the previous state, since some of those are NOT 1 tic late. + INT32 frame = (mobj->frame & ~FF_FRAMEMASK); + P_SetPrecipMobjState(mobj, S_RAINRETURN); + mobj->frame = frame; + return; + } + else + { + if (!P_SetPrecipMobjState(mobj, mobj->state->nextstate)) + return; + } + } + } + + if (mobj->precipflags & PCF_SPLASH) + return; + // adjust height if ((mobj->z += mobj->momz) <= mobj->floorz) { - // no splashes on sky or bottomless pits - if (mobj->precipflags & PCF_PIT) + if ((mobj->info->deathstate == S_NULL) || (mobj->precipflags & PCF_PIT)) // no splashes on sky or bottomless pits + { mobj->z = mobj->ceilingz; + } else { + P_SetPrecipMobjState(mobj, mobj->info->deathstate); mobj->z = mobj->floorz; - P_SetPrecipMobjState(mobj, S_SPLASH1); + mobj->precipflags |= PCF_SPLASH; } } } @@ -6354,9 +6363,8 @@ fixed_t P_CalculateShadowFloor(mobj_t *mobj, fixed_t x, fixed_t y, fixed_t z, fi } } -#if 0 mobj->standingslope = slope; -#endif + #ifdef HWRENDER mobj->modeltilt = slope; #endif @@ -10754,17 +10762,21 @@ mobj_t *P_SpawnShadowMobj(mobj_t * caster) static precipmobj_t *P_SpawnPrecipMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) { + const mobjinfo_t *info = &mobjinfo[type]; state_t *st; precipmobj_t *mobj = Z_Calloc(sizeof (*mobj), PU_LEVEL, NULL); fixed_t starting_floorz; + mobj->type = type; + mobj->info = info; + mobj->x = x; mobj->y = y; - mobj->flags = mobjinfo[type].flags; + mobj->flags = info->flags; // do not set the state with P_SetMobjState, // because action routines can not be called yet - st = &states[mobjinfo[type].spawnstate]; + st = &states[info->spawnstate]; mobj->state = st; mobj->tics = st->tics; @@ -10787,7 +10799,7 @@ static precipmobj_t *P_SpawnPrecipMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype mobj->subsector->sector->ceilingheight; mobj->z = z; - mobj->momz = mobjinfo[type].speed; + mobj->momz = info->speed; mobj->thinker.function.acp1 = (actionf_p1)P_NullPrecipThinker; P_AddThinker(&mobj->thinker); @@ -10804,21 +10816,6 @@ static precipmobj_t *P_SpawnPrecipMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype return mobj; } -static inline precipmobj_t *P_SpawnRainMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) -{ - precipmobj_t *mo = P_SpawnPrecipMobj(x,y,z,type); - mo->precipflags |= PCF_RAIN; - //mo->thinker.function.acp1 = (actionf_p1)P_RainThinker; - return mo; -} - -static inline precipmobj_t *P_SpawnSnowMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) -{ - precipmobj_t *mo = P_SpawnPrecipMobj(x,y,z,type); - //mo->thinker.function.acp1 = (actionf_p1)P_SnowThinker; - return mo; -} - // // P_RemoveMobj // @@ -11007,8 +11004,8 @@ consvar_t cv_suddendeath = {"suddendeath", "Off", CV_NETVAR|CV_CHEAT|CV_NOSHOWHE void P_SpawnPrecipitation(void) { - INT32 i, mrand; - fixed_t basex, basey, x, y, height; + INT32 i, j, k; + fixed_t basex, basey, x, y, z, height; subsector_t *precipsector = NULL; precipmobj_t *rainmo = NULL; @@ -11021,8 +11018,9 @@ void P_SpawnPrecipitation(void) basex = bmaporgx + (i % bmapwidth) * MAPBLOCKSIZE; basey = bmaporgy + (i / bmapwidth) * MAPBLOCKSIZE; - //for (j = 0; j < cv_precipdensity.value; ++j) -- density is 1 for kart always { + UINT16 numparticles = 0; + x = basex + ((M_RandomKey(MAPBLOCKUNITS<<3)<>3); y = basey + ((M_RandomKey(MAPBLOCKUNITS<<3)<>3); @@ -11037,40 +11035,43 @@ void P_SpawnPrecipitation(void) if (precipsector->sector->ceilingpic != skyflatnum) continue; + height = abs(precipsector->sector->ceilingheight - precipsector->sector->floorheight); + // Exists, but is too small for reasonable precipitation. - if (!(precipsector->sector->floorheight <= precipsector->sector->ceilingheight - (32<sector->ceilingheight; + // Hack around a quirk of this entire system, where taller sectors look like they get less precipitation. + numparticles = 1 + (height / (MAPBLOCKUNITS<<4<sector->ceilingheight; + + for (j = 0; j < numparticles; j++) { - rainmo = P_SpawnSnowMobj(x, y, height, MT_SNOWFLAKE); - mrand = M_RandomByte(); - if (mrand < 64) - P_SetPrecipMobjState(rainmo, S_SNOW3); - else if (mrand < 144) - P_SetPrecipMobjState(rainmo, S_SNOW2); + rainmo = P_SpawnPrecipMobj(x, y, z, precipprops[curWeather].type); + + if (precipprops[curWeather].randomstates > 0) + { + UINT8 mrand = M_RandomByte(); + UINT8 threshold = UINT8_MAX / (precipprops[curWeather].randomstates + 1); + statenum_t st = mobjinfo[precipprops[curWeather].type].spawnstate; + + for (k = 0; k < precipprops[curWeather].randomstates; k++) + { + if (mrand < (threshold * (k+1))) + { + P_SetPrecipMobjState(rainmo, st+k+1); + break; + } + } + } + + // Randomly assign a height, now that floorz is set. + rainmo->z = M_RandomRange(rainmo->floorz>>FRACBITS, rainmo->ceilingz>>FRACBITS)<z = M_RandomRange(rainmo->floorz>>FRACBITS, rainmo->ceilingz>>FRACBITS)< 255) volume = 255; - if (sounds_rain && (!leveltime || leveltime % 80 == 1)) - S_StartSoundAtVolume(players[displayplayers[0]].mo, sfx_rainin, volume); + if (rainsfx != sfx_None && (!leveltime || leveltime % rainfreq == 1)) + S_StartSoundAtVolume(players[displayplayers[0]].mo, rainsfx, volume); if (!sounds_thunder) return; diff --git a/src/p_mobj.h b/src/p_mobj.h index aec2ed951..da3ba88e6 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -253,25 +253,23 @@ typedef enum // PRECIPITATION flags ?! ?! ?! // typedef enum { - // Don't draw. - PCF_INVISIBLE = 1, - // Above pit. - PCF_PIT = 2, - // Above FOF. - PCF_FOF = 4, - // Above MOVING FOF (this means we need to keep floorz up to date...) - PCF_MOVINGFOF = 8, - // Is rain. - PCF_RAIN = 16, - // Ran the thinker this tic. - PCF_THUNK = 32, + PCF_INVISIBLE = 1, // Don't draw. + PCF_PIT = 1<<1, // Above pit. + PCF_FOF = 1<<2, // Above FOF. + PCF_MOVINGFOF = 1<<3, // Above MOVING FOF (this means we need to keep floorz up to date...) + PCF_SPLASH = 1<<4, // Splashed on the ground, return to the ceiling after the animation's over + PCF_THUNK = 1<<5, // Ran the thinker this tic. } precipflag_t; + // Map Object definition. typedef struct mobj_s { // List: thinker links. thinker_t thinker; + mobjtype_t type; + const mobjinfo_t *info; // &mobjinfo[mobj->type] + // Info for drawing: position. fixed_t x, y, z; @@ -321,9 +319,6 @@ typedef struct mobj_s struct mobj_s *hnext; struct mobj_s *hprev; - mobjtype_t type; - const mobjinfo_t *info; // &mobjinfo[mobj->type] - INT32 health; // for player this is rings + 1 // Movement direction, movement generation (zig-zagging). @@ -392,6 +387,9 @@ typedef struct precipmobj_s // List: thinker links. thinker_t thinker; + mobjtype_t type; + const mobjinfo_t *info; // &mobjinfo[mobj->type] + // Info for drawing: position. fixed_t x, y, z; @@ -459,8 +457,7 @@ void P_SpawnParaloop(fixed_t x, fixed_t y, fixed_t z, fixed_t radius, INT32 numb boolean P_BossTargetPlayer(mobj_t *actor, boolean closest); boolean P_SupermanLook4Players(mobj_t *actor); void P_DestroyRobots(void); -void P_SnowThinker(precipmobj_t *mobj); -void P_RainThinker(precipmobj_t *mobj); +void P_PrecipThinker(precipmobj_t *mobj); void P_NullPrecipThinker(precipmobj_t *mobj); void P_RemovePrecipMobj(precipmobj_t *mobj); void P_SetScale(mobj_t *mobj, fixed_t newscale); diff --git a/src/p_spec.c b/src/p_spec.c index 93e88e2ce..d92fb2f3b 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -2003,57 +2003,28 @@ void P_LinedefExecute(INT16 tag, mobj_t *actor, sector_t *caller) // // Switches the weather! // -void P_SwitchWeather(INT32 weathernum) +void P_SwitchWeather(UINT8 newWeather) { boolean purge = false; - INT32 swap = 0; + mobjtype_t swap = MT_NULL; - switch (weathernum) + if (precipprops[newWeather].type == MT_NULL) { - case PRECIP_NONE: // None - if (curWeather == PRECIP_NONE) - return; // Nothing to do. - purge = true; - break; - case PRECIP_STORM: // Storm - case PRECIP_STORM_NOSTRIKES: // Storm w/ no lightning - case PRECIP_RAIN: // Rain - if (curWeather == PRECIP_SNOW || curWeather == PRECIP_BLANK || curWeather == PRECIP_STORM_NORAIN) - swap = PRECIP_RAIN; - break; - case PRECIP_SNOW: // Snow - if (curWeather == PRECIP_SNOW) - return; // Nothing to do. - if (curWeather == PRECIP_RAIN || curWeather == PRECIP_STORM || curWeather == PRECIP_STORM_NOSTRIKES || curWeather == PRECIP_BLANK || curWeather == PRECIP_STORM_NORAIN) - swap = PRECIP_SNOW; // Need to delete the other precips. - break; - case PRECIP_STORM_NORAIN: // Storm w/o rain - if (curWeather == PRECIP_SNOW - || curWeather == PRECIP_STORM - || curWeather == PRECIP_STORM_NOSTRIKES - || curWeather == PRECIP_RAIN - || curWeather == PRECIP_BLANK) - swap = PRECIP_STORM_NORAIN; - else if (curWeather == PRECIP_STORM_NORAIN) - return; - break; - case PRECIP_BLANK: - if (curWeather == PRECIP_SNOW - || curWeather == PRECIP_STORM - || curWeather == PRECIP_STORM_NOSTRIKES - || curWeather == PRECIP_RAIN) - swap = PRECIP_BLANK; - else if (curWeather == PRECIP_STORM_NORAIN) - swap = PRECIP_BLANK; - else if (curWeather == PRECIP_BLANK) - return; - break; - default: - CONS_Debug(DBG_GAMELOGIC, "P_SwitchWeather: Unknown weather type %d.\n", weathernum); - break; + // New type is null, we want to purge the weather. + if (precipprops[curWeather].type == MT_NULL) + return; // Nothing to do. + purge = true; + } + else + { + if (precipprops[curWeather].type != MT_NULL) + { + // There are already existing weather particles to reuse. + swap = precipprops[newWeather].type; + } } - if (purge) + if (purge == true) { thinker_t *think; precipmobj_t *precipmobj; @@ -2068,136 +2039,53 @@ void P_SwitchWeather(INT32 weathernum) P_RemovePrecipMobj(precipmobj); } } - else if (swap && !((swap == PRECIP_BLANK && curWeather == PRECIP_STORM_NORAIN) || (swap == PRECIP_STORM_NORAIN && curWeather == PRECIP_BLANK))) // Rather than respawn all that crap, reuse it! + else if (swap != MT_NULL) // Rather than respawn all that crap, reuse it! { thinker_t *think; precipmobj_t *precipmobj; - state_t *st; + statenum_t st; for (think = thinkercap.next; think != &thinkercap; think = think->next) { if (think->function.acp1 != (actionf_p1)P_NullPrecipThinker) continue; // not a precipmobj thinker + precipmobj = (precipmobj_t *)think; - if (swap == PRECIP_RAIN) // Snow To Rain + precipmobj->flags = mobjinfo[swap].flags; + + st = mobjinfo[swap].spawnstate; + + if (precipprops[curWeather].randomstates > 0) { - precipmobj->flags = mobjinfo[MT_RAIN].flags; - st = &states[mobjinfo[MT_RAIN].spawnstate]; - precipmobj->state = st; - precipmobj->tics = st->tics; - precipmobj->sprite = st->sprite; - precipmobj->frame = st->frame; - precipmobj->momz = mobjinfo[MT_RAIN].speed; + UINT8 mrand = M_RandomByte(); + UINT8 threshold = UINT8_MAX / (precipprops[curWeather].randomstates + 1); + UINT8 i; - precipmobj->precipflags &= ~PCF_INVISIBLE; - - precipmobj->precipflags |= PCF_RAIN; - //think->function.acp1 = (actionf_p1)P_RainThinker; + for (i = 0; i < precipprops[curWeather].randomstates; i++) + { + if (mrand < (threshold * (i+1))) + { + st += i+1; + break; + } + } } - else if (swap == PRECIP_SNOW) // Rain To Snow - { - INT32 z; - precipmobj->flags = mobjinfo[MT_SNOWFLAKE].flags; - z = M_RandomByte(); + precipmobj->state = &states[st]; + precipmobj->tics = precipmobj->state->tics; + precipmobj->sprite = precipmobj->state->sprite; + precipmobj->frame = precipmobj->state->frame; - if (z < 64) - z = 2; - else if (z < 144) - z = 1; - else - z = 0; - - st = &states[mobjinfo[MT_SNOWFLAKE].spawnstate+z]; - precipmobj->state = st; - precipmobj->tics = st->tics; - precipmobj->sprite = st->sprite; - precipmobj->frame = st->frame; - precipmobj->momz = mobjinfo[MT_SNOWFLAKE].speed; - - precipmobj->precipflags &= ~(PCF_INVISIBLE|PCF_RAIN); - - //think->function.acp1 = (actionf_p1)P_SnowThinker; - } - else if (swap == PRECIP_BLANK || swap == PRECIP_STORM_NORAIN) // Remove precip, but keep it around for reuse. - { - //think->function.acp1 = (actionf_p1)P_NullPrecipThinker; - - precipmobj->precipflags |= PCF_INVISIBLE; - } + precipmobj->momz = mobjinfo[swap].speed; + precipmobj->precipflags &= ~PCF_INVISIBLE; } } - switch (weathernum) - { - case PRECIP_SNOW: // snow - curWeather = PRECIP_SNOW; + curWeather = newWeather; - if (!swap) - P_SpawnPrecipitation(); - - break; - case PRECIP_RAIN: // rain - { - boolean dontspawn = false; - - if (curWeather == PRECIP_RAIN || curWeather == PRECIP_STORM || curWeather == PRECIP_STORM_NOSTRIKES) - dontspawn = true; - - curWeather = PRECIP_RAIN; - - if (!dontspawn && !swap) - P_SpawnPrecipitation(); - - break; - } - case PRECIP_STORM: // storm - { - boolean dontspawn = false; - - if (curWeather == PRECIP_RAIN || curWeather == PRECIP_STORM || curWeather == PRECIP_STORM_NOSTRIKES) - dontspawn = true; - - curWeather = PRECIP_STORM; - - if (!dontspawn && !swap) - P_SpawnPrecipitation(); - - break; - } - case PRECIP_STORM_NOSTRIKES: // storm w/o lightning - { - boolean dontspawn = false; - - if (curWeather == PRECIP_RAIN || curWeather == PRECIP_STORM || curWeather == PRECIP_STORM_NOSTRIKES) - dontspawn = true; - - curWeather = PRECIP_STORM_NOSTRIKES; - - if (!dontspawn && !swap) - P_SpawnPrecipitation(); - - break; - } - case PRECIP_STORM_NORAIN: // storm w/o rain - curWeather = PRECIP_STORM_NORAIN; - - if (!swap) - P_SpawnPrecipitation(); - - break; - case PRECIP_BLANK: - curWeather = PRECIP_BLANK; - - if (!swap) - P_SpawnPrecipitation(); - - break; - default: - curWeather = PRECIP_NONE; - break; - } + if (swap == MT_NULL && precipprops[newWeather].type != MT_NULL) + P_SpawnPrecipitation(); } /** Gets an object. @@ -5672,25 +5560,10 @@ void P_InitSpecials(void) CheckForBustableBlocks = CheckForBouncySector = CheckForQuicksand = CheckForMarioBlocks = CheckForFloatBob = CheckForReverseGravity = false; - // Set curWeather - switch (mapheaderinfo[gamemap-1]->weather) - { - case PRECIP_SNOW: // snow - case PRECIP_RAIN: // rain - case PRECIP_STORM: // storm - case PRECIP_STORM_NORAIN: // storm w/o rain - case PRECIP_STORM_NOSTRIKES: // storm w/o lightning - curWeather = mapheaderinfo[gamemap-1]->weather; - break; - default: // blank/none - curWeather = PRECIP_NONE; - break; - } + // Set weather + curWeather = globalweather = mapheaderinfo[gamemap-1]->weather; - // Set globalweather - globalweather = mapheaderinfo[gamemap-1]->weather; - - P_InitTagLists(); // Create xref tables for tags + P_InitTagLists(); // Create xref tables for tags } /** After the map has loaded, scans for specials that spawn 3Dfloors and diff --git a/src/p_spec.h b/src/p_spec.h index b604ac951..1aca41155 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -59,7 +59,7 @@ INT32 P_FindMinSurroundingLight(sector_t *sector, INT32 max); void P_SetupSignExit(player_t *player); boolean P_IsFlagAtBase(mobjtype_t flag); -void P_SwitchWeather(INT32 weathernum); +void P_SwitchWeather(UINT8 newWeather); boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller); void P_LinedefExecute(INT16 tag, mobj_t *actor, sector_t *caller); diff --git a/src/p_tick.c b/src/p_tick.c index b285c35d0..9e4de8e33 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -58,8 +58,6 @@ void Command_Numthinkers_f(void) CONS_Printf(M_GetText("numthinkers <#>: Count number of thinkers\n")); CONS_Printf( "\t1: P_MobjThinker\n" - /*"\t2: P_RainThinker\n" - "\t3: P_SnowThinker\n"*/ "\t2: P_NullPrecipThinker\n" "\t3: T_Friction\n" "\t4: T_Pusher\n" @@ -75,14 +73,6 @@ void Command_Numthinkers_f(void) action = (actionf_p1)P_MobjThinker; CONS_Printf(M_GetText("Number of %s: "), "P_MobjThinker"); break; - /*case 2: - action = (actionf_p1)P_RainThinker; - CONS_Printf(M_GetText("Number of %s: "), "P_RainThinker"); - break; - case 3: - action = (actionf_p1)P_SnowThinker; - CONS_Printf(M_GetText("Number of %s: "), "P_SnowThinker"); - break;*/ case 2: action = (actionf_p1)P_NullPrecipThinker; CONS_Printf(M_GetText("Number of %s: "), "P_NullPrecipThinker"); diff --git a/src/r_things.c b/src/r_things.c index 1afbb125c..847f011ec 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -1639,14 +1639,10 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing) // okay, we can't return now except for vertical clipping... this is a hack, but weather isn't networked, so it should be ok if (!(thing->precipflags & PCF_THUNK)) { - if (thing->precipflags & PCF_RAIN) - P_RainThinker(thing); - else - P_SnowThinker(thing); + P_PrecipThinker(thing); thing->precipflags |= PCF_THUNK; } - //SoM: 3/17/2000: Disregard sprites that are out of view.. gzt = thing->z + spritecachedinfo[lump].topoffset; gz = gzt - spritecachedinfo[lump].height; From 100a67f8f0c048585b5d45993429b68b33af4bc3 Mon Sep 17 00:00:00 2001 From: James Date: Sat, 16 Nov 2019 16:14:54 -0500 Subject: [PATCH 085/163] Prevent LF2_EXISTSHACK from ever getting removed --- src/p_setup.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/p_setup.c b/src/p_setup.c index 5acc72746..b51dabdd0 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -181,6 +181,8 @@ FUNCNORETURN static ATTRNORETURN void CorruptMapError(const char *msg) static void P_ClearSingleMapHeaderInfo(INT16 i) { const INT16 num = (INT16)(i-1); + INT32 exists = (mapheaderinfo[num]->menuflags & LF2_EXISTSHACK); + DEH_WriteUndoline("LEVELNAME", mapheaderinfo[num]->lvlttl, UNDO_NONE); mapheaderinfo[num]->lvlttl[0] = '\0'; DEH_WriteUndoline("SUBTITLE", mapheaderinfo[num]->subttl, UNDO_NONE); @@ -246,7 +248,7 @@ static void P_ClearSingleMapHeaderInfo(INT16 i) DEH_WriteUndoline("LEVELFLAGS", va("%d", mapheaderinfo[num]->levelflags), UNDO_NONE); mapheaderinfo[num]->levelflags = 0; DEH_WriteUndoline("MENUFLAGS", va("%d", mapheaderinfo[num]->menuflags), UNDO_NONE); - mapheaderinfo[num]->menuflags = (mainwads ? 0 : LF2_EXISTSHACK); // see p_setup.c - prevents replacing maps in addons with easier versions + mapheaderinfo[num]->menuflags = exists; // see p_setup.c - prevents replacing maps in addons with easier versions // TODO grades support for delfile (pfft yeah right) P_DeleteGrades(num); // SRB2Kart From 94a00bb497d784a8c10cdca9509254aae1b32d4a Mon Sep 17 00:00:00 2001 From: James Date: Sat, 16 Nov 2019 16:28:48 -0500 Subject: [PATCH 086/163] Option to hide/show viewpoint text --- src/d_netcmd.c | 3 +++ src/d_netcmd.h | 1 + src/st_stuff.c | 56 ++++++++++++++++++++++++++------------------------ 3 files changed, 33 insertions(+), 27 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 70dff2fcf..426441592 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -454,6 +454,8 @@ consvar_t cv_pingtimeout = {"pingtimeout", "10", CV_SAVE, pingtimeout_cons_t, NU static CV_PossibleValue_t showping_cons_t[] = {{0, "Off"}, {1, "Always"}, {2, "Warning"}, {0, NULL}}; consvar_t cv_showping = {"showping", "Always", CV_SAVE, showping_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_showviewpointtext = {"showviewpointtext", "On", CV_SAVE, CV_OnOff, 0, 0, NULL, NULL, 0, 0, NULL}; + // Intermission time Tails 04-19-2002 static CV_PossibleValue_t inttime_cons_t[] = {{0, "MIN"}, {3600, "MAX"}, {0, NULL}}; consvar_t cv_inttime = {"inttime", "20", CV_NETVAR, inttime_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; @@ -713,6 +715,7 @@ void D_RegisterServerCommands(void) CV_RegisterVar(&cv_maxping); CV_RegisterVar(&cv_pingtimeout); CV_RegisterVar(&cv_showping); + CV_RegisterVar(&cv_showviewpointtext); #ifdef SEENAMES CV_RegisterVar(&cv_allowseenames); diff --git a/src/d_netcmd.h b/src/d_netcmd.h index d1f28665c..e79f6b9d9 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -146,6 +146,7 @@ extern consvar_t cv_specialrings, cv_powerstones, cv_matchboxes, cv_competitionb extern consvar_t cv_maxping; extern consvar_t cv_pingtimeout; extern consvar_t cv_showping; +extern consvar_t cv_showviewpointtext; extern consvar_t cv_skipmapcheck; diff --git a/src/st_stuff.c b/src/st_stuff.c index caed81f3e..db179545f 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -1894,38 +1894,40 @@ static void ST_overlayDrawer(void) V_DrawScaledPatch(hudinfo[HUD_GRAVBOOTSICO].x, STRINGY(hudinfo[HUD_GRAVBOOTSICO].y), V_SNAPTORIGHT, gravboots); */ - if (!(multiplayer && demo.playback)) + if (cv_showviewpointtext.value) { - if(!P_IsLocalPlayer(stplyr)) + if (!(multiplayer && demo.playback)) { - /*char name[MAXPLAYERNAME+1]; - // shorten the name if its more than twelve characters. - strlcpy(name, player_names[stplyr-players], 13);*/ + if(!P_IsLocalPlayer(stplyr)) + { + /*char name[MAXPLAYERNAME+1]; + // shorten the name if its more than twelve characters. + strlcpy(name, player_names[stplyr-players], 13);*/ - // Show name of player being displayed - V_DrawCenteredString((BASEVIDWIDTH/2), BASEVIDHEIGHT-40, 0, M_GetText("Viewpoint:")); - V_DrawCenteredString((BASEVIDWIDTH/2), BASEVIDHEIGHT-32, V_ALLOWLOWERCASE, player_names[stplyr-players]); + // Show name of player being displayed + V_DrawCenteredString((BASEVIDWIDTH/2), BASEVIDHEIGHT-40, 0, M_GetText("VIEWPOINT:")); + V_DrawCenteredString((BASEVIDWIDTH/2), BASEVIDHEIGHT-32, V_ALLOWLOWERCASE, player_names[stplyr-players]); + } } - } - else if (!demo.title) - { + else if (!demo.title) + { + if (!splitscreen) + { + V_DrawCenteredString((BASEVIDWIDTH/2), BASEVIDHEIGHT-40, V_HUDTRANSHALF, M_GetText("VIEWPOINT:")); + V_DrawCenteredString((BASEVIDWIDTH/2), BASEVIDHEIGHT-32, V_HUDTRANSHALF|V_ALLOWLOWERCASE, player_names[stplyr-players]); + } + else if (splitscreen == 1) + { + char name[MAXPLAYERNAME+12]; - if (!splitscreen) - { - V_DrawCenteredString((BASEVIDWIDTH/2), BASEVIDHEIGHT-40, V_HUDTRANSHALF, M_GetText("Viewpoint:")); - V_DrawCenteredString((BASEVIDWIDTH/2), BASEVIDHEIGHT-32, V_HUDTRANSHALF|V_ALLOWLOWERCASE, player_names[stplyr-players]); - } - else if (splitscreen == 1) - { - char name[MAXPLAYERNAME+12]; - - INT32 y = (stplyr == &players[displayplayers[0]]) ? 4 : BASEVIDHEIGHT/2-12; - sprintf(name, "VIEWPOINT: %s", player_names[stplyr-players]); - V_DrawRightAlignedThinString(BASEVIDWIDTH-40, y, V_HUDTRANSHALF|V_ALLOWLOWERCASE|K_calcSplitFlags(V_SNAPTOTOP|V_SNAPTOBOTTOM|V_SNAPTORIGHT), name); - } - else if (splitscreen) - { - V_DrawCenteredThinString((vid.width/vid.dupx)/4, BASEVIDHEIGHT/2 - 12, V_HUDTRANSHALF|V_ALLOWLOWERCASE|K_calcSplitFlags(V_SNAPTOBOTTOM|V_SNAPTOLEFT), player_names[stplyr-players]); + INT32 y = (stplyr == &players[displayplayers[0]]) ? 4 : BASEVIDHEIGHT/2-12; + sprintf(name, "VIEWPOINT: %s", player_names[stplyr-players]); + V_DrawRightAlignedThinString(BASEVIDWIDTH-40, y, V_HUDTRANSHALF|V_ALLOWLOWERCASE|K_calcSplitFlags(V_SNAPTOTOP|V_SNAPTOBOTTOM|V_SNAPTORIGHT), name); + } + else if (splitscreen) + { + V_DrawCenteredThinString((vid.width/vid.dupx)/4, BASEVIDHEIGHT/2 - 12, V_HUDTRANSHALF|V_ALLOWLOWERCASE|K_calcSplitFlags(V_SNAPTOBOTTOM|V_SNAPTOLEFT), player_names[stplyr-players]); + } } } From 5f26312a0a390ccff6cf6bdca6135da2d52c37f4 Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 22 Feb 2020 18:30:20 -0800 Subject: [PATCH 087/163] Drop frames as the host according to the lowest ping among clients --- src/d_clisrv.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 1dacabd9f..7851e1b62 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -4960,6 +4960,28 @@ static void CL_SendClientCmd(void) size_t packetsize = 0; boolean mis = false; + int fastest; + int lag; + int i; + + fastest = 0; + + if (server) + { + for (i = 0; i < MAXPLAYERS; ++i) + { + if (playernode[i] > 0 && playeringame[i]) + { + lag = GetLag(playernode[i]); + if (! fastest || lag < fastest) + fastest = lag; + } + } + } + + if (fastest && ( gametic % fastest )) + return; + netbuffer->packettype = PT_CLIENTCMD; if (cl_packetmissed) From 56f6b058d039dfbd64c6fa5e3747342a2aa3bd5f Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 22 Feb 2020 18:33:31 -0800 Subject: [PATCH 088/163] Make host lag optional with "lagless", lag by default (lagless off) --- src/d_clisrv.c | 2 +- src/d_netcmd.c | 3 +++ src/d_netcmd.h | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 7851e1b62..1f5e1f1ae 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -4966,7 +4966,7 @@ static void CL_SendClientCmd(void) fastest = 0; - if (server) + if (server && ! cv_lagless.value) { for (i = 0; i < MAXPLAYERS; ++i) { diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 70dff2fcf..ede7558ad 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -447,6 +447,8 @@ consvar_t cv_jointimeout = {"jointimeout", "105", CV_CALL|CV_SAVE, nettimeout_co static CV_PossibleValue_t maxping_cons_t[] = {{0, "MIN"}, {1000, "MAX"}, {0, NULL}}; consvar_t cv_maxping = {"maxping", "800", CV_SAVE, maxping_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_lagless = {"lagless", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; + static CV_PossibleValue_t pingtimeout_cons_t[] = {{8, "MIN"}, {120, "MAX"}, {0, NULL}}; consvar_t cv_pingtimeout = {"pingtimeout", "10", CV_SAVE, pingtimeout_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; @@ -711,6 +713,7 @@ void D_RegisterServerCommands(void) CV_RegisterVar(&cv_skipmapcheck); CV_RegisterVar(&cv_sleep); CV_RegisterVar(&cv_maxping); + CV_RegisterVar(&cv_lagless); CV_RegisterVar(&cv_pingtimeout); CV_RegisterVar(&cv_showping); diff --git a/src/d_netcmd.h b/src/d_netcmd.h index d1f28665c..7a11a809d 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -144,6 +144,7 @@ extern consvar_t cv_ringslinger, cv_soundtest; extern consvar_t cv_specialrings, cv_powerstones, cv_matchboxes, cv_competitionboxes; extern consvar_t cv_maxping; +extern consvar_t cv_lagless; extern consvar_t cv_pingtimeout; extern consvar_t cv_showping; From 6484a5cd93179581b4e07c52931adc9c52752c66 Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 22 Feb 2020 18:38:36 -0800 Subject: [PATCH 089/163] Show LAGLESS on the scores if the host is being dishonest --- src/k_kart.c | 47 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 230662d2a..2000c3765 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -7085,6 +7085,9 @@ static patch_t *kp_lapanim_hand[3]; static patch_t *kp_yougotem; static patch_t *kp_itemminimap; +static patch_t *kp_alagles[10]; +static patch_t *kp_blagles[6]; + void K_LoadKartHUDGraphics(void) { INT32 i, j; @@ -7385,6 +7388,20 @@ void K_LoadKartHUDGraphics(void) kp_yougotem = (patch_t *) W_CachePatchName("YOUGOTEM", PU_HUDGFX); kp_itemminimap = (patch_t *) W_CachePatchName("MMAPITEM", PU_HUDGFX); + + sprintf(buffer, "ALAGLESx"); + for (i = 0; i < 10; ++i) + { + buffer[7] = '0'+i; + kp_alagles[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + sprintf(buffer, "BLAGLESx"); + for (i = 0; i < 6; ++i) + { + buffer[7] = '0'+i; + kp_blagles[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } } // For the item toggle menu @@ -8357,9 +8374,11 @@ static boolean K_drawKartPositionFaces(void) // void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, INT32 whiteplayer, INT32 hilicol) { + static tic_t alagles_timer = 9; INT32 i, rightoffset = 240; const UINT8 *colormap; INT32 dupadjust = (vid.width/vid.dupx), duptweak = (dupadjust - BASEVIDWIDTH)/2; + int y2; //this function is designed for 9 or less score lines only //I_Assert(scorelines <= 9); -- not today bitch, kart fixed it up @@ -8385,10 +8404,34 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I STRBUFCPY(strtime, tab[i].name); + y2 = y; + + if (tab[i].num == 0 && cv_lagless.value) + { + y2 = ( y - 4 ); + + V_DrawScaledPatch(x + 20, y2, 0, kp_blagles[(leveltime / 3) % 6]); + // every 70 tics + if (( leveltime % 70 ) == 0) + { + alagles_timer = 9; + } + if (alagles_timer > 0) + { + V_DrawScaledPatch(x + 20, y2, 0, kp_alagles[alagles_timer]); + if (( leveltime % 2 ) == 0) + alagles_timer--; + } + else + V_DrawScaledPatch(x + 20, y2, 0, kp_alagles[0]); + + y2 += SHORT (kp_alagles[0]->height) + 1; + } + if (scorelines > 8) - V_DrawThinString(x + 20, y, ((tab[i].num == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE|V_6WIDTHSPACE, strtime); + V_DrawThinString(x + 20, y2, ((tab[i].num == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE|V_6WIDTHSPACE, strtime); else - V_DrawString(x + 20, y, ((tab[i].num == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE, strtime); + V_DrawString(x + 20, y2, ((tab[i].num == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE, strtime); if (players[tab[i].num].mo->color) { From 352d576979585e1f04b5d0b6d06a7b05cbc1305a Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 22 Feb 2020 19:08:03 -0800 Subject: [PATCH 090/163] Don't turn off lagless at map start; draw LAGLESS in intermission --- src/d_clisrv.c | 3 ++- src/d_clisrv.h | 2 ++ src/d_netcmd.c | 12 +++++++++++- src/g_game.c | 2 ++ src/k_kart.c | 2 +- src/y_inter.c | 36 ++++++++++++++++++++++++++++++++++-- 6 files changed, 52 insertions(+), 5 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 1f5e1f1ae..e520c8155 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -97,6 +97,7 @@ UINT16 pingmeasurecount = 1; UINT32 realpingtable[MAXPLAYERS]; //the base table of ping where an average will be sent to everyone. UINT32 playerpingtable[MAXPLAYERS]; //table of player latency values. tic_t servermaxping = 800; // server's max ping. Defaults to 800 +boolean server_lagless; SINT8 nodetoplayer[MAXNETNODES]; SINT8 nodetoplayer2[MAXNETNODES]; // say the numplayer for this node if any (splitscreen) SINT8 nodetoplayer3[MAXNETNODES]; // say the numplayer for this node if any (splitscreen == 2) @@ -4966,7 +4967,7 @@ static void CL_SendClientCmd(void) fastest = 0; - if (server && ! cv_lagless.value) + if (server && ! server_lagless) { for (i = 0; i < MAXPLAYERS; ++i) { diff --git a/src/d_clisrv.h b/src/d_clisrv.h index 0bd85b614..a33d06a2c 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -543,6 +543,8 @@ extern UINT32 realpingtable[MAXPLAYERS]; extern UINT32 playerpingtable[MAXPLAYERS]; extern tic_t servermaxping; +extern boolean server_lagless; + extern consvar_t #ifdef VANILLAJOINNEXTROUND cv_joinnextround, diff --git a/src/d_netcmd.c b/src/d_netcmd.c index ede7558ad..6b196d10d 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -93,6 +93,8 @@ static void TeamScramble_OnChange(void); static void NetTimeout_OnChange(void); static void JoinTimeout_OnChange(void); +static void Lagless_OnChange (void); + static void Ringslinger_OnChange(void); static void Gravity_OnChange(void); static void ForceSkin_OnChange(void); @@ -447,7 +449,7 @@ consvar_t cv_jointimeout = {"jointimeout", "105", CV_CALL|CV_SAVE, nettimeout_co static CV_PossibleValue_t maxping_cons_t[] = {{0, "MIN"}, {1000, "MAX"}, {0, NULL}}; consvar_t cv_maxping = {"maxping", "800", CV_SAVE, maxping_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_lagless = {"lagless", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_lagless = {"lagless", "Off", CV_SAVE|CV_NETVAR|CV_CALL, CV_OnOff, Lagless_OnChange, 0, NULL, NULL, 0, 0, NULL}; static CV_PossibleValue_t pingtimeout_cons_t[] = {{8, "MIN"}, {120, "MAX"}, {0, NULL}}; consvar_t cv_pingtimeout = {"pingtimeout", "10", CV_SAVE, pingtimeout_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; @@ -4772,6 +4774,14 @@ static void JoinTimeout_OnChange(void) jointimeout = (tic_t)cv_jointimeout.value; } +static void +Lagless_OnChange (void) +{ + /* don't back out of dishonesty, or go lagless after playing honestly */ + if (cv_lagless.value && gamestate == GS_LEVEL) + server_lagless = true; +} + UINT32 timelimitintics = 0; /** Deals with a timelimit change by printing the change to the console. diff --git a/src/g_game.c b/src/g_game.c index d4d48f7c2..af32050de 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1781,6 +1781,8 @@ void G_DoLoadLevel(boolean resetplayer) // clear hud messages remains (usually from game startup) CON_ClearHUD(); + + server_lagless = cv_lagless.value; } static INT32 pausedelay = 0; diff --git a/src/k_kart.c b/src/k_kart.c index 2000c3765..4016b7ced 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -8406,7 +8406,7 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I y2 = y; - if (tab[i].num == 0 && cv_lagless.value) + if (tab[i].num == 0 && server_lagless) { y2 = ( y - 4 ); diff --git a/src/y_inter.c b/src/y_inter.c index f5380d565..ff168070c 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -435,6 +435,7 @@ void Y_IntermissionDrawer(void) INT32 y = 41, gutter = ((data.match.numplayers > NUMFORNEWCOLUMN) ? 0 : (BASEVIDWIDTH/2)); INT32 dupadjust = (vid.width/vid.dupx), duptweak = (dupadjust - BASEVIDWIDTH)/2; const char *timeheader; + int y2; if (data.match.rankingsmode) timeheader = "PWR.LV"; @@ -492,10 +493,41 @@ void Y_IntermissionDrawer(void) STRBUFCPY(strtime, data.match.name[i]); + y2 = y; + + if (data.match.num[i] == 0 && server_lagless) + { + static int alagles_timer = 0; + patch_t *alagles; + + y2 = ( y - 4 ); + + V_DrawScaledPatch(x + 36, y2, 0, W_CachePatchName(va("BLAGLES%d", (intertic / 3) % 6), PU_CACHE)); + // every 70 tics + if (( leveltime % 70 ) == 0) + { + alagles_timer = 9; + } + if (alagles_timer > 0) + { + alagles = W_CachePatchName(va("ALAGLES%d", alagles_timer), PU_CACHE); + V_DrawScaledPatch(x + 36, y2, 0, alagles); + if (( leveltime % 2 ) == 0) + alagles_timer--; + } + else + { + alagles = W_CachePatchName("ALAGLES0", PU_CACHE); + V_DrawScaledPatch(x + 36, y2, 0, alagles); + } + + y2 += SHORT (alagles->height) + 1; + } + if (data.match.numplayers > NUMFORNEWCOLUMN) - V_DrawThinString(x+36, y-1, ((data.match.num[i] == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE|V_6WIDTHSPACE, strtime); + V_DrawThinString(x+36, y2-1, ((data.match.num[i] == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE|V_6WIDTHSPACE, strtime); else - V_DrawString(x+36, y, ((data.match.num[i] == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE, strtime); + V_DrawString(x+36, y2, ((data.match.num[i] == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE, strtime); if (data.match.rankingsmode) { From a511d9358a81fb743a5b9b789c61d6f31c1b38c3 Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 22 Feb 2020 19:38:36 -0800 Subject: [PATCH 091/163] Terrible code to show the ping counter for honest servers --- src/d_clisrv.c | 75 +++++++++++++++++++++++++++++++++-------------- src/sdl/i_video.c | 5 +++- 2 files changed, 57 insertions(+), 23 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index e520c8155..28a519694 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -97,6 +97,7 @@ UINT16 pingmeasurecount = 1; UINT32 realpingtable[MAXPLAYERS]; //the base table of ping where an average will be sent to everyone. UINT32 playerpingtable[MAXPLAYERS]; //table of player latency values. tic_t servermaxping = 800; // server's max ping. Defaults to 800 +static tic_t lowest_lag; boolean server_lagless; SINT8 nodetoplayer[MAXNETNODES]; SINT8 nodetoplayer2[MAXNETNODES]; // say the numplayer for this node if any (splitscreen) @@ -4961,26 +4962,7 @@ static void CL_SendClientCmd(void) size_t packetsize = 0; boolean mis = false; - int fastest; - int lag; - int i; - - fastest = 0; - - if (server && ! server_lagless) - { - for (i = 0; i < MAXPLAYERS; ++i) - { - if (playernode[i] > 0 && playeringame[i]) - { - lag = GetLag(playernode[i]); - if (! fastest || lag < fastest) - fastest = lag; - } - } - } - - if (fastest && ( gametic % fastest )) + if (lowest_lag && ( gametic % lowest_lag )) return; netbuffer->packettype = PT_CLIENTCMD; @@ -5451,16 +5433,65 @@ static tic_t gametime = 0; static void UpdatePingTable(void) { + int fastest; + tic_t lag; + INT32 i; + if (server) { if (netgame && !(gametime % 35)) // update once per second. PingUpdate(); + + fastest = 0; + // update node latency values so we can take an average later. for (i = 0; i < MAXPLAYERS; i++) - if (playeringame[i]) - realpingtable[i] += G_TicsToMilliseconds(GetLag(playernode[i])); + { + if (playeringame[i] && playernode[i] > 0) + { + if (! server_lagless && playernode[i] > 0) + { + lag = GetLag(playernode[i]); + realpingtable[i] += G_TicsToMilliseconds(lag); + + if (! fastest || lag < fastest) + fastest = lag; + } + else + realpingtable[i] += G_TicsToMilliseconds(GetLag(playernode[i])); + } + } pingmeasurecount++; + + if (server_lagless) + lowest_lag = 0; + else + { + lowest_lag = fastest; + + if (fastest) + lag = fastest; + else + lag = GetLag(0); + + lag = ( realpingtable[0] + G_TicsToMilliseconds(lag) ); + + switch (playerpernode[0]) + { + case 4: + realpingtable[nodetoplayer4[0]] = lag; + /*FALLTHRU*/ + case 3: + realpingtable[nodetoplayer3[0]] = lag; + /*FALLTHRU*/ + case 2: + realpingtable[nodetoplayer2[0]] = lag; + /*FALLTHRU*/ + case 1: + realpingtable[nodetoplayer[0]] = lag; + } + } } } diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index e5f1c23fc..c900417ab 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -1382,8 +1382,11 @@ void I_FinishUpdate(void) if (cv_ticrate.value) SCR_DisplayTicRate(); - if (cv_showping.value && netgame && consoleplayer != serverplayer) + if (cv_showping.value && netgame && + ( consoleplayer != serverplayer || ! server_lagless )) + { SCR_DisplayLocalPing(); + } } if (rendermode == render_soft && screens[0]) From 4f73e2666188df1d0b6b896cbe68f57152ff5b62 Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 22 Feb 2020 19:44:16 -0800 Subject: [PATCH 092/163] Show server's ping on scores if not LAGLESS --- 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 4016b7ced..3c01f22ab 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -8399,7 +8399,7 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I continue; //ignore them. if (netgame // don't draw it offline - && tab[i].num != serverplayer) + && ( tab[i].num != serverplayer || ! server_lagless )) HU_drawPing(x + ((i < 8) ? -17 : rightoffset + 11), y-4, playerpingtable[tab[i].num], 0); STRBUFCPY(strtime, tab[i].name); From a63157b937a347e60666bed1d1cde43838e2fc2f Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Tue, 25 Feb 2020 00:04:39 -0500 Subject: [PATCH 093/163] Flip facing direction --- src/p_spec.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/p_spec.c b/src/p_spec.c index 9f8bc6f4b..2d8e355ed 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -3251,7 +3251,7 @@ static void P_SetupSignObject(mobj_t *sign, mobj_t *pmo) P_SetTarget(&cur->hnext->target, sign); P_SetMobjState(cur->hnext, S_SIGN_FACE); cur->hnext->extravalue1 = 6; - cur->hnext->extravalue2 = 2; + cur->hnext->extravalue2 = 0; prev = cur; cur = cur->hnext; @@ -3263,7 +3263,7 @@ static void P_SetupSignObject(mobj_t *sign, mobj_t *pmo) cur->hnext->skin = pmo->skin; P_SetMobjState(cur->hnext, S_PLAY_SIGN); cur->hnext->extravalue1 = 7; - cur->hnext->extravalue2 = 2; + cur->hnext->extravalue2 = 0; prev = cur; cur = cur->hnext; @@ -3274,7 +3274,7 @@ static void P_SetupSignObject(mobj_t *sign, mobj_t *pmo) P_SetTarget(&cur->hnext->target, sign); P_SetMobjState(cur->hnext, S_SIGN_BACK); cur->hnext->extravalue1 = 6; - cur->hnext->extravalue2 = 0; + cur->hnext->extravalue2 = 2; prev = cur; cur = cur->hnext; From 6956e1d24ed96c3d8853e50d124fe450725094a6 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sun, 1 Mar 2020 13:13:59 -0500 Subject: [PATCH 094/163] Store random states in mobjinfo instead of precipprops My reasoning is that it wouldn't make much sense to have a weather type that has snow, but doesn't randomize the sprite it uses, so we don't need to copy-paste the same "randomstates" for each weather type. --- src/doomstat.h | 1 - src/g_game.c | 14 +++++++------- src/info.c | 4 ++-- src/p_mobj.c | 12 +++++++----- src/p_spec.c | 7 ++++--- 5 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/doomstat.h b/src/doomstat.h index cdc09cf86..f4f7acfd0 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -68,7 +68,6 @@ typedef struct { mobjtype_t type; precipeffect_t effects; - UINT8 randomstates; } precipprops_t; extern precipprops_t precipprops[MAXPRECIP]; diff --git a/src/g_game.c b/src/g_game.c index e15968b3e..488f350a7 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -89,13 +89,13 @@ UINT8 curWeather = PRECIP_NONE; precipprops_t precipprops[MAXPRECIP] = { - {MT_NULL, 0, 0}, // PRECIP_NONE - {MT_RAIN, 0, 0}, // PRECIP_RAIN - {MT_SNOWFLAKE, 0, 2}, // PRECIP_SNOW - {MT_BLIZZARDSNOW, 0, 2}, // PRECIP_BLIZZARD - {MT_RAIN, PRECIPFX_THUNDER|PRECIPFX_LIGHTNING, 0}, // PRECIP_STORM - {MT_NULL, PRECIPFX_THUNDER|PRECIPFX_LIGHTNING, 0}, // PRECIP_STORM_NORAIN - {MT_RAIN, PRECIPFX_THUNDER, 0} // PRECIP_STORM_NOSTRIKES + {MT_NULL, 0}, // PRECIP_NONE + {MT_RAIN, 0}, // PRECIP_RAIN + {MT_SNOWFLAKE, 0}, // PRECIP_SNOW + {MT_BLIZZARDSNOW, 0}, // PRECIP_BLIZZARD + {MT_RAIN, PRECIPFX_THUNDER|PRECIPFX_LIGHTNING}, // PRECIP_STORM + {MT_NULL, PRECIPFX_THUNDER|PRECIPFX_LIGHTNING}, // PRECIP_STORM_NORAIN + {MT_RAIN, PRECIPFX_THUNDER} // PRECIP_STORM_NOSTRIKES }; INT32 cursaveslot = -1; // Auto-save 1p savegame slot diff --git a/src/info.c b/src/info.c index 4542e6c3a..018d354f5 100644 --- a/src/info.c +++ b/src/info.c @@ -11486,7 +11486,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 4*FRACUNIT, // height 0, // display offset 0, // mass - 0, // damage + 2, // damage sfx_None, // activesound MF_NOBLOCKMAP, // flags S_NULL // raisestate @@ -11513,7 +11513,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 4*FRACUNIT, // height 0, // display offset 0, // mass - 0, // damage + 2, // damage sfx_None, // activesound MF_NOBLOCKMAP, // flags S_NULL // raisestate diff --git a/src/p_mobj.c b/src/p_mobj.c index 87fb7a186..2f514f526 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -11005,6 +11005,8 @@ consvar_t cv_suddendeath = {"suddendeath", "Off", CV_NETVAR|CV_CHEAT|CV_NOSHOWHE void P_SpawnPrecipitation(void) { INT32 i, j, k; + mobjtype_t type = precipprops[curWeather].type; + INT32 randomstates = mobjinfo[type].damage; fixed_t basex, basey, x, y, z, height; subsector_t *precipsector = NULL; precipmobj_t *rainmo = NULL; @@ -11049,15 +11051,15 @@ void P_SpawnPrecipitation(void) for (j = 0; j < numparticles; j++) { - rainmo = P_SpawnPrecipMobj(x, y, z, precipprops[curWeather].type); + rainmo = P_SpawnPrecipMobj(x, y, z, type); - if (precipprops[curWeather].randomstates > 0) + if (randomstates > 0 && randomstates < UINT8_MAX) { UINT8 mrand = M_RandomByte(); - UINT8 threshold = UINT8_MAX / (precipprops[curWeather].randomstates + 1); - statenum_t st = mobjinfo[precipprops[curWeather].type].spawnstate; + UINT8 threshold = UINT8_MAX / (randomstates + 1); + statenum_t st = mobjinfo[type].spawnstate; - for (k = 0; k < precipprops[curWeather].randomstates; k++) + for (k = 0; k < randomstates; k++) { if (mrand < (threshold * (k+1))) { diff --git a/src/p_spec.c b/src/p_spec.c index d92fb2f3b..1cca2cd5e 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -2041,6 +2041,7 @@ void P_SwitchWeather(UINT8 newWeather) } else if (swap != MT_NULL) // Rather than respawn all that crap, reuse it! { + INT32 randomstates = mobjinfo[swap].damage; thinker_t *think; precipmobj_t *precipmobj; statenum_t st; @@ -2056,13 +2057,13 @@ void P_SwitchWeather(UINT8 newWeather) st = mobjinfo[swap].spawnstate; - if (precipprops[curWeather].randomstates > 0) + if (randomstates > 0 && randomstates < UINT8_MAX) { UINT8 mrand = M_RandomByte(); - UINT8 threshold = UINT8_MAX / (precipprops[curWeather].randomstates + 1); + UINT8 threshold = UINT8_MAX / (randomstates + 1); UINT8 i; - for (i = 0; i < precipprops[curWeather].randomstates; i++) + for (i = 0; i < randomstates; i++) { if (mrand < (threshold * (i+1))) { From 76e75d9756a9e07e66cdb17db84eeb6f76835948 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sun, 1 Mar 2020 14:36:13 -0500 Subject: [PATCH 095/163] Fix offroad collision Adds an option to P_MobjTouchingSectorSpecial to only use non-FOF sectors' special if you're touching their floor/ceiling. --- src/k_kart.c | 2 +- src/lua_baselib.c | 3 ++- src/p_floor.c | 2 +- src/p_inter.c | 2 +- src/p_map.c | 6 ++--- src/p_mobj.c | 16 ++++--------- src/p_spec.c | 61 ++++++++++++++++++++++++++++++++++++++++------- src/p_spec.h | 2 +- 8 files changed, 66 insertions(+), 28 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 6c268269d..cb82dba93 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1542,7 +1542,7 @@ static UINT8 K_CheckOffroadCollide(mobj_t *mo) for (i = 2; i < 5; i++) { - if (P_MobjTouchingSectorSpecial(mo, 1, i)) + if (P_MobjTouchingSectorSpecial(mo, 1, i, true)) return i-1; } diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 3d836dad2..bb7d1111b 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -1318,10 +1318,11 @@ static int lib_pMobjTouchingSectorSpecial(lua_State *L) mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); INT32 section = (INT32)luaL_checkinteger(L, 2); INT32 number = (INT32)luaL_checkinteger(L, 3); + boolean touchground = lua_optboolean(L, 4); //HUDSAFE if (!mo) return LUA_ErrInvalid(L, "mobj_t"); - LUA_PushUserdata(L, P_MobjTouchingSectorSpecial(mo, section, number), META_SECTOR); + LUA_PushUserdata(L, P_MobjTouchingSectorSpecial(mo, section, number, touchground), META_SECTOR); return 1; } diff --git a/src/p_floor.c b/src/p_floor.c index a4e4f2747..3621e0886 100644 --- a/src/p_floor.c +++ b/src/p_floor.c @@ -2275,7 +2275,7 @@ void T_EachTimeThinker(levelspecthink_t *eachtime) continue; if (!(players[i].mo->subsector->sector == sec - || P_MobjTouchingSectorSpecial(players[i].mo, 2, (GETSECSPECIAL(sec->special, 2))) == sec)) + || P_MobjTouchingSectorSpecial(players[i].mo, 2, (GETSECSPECIAL(sec->special, 2)), false) == sec)) continue; if (floortouch == true && P_IsObjectOnRealGround(players[i].mo, sec)) diff --git a/src/p_inter.c b/src/p_inter.c index a1a44c998..b81d3296d 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -925,7 +925,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) special->fuse = 1; special->flags2 |= MF2_JUSTATTACKED; - if (!P_MobjTouchingSectorSpecial(player->mo, 4, 2 + flagteam)) + if (!P_MobjTouchingSectorSpecial(player->mo, 4, 2 + flagteam, false)) { CONS_Printf(M_GetText("%s returned the %c%s%c to base.\n"), plname, flagcolor, flagtext, 0x80); diff --git a/src/p_map.c b/src/p_map.c index 22e624311..19006fe49 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -2790,10 +2790,10 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) fixed_t maxstep = FixedMul(MAXSTEPMOVE, mapobjectscale); // If using type Section1:13, double the maxstep. - if (P_MobjTouchingSectorSpecial(thing, 1, 13)) + if (P_MobjTouchingSectorSpecial(thing, 1, 13, false)) maxstep <<= 1; // If using type Section1:12, no maxstep. For short walls, like Egg Zeppelin - else if (P_MobjTouchingSectorSpecial(thing, 1, 12)) + else if (P_MobjTouchingSectorSpecial(thing, 1, 12, false)) maxstep = 0; if (thing->type == MT_SKIM) @@ -2817,7 +2817,7 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) return false; // mobj must lower itself to fit // Ramp test - if ((maxstep > 0) && !(P_MobjTouchingSectorSpecial(thing, 1, 14))) + if ((maxstep > 0) && !(P_MobjTouchingSectorSpecial(thing, 1, 14, false))) { // If the floor difference is MAXSTEPMOVE or less, and the sector isn't Section1:14, ALWAYS // step down! Formerly required a Section1:13 sector for the full MAXSTEPMOVE, but no more. diff --git a/src/p_mobj.c b/src/p_mobj.c index 1fd846ff1..2bb445e73 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -8355,7 +8355,7 @@ void P_MobjThinker(mobj_t *mobj) P_Thrust(mobj, mobj->angle, thrustamount); - if (P_MobjTouchingSectorSpecial(mobj, 3, 1)) + if (P_MobjTouchingSectorSpecial(mobj, 3, 1, true)) K_DoPogoSpring(mobj, 0, 1); if (mobj->threshold > 0) @@ -8385,7 +8385,7 @@ void P_MobjThinker(mobj_t *mobj) K_DriftDustHandling(mobj); - if (P_MobjTouchingSectorSpecial(mobj, 3, 1)) + if (P_MobjTouchingSectorSpecial(mobj, 3, 1, true)) K_DoPogoSpring(mobj, 0, 1); break; @@ -8437,14 +8437,8 @@ void P_MobjThinker(mobj_t *mobj) mobj->angle = R_PointToAngle2(0, 0, mobj->momx, mobj->momy); P_Thrust(mobj, mobj->angle, thrustamount); - if (grounded) - { - sector_t *sec2 = P_ThingOnSpecial3DFloor(mobj); - if ((sec2 && GETSECSPECIAL(sec2->special, 3) == 1) - || (P_IsObjectOnRealGround(mobj, mobj->subsector->sector) - && GETSECSPECIAL(mobj->subsector->sector->special, 3) == 1)) - K_DoPogoSpring(mobj, 0, 1); - } + if (P_MobjTouchingSectorSpecial(mobj, 3, 1, true)) + K_DoPogoSpring(mobj, 0, 1); if (mobj->threshold > 0) mobj->threshold--; @@ -9592,7 +9586,7 @@ void P_MobjThinker(mobj_t *mobj) break; case MT_BLUEFLAG: case MT_REDFLAG: - if (P_MobjTouchingSectorSpecial(mobj, 4, 2)) + if (P_MobjTouchingSectorSpecial(mobj, 4, 2, false)) mobj->fuse = 1; // Return to base. break; case MT_CANNONBALL: diff --git a/src/p_spec.c b/src/p_spec.c index 7786329ec..9ce9c9faf 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -3371,11 +3371,12 @@ boolean P_IsFlagAtBase(mobjtype_t flag) // the particular type that it finds. // Returns NULL if it doesn't find it. // -// Sal: There was absolutely no reason for -// this to be a player_t only function. +// Sal: Couldn't see a reason for this to +// be a player_t only function. // -sector_t *P_MobjTouchingSectorSpecial(mobj_t *mo, INT32 section, INT32 number) +sector_t *P_MobjTouchingSectorSpecial(mobj_t *mo, INT32 section, INT32 number, boolean touchground) { + fixed_t topheight, bottomheight; msecnode_t *node; ffloor_t *rover; @@ -3384,13 +3385,34 @@ sector_t *P_MobjTouchingSectorSpecial(mobj_t *mo, INT32 section, INT32 number) // Check default case first if (GETSECSPECIAL(mo->subsector->sector->special, section) == number) - return mo->subsector->sector; + { + if (touchground) + { + topheight = P_GetSpecialTopZ(mo, mo->subsector->sector, mo->subsector->sector); + bottomheight = P_GetSpecialBottomZ(mo, mo->subsector->sector, mo->subsector->sector); + + // Thing must be on top of the floor to be affected... + if (mo->subsector->sector->flags & SF_FLIPSPECIAL_FLOOR) + { + if (!(mo->eflags & MFE_VERTICALFLIP) && mo->z <= bottomheight) + return mo->subsector->sector; + } + + if (mo->subsector->sector->flags & SF_FLIPSPECIAL_CEILING) + { + if ((mo->eflags & MFE_VERTICALFLIP) && mo->z + mo->height >= topheight) + return mo->subsector->sector; + } + } + else + { + return mo->subsector->sector; + } + } // Hmm.. maybe there's a FOF that has it... for (rover = mo->subsector->sector->ffloors; rover; rover = rover->next) { - fixed_t topheight, bottomheight; - if (GETSECSPECIAL(rover->master->frontsector->special, section) != number) continue; @@ -3444,14 +3466,35 @@ sector_t *P_MobjTouchingSectorSpecial(mobj_t *mo, INT32 section, INT32 number) // are we allowed to touch it? if (node->m_sector == mo->subsector->sector || (node->m_sector->flags & SF_TRIGGERSPECIAL_TOUCH)) - return node->m_sector; + { + if (touchground) + { + topheight = P_GetSpecialTopZ(mo, node->m_sector, node->m_sector); + bottomheight = P_GetSpecialBottomZ(mo, node->m_sector, node->m_sector); + + // Thing must be on top of the floor to be affected... + if (node->m_sector->flags & SF_FLIPSPECIAL_FLOOR) + { + if (!(mo->eflags & MFE_VERTICALFLIP) && mo->z <= bottomheight) + return node->m_sector; + } + + if (node->m_sector->flags & SF_FLIPSPECIAL_CEILING) + { + if ((mo->eflags & MFE_VERTICALFLIP) && mo->z + mo->height >= topheight) + return node->m_sector; + } + } + else + { + return node->m_sector; + } + } } // Hmm.. maybe there's a FOF that has it... for (rover = node->m_sector->ffloors; rover; rover = rover->next) { - fixed_t topheight, bottomheight; - if (GETSECSPECIAL(rover->master->frontsector->special, section) != number) continue; diff --git a/src/p_spec.h b/src/p_spec.h index 8431fcc75..eff7463ca 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -37,7 +37,7 @@ void P_SpawnSpecials(INT32 fromnetsave); // every tic void P_UpdateSpecials(void); -sector_t *P_MobjTouchingSectorSpecial(mobj_t *mo, INT32 section, INT32 number); +sector_t *P_MobjTouchingSectorSpecial(mobj_t *mo, INT32 section, INT32 number, boolean touchground); void P_PlayerInSpecialSector(player_t *player); void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *roversector); From 0c1d0d8f5c74fb159a8935882375e481543c30c5 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sun, 1 Mar 2020 14:56:16 -0500 Subject: [PATCH 096/163] Don't need abs here --- 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 2f514f526..1f2e5e3de 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -11037,7 +11037,7 @@ void P_SpawnPrecipitation(void) if (precipsector->sector->ceilingpic != skyflatnum) continue; - height = abs(precipsector->sector->ceilingheight - precipsector->sector->floorheight); + height = precipsector->sector->ceilingheight - precipsector->sector->floorheight; // Exists, but is too small for reasonable precipitation. if (height < 64< Date: Sun, 1 Mar 2020 17:07:52 -0500 Subject: [PATCH 097/163] Remove our shadows --- src/k_kart.c | 164 ++++++++++++++++++++++++++++- src/p_local.h | 2 - src/p_mobj.c | 282 -------------------------------------------------- src/p_tick.c | 2 - 4 files changed, 162 insertions(+), 288 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 230662d2a..e82f61413 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -4345,6 +4345,167 @@ void K_RepairOrbitChain(mobj_t *orbit) } } +// Simplified version of a code bit in P_MobjFloorZ +static fixed_t K_BananaSlopeZ(pslope_t *slope, fixed_t x, fixed_t y, fixed_t radius, boolean ceiling) +{ + fixed_t testx, testy; + + if (slope->d.x < 0) + testx = radius; + else + testx = -radius; + + if (slope->d.y < 0) + testy = radius; + else + testy = -radius; + + if ((slope->zdelta > 0) ^ !!(ceiling)) + { + testx = -testx; + testy = -testy; + } + + testx += x; + testy += y; + + return P_GetZAt(slope, testx, testy); +} + +static void K_CalculateBananaSlope(mobj_t *mobj, fixed_t x, fixed_t y, fixed_t z, fixed_t radius, fixed_t height, boolean flip, boolean player) +{ + fixed_t newz; + sector_t *sec; +#ifdef ESLOPE + pslope_t *slope = NULL; +#endif + + sec = R_PointInSubsector(x, y)->sector; + + if (flip) + { +#ifdef ESLOPE + if (sec->c_slope) + { + slope = sec->c_slope; + newz = K_BananaSlopeZ(slope, x, y, radius, true); + } + else +#endif + newz = sec->ceilingheight; + } + else + { +#ifdef ESLOPE + if (sec->f_slope) + { + slope = sec->f_slope; + newz = K_BananaSlopeZ(slope, x, y, radius, false); + } + else +#endif + newz = sec->floorheight; + } + + // Check FOFs for a better suited slope + if (sec->ffloors) + { + ffloor_t *rover; + + for (rover = sec->ffloors; rover; rover = rover->next) + { + fixed_t top, bottom; + fixed_t d1, d2; + + if (!(rover->flags & FF_EXISTS)) + continue; + + if ((!(((rover->flags & FF_BLOCKPLAYER && player) + || (rover->flags & FF_BLOCKOTHERS && !player)) + || (rover->flags & FF_QUICKSAND)) + || (rover->flags & FF_SWIMMABLE))) + continue; + +#ifdef ESLOPE + if (*rover->t_slope) + top = K_BananaSlopeZ(*rover->t_slope, x, y, radius, false); + else +#endif + top = *rover->topheight; + +#ifdef ESLOPE + if (*rover->b_slope) + bottom = K_BananaSlopeZ(*rover->b_slope, x, y, radius, true); + else +#endif + bottom = *rover->bottomheight; + + if (flip) + { + if (rover->flags & FF_QUICKSAND) + { + if (z < top && (z + height) > bottom) + { + if (newz > (z + height)) + { + newz = (z + height); + slope = NULL; + } + } + continue; + } + + d1 = (z + height) - (top + ((bottom - top)/2)); + d2 = z - (top + ((bottom - top)/2)); + + if (bottom < newz && abs(d1) < abs(d2)) + { + newz = bottom; +#ifdef ESLOPE + if (*rover->b_slope) + slope = *rover->b_slope; +#endif + } + } + else + { + if (rover->flags & FF_QUICKSAND) + { + if (z < top && (z + height) > bottom) + { + if (newz < z) + { + newz = z; + slope = NULL; + } + } + continue; + } + + d1 = z - (bottom + ((top - bottom)/2)); + d2 = (z + height) - (bottom + ((top - bottom)/2)); + + if (top > newz && abs(d1) < abs(d2)) + { + newz = top; +#ifdef ESLOPE + if (*rover->t_slope) + slope = *rover->t_slope; +#endif + } + } + } + } + +#if 0 + mobj->standingslope = slope; +#endif + +#ifdef HWRENDER + mobj->modeltilt = slope; +#endif +} + // Move the hnext chain! static void K_MoveHeldObjects(player_t *player) { @@ -4532,8 +4693,7 @@ static void K_MoveHeldObjects(player_t *player) #ifdef ESLOPE if (P_IsObjectOnGround(cur)) { - // Slope values are set in the function, but we DON'T want to use its return value. - P_CalculateShadowFloor(cur, cur->x, cur->y, cur->z, + K_CalculateBananaSlope(cur, cur->x, cur->y, cur->z, cur->radius, cur->height, (cur->eflags & MFE_VERTICALFLIP), false); } #endif diff --git a/src/p_local.h b/src/p_local.h index 33f0026da..3ca171b24 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -228,8 +228,6 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state); boolean P_SetMobjState(mobj_t *mobj, statenum_t state); //void P_RunShields(void); void P_RunOverlays(void); -fixed_t P_CalculateShadowFloor(mobj_t *mobj, fixed_t x, fixed_t y, fixed_t z, fixed_t radius, fixed_t height, boolean flip, boolean player); -void P_RunShadows(void); void P_RunBattleOvertime(void); void P_MobjThinker(mobj_t *mobj); boolean P_RailThinker(mobj_t *mobj); diff --git a/src/p_mobj.c b/src/p_mobj.c index b220ff4e6..956d095f3 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -47,7 +47,6 @@ consvar_t cv_splats = {"splats", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0 actioncache_t actioncachehead; static mobj_t *overlaycap = NULL; -static mobj_t *shadowcap = NULL; mobj_t *waypointcap = NULL; void P_InitCachedActions(void) @@ -6200,287 +6199,6 @@ static void P_RemoveOverlay(mobj_t *thing) } } -// Simplified version of a code bit in P_MobjFloorZ -static fixed_t P_ShadowSlopeZ(pslope_t *slope, fixed_t x, fixed_t y, fixed_t radius, boolean ceiling) -{ - fixed_t testx, testy; - - if (slope->d.x < 0) - testx = radius; - else - testx = -radius; - - if (slope->d.y < 0) - testy = radius; - else - testy = -radius; - - if ((slope->zdelta > 0) ^ !!(ceiling)) - { - testx = -testx; - testy = -testy; - } - - testx += x; - testy += y; - - return P_GetZAt(slope, testx, testy); -} - -// Sets standingslope/modeltilt, returns z position for shadows; used also for stuff like bananas -// (I would've preferred to be able to return both the slope & z, but I'll take what I can get...) -fixed_t P_CalculateShadowFloor(mobj_t *mobj, fixed_t x, fixed_t y, fixed_t z, fixed_t radius, fixed_t height, boolean flip, boolean player) -{ - fixed_t newz; - sector_t *sec; -#ifdef ESLOPE - pslope_t *slope = NULL; -#endif - - sec = R_PointInSubsector(x, y)->sector; - - if (flip) - { -#ifdef ESLOPE - if (sec->c_slope) - { - slope = sec->c_slope; - newz = P_ShadowSlopeZ(slope, x, y, radius, true); - } - else -#endif - newz = sec->ceilingheight; - } - else - { -#ifdef ESLOPE - if (sec->f_slope) - { - slope = sec->f_slope; - newz = P_ShadowSlopeZ(slope, x, y, radius, false); - } - else -#endif - newz = sec->floorheight; - } - - // Check FOFs for a better suited slope - if (sec->ffloors) - { - ffloor_t *rover; - - for (rover = sec->ffloors; rover; rover = rover->next) - { - fixed_t top, bottom; - fixed_t d1, d2; - - if (!(rover->flags & FF_EXISTS)) - continue; - - if ((!(((rover->flags & FF_BLOCKPLAYER && player) - || (rover->flags & FF_BLOCKOTHERS && !player)) - || (rover->flags & FF_QUICKSAND)) - || (rover->flags & FF_SWIMMABLE))) - continue; - -#ifdef ESLOPE - if (*rover->t_slope) - top = P_ShadowSlopeZ(*rover->t_slope, x, y, radius, false); - else -#endif - top = *rover->topheight; - -#ifdef ESLOPE - if (*rover->b_slope) - bottom = P_ShadowSlopeZ(*rover->b_slope, x, y, radius, true); - else -#endif - bottom = *rover->bottomheight; - - if (flip) - { - if (rover->flags & FF_QUICKSAND) - { - if (z < top && (z + height) > bottom) - { - if (newz > (z + height)) - { - newz = (z + height); - slope = NULL; - } - } - continue; - } - - d1 = (z + height) - (top + ((bottom - top)/2)); - d2 = z - (top + ((bottom - top)/2)); - - if (bottom < newz && abs(d1) < abs(d2)) - { - newz = bottom; -#ifdef ESLOPE - if (*rover->b_slope) - slope = *rover->b_slope; -#endif - } - } - else - { - if (rover->flags & FF_QUICKSAND) - { - if (z < top && (z + height) > bottom) - { - if (newz < z) - { - newz = z; - slope = NULL; - } - } - continue; - } - - d1 = z - (bottom + ((top - bottom)/2)); - d2 = (z + height) - (bottom + ((top - bottom)/2)); - - if (top > newz && abs(d1) < abs(d2)) - { - newz = top; -#ifdef ESLOPE - if (*rover->t_slope) - slope = *rover->t_slope; -#endif - } - } - } - } - -#if 0 - mobj->standingslope = slope; -#endif -#ifdef HWRENDER - mobj->modeltilt = slope; -#endif - - return newz; -} - -void P_RunShadows(void) -{ - mobj_t *mobj, *next, *dest; - - for (mobj = shadowcap; mobj; mobj = next) - { - boolean flip; - fixed_t newz; - - next = mobj->hnext; - P_SetTarget(&mobj->hnext, NULL); - - if (!mobj->target || P_MobjWasRemoved(mobj->target)) - { - mobj->flags2 |= MF2_DONTDRAW; - continue; // shouldn't you already be dead? - } - - K_MatchGenericExtraFlags(mobj, mobj->target); - flip = (mobj->eflags & MFE_VERTICALFLIP); - - newz = P_CalculateShadowFloor(mobj, mobj->target->x, mobj->target->y, mobj->target->z, - mobj->target->radius, mobj->target->height, flip, (mobj->target->player != NULL)); - - if (flip) - { - if ((mobj->target->z + mobj->target->height) > newz) - mobj->flags2 |= MF2_DONTDRAW; - } - else - { - if (mobj->target->z < newz) - mobj->flags2 |= MF2_DONTDRAW; - } - - // First scale to the same radius - P_SetScale(mobj, FixedDiv(mobj->target->radius, mobj->info->radius)); - - dest = mobj->target; - - if (dest->type == MT_THUNDERSHIELD) - dest = dest->target; - - P_TeleportMove(mobj, dest->x, dest->y, mobj->target->z); - - if ((flip && newz > (mobj->z + mobj->height)) || (!flip && newz < mobj->z)) - { - INT32 i; - fixed_t prevz; - - mobj->z = newz; - - for (i = 0; i < MAXFFLOORS; i++) - { - prevz = mobj->z; - - // Now scale again based on height difference - P_SetScale(mobj, FixedDiv(mobj->scale, max(FRACUNIT, ((mobj->target->z-mobj->z)/200)+FRACUNIT))); - - // Check new position to see if you should still be on that ledge - P_TeleportMove(mobj, dest->x, dest->y, mobj->z); - - mobj->z = newz; - - if (mobj->z == prevz) - break; - } - } - - if (mobj->target->type == MT_FLOATINGITEM) - P_SetScale(mobj, mobj->scale/3); - } - P_SetTarget(&shadowcap, NULL); -} - -// called whenever shadows think -// It must be done this way so that level changes don't break when the shadowcap can't be reset -static void P_AddShadow(mobj_t *thing) -{ - I_Assert(thing != NULL); - - if (shadowcap == NULL) - P_SetTarget(&shadowcap, thing); - else { - mobj_t *mo; - for (mo = shadowcap; mo && mo->hnext; mo = mo->hnext) - ; - - I_Assert(mo != NULL); - I_Assert(mo->hnext == NULL); - - P_SetTarget(&mo->hnext, thing); - } - P_SetTarget(&thing->hnext, NULL); -} - -// Called only when MT_SHADOW (or anything else in the shadowcap list) is removed. -// Keeps the hnext list from corrupting. -static void P_RemoveShadow(mobj_t *thing) -{ - mobj_t *mo; - if (shadowcap == thing) - { - P_SetTarget(&shadowcap, thing->hnext); - P_SetTarget(&thing->hnext, NULL); - return; - } - - for (mo = shadowcap; mo; mo = mo->hnext) - if (mo->hnext == thing) - { - P_SetTarget(&mo->hnext, thing->hnext); - P_SetTarget(&thing->hnext, NULL); - return; - } -} - // SAL'S KART BATTLE MODE OVERTIME HANDLER #define MAXPLANESPERSECTOR (MAXFFLOORS+1)*2 static void P_SpawnOvertimeParticles(fixed_t x, fixed_t y, fixed_t scale, mobjtype_t type, boolean ceiling) diff --git a/src/p_tick.c b/src/p_tick.c index b285c35d0..9c73f6946 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -658,8 +658,6 @@ void P_Ticker(boolean run) //P_RunShields(); P_RunOverlays(); - P_RunShadows(); - P_UpdateSpecials(); P_RespawnSpecials(); From 30d391b6b207471a4763e39f0c3e9c4e5831aa83 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sun, 1 Mar 2020 17:35:49 -0500 Subject: [PATCH 098/163] FINISH removing the shadows... --- src/dehacked.c | 2 - src/info.c | 33 +---------- src/info.h | 4 -- src/lua_baselib.c | 11 ---- src/p_local.h | 2 - src/p_mobj.c | 146 ---------------------------------------------- 6 files changed, 1 insertion(+), 197 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index 5f9249d89..4d72835bb 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -7849,8 +7849,6 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_LAMPPOST", "MT_MOSSYTREE", - "MT_SHADOW", - "MT_BUMP", "MT_FLINGENERGY", diff --git a/src/info.c b/src/info.c index 4f448cc00..06ac8d9d4 100644 --- a/src/info.c +++ b/src/info.c @@ -60,7 +60,7 @@ char sprnames[NUMSPRITES + 1][5] = "WIPD","DRIF","BDRF","DUST","RSHE","FITM","BANA","ORBN","JAWZ","SSMN", "KRBM","BHOG","BHBM","SPBM","THNS","SINK","SITR","KBLN","DEZL","POKE", "AUDI","DECO","DOOD","SNES","GBAS","SPRS","BUZB","CHOM","SACO","CRAB", - "SHAD","BRNG","BUMP","FLEN","CLAS","PSHW","ISTA","ISTB","ARRO","ITEM", + "BRNG","BUMP","FLEN","CLAS","PSHW","ISTA","ISTB","ARRO","ITEM", "ITMO","ITMI","ITMN","WANT","PBOM","HIT1","HIT2","HIT3","RETI","AIDU", "KSPK","LZI1","LZI2","KLIT","FZSM","FZBM","FPRT","SBUS","MARB","FUFO", "RUST","BLON","VAPE","HTZA","HTZB","SGVA","SGVB","SGVC","PGTR","PGF1", @@ -2988,10 +2988,6 @@ state_t states[NUMSTATES] = {SPR_CRAB, 10, -1, {NULL}, 0, 0, S_NULL}, // S_LAMPPOST {SPR_CRAB, 11, -1, {NULL}, 0, 0, S_NULL}, // S_MOSSYTREE - // Fake Shadow - {SPR_SHAD, FF_TRANS50, -1, {NULL}, 0, 0, S_NULL}, // S_SHADOW - {SPR_SHAD, FF_FULLBRIGHT|FF_TRANS50|1, -1, {NULL}, 0, 0, S_NULL}, // S_WHITESHADOW - {SPR_BUMP, FF_FULLBRIGHT, 3, {NULL}, 0, 0, S_BUMP2}, // S_BUMP1 {SPR_BUMP, FF_FULLBRIGHT|1, 3, {NULL}, 0, 0, S_BUMP3}, // S_BUMP2 {SPR_BUMP, FF_FULLBRIGHT|2, 3, {NULL}, 0, 0, S_NULL}, // S_BUMP3 @@ -17251,33 +17247,6 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, - { // MT_SHADOW - -1, // doomednum - S_SHADOW, // spawnstate - 1000, // spawnhealth - S_NULL, // seestate - sfx_None, // seesound - 8, // reactiontime - sfx_None, // attacksound - S_NULL, // painstate - 0, // painchance - sfx_None, // painsound - S_NULL, // meleestate - S_NULL, // missilestate - S_NULL, // deathstate - S_NULL, // xdeathstate - sfx_None, // deathsound - 60*FRACUNIT, // speed - 50*FRACUNIT, // radius - 1*FRACUNIT, // height - -1, // display offset - 100, // mass - 0, // damage - sfx_None, // activesound - MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP, // flags - S_NULL // raisestate - }, - { // MT_BUMP -1, // doomednum S_BUMP1, // spawnstate diff --git a/src/info.h b/src/info.h index 27488d916..a3283217c 100644 --- a/src/info.h +++ b/src/info.h @@ -639,7 +639,6 @@ typedef enum sprite SPR_CHOM, // Sapphire Coast Chomper SPR_SACO, // Sapphire Coast Fauna SPR_CRAB, // Crystal Abyss mobs - SPR_SHAD, // TD shadows SPR_BRNG, // Chaotix Big Ring SPR_BUMP, // Player/shell bump @@ -3668,9 +3667,6 @@ typedef enum state S_LAMPPOST, S_MOSSYTREE, - S_SHADOW, - S_WHITESHADOW, - S_BUMP1, S_BUMP2, S_BUMP3, diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 7464743c3..da0a60801 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -639,16 +639,6 @@ static int lib_pCheckSolidLava(lua_State *L) return 1; } -static int lib_pSpawnShadowMobj(lua_State *L) -{ - mobj_t *caster = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); - NOHUD - if (!caster) - return LUA_ErrInvalid(L, "mobj_t"); - P_SpawnShadowMobj(caster); - return 0; -} - // P_USER //////////// @@ -2672,7 +2662,6 @@ static luaL_Reg lib[] = { {"P_InsideANonSolidFFloor",lib_pInsideANonSolidFFloor}, {"P_CheckDeathPitCollide",lib_pCheckDeathPitCollide}, {"P_CheckSolidLava",lib_pCheckSolidLava}, - {"P_SpawnShadowMobj",lib_pSpawnShadowMobj}, // p_user {"P_GetPlayerHeight",lib_pGetPlayerHeight}, diff --git a/src/p_local.h b/src/p_local.h index 3ca171b24..1358bf195 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -216,8 +216,6 @@ void P_RespawnSpecials(void); mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type); -mobj_t *P_SpawnShadowMobj(mobj_t * caster); - void P_RecalcPrecipInSector(sector_t *sector); void P_PrecipitationEffects(void); diff --git a/src/p_mobj.c b/src/p_mobj.c index 956d095f3..774fe45bc 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -6610,15 +6610,6 @@ void P_MobjThinker(mobj_t *mobj) P_AddOverlay(mobj); break; - case MT_SHADOW: - if (!mobj->target) - { - P_RemoveMobj(mobj); - return; - } - - P_AddShadow(mobj); - break; /*case MT_BLACKORB: case MT_WHITEORB: case MT_GREENORB: @@ -10298,29 +10289,6 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) break; } - switch (mobj->type) - { - case MT_PLAYER: - case MT_SMALLMACE: case MT_BIGMACE: - case MT_PUMA: case MT_BIGPUMA: - case MT_FALLINGROCK: - case MT_SMK_MOLE: case MT_SMK_THWOMP: - //case MT_RANDOMITEM: - case MT_FLOATINGITEM: - case MT_BATTLEBUMPER: - case MT_BANANA: case MT_BANANA_SHIELD: - //case MT_EGGMANITEM: case MT_EGGMANITEM_SHIELD: - case MT_ORBINAUT: case MT_ORBINAUT_SHIELD: - case MT_JAWZ: case MT_JAWZ_DUD: case MT_JAWZ_SHIELD: - case MT_SSMINE: case MT_SSMINE_SHIELD: - case MT_BALLHOG: case MT_SINK: - case MT_THUNDERSHIELD: case MT_ROCKETSNEAKER: - case MT_SPB: - P_SpawnShadowMobj(mobj); - default: - break; - } - if (!(mobj->flags & MF_NOTHINK)) P_AddThinker(&mobj->thinker); @@ -10356,120 +10324,6 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) return mobj; } -// -// P_SpawnShadowMobj -// warning: Do not send a shadow mobj as a caster into here, or try to spawn spawn shadows for shadows in P_SpawnMobj, we do not want recursive shadows -// -mobj_t *P_SpawnShadowMobj(mobj_t * caster) -{ - const mobjinfo_t *info = &mobjinfo[MT_SHADOW]; - state_t *st; - mobj_t *mobj = Z_Calloc(sizeof (*mobj), PU_LEVEL, NULL); - - // this is officially a mobj, declared as soon as possible. - mobj->thinker.function.acp1 = (actionf_p1)P_MobjThinker; - mobj->type = MT_SHADOW; - mobj->info = info; - - mobj->x = caster->x; - mobj->y = caster->y; - - mobj->radius = info->radius; - mobj->height = info->height; - mobj->flags = info->flags; - - mobj->health = info->spawnhealth; - - mobj->reactiontime = info->reactiontime; - - mobj->lastlook = -1; // stuff moved in P_enemy.P_LookForPlayer - - // do not set the state with P_SetMobjState, - // because action routines can not be called yet - if (caster->frame & FF_FULLBRIGHT) - st = &states[S_WHITESHADOW]; - else - st = &states[info->spawnstate]; - - mobj->state = st; - mobj->tics = st->tics; - mobj->sprite = st->sprite; - mobj->frame = st->frame; // FF_FRAMEMASK for frame, and other bits.. - P_SetupStateAnimation(mobj, st); - - mobj->friction = ORIG_FRICTION; - - mobj->movefactor = ORIG_FRICTION_FACTOR; - - // All mobjs are created at 100% scale. - mobj->scale = FRACUNIT; - mobj->destscale = mobj->scale; - mobj->scalespeed = FRACUNIT/12; - - if (mapobjectscale != FRACUNIT) //&& !(mobj->type == MT_BLACKEGGMAN) - { - mobj->destscale = mapobjectscale; - mobj->scalespeed = mapobjectscale/12; - } - - P_SetScale(mobj, mobj->destscale); - - // set subsector and/or block links - P_SetThingPosition(mobj); - I_Assert(mobj->subsector != NULL); - - // Make sure scale matches destscale immediately when spawned - P_SetScale(mobj, mobj->destscale); - - mobj->floorz = mobj->subsector->sector->floorheight; - mobj->ceilingz = mobj->subsector->sector->ceilingheight; - - // Tells MobjCheckWater that the water height was not set. - mobj->watertop = INT32_MAX; - - mobj->z = mobj->floorz; - - // defaults onground - if (mobj->z == mobj->floorz) - mobj->eflags |= MFE_ONGROUND; - - if (!(mobj->flags & MF_NOTHINK)) - P_AddThinker(&mobj->thinker); - - // Call action functions when the state is set - if (st->action.acp1 && (mobj->flags & MF_RUNSPAWNFUNC)) - { - if (levelloading) - { - // Cache actions in a linked list - // with function pointer, and - // var1 & var2, which will be executed - // when the level finishes loading. - P_AddCachedAction(mobj, mobj->info->spawnstate); - } - else - { - var1 = st->var1; - var2 = st->var2; -#ifdef HAVE_BLUA - astate = st; -#endif - st->action.acp1(mobj); - // DANGER! This is the ONLY way for P_SpawnMobj to return NULL! - // Avoid using MF_RUNSPAWNFUNC on mobjs whose spawn state expects target or tracer to already be set! - if (P_MobjWasRemoved(mobj)) - return NULL; - } - } - - if (CheckForReverseGravity && !(mobj->flags & MF_NOBLOCKMAP)) - P_CheckGravity(mobj, false); - - P_SetTarget(&mobj->target, caster); // set the shadow's caster as the target - - return mobj; -} - static precipmobj_t *P_SpawnPrecipMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) { state_t *st; From 7a3733d75c8c5404efbeef5d1f99693f9ea4b068 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sun, 1 Mar 2020 17:37:10 -0500 Subject: [PATCH 099/163] More remenants --- src/dehacked.c | 3 --- src/p_mobj.c | 3 --- 2 files changed, 6 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index 4d72835bb..aa3080879 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -6779,9 +6779,6 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_LAMPPOST", "S_MOSSYTREE", - "S_SHADOW", - "S_WHITESHADOW", - "S_BUMP1", "S_BUMP2", "S_BUMP3", diff --git a/src/p_mobj.c b/src/p_mobj.c index 774fe45bc..7c0c3d882 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -10448,9 +10448,6 @@ void P_RemoveMobj(mobj_t *mobj) if (mobj->type == MT_OVERLAY) P_RemoveOverlay(mobj); - if (mobj->type == MT_SHADOW) - P_RemoveShadow(mobj); - if (mobj->type == MT_SPB) spbplace = -1; From 834225c597e7fd9b5f5e0430a9f73010e41525e5 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sun, 1 Mar 2020 17:50:58 -0500 Subject: [PATCH 100/163] One more... I swear it's the last... --- src/info.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/info.h b/src/info.h index a3283217c..2b0e8e07f 100644 --- a/src/info.h +++ b/src/info.h @@ -4766,8 +4766,6 @@ typedef enum mobj_type MT_LAMPPOST, MT_MOSSYTREE, - MT_SHADOW, - MT_BUMP, MT_FLINGENERGY, From 18637b9eb2052dd52c32d509268cf1512f4603e3 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sun, 1 Mar 2020 22:12:50 -0500 Subject: [PATCH 101/163] 2.2 shadow port --- src/hardware/hw_main.c | 314 +++++++++------------------- src/k_kart.c | 1 + src/lua_mobjlib.c | 18 +- src/p_inter.c | 1 + src/p_mobj.c | 64 ++++++ src/p_mobj.h | 3 + src/r_main.c | 4 +- src/r_main.h | 2 +- src/r_things.c | 454 ++++++++++++++++++++++++++++++++++++----- src/r_things.h | 13 +- 10 files changed, 598 insertions(+), 276 deletions(-) diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 659af386d..281812469 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -39,6 +39,7 @@ #include "../st_stuff.h" #include "../i_system.h" #include "../m_cheat.h" +#include "../r_things.h" // R_GetShadowZ #ifdef ESLOPE #include "../p_slopes.h" #endif @@ -4058,37 +4059,6 @@ static gr_vissprite_t *HWR_NewVisSprite(void) return HWR_GetVisSprite(gr_visspritecount++); } -// Finds a floor through which light does not pass. -static fixed_t HWR_OpaqueFloorAtPos(fixed_t x, fixed_t y, fixed_t z, fixed_t height) -{ - const sector_t *sec = R_PointInSubsector(x, y)->sector; - fixed_t floorz = sec->floorheight; - - if (sec->ffloors) - { - ffloor_t *rover; - fixed_t delta1, delta2; - const fixed_t thingtop = z + height; - - for (rover = sec->ffloors; rover; rover = rover->next) - { - if (!(rover->flags & FF_EXISTS) - || !(rover->flags & FF_RENDERPLANES) - || rover->flags & FF_TRANSLUCENT - || rover->flags & FF_FOG - || rover->flags & FF_INVERTPLANES) - continue; - - delta1 = z - (*rover->bottomheight + ((*rover->topheight - *rover->bottomheight)/2)); - delta2 = thingtop - (*rover->bottomheight + ((*rover->topheight - *rover->bottomheight)/2)); - if (*rover->topheight > floorz && abs(delta1) < abs(delta2)) - floorz = *rover->topheight; - } - } - - return floorz; -} - // // HWR_DoCulling // Hardware version of R_DoCulling @@ -4129,185 +4099,116 @@ static boolean HWR_DoCulling(line_t *cullheight, line_t *viewcullheight, float v return false; } -static void HWR_DrawSpriteShadow(gr_vissprite_t *spr, GLPatch_t *gpatch, float this_scale) +static void HWR_DrawDropShadow(mobj_t *thing, fixed_t scale) { - FOutVector swallVerts[4]; + GLPatch_t *gpatch; + FOutVector shadowVerts[4]; FSurfaceInfo sSurf; - fixed_t floorheight, mobjfloor; - float offset = 0; + float fscale; float fx; float fy; float offset; + UINT8 lightlevel = 0; + extracolormap_t *colormap = NULL; + UINT8 i; - mobjfloor = HWR_OpaqueFloorAtPos( - spr->mobj->x, spr->mobj->y, - spr->mobj->z, spr->mobj->height); - if (cv_shadowoffs.value) + INT32 light; + fixed_t scalemul; + UINT16 alpha; + fixed_t floordiff; + fixed_t floorz; + fixed_t slopez; + pslope_t *floorslope; + + floorz = R_GetShadowZ(thing, &floorslope); + floordiff = abs(thing->z - floorz); + + alpha = floordiff / (4*FRACUNIT) + 75; + if (alpha >= 255) return; + alpha = 255 - alpha; + + if (thing->whiteshadow) { - angle_t shadowdir; - - // Set direction - if (splitscreen && stplyr == &players[displayplayers[1]]) - shadowdir = localangle[1] + FixedAngle(cv_cam2_rotate.value); - else if (splitscreen > 1 && stplyr == &players[displayplayers[2]]) - shadowdir = localangle[2] + FixedAngle(cv_cam3_rotate.value); - else if (splitscreen > 2 && stplyr == &players[displayplayers[3]]) - shadowdir = localangle[3] + FixedAngle(cv_cam4_rotate.value); - else - shadowdir = localangle[0] + FixedAngle(cv_cam_rotate.value); - - // Find floorheight - floorheight = HWR_OpaqueFloorAtPos( - spr->mobj->x + P_ReturnThrustX(spr->mobj, shadowdir, spr->mobj->z - mobjfloor), - spr->mobj->y + P_ReturnThrustY(spr->mobj, shadowdir, spr->mobj->z - mobjfloor), - spr->mobj->z, spr->mobj->height); - - // The shadow is falling ABOVE it's mobj? - // Don't draw it, then! - if (spr->mobj->z < floorheight) - return; - else - { - fixed_t floorz; - floorz = HWR_OpaqueFloorAtPos( - spr->mobj->x + P_ReturnThrustX(spr->mobj, shadowdir, spr->mobj->z - floorheight), - spr->mobj->y + P_ReturnThrustY(spr->mobj, shadowdir, spr->mobj->z - floorheight), - spr->mobj->z, spr->mobj->height); - // The shadow would be falling on a wall? Don't draw it, then. - // Would draw midair otherwise. - if (floorz < floorheight) - return; - } - - floorheight = FixedInt(spr->mobj->z - floorheight); - - offset = floorheight; + gpatch = (GLPatch_t *)W_CachePatchName("LSHADOW", PU_CACHE); + lightlevel = 255; } else - floorheight = FixedInt(spr->mobj->z - mobjfloor); + { + gpatch = (GLPatch_t *)W_CachePatchName("DSHADOW", PU_CACHE); + lightlevel = 0; + } + + if (!(gpatch && gpatch->mipmap.grInfo.format)) return; + HWR_GetPatch(gpatch); + + scalemul = FixedMul(FRACUNIT - floordiff/640, scale); + scalemul = FixedMul(scalemul, (thing->radius*2) / gpatch->height); + + fscale = FIXED_TO_FLOAT(scalemul); + fx = FIXED_TO_FLOAT(thing->x); + fy = FIXED_TO_FLOAT(thing->y); - // create the sprite billboard - // // 3--2 // | /| // |/ | // 0--1 - // x1/x2 were already scaled in HWR_ProjectSprite - // First match the normal sprite - swallVerts[0].x = swallVerts[3].x = spr->x1; - swallVerts[2].x = swallVerts[1].x = spr->x2; - swallVerts[0].z = swallVerts[3].z = spr->z1; - swallVerts[2].z = swallVerts[1].z = spr->z2; - - if (spr->mobj && fabsf(this_scale - 1.0f) > 1.0E-36f) - { - // Always a pixel above the floor, perfectly flat. - swallVerts[0].y = swallVerts[1].y = swallVerts[2].y = swallVerts[3].y = spr->ty - gpatch->topoffset * this_scale - (floorheight+3); - - // Now transform the TOP vertices along the floor in the direction of the camera - swallVerts[3].x = spr->x1 + ((gpatch->height * this_scale) + offset) * gr_viewcos; - swallVerts[2].x = spr->x2 + ((gpatch->height * this_scale) + offset) * gr_viewcos; - swallVerts[3].z = spr->z1 + ((gpatch->height * this_scale) + offset) * gr_viewsin; - swallVerts[2].z = spr->z2 + ((gpatch->height * this_scale) + offset) * gr_viewsin; - } + if (thing && fabsf(fscale - 1.0f) > 1.0E-36f) + offset = (gpatch->height/2) * fscale; else - { - // Always a pixel above the floor, perfectly flat. - swallVerts[0].y = swallVerts[1].y = swallVerts[2].y = swallVerts[3].y = spr->ty - gpatch->topoffset - (floorheight+3); + offset = (float)(gpatch->height/2); - // Now transform the TOP vertices along the floor in the direction of the camera - swallVerts[3].x = spr->x1 + (gpatch->height + offset) * gr_viewcos; - swallVerts[2].x = spr->x2 + (gpatch->height + offset) * gr_viewcos; - swallVerts[3].z = spr->z1 + (gpatch->height + offset) * gr_viewsin; - swallVerts[2].z = spr->z2 + (gpatch->height + offset) * gr_viewsin; + shadowVerts[2].x = shadowVerts[3].x = fx + offset; + shadowVerts[1].x = shadowVerts[0].x = fx - offset; + shadowVerts[1].z = shadowVerts[2].z = fy - offset; + shadowVerts[0].z = shadowVerts[3].z = fy + offset; + + for (i = 0; i < 4; i++) + { + float oldx = shadowVerts[i].x; + float oldy = shadowVerts[i].z; + shadowVerts[i].x = fx + ((oldx - fx) * gr_viewcos) - ((oldy - fy) * gr_viewsin); + shadowVerts[i].z = fy + ((oldx - fx) * gr_viewsin) + ((oldy - fy) * gr_viewcos); } - // We also need to move the bottom ones away when shadowoffs is on - if (cv_shadowoffs.value) + if (floorslope) { - swallVerts[0].x = spr->x1 + offset * gr_viewcos; - swallVerts[1].x = spr->x2 + offset * gr_viewcos; - swallVerts[0].z = spr->z1 + offset * gr_viewsin; - swallVerts[1].z = spr->z2 + offset * gr_viewsin; - } - - if (spr->flip) - { - swallVerts[0].sow = swallVerts[3].sow = gpatch->max_s; - swallVerts[2].sow = swallVerts[1].sow = 0; - } - else - { - swallVerts[0].sow = swallVerts[3].sow = 0; - swallVerts[2].sow = swallVerts[1].sow = gpatch->max_s; - } - - // flip the texture coords (look familiar?) - if (spr->vflip) - { - swallVerts[3].tow = swallVerts[2].tow = gpatch->max_t; - swallVerts[0].tow = swallVerts[1].tow = 0; - } - else - { - swallVerts[3].tow = swallVerts[2].tow = 0; - swallVerts[0].tow = swallVerts[1].tow = gpatch->max_t; - } - - sSurf.FlatColor.s.red = 0x00; - sSurf.FlatColor.s.blue = 0x00; - sSurf.FlatColor.s.green = 0x00; - - /*if (spr->mobj->frame & FF_TRANSMASK || spr->mobj->flags2 & MF2_SHADOW) - { - sector_t *sector = spr->mobj->subsector->sector; - UINT8 lightlevel = 255; - extracolormap_t *colormap = sector->extra_colormap; - - if (sector->numlights) + for (i = 0; i < 4; i++) { - INT32 light = R_GetPlaneLight(sector, spr->mobj->floorz, false); - - if (!(spr->mobj->frame & FF_FULLBRIGHT)) - { - lightlevel = *sector->lightlist[light].lightlevel; - if (spr->mobj->frame & FF_SEMIBRIGHT) - lightlevel = 128 + (lightlevel>>1); - } - - if (sector->lightlist[light].extra_colormap) - colormap = sector->lightlist[light].extra_colormap; + slopez = P_GetZAt(floorslope, FLOAT_TO_FIXED(shadowVerts[i].x), FLOAT_TO_FIXED(shadowVerts[i].z)); + shadowVerts[i].y = FIXED_TO_FLOAT(slopez) + 0.05f; } - else - { - lightlevel = sector->lightlevel; - - if (sector->extra_colormap) - colormap = sector->extra_colormap; - } - - if (colormap) - sSurf.FlatColor.rgba = HWR_Lighting(lightlevel/2, colormap->rgba, colormap->fadergba, false, true); - else - sSurf.FlatColor.rgba = HWR_Lighting(lightlevel/2, NORMALFOG, FADEFOG, false, true); - }*/ - - // shadow is always half as translucent as the sprite itself - if (!cv_translucency.value) // use default translucency (main sprite won't have any translucency) - sSurf.FlatColor.s.alpha = 0x80; // default - else if (spr->mobj->flags2 & MF2_SHADOW) - sSurf.FlatColor.s.alpha = 0x20; - else if (spr->mobj->frame & FF_TRANSMASK) - { - HWR_TranstableToAlpha((spr->mobj->frame & FF_TRANSMASK)>>FF_TRANSSHIFT, &sSurf); - sSurf.FlatColor.s.alpha /= 2; //cut alpha in half! } else - sSurf.FlatColor.s.alpha = 0x80; // default - - if (sSurf.FlatColor.s.alpha > floorheight/4) { - sSurf.FlatColor.s.alpha = (UINT8)(sSurf.FlatColor.s.alpha - floorheight/4); - HWD.pfnDrawPolygon(&sSurf, swallVerts, 4, PF_Translucent|PF_Modulated|PF_Clip); + for (i = 0; i < 4; i++) + shadowVerts[i].y = FIXED_TO_FLOAT(floorz) + 0.05f; } + + shadowVerts[0].sow = shadowVerts[3].sow = 0; + shadowVerts[2].sow = shadowVerts[1].sow = gpatch->max_s; + + shadowVerts[3].tow = shadowVerts[2].tow = 0; + shadowVerts[0].tow = shadowVerts[1].tow = gpatch->max_t; + + if (thing->subsector->sector->numlights) + { + light = R_GetPlaneLight(thing->subsector->sector, floorz, false); // Always use the light at the top instead of whatever I was doing before + + if (thing->subsector->sector->lightlist[light].extra_colormap) + colormap = thing->subsector->sector->lightlist[light].extra_colormap; + } + else + { + if (thing->subsector->sector->extra_colormap) + colormap = thing->subsector->sector->extra_colormap; + } + + if (colormap) + sSurf.FlatColor.rgba = HWR_Lighting(lightlevel, colormap->rgba, colormap->fadergba, false, false); + else + sSurf.FlatColor.rgba = HWR_Lighting(lightlevel, NORMALFOG, FADEFOG, false, false); + + sSurf.FlatColor.s.alpha = alpha; + + HWD.pfnDrawPolygon(&sSurf, shadowVerts, 4, PF_Translucent|PF_Modulated|PF_Clip); } // This is expecting a pointer to an array containing 4 wallVerts for a sprite @@ -4384,22 +4285,6 @@ static void HWR_SplitSprite(gr_vissprite_t *spr) //Hurdler: 25/04/2000: now support colormap in hardware mode HWR_GetMappedPatch(gpatch, spr->colormap); - // Draw shadow BEFORE sprite - if (cv_shadow.value // Shadows enabled - && (spr->mobj->flags & (MF_SCENERY|MF_SPAWNCEILING|MF_NOGRAVITY)) != (MF_SCENERY|MF_SPAWNCEILING|MF_NOGRAVITY) // Ceiling scenery have no shadow. - && !(spr->mobj->flags2 & MF2_DEBRIS) // Debris have no corona or shadow. -#ifdef ALAM_LIGHTING - && !(t_lspr[spr->mobj->sprite]->type // Things with dynamic lights have no shadow. - && (!spr->mobj->player || spr->mobj->player->powers[pw_super])) // Except for non-super players. -#endif - && (spr->mobj->z >= spr->mobj->floorz)) // Without this, your shadow shows on the floor, even after you die and fall through the ground. - { - //////////////////// - // SHADOW SPRITE! // - //////////////////// - HWR_DrawSpriteShadow(spr, gpatch, this_scale); - } - baseWallVerts[0].x = baseWallVerts[3].x = spr->x1; baseWallVerts[2].x = baseWallVerts[1].x = spr->x2; baseWallVerts[0].z = baseWallVerts[3].z = spr->z1; @@ -4802,22 +4687,6 @@ static void HWR_DrawSprite(gr_vissprite_t *spr) //Hurdler: 25/04/2000: now support colormap in hardware mode HWR_GetMappedPatch(gpatch, spr->colormap); - // Draw shadow BEFORE sprite - if (cv_shadow.value // Shadows enabled - && (spr->mobj->flags & (MF_SCENERY|MF_SPAWNCEILING|MF_NOGRAVITY)) != (MF_SCENERY|MF_SPAWNCEILING|MF_NOGRAVITY) // Ceiling scenery have no shadow. - && !(spr->mobj->flags2 & MF2_DEBRIS) // Debris have no corona or shadow. -#ifdef ALAM_LIGHTING - && !(t_lspr[spr->mobj->sprite]->type // Things with dynamic lights have no shadow. - && (!spr->mobj->player || spr->mobj->player->powers[pw_super])) // Except for non-super players. -#endif - && (spr->mobj->z >= spr->mobj->floorz)) // Without this, your shadow shows on the floor, even after you die and fall through the ground. - { - //////////////////// - // SHADOW SPRITE! // - //////////////////// - HWR_DrawSpriteShadow(spr, gpatch, this_scale); - } - // if it has a dispoffset, push it a little towards the camera if (spr->dispoffset) { float co = -gr_viewcos*(0.05f*spr->dispoffset); @@ -5430,6 +5299,12 @@ static void HWR_DrawSprites(void) HWR_DrawPrecipitationSprite(spr); else #endif + { + if (spr->mobj && spr->mobj->shadowscale && cv_shadow.value) + { + HWR_DrawDropShadow(spr->mobj, spr->mobj->shadowscale); + } + if (spr->mobj && spr->mobj->skin && spr->mobj->sprite == SPR_PLAY) { // 8/1/19: Only don't display player models if no default SPR_PLAY is found. @@ -5445,6 +5320,7 @@ static void HWR_DrawSprites(void) else HWR_DrawMD2(spr); } + } } } } diff --git a/src/k_kart.c b/src/k_kart.c index e82f61413..10e395d0e 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6212,6 +6212,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) P_SetMobjState(ring, S_FASTRING1); ring->extravalue1 = 1; // Ring use animation timer ring->extravalue2 = 1; // Ring use animation flag + ring->shadowscale = 0; P_SetTarget(&ring->target, player->mo); // user player->kartstuff[k_rings]--; player->kartstuff[k_ringdelay] = 3; diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c index dfb344e34..47efd729f 100644 --- a/src/lua_mobjlib.c +++ b/src/lua_mobjlib.c @@ -85,7 +85,9 @@ enum mobj_e { #ifdef ESLOPE mobj_standingslope, #endif - mobj_colorized + mobj_colorized, + mobj_shadowscale, + mobj_whiteshadow }; static const char *const mobj_opt[] = { @@ -149,6 +151,8 @@ static const char *const mobj_opt[] = { "standingslope", #endif "colorized", + "shadowscale", + "whiteshadow", NULL}; #define UNIMPLEMENTED luaL_error(L, LUA_QL("mobj_t") " field " LUA_QS " is not implemented for Lua and cannot be accessed.", mobj_opt[field]) @@ -360,6 +364,12 @@ static int mobj_get(lua_State *L) case mobj_colorized: lua_pushboolean(L, mo->colorized); break; + case mobj_shadowscale: + lua_pushfixed(L, mo->shadowscale); + break; + case mobj_whiteshadow: + lua_pushboolean(L, mo->whiteshadow); + break; default: // extra custom variables in Lua memory lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS); I_Assert(lua_istable(L, -1)); @@ -677,6 +687,12 @@ static int mobj_set(lua_State *L) case mobj_colorized: mo->colorized = luaL_checkboolean(L, 3); break; + case mobj_shadowscale: + mo->shadowscale = luaL_checkfixed(L, 3); + break; + case mobj_whiteshadow: + mo->whiteshadow = luaL_checkboolean(L, 3); + break; default: lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS); I_Assert(lua_istable(L, -1)); diff --git a/src/p_inter.c b/src/p_inter.c index 7a975951c..7fe9eeda3 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -2157,6 +2157,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source) target->flags &= ~(MF_SHOOTABLE|MF_FLOAT|MF_SPECIAL); target->flags2 &= ~(MF2_SKULLFLY|MF2_NIGHTSPULL); target->health = 0; // This makes it easy to check if something's dead elsewhere. + target->shadowscale = 0; #ifdef HAVE_BLUA if (LUAh_MobjDeath(target, inflictor, source) || P_MobjWasRemoved(target)) diff --git a/src/p_mobj.c b/src/p_mobj.c index 7c0c3d882..2cbda8c33 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -3729,11 +3729,15 @@ static void P_PlayerMobjThinker(mobj_t *mobj) mobj->z += mobj->momz; P_SetThingPosition(mobj); P_CheckPosition(mobj, mobj->x, mobj->y); + mobj->floorz = tmfloorz; + mobj->ceilingz = tmceilingz; goto animonly; } else if (mobj->player->pflags & PF_MACESPIN && mobj->tracer) { P_CheckPosition(mobj, mobj->x, mobj->y); + mobj->floorz = tmfloorz; + mobj->ceilingz = tmceilingz; goto animonly; } @@ -9900,6 +9904,63 @@ void P_SceneryThinker(mobj_t *mobj) // GAME SPAWN FUNCTIONS // +static void P_DefaultMobjShadowScale(mobj_t *thing) +{ + thing->shadowscale = 0; + thing->whiteshadow = (thing->frame & FF_FULLBRIGHT); + + switch (thing->type) + { + case MT_PLAYER: + case MT_SMALLMACE: + case MT_BIGMACE: + case MT_PUMA: + case MT_BIGPUMA: + case MT_FALLINGROCK: + case MT_SMK_MOLE: + case MT_SMK_THWOMP: + case MT_BATTLEBUMPER: + case MT_BANANA: + case MT_ORBINAUT: + case MT_ORBINAUT_SHIELD: + case MT_JAWZ: + case MT_JAWZ_DUD: + case MT_JAWZ_SHIELD: + case MT_SSMINE: + case MT_SSMINE_SHIELD: + case MT_BALLHOG: + case MT_SINK: + case MT_THUNDERSHIELD: + case MT_ROCKETSNEAKER: + case MT_SPB: + thing->shadowscale = 3*FRACUNIT/2; + break; + case MT_BANANA_SHIELD: + thing->shadowscale = 12*FRACUNIT/5; + break; + case MT_RANDOMITEM: + thing->shadowscale = FRACUNIT/2; + thing->whiteshadow = false; + break; + case MT_EGGMANITEM: + thing->shadowscale = FRACUNIT; + thing->whiteshadow = false; + break; + case MT_EGGMANITEM_SHIELD: + thing->shadowscale = 3*FRACUNIT/2; + thing->whiteshadow = false; + break; + case MT_RING: + case MT_FLOATINGITEM: + thing->shadowscale = FRACUNIT/2; + break; + default: + if (thing->flags & (MF_ENEMY|MF_BOSS)) + thing->shadowscale = FRACUNIT; + break; + } +} + // // P_SpawnMobj // @@ -10000,6 +10061,9 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) mobj->colorized = false; + // Set shadowscale here, before spawn hook so that Lua can change it + P_DefaultMobjShadowScale(mobj); + #ifdef HAVE_BLUA // DANGER! This can cause P_SpawnMobj to return NULL! // Avoid using P_RemoveMobj on the newly created mobj in "MobjSpawn" Lua hooks! diff --git a/src/p_mobj.h b/src/p_mobj.h index aec2ed951..7b3d278f7 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -377,6 +377,9 @@ typedef struct mobj_s boolean colorized; // Whether the mobj uses the rainbow colormap + fixed_t shadowscale; // If this object casts a shadow, and the size relative to radius + boolean whiteshadow; // Use white shadow, set to true by default for fullbright objects + // WARNING: New fields must be added separately to savegame and Lua. } mobj_t; diff --git a/src/r_main.c b/src/r_main.c index 0d14bed73..a4fa9d463 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -168,8 +168,7 @@ consvar_t cv_flipcam2 = {"flipcam2", "No", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo, consvar_t cv_flipcam3 = {"flipcam3", "No", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo, FlipCam3_OnChange, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_flipcam4 = {"flipcam4", "No", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo, FlipCam4_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_shadow = {"shadow", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_shadowoffs = {"offsetshadows", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_shadow = {"shadow", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_skybox = {"skybox", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_soniccd = {"soniccd", "Off", CV_NETVAR|CV_NOSHOWHELP, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_allowmlook = {"allowmlook", "Yes", CV_NETVAR, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL}; @@ -1505,7 +1504,6 @@ void R_RegisterEngineStuff(void) CV_RegisterVar(&cv_chasecam3); CV_RegisterVar(&cv_chasecam4); CV_RegisterVar(&cv_shadow); - CV_RegisterVar(&cv_shadowoffs); CV_RegisterVar(&cv_skybox); CV_RegisterVar(&cv_cam_dist); diff --git a/src/r_main.h b/src/r_main.h index 38a589682..879d4c6eb 100644 --- a/src/r_main.h +++ b/src/r_main.h @@ -76,7 +76,7 @@ extern consvar_t cv_showhud, cv_translucenthud; extern consvar_t cv_homremoval; extern consvar_t cv_chasecam, cv_chasecam2, cv_chasecam3, cv_chasecam4; extern consvar_t cv_flipcam, cv_flipcam2, cv_flipcam3, cv_flipcam4; -extern consvar_t cv_shadow, cv_shadowoffs; +extern consvar_t cv_shadow; extern consvar_t cv_translucency; extern consvar_t /*cv_precipdensity,*/ cv_drawdist, /*cv_drawdist_nights,*/ cv_drawdist_precip; extern consvar_t cv_fov; diff --git a/src/r_things.c b/src/r_things.c index 1afbb125c..b747d3493 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -802,9 +802,7 @@ static void R_DrawFlippedMaskedColumn(column_t *column, INT32 texheight) static void R_DrawVisSprite(vissprite_t *vis) { column_t *column; -#ifdef RANGECHECK INT32 texturecolumn; -#endif fixed_t frac; patch_t *patch = W_CacheLumpNum(vis->patch, PU_CACHE); fixed_t this_scale = vis->mobj->scale; @@ -920,6 +918,7 @@ static void R_DrawVisSprite(vissprite_t *vis) if (!(vis->scalestep)) { sprtopscreen = centeryfrac - FixedMul(dc_texturemid, spryscale); + sprtopscreen += vis->shear.tan * vis->shear.offset; dc_iscale = FixedDiv(FRACUNIT, vis->scale); } @@ -942,31 +941,50 @@ static void R_DrawVisSprite(vissprite_t *vis) vis->x2--; #endif - for (dc_x = vis->x1; dc_x <= vis->x2; dc_x++, frac += vis->xiscale) + // Split drawing loops for paper and non-paper to reduce conditional checks per sprite + if (vis->scalestep) { - if (vis->scalestep) // currently papersprites only + // Papersprite drawing loop + for (dc_x = vis->x1; dc_x <= vis->x2; dc_x++, spryscale += vis->scalestep) { -#ifndef RANGECHECK - if ((frac>>FRACBITS) < 0 || (frac>>FRACBITS) >= SHORT(patch->width)) // if this doesn't work i'm removing papersprites - break; -#endif + angle_t angle = ((vis->centerangle + xtoviewangle[dc_x]) >> ANGLETOFINESHIFT) & 0xFFF; + texturecolumn = (vis->paperoffset - FixedMul(FINETANGENT(angle), vis->paperdistance)) / this_scale; + + if (texturecolumn < 0 || texturecolumn >= SHORT(patch->width)) + continue; + + if (vis->xiscale < 0) // Flipped sprite + texturecolumn = SHORT(patch->width) - 1 - texturecolumn; + sprtopscreen = (centeryfrac - FixedMul(dc_texturemid, spryscale)); dc_iscale = (0xffffffffu / (unsigned)spryscale); - spryscale += vis->scalestep; - } -#ifdef RANGECHECK - texturecolumn = frac>>FRACBITS; - if (texturecolumn < 0 || texturecolumn >= SHORT(patch->width)) - I_Error("R_DrawSpriteRange: bad texturecolumn"); - column = (column_t *)((UINT8 *)patch + LONG(patch->columnofs[texturecolumn])); + column = (column_t *)((UINT8 *)patch + LONG(patch->columnofs[texturecolumn])); + + if (vis->cut & SC_VFLIP) + R_DrawFlippedMaskedColumn(column, patch->height); + else + R_DrawMaskedColumn(column); + } + } + else + { + // Non-paper drawing loop + for (dc_x = vis->x1; dc_x <= vis->x2; dc_x++, frac += vis->xiscale, sprtopscreen += vis->shear.tan) + { +#ifdef RANGECHECK + texturecolumn = frac>>FRACBITS; + if (texturecolumn < 0 || texturecolumn >= SHORT(patch->width)) + I_Error("R_DrawSpriteRange: bad texturecolumn at %d from end", vis->x2 - dc_x); + column = (column_t *)((UINT8 *)patch + LONG(patch->columnofs[texturecolumn])); #else - column = (column_t *)((UINT8 *)patch + LONG(patch->columnofs[frac>>FRACBITS])); + column = (column_t *)((UINT8 *)patch + LONG(patch->columnofs[frac>>FRACBITS])); #endif - if (vis->cut & SC_VFLIP) - R_DrawFlippedMaskedColumn(column, patch->height); - else - R_DrawMaskedColumn(column); + if (vis->cut & SC_VFLIP) + R_DrawFlippedMaskedColumn(column, patch->height); + else + R_DrawMaskedColumn(column); + } } colfunc = basecolfunc; @@ -1138,6 +1156,278 @@ static void R_SplitSprite(vissprite_t *sprite) } } +// +// R_GetShadowZ(thing, shadowslope) +// Get the first visible floor below the object for shadows +// shadowslope is filled with the floor's slope, if provided +// +fixed_t R_GetShadowZ(mobj_t *thing, pslope_t **shadowslope) +{ + fixed_t z, floorz = INT32_MIN; + pslope_t *slope, *floorslope = NULL; + msecnode_t *node; + sector_t *sector; + ffloor_t *rover; + + for (node = thing->touching_sectorlist; node; node = node->m_sectorlist_next) + { + sector = node->m_sector; + + slope = (sector->heightsec != -1) ? NULL : sector->f_slope; + z = slope ? P_GetZAt(slope, thing->x, thing->y) : ( + (sector->heightsec != -1) ? sectors[sector->heightsec].floorheight : sector->floorheight + ); + + if (z < thing->z+thing->height/2 && z > floorz) + { + floorz = z; + floorslope = slope; + } + + if (sector->ffloors) + for (rover = sector->ffloors; rover; rover = rover->next) + { + if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_RENDERPLANES) || (rover->alpha < 90 && !(rover->flags & FF_SWIMMABLE))) + continue; + + z = *rover->t_slope ? P_GetZAt(*rover->t_slope, thing->x, thing->y) : *rover->topheight; + if (z < thing->z+thing->height/2 && z > floorz) + { + floorz = z; + floorslope = *rover->t_slope; + } + } + } + + if (thing->floorz > floorz + (!floorslope ? 0 : FixedMul(abs(floorslope->zdelta), thing->radius*3/2))) + { + floorz = thing->floorz; + floorslope = NULL; + } + +#if 0 // Unfortunately, this drops CEZ2 down to sub-17 FPS on my i7. +//#ifdef POLYOBJECTS + // Check polyobjects and see if floorz needs to be altered, for rings only because they don't update floorz + if (thing->type == MT_RING) + { + INT32 xl, xh, yl, yh, bx, by; + + xl = (unsigned)(thing->x - thing->radius - bmaporgx)>>MAPBLOCKSHIFT; + xh = (unsigned)(thing->x + thing->radius - bmaporgx)>>MAPBLOCKSHIFT; + yl = (unsigned)(thing->y - thing->radius - bmaporgy)>>MAPBLOCKSHIFT; + yh = (unsigned)(thing->y + thing->radius - bmaporgy)>>MAPBLOCKSHIFT; + + BMBOUNDFIX(xl, xh, yl, yh); + + validcount++; + + for (by = yl; by <= yh; by++) + for (bx = xl; bx <= xh; bx++) + { + INT32 offset; + polymaplink_t *plink; // haleyjd 02/22/06 + + if (bx < 0 || by < 0 || bx >= bmapwidth || by >= bmapheight) + continue; + + offset = by*bmapwidth + bx; + + // haleyjd 02/22/06: consider polyobject lines + plink = polyblocklinks[offset]; + + while (plink) + { + polyobj_t *po = plink->po; + + if (po->validcount != validcount) // if polyobj hasn't been checked + { + po->validcount = validcount; + + if (!P_MobjInsidePolyobj(po, thing) || !(po->flags & POF_RENDERPLANES)) + { + plink = (polymaplink_t *)(plink->link.next); + continue; + } + + // We're inside it! Yess... + z = po->lines[0]->backsector->ceilingheight; + + if (z < thing->z+thing->height/2 && z > floorz) + { + floorz = z; + floorslope = NULL; + } + } + plink = (polymaplink_t *)(plink->link.next); + } + } + } +#endif + + if (shadowslope != NULL) + *shadowslope = floorslope; + + return floorz; +} + +static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale, fixed_t tx, fixed_t tz) +{ + vissprite_t *shadow; + patch_t *patch; + fixed_t xscale, yscale, shadowxscale, shadowyscale, shadowskew, x1, x2; + INT32 light = 0; + fixed_t scalemul; UINT8 trans; + fixed_t floordiff; + fixed_t floorz; + pslope_t *floorslope; + + floorz = R_GetShadowZ(thing, &floorslope); + + if (abs(floorz-viewz)/tz > 4) return; // Prevent stretchy shadows and possible crashes + + floordiff = abs(thing->z - floorz); + + trans = floordiff / (100*FRACUNIT) + 3; + if (trans >= 9) return; + + scalemul = FixedMul(FRACUNIT - floordiff/640, scale); + + if (thing->whiteshadow) + patch = W_CachePatchName("LSHADOW", PU_CACHE); + else + patch = W_CachePatchName("DSHADOW", PU_CACHE); + + xscale = FixedDiv(projection, tz); + yscale = FixedDiv(projectiony, tz); + shadowxscale = FixedMul(thing->radius*2, scalemul); + shadowyscale = FixedMul(FixedMul(thing->radius*2, scalemul), FixedDiv(abs(floorz - viewz), tz)); + shadowyscale = min(shadowyscale, shadowxscale) / patch->height; + shadowxscale /= patch->width; + shadowskew = 0; + + if (floorslope) + { + // haha let's try some dumb stuff + fixed_t xslope, zslope; + angle_t sloperelang = (R_PointToAngle(thing->x, thing->y) - floorslope->xydirection) >> ANGLETOFINESHIFT; + + xslope = FixedMul(FINESINE(sloperelang), floorslope->zdelta); + zslope = FixedMul(FINECOSINE(sloperelang), floorslope->zdelta); + + //CONS_Printf("Shadow is sloped by %d %d\n", xslope, zslope); + + if (viewz < floorz) + shadowyscale += FixedMul(FixedMul(thing->radius*2 / patch->height, scalemul), zslope); + else + shadowyscale -= FixedMul(FixedMul(thing->radius*2 / patch->height, scalemul), zslope); + + shadowyscale = abs(shadowyscale); + + shadowskew = xslope; + } + + tx -= patch->width * shadowxscale/2; + x1 = (centerxfrac + FixedMul(tx,xscale))>>FRACBITS; + if (x1 >= viewwidth) return; + + tx += patch->width * shadowxscale; + x2 = ((centerxfrac + FixedMul(tx,xscale))>>FRACBITS); x2--; + if (x2 < 0 || x2 <= x1) return; + + if (shadowyscale < FRACUNIT/patch->height) return; // fix some crashes? + + shadow = R_NewVisSprite(); + + if (thing->whiteshadow) + shadow->patch = W_CheckNumForName("LSHADOW"); + else + shadow->patch = W_CheckNumForName("DSHADOW"); + + shadow->heightsec = vis->heightsec; + + shadow->thingheight = FRACUNIT; + shadow->pz = floorz; + shadow->pzt = shadow->pz + shadow->thingheight; + + shadow->mobjflags = 0; + shadow->sortscale = vis->sortscale; + shadow->dispoffset = vis->dispoffset - 5; + shadow->gx = thing->x; + shadow->gy = thing->y; + shadow->gzt = shadow->pz + patch->height * shadowyscale / 2; + shadow->gz = shadow->gzt - patch->height * shadowyscale; + shadow->texturemid = FixedMul(thing->scale, FixedDiv(shadow->gzt - viewz, shadowyscale)); + if (thing->skin && ((skin_t *)thing->skin)->flags & SF_HIRES) + shadow->texturemid = FixedMul(shadow->texturemid, ((skin_t *)thing->skin)->highresscale); + shadow->scalestep = 0; + shadow->shear.tan = shadowskew; // repurposed variable + + shadow->mobj = thing; // Easy access! Tails 06-07-2002 + + shadow->x1 = x1 < 0 ? 0 : x1; + shadow->x2 = x2 >= viewwidth ? viewwidth-1 : x2; + + // PORTAL SEMI-CLIPPING + if (portalrender) + { + if (shadow->x1 < portalclipstart) + shadow->x1 = portalclipstart; + if (shadow->x2 >= portalclipend) + shadow->x2 = portalclipend-1; + } + + shadow->xscale = FixedMul(xscale, shadowxscale); //SoM: 4/17/2000 + shadow->scale = FixedMul(yscale, shadowyscale); + shadow->sector = vis->sector; + shadow->szt = (INT16)((centeryfrac - FixedMul(shadow->gzt - viewz, yscale))>>FRACBITS); + shadow->sz = (INT16)((centeryfrac - FixedMul(shadow->gz - viewz, yscale))>>FRACBITS); + shadow->cut = SC_ISSCALED|SC_SHADOW; //check this + + shadow->startfrac = 0; + //shadow->xiscale = 0x7ffffff0 / (shadow->xscale/2); + shadow->xiscale = (patch->width<x1 > x1) + shadow->startfrac += shadow->xiscale*(shadow->x1-x1); + + // reusing x1 variable + x1 += (x2-x1)/2; + shadow->shear.offset = shadow->x1-x1; + + if (thing->subsector->sector->numlights) + { + INT32 lightnum; +#ifdef ESLOPE // R_GetPlaneLight won't work on sloped lights! + light = thing->subsector->sector->numlights - 1; + + for (lightnum = 1; lightnum < thing->subsector->sector->numlights; lightnum++) { + fixed_t h = thing->subsector->sector->lightlist[lightnum].slope ? P_GetZAt(thing->subsector->sector->lightlist[lightnum].slope, thing->x, thing->y) + : thing->subsector->sector->lightlist[lightnum].height; + if (h <= shadow->gzt) { + light = lightnum - 1; + break; + } + } +#else + light = R_GetPlaneLight(thing->subsector->sector, shadow->gzt, false); +#endif + } + + if (thing->subsector->sector->numlights) + shadow->extra_colormap = thing->subsector->sector->lightlist[light].extra_colormap; + else + shadow->extra_colormap = thing->subsector->sector->extra_colormap; + + shadow->transmap = transtables + (trans<whiteshadow) + shadow->colormap = scalelight[LIGHTLEVELS - 1][0]; // full bright! + else + shadow->colormap = scalelight[0][0]; // full dark! + + objectsdrawn++; +} + // // R_ProjectSprite // Generates a vissprite for a thing @@ -1168,7 +1458,11 @@ static void R_ProjectSprite(mobj_t *thing) fixed_t iscale; fixed_t scalestep; // toast '16 fixed_t offset, offset2; - boolean papersprite = (thing->frame & FF_PAPERSPRITE); + + fixed_t basetx; // drop shadows + + boolean papersprite = !!(thing->frame & FF_PAPERSPRITE); + fixed_t paperoffset = 0, paperdistance = 0; angle_t centerangle = 0; //SoM: 3/17/2000 fixed_t gz, gzt; @@ -1176,8 +1470,6 @@ static void R_ProjectSprite(mobj_t *thing) INT32 light = 0; fixed_t this_scale = thing->scale; - fixed_t ang_scale = FRACUNIT; - // transform the origin point tr_x = thing->x - viewx; tr_y = thing->y - viewy; @@ -1188,15 +1480,15 @@ static void R_ProjectSprite(mobj_t *thing) tz = gxt-gyt; // thing is behind view plane? - if (!(papersprite) && (tz < FixedMul(MINZ, this_scale))) // papersprite clipping is handled later + if (!papersprite && (tz < FixedMul(MINZ, this_scale))) // papersprite clipping is handled later return; gxt = -FixedMul(tr_x, viewsin); gyt = FixedMul(tr_y, viewcos); - tx = -(gyt + gxt); + basetx = tx = -(gyt + gxt); // too far off the side? - if (abs(tx) > tz<<2) + if (!papersprite && abs(tx) > tz<<2) // papersprite clipping is handled later return; // aspect ratio stuff @@ -1249,8 +1541,6 @@ static void R_ProjectSprite(mobj_t *thing) ang = R_PointToAngle (thing->x, thing->y) - thing->player->frameangle; else ang = R_PointToAngle (thing->x, thing->y) - thing->angle; - if (papersprite) - ang_scale = abs(FINESINE(ang>>ANGLETOFINESHIFT)); } if (sprframe->rotate == SRF_SINGLE) @@ -1288,27 +1578,12 @@ static void R_ProjectSprite(mobj_t *thing) else offset = -spritecachedinfo[lump].offset; offset = FixedMul(offset, this_scale); - tx += FixedMul(offset, ang_scale); - x1 = (centerxfrac + FixedMul (tx,xscale)) >>FRACBITS; - - // off the right side? - if (x1 > viewwidth) - return; - offset2 = FixedMul(spritecachedinfo[lump].width, this_scale); - tx += FixedMul(offset2, ang_scale); - x2 = ((centerxfrac + FixedMul (tx,xscale)) >> FRACBITS) - 1; - - // off the left side - if (x2 < 0) - return; if (papersprite) { - fixed_t yscale2, cosmul, sinmul, tz2; - - if (x2 <= x1) - return; + fixed_t xscale2, yscale2, cosmul, sinmul, tx2, tz2; + INT32 range; if (ang >= ANGLE_180) { @@ -1325,7 +1600,23 @@ static void R_ProjectSprite(mobj_t *thing) gyt = -FixedMul(tr_y, viewsin); tz = gxt-gyt; yscale = FixedDiv(projectiony, tz); - if (yscale < 64) return; // Fix some funky visuals + //if (yscale < 64) return; // Fix some funky visuals + + gxt = -FixedMul(tr_x, viewsin); + gyt = FixedMul(tr_y, viewcos); + tx = -(gyt + gxt); + xscale = FixedDiv(projection, tz); + x1 = (centerxfrac + FixedMul(tx,xscale))>>FRACBITS; + + // Get paperoffset (offset) and paperoffset (distance) + paperoffset = -FixedMul(tr_x, cosmul) - FixedMul(tr_y, sinmul); + paperdistance = -FixedMul(tr_x, sinmul) + FixedMul(tr_y, cosmul); + if (paperdistance < 0) + { + paperoffset = -paperoffset; + paperdistance = -paperdistance; + } + centerangle = viewangle - thing->angle; tr_x += FixedMul(offset2, cosmul); tr_y += FixedMul(offset2, sinmul); @@ -1333,13 +1624,52 @@ static void R_ProjectSprite(mobj_t *thing) gyt = -FixedMul(tr_y, viewsin); tz2 = gxt-gyt; yscale2 = FixedDiv(projectiony, tz2); - if (yscale2 < 64) return; // ditto + //if (yscale2 < 64) return; // ditto + + gxt = -FixedMul(tr_x, viewsin); + gyt = FixedMul(tr_y, viewcos); + tx2 = -(gyt + gxt); + xscale2 = FixedDiv(projection, tz2); + x2 = ((centerxfrac + FixedMul(tx2,xscale2))>>FRACBITS); if (max(tz, tz2) < FixedMul(MINZ, this_scale)) // non-papersprite clipping is handled earlier return; - scalestep = (yscale2 - yscale)/(x2 - x1); - scalestep = scalestep ? scalestep : 1; + // Needs partially clipped + if (tz < FixedMul(MINZ, this_scale)) + { + fixed_t div = FixedDiv(tz2-tz, FixedMul(MINZ, this_scale)-tz); + tx += FixedDiv(tx2-tx, div); + tz = FixedMul(MINZ, this_scale); + yscale = FixedDiv(projectiony, tz); + xscale = FixedDiv(projection, tz); + x1 = (centerxfrac + FixedMul(tx,xscale))>>FRACBITS; + } + else if (tz2 < FixedMul(MINZ, this_scale)) + { + fixed_t div = FixedDiv(tz-tz2, FixedMul(MINZ, this_scale)-tz2); + tx2 += FixedDiv(tx-tx2, div); + tz2 = FixedMul(MINZ, this_scale); + yscale2 = FixedDiv(projectiony, tz2); + xscale2 = FixedDiv(projection, tz2); + x2 = (centerxfrac + FixedMul(tx2,xscale2))>>FRACBITS; + } + + // off the right side? + if (x1 > viewwidth) + return; + + // off the left side + if (x2 < 0) + return; + + if ((range = x2 - x1) <= 0) + return; + + range++; // fencepost problem + + scalestep = ((yscale2 - yscale)/range) ?: 1; + xscale = FixedDiv(range<>FRACBITS; - xscale = FixedMul(xscale, ang_scale); + // off the right side? + if (x1 > viewwidth) + return; + + tx += offset2; + x2 = ((centerxfrac + FixedMul(tx,xscale))>>FRACBITS); x2--; + + // off the left side + if (x2 < 0) + return; + } // PORTAL SPRITE CLIPPING if (portalrender) @@ -1445,6 +1786,11 @@ static void R_ProjectSprite(mobj_t *thing) vis->pzt = vis->pz + vis->thingheight; vis->texturemid = vis->gzt - viewz; vis->scalestep = scalestep; + vis->paperoffset = paperoffset; + vis->paperdistance = paperdistance; + vis->centerangle = centerangle; + vis->shear.tan = 0; + vis->shear.offset = 0; vis->mobj = thing; // Easy access! Tails 06-07-2002 @@ -1537,6 +1883,9 @@ static void R_ProjectSprite(mobj_t *thing) if (thing->subsector->sector->numlights) R_SplitSprite(vis); + if (thing->shadowscale && cv_shadow.value) + R_ProjectDropShadow(thing, vis, thing->shadowscale, basetx, tz); + // Debug ++objectsdrawn; } @@ -1670,6 +2019,9 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing) vis->pzt = vis->pz + vis->thingheight; vis->texturemid = vis->gzt - viewz; vis->scalestep = 0; + vis->paperdistance = 0; + vis->shear.tan = 0; + vis->shear.offset = 0; vis->x1 = x1 < 0 ? 0 : x1; vis->x2 = x2 >= viewwidth ? viewwidth-1 : x2; diff --git a/src/r_things.h b/src/r_things.h index 4837b4aee..a50e0803d 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -50,6 +50,8 @@ void R_SortVisSprites(void); // (only sprites from namelist are added or replaced) void R_AddSpriteDefs(UINT16 wadnum); +fixed_t R_GetShadowZ(mobj_t *thing, pslope_t **shadowslope); + #ifdef DELFILE void R_DelSpriteDefs(UINT16 wadnum); #endif @@ -114,7 +116,8 @@ typedef enum SC_FULLBRIGHT = 1<<4, SC_SEMIBRIGHT = 1<<5, SC_VFLIP = 1<<6, - SC_ISSCALED = 1>>7, + SC_ISSCALED = 1<<7, + SC_SHADOW = 1<<8, // masks SC_CUTMASK = SC_TOP|SC_BOTTOM, SC_FLAGMASK = ~SC_CUTMASK @@ -139,8 +142,16 @@ typedef struct vissprite_s fixed_t startfrac; // horizontal position of x1 fixed_t scale, sortscale; // sortscale only differs from scale for flat sprites fixed_t scalestep; // only for flat sprites, 0 otherwise + fixed_t paperoffset, paperdistance; // for paper sprites, offset/dist relative to the angle fixed_t xiscale; // negative if flipped + angle_t centerangle; // for paper sprites + + struct { + fixed_t tan; // The amount to shear the sprite vertically per row + INT32 offset; // The center of the shearing location offset from x1 + } shear; + fixed_t texturemid; lumpnum_t patch; From d91e8205dc992576766ebf06f51ff52e7174daae Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Tue, 3 Mar 2020 14:22:52 -0500 Subject: [PATCH 102/163] 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 103/163] 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 104/163] 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 105/163] 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 106/163] 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 107/163] 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 108/163] 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 109/163] 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 ff190b7a880effc95059bb7cde8705c835cea6c4 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Tue, 3 Mar 2020 17:05:04 -0500 Subject: [PATCH 110/163] Fix unsigned/signed comparison --- src/p_mobj.c | 4 ++-- src/p_spec.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 1f2e5e3de..7533e8fc5 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -11006,7 +11006,7 @@ void P_SpawnPrecipitation(void) { INT32 i, j, k; mobjtype_t type = precipprops[curWeather].type; - INT32 randomstates = mobjinfo[type].damage; + UINT8 randomstates = (UINT8)mobjinfo[type].damage; fixed_t basex, basey, x, y, z, height; subsector_t *precipsector = NULL; precipmobj_t *rainmo = NULL; @@ -11053,7 +11053,7 @@ void P_SpawnPrecipitation(void) { rainmo = P_SpawnPrecipMobj(x, y, z, type); - if (randomstates > 0 && randomstates < UINT8_MAX) + if (randomstates > 0) { UINT8 mrand = M_RandomByte(); UINT8 threshold = UINT8_MAX / (randomstates + 1); diff --git a/src/p_spec.c b/src/p_spec.c index 1cca2cd5e..953503f9d 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -2041,7 +2041,7 @@ void P_SwitchWeather(UINT8 newWeather) } else if (swap != MT_NULL) // Rather than respawn all that crap, reuse it! { - INT32 randomstates = mobjinfo[swap].damage; + UINT8 randomstates = (UINT8)mobjinfo[swap].damage; thinker_t *think; precipmobj_t *precipmobj; statenum_t st; @@ -2057,7 +2057,7 @@ void P_SwitchWeather(UINT8 newWeather) st = mobjinfo[swap].spawnstate; - if (randomstates > 0 && randomstates < UINT8_MAX) + if (randomstates > 0) { UINT8 mrand = M_RandomByte(); UINT8 threshold = UINT8_MAX / (randomstates + 1); From d459407866625e3c3d7fc102a5bcc330080a6a88 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Tue, 3 Mar 2020 17:22:13 -0500 Subject: [PATCH 111/163] Forgotten player check here --- 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 4f39319a6..9e34e04d9 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -2790,7 +2790,7 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) const fixed_t maxstepmove = FixedMul(MAXSTEPMOVE, mapobjectscale); fixed_t maxstep = maxstepmove; - if (thing->player->kartstuff[k_waterskip]) + if (thing->player && thing->player->kartstuff[k_waterskip]) maxstep += maxstepmove; // Add some extra stepmove when waterskipping // If using type Section1:13, double the maxstep. From 721fb369fd5920646b363a4890271f1aadebe7a9 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Tue, 3 Mar 2020 18:34:17 -0500 Subject: [PATCH 112/163] 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 113/163] 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 11930b41bf704f68d7d6594e1a6e992603235d37 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 3 Mar 2020 20:11:03 -0800 Subject: [PATCH 114/163] Use tic_t --- src/d_clisrv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 28a519694..308eb2616 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -5433,7 +5433,7 @@ static tic_t gametime = 0; static void UpdatePingTable(void) { - int fastest; + tic_t fastest; tic_t lag; INT32 i; From 76188e89fbd875b9c615f12932c60036278664b3 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Fri, 6 Mar 2020 12:54:54 -0500 Subject: [PATCH 115/163] Fullbright boosters We no longer have to maintain this standard manually. I actually suggest making booster sectors use the lightlevel of their surrounding sectors so that only the booster itself glows. (OGL support is commented out, since it conflicts with sal-lighting and should probably be done there) --- src/hardware/hw_main.c | 3 +++ src/r_bsp.c | 17 +++++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index f2499bcf6..6ccaf1bc5 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -3564,6 +3564,9 @@ static void HWR_Subsector(size_t num) sub->sector->extra_colormap = gr_frontsector->extra_colormap; + //R_PlaneLightOverride(gr_frontsector, false, &floorlightlevel); + //R_PlaneLightOverride(gr_frontsector, true, &ceilinglightlevel); + // render floor ? #ifdef DOPLANES // yeah, easy backface cull! :) diff --git a/src/r_bsp.c b/src/r_bsp.c index 296cbbe87..a748af51c 100644 --- a/src/r_bsp.c +++ b/src/r_bsp.c @@ -57,6 +57,16 @@ static boolean R_NoEncore(sector_t *sector, boolean ceiling) return ((boolean)(sector->flags & SF_FLIPSPECIAL_FLOOR)); } +static void R_PlaneLightOverride(sector_t *sector, boolean ceiling, INT32 *lightlevel) +{ + if (GETSECSPECIAL(sector->special, 4) == 6) // Fullbright sneaker panels + { + if ((ceiling && (sector->flags & SF_FLIPSPECIAL_CEILING)) + || (!ceiling && (sector->flags & SF_FLIPSPECIAL_FLOOR))) + *lightlevel = 255; + } +} + // // R_ClearDrawSegs // @@ -895,6 +905,9 @@ static void R_Subsector(size_t num) sub->sector->extra_colormap = frontsector->extra_colormap; + R_PlaneLightOverride(frontsector, false, &floorlightlevel); + R_PlaneLightOverride(frontsector, true, &ceilinglightlevel); + if ((( #ifdef ESLOPE frontsector->f_slope ? P_GetZAt(frontsector->f_slope, viewx, viewy) : @@ -923,8 +936,8 @@ static void R_Subsector(size_t num) || (frontsector->heightsec != -1 && sectors[frontsector->heightsec].floorpic == skyflatnum))) { - ceilingplane = R_FindPlane(frontsector->ceilingheight, frontsector->ceilingpic, - ceilinglightlevel, frontsector->ceiling_xoffs, frontsector->ceiling_yoffs, frontsector->ceilingpic_angle, + ceilingplane = R_FindPlane(frontsector->ceilingheight, frontsector->ceilingpic, ceilinglightlevel, + frontsector->ceiling_xoffs, frontsector->ceiling_yoffs, frontsector->ceilingpic_angle, ceilingcolormap, NULL #ifdef POLYOBJECTS_PLANES , NULL From 83c74af6968a3b80001391fb44e64d2993735f74 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Fri, 6 Mar 2020 12:56:12 -0500 Subject: [PATCH 116/163] Fix bad master merge --- src/p_mobj.c | 163 --------------------------------------------------- 1 file changed, 163 deletions(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 42bd714df..a532dddc5 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -6209,169 +6209,6 @@ static void P_RemoveOverlay(mobj_t *thing) } } -// Simplified version of a code bit in P_MobjFloorZ -static fixed_t P_ShadowSlopeZ(pslope_t *slope, fixed_t x, fixed_t y, fixed_t radius, boolean ceiling) -{ - fixed_t testx, testy; - - if (slope->d.x < 0) - testx = radius; - else - testx = -radius; - - if (slope->d.y < 0) - testy = radius; - else - testy = -radius; - - if ((slope->zdelta > 0) ^ !!(ceiling)) - { - testx = -testx; - testy = -testy; - } - - testx += x; - testy += y; - - return P_GetZAt(slope, testx, testy); -} - -// Sets standingslope/modeltilt, returns z position for shadows; used also for stuff like bananas -// (I would've preferred to be able to return both the slope & z, but I'll take what I can get...) -fixed_t P_CalculateShadowFloor(mobj_t *mobj, fixed_t x, fixed_t y, fixed_t z, fixed_t radius, fixed_t height, boolean flip, boolean player) -{ - fixed_t newz; - sector_t *sec; -#ifdef ESLOPE - pslope_t *slope = NULL; -#endif - - sec = R_PointInSubsector(x, y)->sector; - - if (flip) - { -#ifdef ESLOPE - if (sec->c_slope) - { - slope = sec->c_slope; - newz = P_ShadowSlopeZ(slope, x, y, radius, true); - } - else -#endif - newz = sec->ceilingheight; - } - else - { -#ifdef ESLOPE - if (sec->f_slope) - { - slope = sec->f_slope; - newz = P_ShadowSlopeZ(slope, x, y, radius, false); - } - else -#endif - newz = sec->floorheight; - } - - // Check FOFs for a better suited slope - if (sec->ffloors) - { - ffloor_t *rover; - - for (rover = sec->ffloors; rover; rover = rover->next) - { - fixed_t top, bottom; - fixed_t d1, d2; - - if (!(rover->flags & FF_EXISTS)) - continue; - - if ((!(((rover->flags & FF_BLOCKPLAYER && player) - || (rover->flags & FF_BLOCKOTHERS && !player)) - || (rover->flags & FF_QUICKSAND)) - || (rover->flags & FF_SWIMMABLE))) - continue; - -#ifdef ESLOPE - if (*rover->t_slope) - top = P_ShadowSlopeZ(*rover->t_slope, x, y, radius, false); - else -#endif - top = *rover->topheight; - -#ifdef ESLOPE - if (*rover->b_slope) - bottom = P_ShadowSlopeZ(*rover->b_slope, x, y, radius, true); - else -#endif - bottom = *rover->bottomheight; - - if (flip) - { - if (rover->flags & FF_QUICKSAND) - { - if (z < top && (z + height) > bottom) - { - if (newz > (z + height)) - { - newz = (z + height); - slope = NULL; - } - } - continue; - } - - d1 = (z + height) - (top + ((bottom - top)/2)); - d2 = z - (top + ((bottom - top)/2)); - - if (bottom < newz && abs(d1) < abs(d2)) - { - newz = bottom; -#ifdef ESLOPE - if (*rover->b_slope) - slope = *rover->b_slope; -#endif - } - } - else - { - if (rover->flags & FF_QUICKSAND) - { - if (z < top && (z + height) > bottom) - { - if (newz < z) - { - newz = z; - slope = NULL; - } - } - continue; - } - - d1 = z - (bottom + ((top - bottom)/2)); - d2 = (z + height) - (bottom + ((top - bottom)/2)); - - if (top > newz && abs(d1) < abs(d2)) - { - newz = top; -#ifdef ESLOPE - if (*rover->t_slope) - slope = *rover->t_slope; -#endif - } - } - } - } - - mobj->standingslope = slope; - -#ifdef HWRENDER - mobj->modeltilt = slope; -#endif - - return newz; -} - // SAL'S KART BATTLE MODE OVERTIME HANDLER #define MAXPLANESPERSECTOR (MAXFFLOORS+1)*2 static void P_SpawnOvertimeParticles(fixed_t x, fixed_t y, fixed_t scale, mobjtype_t type, boolean ceiling) From d8e0bf61f64328e2a8853381821126b77ac1e045 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sun, 8 Mar 2020 00:26:33 -0500 Subject: [PATCH 117/163] 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 7174b707a178e9ec1a2abc57ac686e34fcae96c4 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sun, 8 Mar 2020 00:39:21 -0500 Subject: [PATCH 118/163] Draw distance uses object scale --- src/hardware/hw_main.c | 4 ++-- src/r_main.c | 2 +- src/r_things.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 6ccaf1bc5..73e65cceb 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -5357,7 +5357,7 @@ static void HWR_AddSprites(sector_t *sec) // Handle all things in sector. // If a limit exists, handle things a tiny bit different. - if ((limit_dist = (fixed_t)(/*(maptol & TOL_NIGHTS) ? cv_drawdist_nights.value : */cv_drawdist.value) << FRACBITS)) + if ((limit_dist = (fixed_t)(cv_drawdist.value) * mapobjectscale)) { for (thing = sec->thinglist; thing; thing = thing->snext) { @@ -5424,7 +5424,7 @@ static void HWR_AddSprites(sector_t *sec) #ifdef HWPRECIP // No to infinite precipitation draw distance. - if ((limit_dist = (fixed_t)cv_drawdist_precip.value << FRACBITS)) + if ((limit_dist = (fixed_t)(cv_drawdist_precip.value) * mapobjectscale)) { for (precipthing = sec->preciplist; precipthing; precipthing = precipthing->snext) { diff --git a/src/r_main.c b/src/r_main.c index a4fa9d463..13fce289e 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -176,7 +176,7 @@ consvar_t cv_showhud = {"showhud", "Yes", CV_CALL, CV_YesNo, R_SetViewSize, 0, consvar_t cv_translucenthud = {"translucenthud", "10", CV_SAVE, translucenthud_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_translucency = {"translucency", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_drawdist = {"drawdist", "Infinite", CV_SAVE, drawdist_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_drawdist = {"drawdist", "8192", CV_SAVE, drawdist_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; //consvar_t cv_drawdist_nights = {"drawdist_nights", "2048", CV_SAVE, drawdist_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_drawdist_precip = {"drawdist_precip", "1024", CV_SAVE, drawdist_precip_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; //consvar_t cv_precipdensity = {"precipdensity", "Moderate", CV_SAVE, precipdensity_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; diff --git a/src/r_things.c b/src/r_things.c index 9ea8bb77b..4b7339c4e 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -2102,7 +2102,7 @@ void R_AddSprites(sector_t *sec, INT32 lightlevel) // Handle all things in sector. // If a limit exists, handle things a tiny bit different. - if ((limit_dist = (fixed_t)(/*(maptol & TOL_NIGHTS) ? cv_drawdist_nights.value : */cv_drawdist.value) << FRACBITS)) + if ((limit_dist = (fixed_t)(cv_drawdist.value) * mapobjectscale)) { for (thing = sec->thinglist; thing; thing = thing->snext) { @@ -2168,7 +2168,7 @@ void R_AddSprites(sector_t *sec, INT32 lightlevel) } // no, no infinite draw distance for precipitation. this option at zero is supposed to turn it off - if ((limit_dist = (fixed_t)cv_drawdist_precip.value << FRACBITS)) + if ((limit_dist = (fixed_t)(cv_drawdist_precip.value) * mapobjectscale)) { for (precipthing = sec->preciplist; precipthing; precipthing = precipthing->snext) { From fecb2cdebdfae7c43df21208d794fa380ea7edb1 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sun, 8 Mar 2020 00:48:56 -0500 Subject: [PATCH 119/163] Fix item lobbing in mobjscale maps This happened when I was trying to fix several mobjscale issues at once for v1 -- I either didn't notice the P_SetObjectMomZ, or didn't realize that this function already handles scale changes for you. --- 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 64320fb68..b3f4a848e 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3658,7 +3658,7 @@ static mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t map if (mo) { angle_t fa = player->mo->angle>>ANGLETOFINESHIFT; - fixed_t HEIGHT = (20 + (dir*10))*mapobjectscale + (player->mo->momz*P_MobjFlip(player->mo)); + fixed_t HEIGHT = (20 + (dir*10))*FRACUNIT + (player->mo->momz*P_MobjFlip(player->mo)); P_SetObjectMomZ(mo, HEIGHT, false); mo->momx = player->mo->momx + FixedMul(FINECOSINE(fa), PROJSPEED*dir); From 8c1aec37137184074d753b5a570749d5b8830198 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sun, 8 Mar 2020 01:12:53 -0500 Subject: [PATCH 120/163] Fix springs having weird launch speeds in mobjscale maps Same issue as lobbing -- scale being double-applied --- src/p_map.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/p_map.c b/src/p_map.c index 9e34e04d9..5a9f4b8c3 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -118,8 +118,8 @@ boolean P_TeleportMove(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z) boolean P_DoSpring(mobj_t *spring, mobj_t *object) { //INT32 pflags; - const fixed_t hscale = mapobjectscale + (mapobjectscale - object->scale); - const fixed_t vscale = mapobjectscale + (object->scale - mapobjectscale); + const fixed_t hscale = FRACUNIT + (FRACUNIT - object->scale); + const fixed_t vscale = FRACUNIT + (object->scale - FRACUNIT); fixed_t vertispeed = spring->info->mass; fixed_t horizspeed = spring->info->damage; UINT8 starcolor = (spring->info->painchance % MAXTRANSLATIONS); From b4933bacb3ae58b096dae11d4afdb09288f33637 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sun, 8 Mar 2020 01:37:48 -0500 Subject: [PATCH 121/163] Didn't work, let's try this objectSpeed was getting scaled with the spring's size, when only the spring's base speed should be scaled --- src/p_map.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/p_map.c b/src/p_map.c index 5a9f4b8c3..a732dc551 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -118,8 +118,8 @@ boolean P_TeleportMove(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z) boolean P_DoSpring(mobj_t *spring, mobj_t *object) { //INT32 pflags; - const fixed_t hscale = FRACUNIT + (FRACUNIT - object->scale); - const fixed_t vscale = FRACUNIT + (object->scale - FRACUNIT); + const fixed_t hscale = mapobjectscale + (mapobjectscale - object->scale); + const fixed_t vscale = mapobjectscale + (object->scale - mapobjectscale); fixed_t vertispeed = spring->info->mass; fixed_t horizspeed = spring->info->damage; UINT8 starcolor = (spring->info->painchance % MAXTRANSLATIONS); @@ -195,7 +195,7 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object) if (horizspeed) { angle_t finalAngle = spring->angle; - fixed_t finalSpeed = horizspeed; + fixed_t finalSpeed = FixedMul(horizspeed, FixedSqrt(FixedMul(hscale, spring->scale))); fixed_t objectSpeed; if (object->player) @@ -273,7 +273,7 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object) // Horizontal speed is used as a minimum thrust, not a direct replacement finalSpeed = max(objectSpeed, finalSpeed); - P_InstaThrustEvenIn2D(object, finalAngle, FixedMul(finalSpeed, FixedSqrt(FixedMul(hscale, spring->scale)))); + P_InstaThrustEvenIn2D(object, finalAngle, finalSpeed); } // Re-solidify From 5d2f380a136ae6b979c229c7db623916b52bab0d Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sun, 8 Mar 2020 17:33:07 -0400 Subject: [PATCH 122/163] Lightsnake scales (The comment about scale not working was actually just paper sprite projection mucking it up) --- 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 b3f4a848e..6542d3a98 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1937,8 +1937,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<mo->y + P_ReturnThrustY(player->mo, newangle, 31<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 @@ -1951,8 +1951,8 @@ void K_RespawnChecker(player_t *player) 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)); + mo->momz = (8 * player->mo->scale) * P_MobjFlip(player->mo); + P_SetScale(mo, (mo->destscale = player->mo->scale)); } } } From f0c98697dc59b0ab67c8854574d9786b49184b67 Mon Sep 17 00:00:00 2001 From: lachwright Date: Tue, 10 Mar 2020 05:18:05 +0800 Subject: [PATCH 123/163] Add camera pitching when driving on downhill slopes --- src/lua_hudlib.c | 10 ++++++++++ src/p_local.h | 2 ++ src/p_user.c | 35 +++++++++++++++++++++++++++++------ 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index ec76552d1..2fe8c33a2 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -139,6 +139,8 @@ enum cameraf { camera_momx, camera_momy, camera_momz, + camera_pan, + camera_pitch, camera_pnum }; @@ -158,6 +160,8 @@ static const char *const camera_opt[] = { "momx", "momy", "momz", + "pan", + "pitch", "pnum", NULL}; @@ -314,6 +318,12 @@ static int camera_get(lua_State *L) case camera_momz: lua_pushinteger(L, cam->momz); break; + case camera_pan: + lua_pushinteger(L, cam->pan); + break; + case camera_pitch: + lua_pushinteger(L, cam->pitch); + break; case camera_pnum: lua_pushinteger(L, camnum); break; diff --git a/src/p_local.h b/src/p_local.h index 1358bf195..85fd08d71 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -107,6 +107,8 @@ typedef struct camera_s // SRB2Kart: camera pans while drifting fixed_t pan; + // SRB2Kart: camera pitches on slopes + angle_t pitch; } camera_t; extern camera_t camera[MAXSPLITSCREENPLAYERS]; diff --git a/src/p_user.c b/src/p_user.c index 8185667a0..376dcf554 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -7234,8 +7234,8 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall { static UINT8 lookbackdelay[4] = {0,0,0,0}; UINT8 num; - angle_t angle = 0, focusangle = 0, focusaiming = 0; - fixed_t x, y, z, dist, height, viewpointx, viewpointy, camspeed, camdist, camheight, pviewheight; + angle_t angle = 0, focusangle = 0, focusaiming = 0, pitch = 0; + fixed_t x, y, z, dist, distxy, distz, height, viewpointx, viewpointy, camspeed, camdist, camheight, pviewheight; fixed_t pan, xpan, ypan; INT32 camrotate; boolean camstill, lookback; @@ -7487,8 +7487,30 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall height -= FixedMul(height, player->karthud[khud_boostcam]); } - x = mo->x - FixedMul(FINECOSINE((angle>>ANGLETOFINESHIFT) & FINEMASK), dist); - y = mo->y - FixedMul(FINESINE((angle>>ANGLETOFINESHIFT) & FINEMASK), dist); + if (mo->standingslope) + { + pitch = (angle_t)FixedMul(P_ReturnThrustX(mo, player->frameangle - mo->standingslope->xydirection, FRACUNIT), (fixed_t)mo->standingslope->zangle); + if (mo->eflags & MFE_VERTICALFLIP) + { + if (pitch >= ANGLE_180) + pitch = 0; + } + else + { + if (pitch < ANGLE_180) + pitch = 0; + } + } + pitch = thiscam->pitch + (angle_t)FixedMul(pitch - thiscam->pitch, camspeed/4); + + if (rendermode == render_opengl) + distxy = FixedMul(dist, FINECOSINE((pitch>>ANGLETOFINESHIFT) & FINEMASK)); + else + distxy = dist; + distz = -FixedMul(dist, FINESINE((pitch>>ANGLETOFINESHIFT) & FINEMASK)); + + x = mo->x - FixedMul(FINECOSINE((angle>>ANGLETOFINESHIFT) & FINEMASK), distxy); + y = mo->y - FixedMul(FINESINE((angle>>ANGLETOFINESHIFT) & FINEMASK), distxy); // SRB2Kart: set camera panning if (camstill || resetcalled || player->playerstate == PST_DEAD) @@ -7519,9 +7541,9 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall pviewheight = FixedMul(32<scale); if (mo->eflags & MFE_VERTICALFLIP) - z = mo->z + mo->height - pviewheight - camheight; + z = mo->z + mo->height - pviewheight - camheight + distz; else - z = mo->z + pviewheight + camheight; + z = mo->z + pviewheight + camheight + distz; #ifndef NOCLIPCAM // Disable all z-clipping for noclip cam // move camera down to move under lower ceilings @@ -7756,6 +7778,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall } thiscam->pan = pan; + thiscam->pitch = pitch; // compute aming to look the viewed point f1 = viewpointx-thiscam->x; From e2c95a0060ac5f835d46d71f87ec16ecb0fd8b4b Mon Sep 17 00:00:00 2001 From: lachwright Date: Tue, 10 Mar 2020 19:30:02 +0800 Subject: [PATCH 124/163] Visibility improvement --- src/p_user.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/p_user.c b/src/p_user.c index 376dcf554..47077615c 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -7786,9 +7786,17 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall dist = FixedHypot(f1, f2); if (mo->eflags & MFE_VERTICALFLIP) + { angle = R_PointToAngle2(0, thiscam->z + thiscam->height, dist, mo->z + mo->height - P_GetPlayerHeight(player)); + if (thiscam->pitch < ANGLE_180 && thiscam->pitch > angle) + angle = thiscam->pitch; + } else + { angle = R_PointToAngle2(0, thiscam->z, dist, mo->z + P_GetPlayerHeight(player)); + if (thiscam->pitch >= ANGLE_180 && thiscam->pitch < angle) + angle = thiscam->pitch; + } if (player->playerstate != PST_DEAD && !((player->pflags & PF_NIGHTSMODE) && player->exiting)) angle += (focusaiming < ANGLE_180 ? focusaiming/2 : InvAngle(InvAngle(focusaiming)/2)); // overcomplicated version of '((signed)focusaiming)/2;' From 49f9d830edb2ffae7f7b6986713756165a4b56d1 Mon Sep 17 00:00:00 2001 From: lachwright Date: Thu, 12 Mar 2020 01:23:09 +0800 Subject: [PATCH 125/163] Attempt to make splitscreen's camtilt better --- src/p_user.c | 4 +++- src/r_main.c | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/p_user.c b/src/p_user.c index 47077615c..d07b71390 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -7503,11 +7503,13 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall } pitch = thiscam->pitch + (angle_t)FixedMul(pitch - thiscam->pitch, camspeed/4); - if (rendermode == render_opengl) + if (rendermode == render_opengl && !cv_grshearing.value) distxy = FixedMul(dist, FINECOSINE((pitch>>ANGLETOFINESHIFT) & FINEMASK)); else distxy = dist; distz = -FixedMul(dist, FINESINE((pitch>>ANGLETOFINESHIFT) & FINEMASK)); + if (splitscreen == 1) // 2 player is weird, this helps keep players on screen + distz = 3*distz/5; x = mo->x - FixedMul(FINECOSINE((angle>>ANGLETOFINESHIFT) & FINEMASK), distxy); y = mo->y - FixedMul(FINESINE((angle>>ANGLETOFINESHIFT) & FINEMASK), distxy); diff --git a/src/r_main.c b/src/r_main.c index a4fa9d463..5967e6c9d 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -833,7 +833,7 @@ static void R_SetupFreelook(void) // clip it in the case we are looking a hardware 90 degrees full aiming // (lmps, network and use F12...) G_SoftwareClipAimingPitch((INT32 *)&aimingangle); - dy = AIMINGTODY(aimingangle) * viewwidth/BASEVIDWIDTH; + dy = AIMINGTODY(aimingangle) * viewheight/BASEVIDHEIGHT; yslope = &yslopetab[viewheight*8 - (viewheight/2 + dy)]; } centery = (viewheight/2) + dy; From c6f3ac651ed57758c045d4a0e1297fda045691bb Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sat, 14 Mar 2020 01:43:54 -0400 Subject: [PATCH 126/163] Switch red & blue sparks, add yellow sparks, add big spark sprites --- src/dehacked.c | 2 + src/info.c | 3 + src/info.h | 2 + src/k_kart.c | 191 ++++++++++++++++++++++++++++++++++--------------- src/p_mobj.c | 6 +- 5 files changed, 144 insertions(+), 60 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index 8a8a159db..efb39dd7d 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -6347,6 +6347,8 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_DRIFTSPARK_B1", "S_DRIFTSPARK_C1", "S_DRIFTSPARK_C2", + "S_DRIFTSPARK_D1", + "S_DRIFTSPARK_D2", // Brake drift sparks "S_BRAKEDRIFT", diff --git a/src/info.c b/src/info.c index 275c70018..6803e8a33 100644 --- a/src/info.c +++ b/src/info.c @@ -2587,6 +2587,9 @@ state_t states[NUMSTATES] = {SPR_DRIF, FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_DRIFTSPARK_C2}, // S_DRIFTSPARK_C1 {SPR_DRIF, FF_FULLBRIGHT|FF_TRANS20, 1, {NULL}, 0, 0, S_DRIFTSPARK_A3}, // S_DRIFTSPARK_C2 (Loop back to A3) + {SPR_DRIF, FF_FULLBRIGHT|3, 2, {NULL}, 0, 0, S_DRIFTSPARK_D2}, // S_DRIFTSPARK_D1 + {SPR_DRIF, FF_FULLBRIGHT|2, 1, {NULL}, 0, 0, S_DRIFTSPARK_A2}, // S_DRIFTSPARK_D2 (Loop back to A2) + {SPR_BDRF, FF_FULLBRIGHT|FF_PAPERSPRITE|FF_ANIMATE, -1, {NULL}, 5, 2, S_BRAKEDRIFT}, // S_BRAKEDRIFT {SPR_DUST, 0, 3, {NULL}, 0, 0, S_DRIFTDUST2}, // S_DRIFTDUST1 diff --git a/src/info.h b/src/info.h index b6cc0e7ff..50887e93f 100644 --- a/src/info.h +++ b/src/info.h @@ -3236,6 +3236,8 @@ typedef enum state S_DRIFTSPARK_B1, S_DRIFTSPARK_C1, S_DRIFTSPARK_C2, + S_DRIFTSPARK_D1, + S_DRIFTSPARK_D2, // Brake drift sparks S_BRAKEDRIFT, diff --git a/src/k_kart.c b/src/k_kart.c index 64320fb68..3f57b3e83 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3151,13 +3151,17 @@ static void K_SpawnDriftSparks(player_t *player) if (!P_IsObjectOnGround(player->mo)) return; - if (!player->kartstuff[k_drift] || player->kartstuff[k_driftcharge] < K_GetKartDriftSparkValue(player)) + if (!player->kartstuff[k_drift] + || (player->kartstuff[k_driftcharge] < K_GetKartDriftSparkValue(player) && !(player->kartstuff[k_driftcharge] < 0))) return; travelangle = player->mo->angle-(ANGLE_45/5)*player->kartstuff[k_drift]; for (i = 0; i < 2; i++) { + SINT8 size = 1; + UINT8 trail = 0; + newx = player->mo->x + P_ReturnThrustX(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_135, FixedMul(32*FRACUNIT, player->mo->scale)); newy = player->mo->y + P_ReturnThrustY(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_135, FixedMul(32*FRACUNIT, player->mo->scale)); spark = P_SpawnMobj(newx, newy, player->mo->z, MT_DRIFTSPARK); @@ -3171,20 +3175,62 @@ static void K_SpawnDriftSparks(player_t *player) spark->momy = player->mo->momy/2; //spark->momz = player->mo->momz/2; - if (player->kartstuff[k_driftcharge] >= K_GetKartDriftSparkValue(player)*4) + if (player->kartstuff[k_driftcharge] < 0) { - spark->color = (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1))); + // Stage 0: Yellow + spark->color = SKINCOLOR_GOLD; + size = 0; + } + else if (player->kartstuff[k_driftcharge] >= K_GetKartDriftSparkValue(player)*4) + { + // Stage 3: Rainbow + size = 2; + + if (player->kartstuff[k_driftcharge] <= (K_GetKartDriftSparkValue(player)*4)+(32*3)) + { + // transition + spark->color = SKINCOLOR_SILVER; + P_SetScale(spark, (spark->destscale = spark->scale*3/2)); + trail = 1; + } + else + { + spark->color = (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1))); + spark->colorized = true; + trail = 2; + } } else if (player->kartstuff[k_driftcharge] >= K_GetKartDriftSparkValue(player)*2) { - if (player->kartstuff[k_driftcharge] <= (K_GetKartDriftSparkValue(player)*2)+(24*3)) - spark->color = SKINCOLOR_RASPBERRY; // transition + // Stage 2: Blue + size = 2; + + if (player->kartstuff[k_driftcharge] <= (K_GetKartDriftSparkValue(player)*2)+(32*3)) + { + // transition + spark->color = SKINCOLOR_PURPLE; + P_SetScale(spark, (spark->destscale = spark->scale*3/2)); + } else - spark->color = SKINCOLOR_KETCHUP; + { + spark->color = SKINCOLOR_SAPPHIRE; + } } else { - spark->color = SKINCOLOR_SAPPHIRE; + // Stage 1: Red + size = 1; + + if (player->kartstuff[k_driftcharge] <= (K_GetKartDriftSparkValue(player))+(32*3)) + { + // transition + spark->color = SKINCOLOR_RASPBERRY; + P_SetScale(spark, (spark->destscale = spark->scale*2)); + } + else + { + spark->color = SKINCOLOR_KETCHUP; + } } if ((player->kartstuff[k_drift] > 0 && player->cmd.driftturn > 0) // Inward drifts @@ -3192,22 +3238,40 @@ static void K_SpawnDriftSparks(player_t *player) { if ((player->kartstuff[k_drift] < 0 && (i & 1)) || (player->kartstuff[k_drift] > 0 && !(i & 1))) - P_SetMobjState(spark, S_DRIFTSPARK_A1); + { + size++; + } else if ((player->kartstuff[k_drift] < 0 && !(i & 1)) || (player->kartstuff[k_drift] > 0 && (i & 1))) - P_SetMobjState(spark, S_DRIFTSPARK_C1); + { + size--; + } } else if ((player->kartstuff[k_drift] > 0 && player->cmd.driftturn < 0) // Outward drifts || (player->kartstuff[k_drift] < 0 && player->cmd.driftturn > 0)) { if ((player->kartstuff[k_drift] < 0 && (i & 1)) || (player->kartstuff[k_drift] > 0 && !(i & 1))) - P_SetMobjState(spark, S_DRIFTSPARK_C1); + { + size--; + } else if ((player->kartstuff[k_drift] < 0 && !(i & 1)) || (player->kartstuff[k_drift] > 0 && (i & 1))) - P_SetMobjState(spark, S_DRIFTSPARK_A1); + { + size++; + } } + if (size == 2) + P_SetMobjState(spark, S_DRIFTSPARK_A1); + else if (size < 1) + P_SetMobjState(spark, S_DRIFTSPARK_C1); + else if (size > 2) + P_SetMobjState(spark, S_DRIFTSPARK_D1); + + if (trail > 0) + spark->tics += trail; + K_MatchGenericExtraFlags(spark, player->mo); } } @@ -5721,42 +5785,40 @@ static void K_KartDrift(player_t *player, boolean onground) // 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)) - && player->kartstuff[k_driftcharge] < dsone && onground) { - player->kartstuff[k_driftcharge] = 0; - } - else 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)) - && (player->kartstuff[k_driftcharge] >= dsone && player->kartstuff[k_driftcharge] < dstwo) - && onground) - { - if (player->kartstuff[k_driftboost] < 20) - player->kartstuff[k_driftboost] = 20; - S_StartSound(player->mo, sfx_s23c); - //K_SpawnDashDustRelease(player); - player->kartstuff[k_driftcharge] = 0; - } - else 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)) - && player->kartstuff[k_driftcharge] < dsthree - && onground) - { - if (player->kartstuff[k_driftboost] < 50) - player->kartstuff[k_driftboost] = 50; - S_StartSound(player->mo, sfx_s23c); - //K_SpawnDashDustRelease(player); - player->kartstuff[k_driftcharge] = 0; - } - else 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)) - && player->kartstuff[k_driftcharge] >= dsthree - && onground) - { - if (player->kartstuff[k_driftboost] < 125) - player->kartstuff[k_driftboost] = 125; - S_StartSound(player->mo, sfx_s23c); - //K_SpawnDashDustRelease(player); + if (player->kartstuff[k_driftcharge] < 0 || player->kartstuff[k_driftcharge] >= dsone) + { + S_StartSound(player->mo, sfx_s23c); + //K_SpawnDashDustRelease(player); + + if (player->kartstuff[k_driftcharge] < 0) + { + // Stage 0: Yellow sparks + 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 (player->kartstuff[k_driftboost] < 20) + player->kartstuff[k_driftboost] = 20; + } + else if (player->kartstuff[k_driftcharge] < dsthree) + { + // Stage 2: Blue sparks + if (player->kartstuff[k_driftboost] < 50) + player->kartstuff[k_driftboost] = 50; + } + else if (player->kartstuff[k_driftcharge] >= dsthree) + { + // Stage 3: Rainbow sparks + if (player->kartstuff[k_driftboost] < 125) + player->kartstuff[k_driftboost] = 125; + } + } + + // Remove charge player->kartstuff[k_driftcharge] = 0; } @@ -5792,10 +5854,16 @@ static void K_KartDrift(player_t *player, boolean onground) player->kartstuff[k_driftend] = 0; } - - // Incease/decrease the drift value to continue drifting in that direction - if (player->kartstuff[k_spinouttimer] == 0 && player->kartstuff[k_jmp] == 1 && onground && player->kartstuff[k_drift] != 0) + if (player->kartstuff[k_spinouttimer] > 0) { + // Stop drifting + player->kartstuff[k_drift] = player->kartstuff[k_driftcharge] = 0; + player->kartstuff[k_aizdriftstrat] = player->kartstuff[k_brakedrift] = 0; + player->kartstuff[k_getsparks] = 0; + } + else if (player->kartstuff[k_jmp] == 1 && onground && player->kartstuff[k_drift] != 0) + { + // Incease/decrease the drift value to continue drifting in that direction fixed_t driftadditive = 24; if (player->kartstuff[k_drift] >= 1) // Drifting to the left @@ -5828,11 +5896,26 @@ static void K_KartDrift(player_t *player, boolean onground) && !player->kartstuff[k_hyudorotimer] && !EITHERSNEAKER(player))) driftadditive = 0; - if (player->speed > minspeed*2) + + if (player->speed >= minspeed*2) + { player->kartstuff[k_getsparks] = 1; + if (player->kartstuff[k_driftcharge] <= -1) + player->kartstuff[k_driftcharge] = dsone; + } + else + { + player->kartstuff[k_getsparks] = 0; + driftadditive = 0; + + if (player->kartstuff[k_driftcharge] >= dsone) + player->kartstuff[k_driftcharge] = -1; + } + // This spawns the drift sparks - if (player->kartstuff[k_driftcharge] + driftadditive >= dsone) + if ((player->kartstuff[k_driftcharge] + driftadditive >= dsone) + || (player->kartstuff[k_driftcharge] < 0)) K_SpawnDriftSparks(player); // Sound whenever you get a different tier of sparks @@ -5849,14 +5932,6 @@ static void K_KartDrift(player_t *player, boolean onground) player->kartstuff[k_driftend] = 0; } - // Stop drifting - if (player->kartstuff[k_spinouttimer] > 0 || player->speed < minspeed) - { - player->kartstuff[k_drift] = player->kartstuff[k_driftcharge] = 0; - player->kartstuff[k_aizdriftstrat] = player->kartstuff[k_brakedrift] = 0; - player->kartstuff[k_getsparks] = 0; - } - if ((!EITHERSNEAKER(player)) || (!player->cmd.driftturn) || (!player->kartstuff[k_aizdriftstrat]) diff --git a/src/p_mobj.c b/src/p_mobj.c index a532dddc5..1ae299749 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -8378,9 +8378,11 @@ void P_MobjThinker(mobj_t *mobj) if (mobj->target->player->kartstuff[k_driftcharge] >= K_GetKartDriftSparkValue(mobj->target->player)*4) mobj->color = (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1))); else if (mobj->target->player->kartstuff[k_driftcharge] >= K_GetKartDriftSparkValue(mobj->target->player)*2) - mobj->color = SKINCOLOR_KETCHUP; - else if (mobj->target->player->kartstuff[k_driftcharge] >= K_GetKartDriftSparkValue(mobj->target->player)) mobj->color = SKINCOLOR_SAPPHIRE; + else if (mobj->target->player->kartstuff[k_driftcharge] >= K_GetKartDriftSparkValue(mobj->target->player)) + mobj->color = SKINCOLOR_KETCHUP; + else if (mobj->target->player->kartstuff[k_driftcharge] < 0) + mobj->color = SKINCOLOR_GOLD; else mobj->color = SKINCOLOR_SILVER; From f37b1268b420f91a055d861268970125e03bec3c Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sat, 14 Mar 2020 18:12:36 -0400 Subject: [PATCH 127/163] Slight adjustments to yellow spark nuances Instead of setting you to yellow sparks when before you could keep red sparks, it keeps your previous "state". When going below 20 speed when you have sparks, you no longer stop charging sparks, but you have to break above 20 speed once you have yellow sparks to get normal sparks again. --- src/k_kart.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 3f57b3e83..3f41e5ee8 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -5897,20 +5897,23 @@ static void K_KartDrift(player_t *player, boolean onground) && !EITHERSNEAKER(player))) driftadditive = 0; - if (player->speed >= minspeed*2) + // Inbetween minspeed and minspeed*2, it'll keep your previous drift-spark state. + if (player->speed > minspeed*2) { player->kartstuff[k_getsparks] = 1; if (player->kartstuff[k_driftcharge] <= -1) - player->kartstuff[k_driftcharge] = dsone; + player->kartstuff[k_driftcharge] = dsone; // Back to red } - else + else if (player->speed <= minspeed) { player->kartstuff[k_getsparks] = 0; driftadditive = 0; if (player->kartstuff[k_driftcharge] >= dsone) - player->kartstuff[k_driftcharge] = -1; + player->kartstuff[k_driftcharge] = -1; // Set yellow sparks + else + player->kartstuff[k_driftcharge] = 0; // No sparks } // This spawns the drift sparks From 9dfbd918a4106b0c65df09d94233d08e4ba489a1 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sat, 14 Mar 2020 18:14:21 -0400 Subject: [PATCH 128/163] Don't forcefully remove drift when bumping walls --- src/p_map.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/p_map.c b/src/p_map.c index 9e34e04d9..8c4bf8240 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -3667,8 +3667,6 @@ void P_BouncePlayerMove(mobj_t *mo) mmomx = mo->player->rmomx; mmomy = mo->player->rmomy; - mo->player->kartstuff[k_drift] = 0; - mo->player->kartstuff[k_driftcharge] = 0; mo->player->kartstuff[k_pogospring] = 0; // trace along the three leading corners From 219d4aebf2bacf13df78eb8006f3c2c375158e4f Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sat, 14 Mar 2020 18:31:53 -0400 Subject: [PATCH 129/163] Actually, might've reintroduced more lawnmower problems; let's play it safe --- 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 3f41e5ee8..c2fee8494 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -5905,7 +5905,7 @@ static void K_KartDrift(player_t *player, boolean onground) if (player->kartstuff[k_driftcharge] <= -1) player->kartstuff[k_driftcharge] = dsone; // Back to red } - else if (player->speed <= minspeed) + else { player->kartstuff[k_getsparks] = 0; driftadditive = 0; From ed9919d47c616de59beed51ed8000c3fb595f98d Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sat, 14 Mar 2020 18:38:01 -0400 Subject: [PATCH 130/163] Remove drift when hitting 0 speed Prevents silliness like this: https://cdn.discordapp.com/attachments/275750804227489794/688515983471804455/kart0027.gif --- 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 c2fee8494..3a7ccfb7d 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -5854,7 +5854,7 @@ static void K_KartDrift(player_t *player, boolean onground) player->kartstuff[k_driftend] = 0; } - if (player->kartstuff[k_spinouttimer] > 0) + if (player->kartstuff[k_spinouttimer] > 0 || player->speed == 0) { // Stop drifting player->kartstuff[k_drift] = player->kartstuff[k_driftcharge] = 0; From f235a50d2ef8d2d3e97701ed8bc90cfe65046ac1 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sat, 14 Mar 2020 18:46:22 -0400 Subject: [PATCH 131/163] Blue sparks trail longer --- src/k_kart.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 3a7ccfb7d..37011eb4b 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3185,25 +3185,25 @@ static void K_SpawnDriftSparks(player_t *player) { // Stage 3: Rainbow size = 2; + trail = 2; if (player->kartstuff[k_driftcharge] <= (K_GetKartDriftSparkValue(player)*4)+(32*3)) { // transition spark->color = SKINCOLOR_SILVER; P_SetScale(spark, (spark->destscale = spark->scale*3/2)); - trail = 1; } else { spark->color = (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1))); spark->colorized = true; - trail = 2; } } else if (player->kartstuff[k_driftcharge] >= K_GetKartDriftSparkValue(player)*2) { // Stage 2: Blue size = 2; + trail = 1; if (player->kartstuff[k_driftcharge] <= (K_GetKartDriftSparkValue(player)*2)+(32*3)) { From 69cc03a4343c42b1f4bbfd2d32910006e70a5e6d Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sat, 14 Mar 2020 19:01:34 -0400 Subject: [PATCH 132/163] This line broke yellow sparks entirely --- src/k_kart.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 37011eb4b..9792c5a3c 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -5912,8 +5912,6 @@ static void K_KartDrift(player_t *player, boolean onground) if (player->kartstuff[k_driftcharge] >= dsone) player->kartstuff[k_driftcharge] = -1; // Set yellow sparks - else - player->kartstuff[k_driftcharge] = 0; // No sparks } // This spawns the drift sparks From 084901a8b1107a697ea0289818875ade14d50007 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sun, 15 Mar 2020 19:21:06 -0400 Subject: [PATCH 133/163] 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 01885c516634f81efc598c3e137abae8684724f3 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sun, 15 Mar 2020 20:52:41 -0400 Subject: [PATCH 134/163] Revert "Actually, might've reintroduced more lawnmower problems; let's play it safe" This reverts commit 219d4aebf2bacf13df78eb8006f3c2c375158e4f. --- 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 9792c5a3c..3a62be45a 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -5905,7 +5905,7 @@ static void K_KartDrift(player_t *player, boolean onground) if (player->kartstuff[k_driftcharge] <= -1) player->kartstuff[k_driftcharge] = dsone; // Back to red } - else + else if (player->speed <= minspeed) { player->kartstuff[k_getsparks] = 0; driftadditive = 0; From 7d1c132ca2d758bf3352f713252b554b0508123d Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sun, 15 Mar 2020 21:21:07 -0400 Subject: [PATCH 135/163] Fix drift spark camera when you go to yellow sparks --- src/p_user.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/p_user.c b/src/p_user.c index 8185667a0..c8b03267f 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -7498,7 +7498,14 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall if (player->kartstuff[k_drift] != 0) { fixed_t panmax = (dist/5); - pan = FixedDiv(FixedMul(min((fixed_t)player->kartstuff[k_driftcharge], K_GetKartDriftSparkValue(player)), panmax), K_GetKartDriftSparkValue(player)); + INT32 driftval = K_GetKartDriftSparkValue(player); + INT32 dc = player->kartstuff[k_driftcharge]; + + if (dc > driftval || dc < 0) + dc = driftval; + + pan = FixedDiv(FixedMul((fixed_t)dc, panmax), driftval); + if (pan > panmax) pan = panmax; if (player->kartstuff[k_drift] < 0) From c4dd88bbaee7b4eaac593f044d03b7f3b29dd332 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sun, 15 Mar 2020 21:59:22 -0400 Subject: [PATCH 136/163] Turn sparkles that warn you when you're about to hit a new drift spark! --- src/dehacked.c | 6 ++++++ src/info.c | 13 +++++++++---- src/info.h | 7 +++++++ src/k_kart.c | 41 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 63 insertions(+), 4 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index efb39dd7d..061eb9388 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -6359,6 +6359,12 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_DRIFTDUST3", "S_DRIFTDUST4", + // Drift Sparkles + "S_DRIFTWARNSPARK1", + "S_DRIFTWARNSPARK2", + "S_DRIFTWARNSPARK3", + "S_DRIFTWARNSPARK4", + // Fast lines "S_FASTLINE1", "S_FASTLINE2", diff --git a/src/info.c b/src/info.c index 6803e8a33..df8f31061 100644 --- a/src/info.c +++ b/src/info.c @@ -57,10 +57,10 @@ char sprnames[NUMSPRITES + 1][5] = "SRBL","SRBM","SRBN","SRBO", //SRB2kart Sprites "RNDM","RPOP","SGNS","FAST","DSHR","BOST","BOSM","KFRE","KINV","KINF", - "WIPD","DRIF","BDRF","DUST","RSHE","FITM","BANA","ORBN","JAWZ","SSMN", - "KRBM","BHOG","BHBM","SPBM","THNS","SINK","SITR","KBLN","DEZL","POKE", - "AUDI","DECO","DOOD","SNES","GBAS","SPRS","BUZB","CHOM","SACO","CRAB", - "BRNG","BUMP","FLEN","CLAS","PSHW","ISTA","ISTB","ARRO","ITEM", + "WIPD","DRIF","BDRF","DUST","DRWS","RSHE","FITM","BANA","ORBN","JAWZ", + "SSMN","KRBM","BHOG","BHBM","SPBM","THNS","SINK","SITR","KBLN","DEZL", + "POKE","AUDI","DECO","DOOD","SNES","GBAS","SPRS","BUZB","CHOM","SACO", + "CRAB","BRNG","BUMP","FLEN","CLAS","PSHW","ISTA","ISTB","ARRO","ITEM", "ITMO","ITMI","ITMN","WANT","PBOM","HIT1","HIT2","HIT3","RETI","AIDU", "KSPK","LZI1","LZI2","KLIT","FZSM","FZBM","FPRT","SBUS","MARB","FUFO", "RUST","BLON","VAPE","HTZA","HTZB","SGVA","SGVB","SGVC","PGTR","PGF1", @@ -2597,6 +2597,11 @@ state_t states[NUMSTATES] = {SPR_DUST, FF_TRANS20|2, 3, {NULL}, 0, 0, S_DRIFTDUST4}, // S_DRIFTDUST3 {SPR_DUST, FF_TRANS20|3, 3, {NULL}, 0, 0, S_NULL}, // S_DRIFTDUST4 + {SPR_DRWS, FF_FULLBRIGHT|0, 3, {NULL}, 0, 0, S_DRIFTWARNSPARK2}, // S_DRIFTWARNSPARK1 + {SPR_DRWS, FF_FULLBRIGHT|1, 3, {NULL}, 0, 0, S_DRIFTWARNSPARK3}, // S_DRIFTWARNSPARK2 + {SPR_DRWS, FF_FULLBRIGHT|FF_TRANS20|2, 3, {NULL}, 0, 0, S_DRIFTWARNSPARK4}, // S_DRIFTWARNSPARK3 + {SPR_DRWS, FF_FULLBRIGHT|FF_TRANS20|3, 3, {NULL}, 0, 0, S_NULL}, // S_DRIFTWARNSPARK4 + {SPR_FAST, FF_PAPERSPRITE|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_FASTLINE2}, // S_FASTLINE1 {SPR_FAST, FF_PAPERSPRITE|FF_FULLBRIGHT|1, 1, {NULL}, 0, 0, S_FASTLINE3}, // S_FASTLINE2 {SPR_FAST, FF_PAPERSPRITE|FF_FULLBRIGHT|2, 1, {NULL}, 0, 0, S_FASTLINE4}, // S_FASTLINE3 diff --git a/src/info.h b/src/info.h index 50887e93f..78902d297 100644 --- a/src/info.h +++ b/src/info.h @@ -608,6 +608,7 @@ typedef enum sprite SPR_DRIF, // Drift Sparks SPR_BDRF, // Brake drift sparks SPR_DUST, // Drift Dust + SPR_DRWS, // Drift dust sparks // Kart Items SPR_RSHE, // Rocket sneaker @@ -3248,6 +3249,12 @@ typedef enum state S_DRIFTDUST3, S_DRIFTDUST4, + // Drift Sparkles + S_DRIFTWARNSPARK1, + S_DRIFTWARNSPARK2, + S_DRIFTWARNSPARK3, + S_DRIFTWARNSPARK4, + // Fast lines S_FASTLINE1, S_FASTLINE2, diff --git a/src/k_kart.c b/src/k_kart.c index 3a62be45a..3b30b1c3f 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3587,6 +3587,47 @@ void K_DriftDustHandling(mobj_t *spawner) S_StartSound(spawner, sfx_screec); K_MatchGenericExtraFlags(dust, spawner); + + // Sparkle-y warning for when you're about to change drift sparks! + if (spawner->player && spawner->player->kartstuff[k_drift]) + { + INT32 driftval = K_GetKartDriftSparkValue(spawner->player); + INT32 warntime = driftval/3; + INT32 dc = spawner->player->kartstuff[k_driftcharge]; + UINT8 c = SKINCOLOR_NONE; + boolean rainbow = false; + + if (dc < 0) + { + c = SKINCOLOR_GOLD; + } + else if (dc >= (driftval - warntime)) + { + if (dc >= ((2*driftval) - warntime)) + { + if (dc >= ((4*driftval) - warntime)) + { + c = (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1))); + rainbow = true; + } + else + { + c = SKINCOLOR_SAPPHIRE; + } + } + else + { + c = SKINCOLOR_KETCHUP; + } + } + + if (c != SKINCOLOR_NONE) + { + P_SetMobjState(dust, S_DRIFTWARNSPARK1); + dust->color = c; + dust->colorized = rainbow; + } + } } } From 3093a3078e744d34d13acd60f464fc550cf84b12 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sun, 15 Mar 2020 22:05:43 -0400 Subject: [PATCH 137/163] Scale dust properly --- src/k_kart.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 3b30b1c3f..db1be60e2 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3550,7 +3550,7 @@ void K_DriftDustHandling(mobj_t *spawner) { angle_t playerangle = spawner->angle; - if (spawner->player->speed < 5<player->speed < 5*spawner->scale) return; if (spawner->player->cmd.forwardmove < 0) @@ -3561,7 +3561,7 @@ void K_DriftDustHandling(mobj_t *spawner) } else { - if (P_AproxDistance(spawner->momx, spawner->momy) < 5<momx, spawner->momy) < 5*spawner->scale) return; anglediff = abs((signed)(spawner->angle - R_PointToAngle2(0, 0, spawner->momx, spawner->momy))); @@ -3572,12 +3572,12 @@ void K_DriftDustHandling(mobj_t *spawner) if (anglediff > ANG10*4) // Trying to turn further than 40 degrees { - fixed_t spawnx = P_RandomRange(-spawnrange, spawnrange)<scale; + fixed_t spawny = P_RandomRange(-spawnrange, spawnrange) * spawner->scale; INT32 speedrange = 2; mobj_t *dust = P_SpawnMobj(spawner->x + spawnx, spawner->y + spawny, spawner->z, MT_DRIFTDUST); - dust->momx = FixedMul(spawner->momx + (P_RandomRange(-speedrange, speedrange)<scale)/4); - dust->momy = FixedMul(spawner->momy + (P_RandomRange(-speedrange, speedrange)<scale)/4); + dust->momx = FixedMul(spawner->momx + (P_RandomRange(-speedrange, speedrange) * spawner->scale), 3*(spawner->scale)/4); + dust->momy = FixedMul(spawner->momy + (P_RandomRange(-speedrange, speedrange) * spawner->scale), 3*(spawner->scale)/4); dust->momz = P_MobjFlip(spawner) * (P_RandomRange(1, 4) * (spawner->scale)); P_SetScale(dust, spawner->scale/2); dust->destscale = spawner->scale * 3; From 4174d9a23ee6411e5e21c6acd27ff92062695882 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sun, 15 Mar 2020 23:02:43 -0400 Subject: [PATCH 138/163] Add a function for retrieving drift spark color, to simplify a lot of code & add transition colors to everything --- src/k_kart.c | 178 ++++++++++++++++++++++++++++----------------------- src/k_kart.h | 1 + src/p_mobj.c | 11 +--- 3 files changed, 102 insertions(+), 88 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index db1be60e2..fb76fd947 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3133,6 +3133,59 @@ static mobj_t *K_SpawnKartMissile(mobj_t *source, mobjtype_t type, angle_t an, I return NULL; } +UINT8 K_DriftSparkColor(player_t *player, INT32 charge) +{ + INT32 ds = K_GetKartDriftSparkValue(player); + UINT8 color = SKINCOLOR_NONE; + + if (charge < 0) + { + // Stage 0: Yellow + color = SKINCOLOR_GOLD; + } + else if (charge >= ds*4) + { + // Stage 3: Rainbow + if (charge <= (ds*4)+(32*3)) + { + // transition + color = SKINCOLOR_SILVER; + } + else + { + color = (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1))); + } + } + else if (charge >= ds*2) + { + // Stage 2: Blue + if (charge <= (ds*2)+(32*3)) + { + // transition + color = SKINCOLOR_PURPLE; + } + else + { + color = SKINCOLOR_SAPPHIRE; + } + } + else if (charge >= ds) + { + // Stage 1: Red + if (charge <= (ds)+(32*3)) + { + // transition + color = SKINCOLOR_RASPBERRY; + } + else + { + color = SKINCOLOR_KETCHUP; + } + } + + return color; +} + static void K_SpawnDriftSparks(player_t *player) { fixed_t newx; @@ -3159,6 +3212,7 @@ static void K_SpawnDriftSparks(player_t *player) for (i = 0; i < 2; i++) { + INT32 ds = K_GetKartDriftSparkValue(player); SINT8 size = 1; UINT8 trail = 0; @@ -3175,62 +3229,51 @@ static void K_SpawnDriftSparks(player_t *player) spark->momy = player->mo->momy/2; //spark->momz = player->mo->momz/2; + spark->color = K_DriftSparkColor(player, player->kartstuff[k_driftcharge]); + if (player->kartstuff[k_driftcharge] < 0) { // Stage 0: Yellow - spark->color = SKINCOLOR_GOLD; size = 0; } - else if (player->kartstuff[k_driftcharge] >= K_GetKartDriftSparkValue(player)*4) + else if (player->kartstuff[k_driftcharge] >= ds*4) { // Stage 3: Rainbow size = 2; trail = 2; - if (player->kartstuff[k_driftcharge] <= (K_GetKartDriftSparkValue(player)*4)+(32*3)) + if (player->kartstuff[k_driftcharge] <= (ds*4)+(32*3)) { // transition - spark->color = SKINCOLOR_SILVER; P_SetScale(spark, (spark->destscale = spark->scale*3/2)); } else { - spark->color = (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1))); spark->colorized = true; } } - else if (player->kartstuff[k_driftcharge] >= K_GetKartDriftSparkValue(player)*2) + else if (player->kartstuff[k_driftcharge] >= ds*2) { // Stage 2: Blue size = 2; trail = 1; - if (player->kartstuff[k_driftcharge] <= (K_GetKartDriftSparkValue(player)*2)+(32*3)) + if (player->kartstuff[k_driftcharge] <= (ds*2)+(32*3)) { // transition - spark->color = SKINCOLOR_PURPLE; P_SetScale(spark, (spark->destscale = spark->scale*3/2)); } - else - { - spark->color = SKINCOLOR_SAPPHIRE; - } } else { // Stage 1: Red size = 1; - if (player->kartstuff[k_driftcharge] <= (K_GetKartDriftSparkValue(player))+(32*3)) + if (player->kartstuff[k_driftcharge] <= (ds)+(32*3)) { // transition - spark->color = SKINCOLOR_RASPBERRY; P_SetScale(spark, (spark->destscale = spark->scale*2)); } - else - { - spark->color = SKINCOLOR_KETCHUP; - } } if ((player->kartstuff[k_drift] > 0 && player->cmd.driftturn > 0) // Inward drifts @@ -3597,28 +3640,16 @@ void K_DriftDustHandling(mobj_t *spawner) UINT8 c = SKINCOLOR_NONE; boolean rainbow = false; - if (dc < 0) + if (dc >= 0) { - c = SKINCOLOR_GOLD; + dc += warntime; } - else if (dc >= (driftval - warntime)) + + c = K_DriftSparkColor(spawner->player, dc); + + if (dc > (4*driftval)+(32*3)) { - if (dc >= ((2*driftval) - warntime)) - { - if (dc >= ((4*driftval) - warntime)) - { - c = (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1))); - rainbow = true; - } - else - { - c = SKINCOLOR_SAPPHIRE; - } - } - else - { - c = SKINCOLOR_KETCHUP; - } + rainbow = true; } if (c != SKINCOLOR_NONE) @@ -9754,57 +9785,44 @@ static void K_drawKartFirstPerson(void) if (stplyr->mo) { - INT32 dsone = K_GetKartDriftSparkValue(stplyr); - INT32 dstwo = dsone*2; - INT32 dsthree = dstwo*2; + UINT8 driftcolor = K_DriftSparkColor(stplyr, stplyr->kartstuff[k_driftcharge]); + const angle_t ang = R_PointToAngle2(0, 0, stplyr->rmomx, stplyr->rmomy) - stplyr->frameangle; + // yes, the following is correct. no, you do not need to swap the x and y. + fixed_t xoffs = -P_ReturnThrustY(stplyr->mo, ang, (BASEVIDWIDTH<<(FRACBITS-2))/2); + fixed_t yoffs = -(P_ReturnThrustX(stplyr->mo, ang, 4*FRACUNIT) - 4*FRACUNIT); -#ifndef DONTLIKETOASTERSFPTWEAKS + if (splitscreen) + xoffs = FixedMul(xoffs, scale); + + xoffs -= (tn)*scale; + xoffs -= (dr)*scale; + + if (stplyr->frameangle == stplyr->mo->angle) { - const angle_t ang = R_PointToAngle2(0, 0, stplyr->rmomx, stplyr->rmomy) - stplyr->frameangle; - // yes, the following is correct. no, you do not need to swap the x and y. - fixed_t xoffs = -P_ReturnThrustY(stplyr->mo, ang, (BASEVIDWIDTH<<(FRACBITS-2))/2); - fixed_t yoffs = -(P_ReturnThrustX(stplyr->mo, ang, 4*FRACUNIT) - 4*FRACUNIT); + const fixed_t mag = FixedDiv(stplyr->speed, 10*stplyr->mo->scale); - if (splitscreen) - xoffs = FixedMul(xoffs, scale); - - xoffs -= (tn)*scale; - xoffs -= (dr)*scale; - - if (stplyr->frameangle == stplyr->mo->angle) + if (mag < FRACUNIT) { - const fixed_t mag = FixedDiv(stplyr->speed, 10*stplyr->mo->scale); - - if (mag < FRACUNIT) - { - xoffs = FixedMul(xoffs, mag); - if (!splitscreen) - yoffs = FixedMul(yoffs, mag); - } + xoffs = FixedMul(xoffs, mag); + if (!splitscreen) + yoffs = FixedMul(yoffs, mag); } - - if (stplyr->mo->momz > 0) // TO-DO: Draw more of the kart so we can remove this if! - yoffs += stplyr->mo->momz/3; - - if (encoremode) - x -= xoffs; - else - x += xoffs; - if (!splitscreen) - y += yoffs; } - // drift sparks! - if ((leveltime & 1) && (stplyr->kartstuff[k_driftcharge] >= dsthree)) - colmap = R_GetTranslationColormap(TC_RAINBOW, (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1))), GTC_CACHE); - else if ((leveltime & 1) && (stplyr->kartstuff[k_driftcharge] >= dstwo)) - colmap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_KETCHUP, GTC_CACHE); - else if ((leveltime & 1) && (stplyr->kartstuff[k_driftcharge] >= dsone)) - colmap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_SAPPHIRE, GTC_CACHE); + if (stplyr->mo->momz > 0) // TO-DO: Draw more of the kart so we can remove this if! + yoffs += stplyr->mo->momz/3; + + if (encoremode) + x -= xoffs; else -#endif - // invincibility/grow/shrink! - if (stplyr->mo->colorized && stplyr->mo->color) + x += xoffs; + if (!splitscreen) + y += yoffs; + + + if ((leveltime & 1) && (driftcolor != SKINCOLOR_NONE)) // drift sparks! + colmap = R_GetTranslationColormap(TC_RAINBOW, driftcolor, GTC_CACHE); + else if (stplyr->mo->colorized && stplyr->mo->color) // invincibility/grow/shrink! colmap = R_GetTranslationColormap(TC_RAINBOW, stplyr->mo->color, GTC_CACHE); } diff --git a/src/k_kart.h b/src/k_kart.h index b91e8c8a1..119b3f9a7 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -40,6 +40,7 @@ void K_ExplodePlayer(player_t *player, mobj_t *source, mobj_t *inflictor); void K_StealBumper(player_t *player, player_t *victim, boolean force); void K_SpawnKartExplosion(fixed_t x, fixed_t y, fixed_t z, fixed_t radius, INT32 number, mobjtype_t type, angle_t rotangle, boolean spawncenter, boolean ghostit, mobj_t *source); void K_SpawnMineExplosion(mobj_t *source, UINT8 color); +UINT8 K_DriftSparkColor(player_t *player, INT32 charge); void K_SpawnBoostTrail(player_t *player); void K_SpawnSparkleTrail(mobj_t *mo); void K_SpawnWipeoutTrail(mobj_t *mo, boolean translucent); diff --git a/src/p_mobj.c b/src/p_mobj.c index 1ae299749..9f688e369 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -8363,6 +8363,7 @@ void P_MobjThinker(mobj_t *mobj) } else { + UINT8 driftcolor = K_DriftSparkColor(mobj->target->player, mobj->target->player->kartstuff[k_driftcharge]); fixed_t newx, newy; angle_t travelangle; @@ -8375,14 +8376,8 @@ void P_MobjThinker(mobj_t *mobj) mobj->angle = travelangle - ((ANGLE_90/5)*mobj->target->player->kartstuff[k_drift]); P_SetScale(mobj, (mobj->destscale = mobj->target->scale)); - if (mobj->target->player->kartstuff[k_driftcharge] >= K_GetKartDriftSparkValue(mobj->target->player)*4) - mobj->color = (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1))); - else if (mobj->target->player->kartstuff[k_driftcharge] >= K_GetKartDriftSparkValue(mobj->target->player)*2) - mobj->color = SKINCOLOR_SAPPHIRE; - else if (mobj->target->player->kartstuff[k_driftcharge] >= K_GetKartDriftSparkValue(mobj->target->player)) - mobj->color = SKINCOLOR_KETCHUP; - else if (mobj->target->player->kartstuff[k_driftcharge] < 0) - mobj->color = SKINCOLOR_GOLD; + if (driftcolor != SKINCOLOR_NONE) + mobj->color = driftcolor; else mobj->color = SKINCOLOR_SILVER; From 4026344ad3e79babbc974e1813e931d6b8e4101a Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sun, 15 Mar 2020 23:21:46 -0400 Subject: [PATCH 139/163] Minor yellow-spark related cleanup Sound plays when you go to yellow sparks & back, and Red transition uses Tangerine instead of Raspberry --- src/k_kart.c | 33 ++++++++++++++++++++++++--------- src/sounds.c | 1 + src/sounds.h | 1 + 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index fb76fd947..ccace01f5 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3175,7 +3175,7 @@ UINT8 K_DriftSparkColor(player_t *player, INT32 charge) if (charge <= (ds)+(32*3)) { // transition - color = SKINCOLOR_RASPBERRY; + color = SKINCOLOR_TANGERINE; } else { @@ -3188,6 +3188,7 @@ UINT8 K_DriftSparkColor(player_t *player, INT32 charge) static void K_SpawnDriftSparks(player_t *player) { + INT32 ds = K_GetKartDriftSparkValue(player); fixed_t newx; fixed_t newy; mobj_t *spark; @@ -3205,14 +3206,13 @@ static void K_SpawnDriftSparks(player_t *player) return; if (!player->kartstuff[k_drift] - || (player->kartstuff[k_driftcharge] < K_GetKartDriftSparkValue(player) && !(player->kartstuff[k_driftcharge] < 0))) + || (player->kartstuff[k_driftcharge] < ds && !(player->kartstuff[k_driftcharge] < 0))) return; travelangle = player->mo->angle-(ANGLE_45/5)*player->kartstuff[k_drift]; for (i = 0; i < 2; i++) { - INT32 ds = K_GetKartDriftSparkValue(player); SINT8 size = 1; UINT8 trail = 0; @@ -5937,6 +5937,7 @@ static void K_KartDrift(player_t *player, boolean onground) { // Incease/decrease the drift value to continue drifting in that direction fixed_t driftadditive = 24; + boolean playsound = false; if (player->kartstuff[k_drift] >= 1) // Drifting to the left { @@ -5975,7 +5976,10 @@ static void K_KartDrift(player_t *player, boolean onground) player->kartstuff[k_getsparks] = 1; if (player->kartstuff[k_driftcharge] <= -1) + { player->kartstuff[k_driftcharge] = dsone; // Back to red + playsound = true; + } } else if (player->speed <= minspeed) { @@ -5983,22 +5987,33 @@ static void K_KartDrift(player_t *player, boolean onground) driftadditive = 0; if (player->kartstuff[k_driftcharge] >= dsone) + { player->kartstuff[k_driftcharge] = -1; // Set yellow sparks + playsound = true; + } } // This spawns the drift sparks if ((player->kartstuff[k_driftcharge] + driftadditive >= dsone) || (player->kartstuff[k_driftcharge] < 0)) + { K_SpawnDriftSparks(player); + } + + if ((player->kartstuff[k_driftcharge] < dsone && player->kartstuff[k_driftcharge]+driftadditive >= dsone) + || (player->kartstuff[k_driftcharge] < dstwo && player->kartstuff[k_driftcharge]+driftadditive >= dstwo) + || (player->kartstuff[k_driftcharge] < dsthree && player->kartstuff[k_driftcharge]+driftadditive >= dsthree)) + { + playsound = true; + } // Sound whenever you get a different tier of sparks - if (P_IsDisplayPlayer(player) // UGHGHGH... - && ((player->kartstuff[k_driftcharge] < dsone && player->kartstuff[k_driftcharge]+driftadditive >= dsone) - || (player->kartstuff[k_driftcharge] < dstwo && player->kartstuff[k_driftcharge]+driftadditive >= dstwo) - || (player->kartstuff[k_driftcharge] < dsthree && player->kartstuff[k_driftcharge]+driftadditive >= dsthree))) + if (playsound && P_IsDisplayPlayer(player)) { - //S_StartSound(player->mo, sfx_s3ka2); - S_StartSoundAtVolume(player->mo, sfx_s3ka2, 192); // Ugh... + if (player->kartstuff[k_driftcharge] == -1) + S_StartSoundAtVolume(player->mo, sfx_sploss, 192); // Yellow spark sound + else + S_StartSoundAtVolume(player->mo, sfx_s3ka2, 192); } player->kartstuff[k_driftcharge] += driftadditive; diff --git a/src/sounds.c b/src/sounds.c index ef150d7a5..adae99df5 100644 --- a/src/sounds.c +++ b/src/sounds.c @@ -817,6 +817,7 @@ sfxinfo_t S_sfx[NUMSFX] = {"toada", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // Arid Sands Toad scream {"bhurry", false, 255, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // v1.0.2 Battle overtime {"bsnipe", false, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Banana sniping + {"sploss", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // Down to yellow sparks {"itfree", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // :shitsfree: {"dbgsal", false, 255, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Debug notification diff --git a/src/sounds.h b/src/sounds.h index 8b9ca609e..e3a8560ca 100644 --- a/src/sounds.h +++ b/src/sounds.h @@ -892,6 +892,7 @@ typedef enum sfx_toada, sfx_bhurry, sfx_bsnipe, + sfx_sploss, sfx_itfree, sfx_dbgsal, From 48e69f7c91aee324fef8946d091c5c641616f774 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Mon, 16 Mar 2020 00:53:33 -0400 Subject: [PATCH 140/163] Air sparks, start on drift boosts --- src/dehacked.c | 7 ++- src/info.c | 34 +++++++++++++- src/info.h | 9 +++- src/k_kart.c | 121 +++++++++++++++++++++++++++++-------------------- src/p_mobj.c | 31 +++++++++++++ 5 files changed, 149 insertions(+), 53 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index 061eb9388..0c5324551 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -6381,7 +6381,11 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_FASTDUST6", "S_FASTDUST7", - // Thunder Shield Burst + // Drift boost effect + "S_DRIFTEXPLODE1", + "S_DRIFTEXPLODE2", + "S_DRIFTEXPLODE3", + "S_DRIFTEXPLODE4", // Sneaker boost effect "S_BOOSTFLAME", @@ -7742,6 +7746,7 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_FASTLINE", "MT_FASTDUST", + "MT_DRIFTEXPLODE", "MT_BOOSTFLAME", "MT_BOOSTSMOKE", "MT_SNEAKERTRAIL", diff --git a/src/info.c b/src/info.c index df8f31061..ef878fe16 100644 --- a/src/info.c +++ b/src/info.c @@ -70,7 +70,7 @@ char sprnames[NUMSPRITES + 1][5] = "DUCK","GTRE","CHES","CHIM","DRGN","LZMN","PGSS","ZTCH","MKMA","MKMP", "RTCH","BOWL","BOWH","BRRL","BRRR","HRSE","TOAH","BFRT","OFRT","RFRT", "PFRT","ASPK","HBST","HBSO","HBSF","WBLZ","WBLN","FWRK","MXCL","RGSP", - "DRAF","GRES","OTFG","XMS4","XMS5","VIEW" + "DRAF","GRES","OTFG","DBOS","XMS4","XMS5","VIEW" }; // Doesn't work with g++, needs actionf_p1 (don't modify this comment) @@ -2616,6 +2616,11 @@ state_t states[NUMSTATES] = {SPR_DSHR, FF_PAPERSPRITE|5, 1, {NULL}, 0, 0, S_FASTDUST7}, // S_FASTDUST6 {SPR_DSHR, FF_PAPERSPRITE|6, 1, {NULL}, 0, 0, S_NULL}, // S_FASTDUST7 + {SPR_DBOS, FF_FULLBRIGHT, 2, {NULL}, 6, 1, S_DRIFTEXPLODE2}, // S_DRIFTEXPLODE1 + {SPR_DBOS, FF_FULLBRIGHT|1, 2, {NULL}, 6, 1, S_DRIFTEXPLODE3}, // S_DRIFTEXPLODE2 + {SPR_DBOS, FF_FULLBRIGHT|2, 2, {NULL}, 6, 1, S_DRIFTEXPLODE4}, // S_DRIFTEXPLODE3 + {SPR_DBOS, FF_FULLBRIGHT|3, 2, {NULL}, 6, 1, S_DRIFTEXPLODE1}, // S_DRIFTEXPLODE4 + {SPR_BOST, FF_FULLBRIGHT|FF_ANIMATE, TICRATE, {NULL}, 6, 1, S_BOOSTSMOKESPAWNER}, // S_BOOSTFLAME {SPR_NULL, 0, TICRATE/2, {NULL}, 0, 0, S_NULL}, // S_BOOSTSMOKESPAWNER @@ -15218,6 +15223,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_DRIFTEXPLODE + -1, // doomednum + S_DRIFTEXPLODE1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 8, // speed + 32*FRACUNIT, // radius + 64*FRACUNIT, // height + 1, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, + { // MT_BOOSTFLAME -1, // doomednum S_BOOSTFLAME, // spawnstate diff --git a/src/info.h b/src/info.h index 78902d297..a20875c68 100644 --- a/src/info.h +++ b/src/info.h @@ -786,6 +786,8 @@ typedef enum sprite SPR_OTFG, + SPR_DBOS, // Drift boost flame + // Xmas-specific sprites that don't fit aboxe SPR_XMS4, SPR_XMS5, @@ -3271,7 +3273,11 @@ typedef enum state S_FASTDUST6, S_FASTDUST7, - // Magnet Burst + // Drift boost effect + S_DRIFTEXPLODE1, + S_DRIFTEXPLODE2, + S_DRIFTEXPLODE3, + S_DRIFTEXPLODE4, // Sneaker boost effect S_BOOSTFLAME, @@ -4664,6 +4670,7 @@ typedef enum mobj_type MT_FASTLINE, MT_FASTDUST, + MT_DRIFTEXPLODE, MT_BOOSTFLAME, MT_BOOSTSMOKE, MT_SNEAKERTRAIL, diff --git a/src/k_kart.c b/src/k_kart.c index ccace01f5..d32927c51 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3202,9 +3202,6 @@ static void K_SpawnDriftSparks(player_t *player) if (leveltime % 2 == 1) return; - if (!P_IsObjectOnGround(player->mo)) - return; - if (!player->kartstuff[k_drift] || (player->kartstuff[k_driftcharge] < ds && !(player->kartstuff[k_driftcharge] < 0))) return; @@ -5861,6 +5858,11 @@ static void K_KartDrift(player_t *player, boolean onground) { if (player->kartstuff[k_driftcharge] < 0 || player->kartstuff[k_driftcharge] >= dsone) { + //mobj_t *overlay = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_DRIFTEXPLODE); + //P_SetTarget(&overlay->target, player->mo); + //P_SetScale(overlay, (overlay->destscale = player->mo->scale)); + //K_FlipFromObject(overlay, player->mo); + S_StartSound(player->mo, sfx_s23c); //K_SpawnDashDustRelease(player); @@ -5869,24 +5871,36 @@ static void K_KartDrift(player_t *player, boolean onground) // Stage 0: Yellow sparks if (player->kartstuff[k_driftboost] < 15) player->kartstuff[k_driftboost] = 15; + + //overlay->color = SKINCOLOR_GOLD; + //overlay->fuse = 8; } else if (player->kartstuff[k_driftcharge] >= dsone && player->kartstuff[k_driftcharge] < dstwo) { // Stage 1: Red sparks if (player->kartstuff[k_driftboost] < 20) player->kartstuff[k_driftboost] = 20; + + //overlay->color = SKINCOLOR_KETCHUP; + //overlay->fuse = 16; } else if (player->kartstuff[k_driftcharge] < dsthree) { // Stage 2: Blue sparks if (player->kartstuff[k_driftboost] < 50) player->kartstuff[k_driftboost] = 50; + + //overlay->color = SKINCOLOR_SAPPHIRE; + //overlay->fuse = 32; } else if (player->kartstuff[k_driftcharge] >= dsthree) { // Stage 3: Rainbow sparks if (player->kartstuff[k_driftboost] < 125) player->kartstuff[k_driftboost] = 125; + + //overlay->color = SKINCOLOR_SILVER; + //overlay->fuse = 120; } } @@ -5933,64 +5947,71 @@ static void K_KartDrift(player_t *player, boolean onground) player->kartstuff[k_aizdriftstrat] = player->kartstuff[k_brakedrift] = 0; player->kartstuff[k_getsparks] = 0; } - else if (player->kartstuff[k_jmp] == 1 && onground && player->kartstuff[k_drift] != 0) + else if (player->kartstuff[k_jmp] == 1 && player->kartstuff[k_drift] != 0) { // Incease/decrease the drift value to continue drifting in that direction fixed_t driftadditive = 24; boolean playsound = false; - if (player->kartstuff[k_drift] >= 1) // Drifting to the left + if (onground) { - player->kartstuff[k_drift]++; - if (player->kartstuff[k_drift] > 5) - player->kartstuff[k_drift] = 5; - - if (player->cmd.driftturn > 0) // Inward - driftadditive += abs(player->cmd.driftturn)/100; - if (player->cmd.driftturn < 0) // Outward - driftadditive -= abs(player->cmd.driftturn)/75; - } - else if (player->kartstuff[k_drift] <= -1) // Drifting to the right - { - player->kartstuff[k_drift]--; - if (player->kartstuff[k_drift] < -5) - player->kartstuff[k_drift] = -5; - - if (player->cmd.driftturn < 0) // Inward - driftadditive += abs(player->cmd.driftturn)/100; - if (player->cmd.driftturn > 0) // Outward - driftadditive -= abs(player->cmd.driftturn)/75; - } - - // Disable drift-sparks until you're going fast enough - if (player->kartstuff[k_getsparks] == 0 - || (player->kartstuff[k_offroad] - && !player->kartstuff[k_invincibilitytimer] - && !player->kartstuff[k_hyudorotimer] - && !EITHERSNEAKER(player))) - driftadditive = 0; - - // Inbetween minspeed and minspeed*2, it'll keep your previous drift-spark state. - if (player->speed > minspeed*2) - { - player->kartstuff[k_getsparks] = 1; - - if (player->kartstuff[k_driftcharge] <= -1) + if (player->kartstuff[k_drift] >= 1) // Drifting to the left { - player->kartstuff[k_driftcharge] = dsone; // Back to red - playsound = true; + player->kartstuff[k_drift]++; + if (player->kartstuff[k_drift] > 5) + player->kartstuff[k_drift] = 5; + + if (player->cmd.driftturn > 0) // Inward + driftadditive += abs(player->cmd.driftturn)/100; + if (player->cmd.driftturn < 0) // Outward + driftadditive -= abs(player->cmd.driftturn)/75; + } + else if (player->kartstuff[k_drift] <= -1) // Drifting to the right + { + player->kartstuff[k_drift]--; + if (player->kartstuff[k_drift] < -5) + player->kartstuff[k_drift] = -5; + + if (player->cmd.driftturn < 0) // Inward + driftadditive += abs(player->cmd.driftturn)/100; + if (player->cmd.driftturn > 0) // Outward + driftadditive -= abs(player->cmd.driftturn)/75; + } + + // Disable drift-sparks until you're going fast enough + if (player->kartstuff[k_getsparks] == 0 + || (player->kartstuff[k_offroad] + && !player->kartstuff[k_invincibilitytimer] + && !player->kartstuff[k_hyudorotimer] + && !EITHERSNEAKER(player))) + driftadditive = 0; + + // Inbetween minspeed and minspeed*2, it'll keep your previous drift-spark state. + if (player->speed > minspeed*2) + { + player->kartstuff[k_getsparks] = 1; + + if (player->kartstuff[k_driftcharge] <= -1) + { + player->kartstuff[k_driftcharge] = dsone; // Back to red + playsound = true; + } + } + else if (player->speed <= minspeed) + { + player->kartstuff[k_getsparks] = 0; + driftadditive = 0; + + if (player->kartstuff[k_driftcharge] >= dsone) + { + player->kartstuff[k_driftcharge] = -1; // Set yellow sparks + playsound = true; + } } } - else if (player->speed <= minspeed) + else { - player->kartstuff[k_getsparks] = 0; driftadditive = 0; - - if (player->kartstuff[k_driftcharge] >= dsone) - { - player->kartstuff[k_driftcharge] = -1; // Set yellow sparks - playsound = true; - } } // This spawns the drift sparks diff --git a/src/p_mobj.c b/src/p_mobj.c index 9f688e369..50bb4009a 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -8289,6 +8289,37 @@ void P_MobjThinker(mobj_t *mobj) S_StartSound(mobj, sfx_s3k4e); mobj->health--; break; + case MT_DRIFTEXPLODE: + if (!mobj->target || !mobj->target->health) + { + P_RemoveMobj(mobj); + return; + } + + mobj->angle = mobj->target->angle; + P_TeleportMove(mobj, mobj->target->x + P_ReturnThrustX(mobj, mobj->angle+ANGLE_180, mobj->target->radius), + mobj->target->y + P_ReturnThrustY(mobj, mobj->angle+ANGLE_180, mobj->target->radius), mobj->target->z); + P_SetScale(mobj, mobj->target->scale); + mobj->flags2 ^= MF2_DONTDRAW; +#ifdef HWRENDER + mobj->modeltilt = mobj->target->modeltilt; +#endif + + { + player_t *p = NULL; + if (mobj->target->target && mobj->target->target->player) + p = mobj->target->target->player; + else if (mobj->target->player) + p = mobj->target->player; + + if (p) + { + if (p->kartstuff[k_driftboost] > mobj->movecount) + ; // reset animation + mobj->movecount = p->kartstuff[k_driftboost]; + } + } + break; case MT_BOOSTFLAME: if (!mobj->target || !mobj->target->health) { From 860c1fe23b5fedd27e80b7361c0002afd35b033e Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Mon, 16 Mar 2020 04:17:45 -0400 Subject: [PATCH 141/163] Opaque rain --- src/info.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/info.c b/src/info.c index 275c70018..0ecc88fca 100644 --- a/src/info.c +++ b/src/info.c @@ -1851,7 +1851,7 @@ state_t states[NUMSTATES] = {SPR_SPHG, 2, 4, {NULL}, 0, 0, S_GHORIZ1}, // S_GHORIZ4 // Rain - {SPR_RAIN, FF_TRANS50, -1, {NULL}, 0, 0, S_NULL}, // S_RAIN1 + {SPR_RAIN, 0, -1, {NULL}, 0, 0, S_NULL}, // S_RAIN1 {SPR_NULL, 0, -1, {NULL}, 0, 0, S_NULL}, // S_RAINRETURN // Snowflake From fccc4d29375a465c1456e87ce20aff2f35d0f9be Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Mon, 16 Mar 2020 04:24:32 -0400 Subject: [PATCH 142/163] Slow down backwards spikeballs (again) --- src/k_kart.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 64320fb68..05be47651 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3603,11 +3603,11 @@ static mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t map if (dir == -1) { // Shoot backward - mo = K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + ANGLE_180 - 0x06000000, 0, PROJSPEED/4); - K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + ANGLE_180 - 0x03000000, 0, PROJSPEED/4); - K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + ANGLE_180, 0, PROJSPEED/4); - K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + ANGLE_180 + 0x03000000, 0, PROJSPEED/4); - K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + ANGLE_180 + 0x06000000, 0, PROJSPEED/4); + mo = K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + ANGLE_180 - 0x06000000, 0, PROJSPEED/16); + K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + ANGLE_180 - 0x03000000, 0, PROJSPEED/16); + K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + ANGLE_180, 0, PROJSPEED/16); + K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + ANGLE_180 + 0x03000000, 0, PROJSPEED/16); + K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + ANGLE_180 + 0x06000000, 0, PROJSPEED/16); } else { @@ -3624,7 +3624,7 @@ static mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t map if (dir == -1 && mapthing != MT_SPB) { // Shoot backward - mo = K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + ANGLE_180, 0, PROJSPEED/2); + mo = K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + ANGLE_180, 0, PROJSPEED/8); } else { From 9e3a37cdc73a493449621bfdfc5648123021cfb3 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Tue, 17 Mar 2020 18:49:16 -0400 Subject: [PATCH 143/163] Make Ballhog thrown more like normal missiles (For some reason, it used banana toss throwing speeds before) --- src/info.c | 2 +- src/k_kart.c | 20 ++++++++++++++------ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/info.c b/src/info.c index 0ecc88fca..09516a75c 100644 --- a/src/info.c +++ b/src/info.c @@ -15982,7 +15982,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_BALLHOG_DEAD, // deathstate S_NULL, // xdeathstate sfx_hogbom, // deathsound - 64*FRACUNIT, // speed + 80*FRACUNIT, // speed 16*FRACUNIT, // radius 32*FRACUNIT, // height 0, // display offset diff --git a/src/k_kart.c b/src/k_kart.c index 05be47651..13786dba5 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3554,10 +3554,18 @@ static mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t map return NULL; // Figure out projectile speed by game speed - if (missile && mapthing != MT_BALLHOG) // Trying to keep compatability... + if (missile) + { + // Use info->speed for missiles PROJSPEED = FixedMul(mobjinfo[mapthing].speed, FRACUNIT + ((gamespeed-1) * (FRACUNIT/4))); + } else + { + // Use pre-determined speed for tossing PROJSPEED = (82 + ((gamespeed-1) * 14)) * FRACUNIT; // Avg Speed is 41 in Normal + } + + // Scale to map size PROJSPEED = FixedMul(PROJSPEED, mapobjectscale); if (altthrow) @@ -3603,11 +3611,11 @@ static mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t map if (dir == -1) { // Shoot backward - mo = K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + ANGLE_180 - 0x06000000, 0, PROJSPEED/16); - K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + ANGLE_180 - 0x03000000, 0, PROJSPEED/16); - K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + ANGLE_180, 0, PROJSPEED/16); - K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + ANGLE_180 + 0x03000000, 0, PROJSPEED/16); - K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + ANGLE_180 + 0x06000000, 0, PROJSPEED/16); + mo = K_SpawnKartMissile(player->mo, mapthing, (player->mo->angle + ANGLE_180) - 0x06000000, 0, PROJSPEED/8); + K_SpawnKartMissile(player->mo, mapthing, (player->mo->angle + ANGLE_180) - 0x03000000, 0, PROJSPEED/8); + K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + ANGLE_180, 0, PROJSPEED/8); + K_SpawnKartMissile(player->mo, mapthing, (player->mo->angle + ANGLE_180) + 0x03000000, 0, PROJSPEED/8); + K_SpawnKartMissile(player->mo, mapthing, (player->mo->angle + ANGLE_180) + 0x06000000, 0, PROJSPEED/8); } else { From 4b2b116481b81de7187693d1303ef12810e11457 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Tue, 17 Mar 2020 18:55:15 -0400 Subject: [PATCH 144/163] Try using GameSpeedScalar for thrown item scaling between game speeds, rather than arbritrary values In Hard mode: Spikeballs will be thrown slightly less with this code change, while Bananas will be thrown slightly farther. That said, the difference is so small that it's very likely not noticable, it just makes the code nicer looking & more consistent :P --- src/k_kart.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 13786dba5..319f7a77f 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3557,12 +3557,12 @@ static mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t map if (missile) { // Use info->speed for missiles - PROJSPEED = FixedMul(mobjinfo[mapthing].speed, FRACUNIT + ((gamespeed-1) * (FRACUNIT/4))); + PROJSPEED = FixedMul(mobjinfo[mapthing].speed, K_GetKartGameSpeedScalar(gamespeed)); } else { // Use pre-determined speed for tossing - PROJSPEED = (82 + ((gamespeed-1) * 14)) * FRACUNIT; // Avg Speed is 41 in Normal + PROJSPEED = FixedMul(82 << FRACBITS, K_GetKartGameSpeedScalar(gamespeed)); } // Scale to map size From 5ef433f9c73525128b9125f4e28516b3289321f4 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Tue, 17 Mar 2020 19:54:32 -0400 Subject: [PATCH 145/163] 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 40e01fb71cc10b14a89b24596d5041df45bc69c7 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Thu, 19 Mar 2020 22:35:07 -0400 Subject: [PATCH 146/163] Increase thrust of destroyed items Roughly matches what it was supposed to look like before gravity was increased --- src/p_map.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/p_map.c b/src/p_map.c index 9e34e04d9..9b574f315 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -810,7 +810,7 @@ static boolean PIT_CheckThing(mobj_t *thing) S_StartSound(tmthing, tmthing->info->deathsound); P_KillMobj(tmthing, thing, thing); - P_SetObjectMomZ(tmthing, 8*FRACUNIT, false); + P_SetObjectMomZ(tmthing, 12*FRACUNIT, false); P_InstaThrust(tmthing, R_PointToAngle2(thing->x, thing->y, tmthing->x, tmthing->y)+ANGLE_90, 16*FRACUNIT); } else if (thing->type == MT_ORBINAUT || thing->type == MT_JAWZ || thing->type == MT_JAWZ_DUD @@ -827,7 +827,7 @@ static boolean PIT_CheckThing(mobj_t *thing) S_StartSound(thing, thing->info->deathsound); P_KillMobj(thing, tmthing, tmthing); - P_SetObjectMomZ(thing, 8*FRACUNIT, false); + P_SetObjectMomZ(thing, 12*FRACUNIT, false); P_InstaThrust(thing, R_PointToAngle2(tmthing->x, tmthing->y, thing->x, thing->y)+ANGLE_90, 16*FRACUNIT); P_SpawnMobj(thing->x/2 + tmthing->x/2, thing->y/2 + tmthing->y/2, thing->z/2 + tmthing->z/2, MT_ITEMCLASH); @@ -841,7 +841,7 @@ static boolean PIT_CheckThing(mobj_t *thing) S_StartSound(tmthing, tmthing->info->deathsound); P_KillMobj(tmthing, thing, thing); - P_SetObjectMomZ(tmthing, 8*FRACUNIT, false); + P_SetObjectMomZ(tmthing, 12*FRACUNIT, false); P_InstaThrust(tmthing, R_PointToAngle2(thing->x, thing->y, tmthing->x, tmthing->y)+ANGLE_90, 16*FRACUNIT); } else if (thing->type == MT_SSMINE_SHIELD || thing->type == MT_SSMINE) @@ -855,7 +855,7 @@ static boolean PIT_CheckThing(mobj_t *thing) S_StartSound(tmthing, tmthing->info->deathsound); P_KillMobj(tmthing, thing, thing); - P_SetObjectMomZ(tmthing, 8*FRACUNIT, false); + P_SetObjectMomZ(tmthing, 12*FRACUNIT, false); P_InstaThrust(tmthing, R_PointToAngle2(thing->x, thing->y, tmthing->x, tmthing->y)+ANGLE_90, 16*FRACUNIT); // Bomb death @@ -975,7 +975,7 @@ static boolean PIT_CheckThing(mobj_t *thing) S_StartSound(tmthing, tmthing->info->deathsound); P_KillMobj(tmthing, thing, thing); - P_SetObjectMomZ(tmthing, 8*FRACUNIT, false); + P_SetObjectMomZ(tmthing, 12*FRACUNIT, false); P_InstaThrust(tmthing, R_PointToAngle2(thing->x, thing->y, tmthing->x, tmthing->y)+ANGLE_90, 16*FRACUNIT); } else if (thing->type == MT_BANANA || thing->type == MT_BANANA_SHIELD @@ -992,7 +992,7 @@ static boolean PIT_CheckThing(mobj_t *thing) S_StartSound(thing, thing->info->deathsound); P_KillMobj(thing, tmthing, tmthing); - P_SetObjectMomZ(thing, 8*FRACUNIT, false); + P_SetObjectMomZ(thing, 12*FRACUNIT, false); P_InstaThrust(thing, R_PointToAngle2(tmthing->x, tmthing->y, thing->x, thing->y)+ANGLE_90, 16*FRACUNIT); P_SpawnMobj(thing->x/2 + tmthing->x/2, thing->y/2 + tmthing->y/2, thing->z/2 + tmthing->z/2, MT_ITEMCLASH); @@ -1006,7 +1006,7 @@ static boolean PIT_CheckThing(mobj_t *thing) S_StartSound(tmthing, tmthing->info->deathsound); P_KillMobj(tmthing, thing, thing); - P_SetObjectMomZ(tmthing, 8*FRACUNIT, false); + P_SetObjectMomZ(tmthing, 12*FRACUNIT, false); P_InstaThrust(tmthing, R_PointToAngle2(thing->x, thing->y, tmthing->x, tmthing->y)+ANGLE_90, 16*FRACUNIT); } @@ -1052,7 +1052,7 @@ static boolean PIT_CheckThing(mobj_t *thing) S_StartSound(thing, thing->info->deathsound); P_KillMobj(thing, tmthing, tmthing); - P_SetObjectMomZ(thing, 8*FRACUNIT, false); + P_SetObjectMomZ(thing, 12*FRACUNIT, false); P_InstaThrust(thing, R_PointToAngle2(tmthing->x, tmthing->y, thing->x, thing->y)+ANGLE_90, 16*FRACUNIT); } @@ -1103,7 +1103,7 @@ static boolean PIT_CheckThing(mobj_t *thing) S_StartSound(thing, thing->info->deathsound); P_KillMobj(thing, tmthing, tmthing); - P_SetObjectMomZ(thing, 8*FRACUNIT, false); + P_SetObjectMomZ(thing, 12*FRACUNIT, false); P_InstaThrust(thing, R_PointToAngle2(tmthing->x, tmthing->y, thing->x, thing->y)+ANGLE_90, 16*FRACUNIT); } else if (thing->type == MT_BANANA_SHIELD || thing->type == MT_BANANA @@ -1131,7 +1131,7 @@ static boolean PIT_CheckThing(mobj_t *thing) S_StartSound(thing, thing->info->deathsound); P_KillMobj(thing, tmthing, tmthing); - P_SetObjectMomZ(thing, 8*FRACUNIT, false); + P_SetObjectMomZ(thing, 12*FRACUNIT, false); P_InstaThrust(thing, R_PointToAngle2(tmthing->x, tmthing->y, thing->x, thing->y)+ANGLE_90, 16*FRACUNIT); } else if (thing->type == MT_SSMINE_SHIELD || thing->type == MT_SSMINE) From 7924606fe465cb75cd6e5c066e26d5a4b45b4edf Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Thu, 19 Mar 2020 22:37:13 -0400 Subject: [PATCH 147/163] Opaque splash --- src/info.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/info.c b/src/info.c index 0ecc88fca..f42532e89 100644 --- a/src/info.c +++ b/src/info.c @@ -1876,9 +1876,9 @@ state_t states[NUMSTATES] = {SPR_SPLH, FF_TRANS50|8, 2, {NULL}, 0, 0, S_NULL}, // S_SPLISH9 // Water Splash - {SPR_SPLA, FF_TRANS50 , 3, {NULL}, 0, 0, S_SPLASH2}, // S_SPLASH1 - {SPR_SPLA, FF_TRANS70|1, 3, {NULL}, 0, 0, S_SPLASH3}, // S_SPLASH2 - {SPR_SPLA, FF_TRANS90|2, 3, {NULL}, 0, 0, S_NULL}, // S_SPLASH3 + {SPR_SPLA, 0, 3, {NULL}, 0, 0, S_SPLASH2}, // S_SPLASH1 + {SPR_SPLA, 1, 3, {NULL}, 0, 0, S_SPLASH3}, // S_SPLASH2 + {SPR_SPLA, 2, 3, {NULL}, 0, 0, S_NULL}, // S_SPLASH3 // Smoke {SPR_SMOK, FF_TRANS50 , 4, {NULL}, 0, 0, S_SMOKE2}, // S_SMOKE1 From 46049dba408c003cdad5a6682dddafff3acb55db Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Thu, 19 Mar 2020 22:52:11 -0400 Subject: [PATCH 148/163] Fix drift spark weirdness in mobjscale maps --- 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 d32927c51..1346a09fd 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3573,7 +3573,7 @@ void K_SpawnDraftDust(mobj_t *mo) void K_DriftDustHandling(mobj_t *spawner) { angle_t anglediff; - const INT16 spawnrange = spawner->radius>>FRACBITS; + const INT16 spawnrange = spawner->radius >> FRACBITS; if (!P_IsObjectOnGround(spawner) || leveltime % 2 != 0) return; @@ -3612,8 +3612,8 @@ void K_DriftDustHandling(mobj_t *spawner) if (anglediff > ANG10*4) // Trying to turn further than 40 degrees { - fixed_t spawnx = P_RandomRange(-spawnrange, spawnrange) * spawner->scale; - fixed_t spawny = P_RandomRange(-spawnrange, spawnrange) * spawner->scale; + fixed_t spawnx = P_RandomRange(-spawnrange, spawnrange) << FRACBITS; + fixed_t spawny = P_RandomRange(-spawnrange, spawnrange) << FRACBITS; INT32 speedrange = 2; mobj_t *dust = P_SpawnMobj(spawner->x + spawnx, spawner->y + spawny, spawner->z, MT_DRIFTDUST); dust->momx = FixedMul(spawner->momx + (P_RandomRange(-speedrange, speedrange) * spawner->scale), 3*(spawner->scale)/4); From b959c01349ba98ac62ff72964346ee2ae5f33206 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Fri, 20 Mar 2020 11:04:26 +0100 Subject: [PATCH 149/163] 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 150/163] 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 151/163] 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 1dd95f5e3b0accdb416f8dff5babf04b9ad4758d Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 21 Mar 2020 12:53:33 -0700 Subject: [PATCH 152/163] gr_shearing doesn't exist in master! --- src/p_user.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/p_user.c b/src/p_user.c index d07b71390..031a607c1 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -7503,7 +7503,11 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall } pitch = thiscam->pitch + (angle_t)FixedMul(pitch - thiscam->pitch, camspeed/4); - if (rendermode == render_opengl && !cv_grshearing.value) + if (rendermode == render_opengl +#ifdef GL_SHADERS/* just so we can't possibly forget about it */ + && !cv_grshearing.value +#endif + ) distxy = FixedMul(dist, FINECOSINE((pitch>>ANGLETOFINESHIFT) & FINEMASK)); else distxy = dist; From 738d1f194008957ebf1b375ac2f67d9a0b2bec78 Mon Sep 17 00:00:00 2001 From: Sryder Date: Sat, 21 Mar 2020 21:32:40 +0000 Subject: [PATCH 153/163] 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 668f80a8ea1a7aa7d3aabb3367fad91e51ef22ed Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sat, 21 Mar 2020 18:47:44 -0400 Subject: [PATCH 154/163] Properly scale... --- src/k_kart.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 1346a09fd..6cdbfa547 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3616,8 +3616,8 @@ void K_DriftDustHandling(mobj_t *spawner) fixed_t spawny = P_RandomRange(-spawnrange, spawnrange) << FRACBITS; INT32 speedrange = 2; mobj_t *dust = P_SpawnMobj(spawner->x + spawnx, spawner->y + spawny, spawner->z, MT_DRIFTDUST); - dust->momx = FixedMul(spawner->momx + (P_RandomRange(-speedrange, speedrange) * spawner->scale), 3*(spawner->scale)/4); - dust->momy = FixedMul(spawner->momy + (P_RandomRange(-speedrange, speedrange) * spawner->scale), 3*(spawner->scale)/4); + dust->momx = FixedMul(spawner->momx + (P_RandomRange(-speedrange, speedrange) * spawner->scale), 3*FRACUNIT/4); + dust->momy = FixedMul(spawner->momy + (P_RandomRange(-speedrange, speedrange) * spawner->scale), 3*FRACUNIT/4); dust->momz = P_MobjFlip(spawner) * (P_RandomRange(1, 4) * (spawner->scale)); P_SetScale(dust, spawner->scale/2); dust->destscale = spawner->scale * 3; From e874af3e43443568c82d9bc02eb4bee119b9a63e Mon Sep 17 00:00:00 2001 From: Sryder Date: Sat, 21 Mar 2020 23:24:04 +0000 Subject: [PATCH 155/163] 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 156/163] 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 157/163] 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 158/163] 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 159/163] 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 160/163] 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 161/163] 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 162/163] 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 163/163] 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;