Merge remote-tracking branch 'origin/master' into kill-whitespace

This commit is contained in:
James R 2020-03-22 14:36:21 -07:00
commit dda610fbd4
32 changed files with 5249 additions and 581 deletions

View file

@ -495,6 +495,9 @@ OBJS:=$(i_main_o) \
$(OBJDIR)/st_stuff.o \
$(OBJDIR)/k_kart.o \
$(OBJDIR)/k_pwrlv.o \
$(OBJDIR)/k_waypoint.o\
$(OBJDIR)/k_pathfind.o\
$(OBJDIR)/k_bheap.o \
$(OBJDIR)/m_aatree.o \
$(OBJDIR)/m_anigif.o \
$(OBJDIR)/m_argv.o \

View file

@ -387,6 +387,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};
@ -1887,8 +1889,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);
}
@ -1899,8 +1899,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);
}
@ -1911,8 +1909,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);
}
@ -1923,8 +1919,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);
}
@ -1932,11 +1926,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;
}
static void Got_PowerLevel(UINT8 **cp,INT32 playernum)
@ -2858,13 +2850,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.

View file

@ -127,6 +127,7 @@ extern consvar_t cv_votetime;
extern consvar_t cv_kartdebugitem, cv_kartdebugamount, cv_kartdebugshrink, cv_kartdebugdistribution, cv_kartdebughuddrop;
extern consvar_t cv_kartdebugcheckpoint, cv_kartdebugnodes, cv_kartdebugcolorize;
extern consvar_t cv_kartdebugwaypoints;
extern consvar_t cv_itemfinder;

View file

@ -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
{
@ -118,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;
@ -240,10 +243,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
@ -331,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;
@ -436,6 +436,8 @@ 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;
waypoint_t *nextwaypoint;
// Bit flags.
// See pflags_t, above.

View file

@ -6660,6 +6660,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",
@ -8180,7 +8185,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.
};
@ -8417,10 +8422,6 @@ static const char *const KARTSTUFF_LIST[] = {
"POSITION",
"OLDPOSITION",
"POSITIONDELAY",
"PREVCHECK",
"NEXTCHECK",
"WAYPOINT",
"STARPOSTWP",
"STARPOSTFLIP",
"RESPAWN",
"DROPDASH",
@ -8502,7 +8503,8 @@ static const char *const KARTSTUFF_LIST[] = {
"TIREGREASE",
"SPRINGSTARS",
"SPRINGCOLOR",
"KILLFIELD"
"KILLFIELD",
"WRONGWAY"
};
#endif

View file

@ -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

View file

@ -2576,7 +2576,6 @@ void G_PlayerReborn(INT32 player)
SINT8 pity;
// SRB2kart
INT32 starpostwp;
INT32 itemtype;
INT32 itemamount;
INT32 itemroulette;
@ -2598,7 +2597,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))
@ -2640,12 +2639,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];
@ -2712,7 +2708,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;
@ -3248,7 +3243,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
@ -4939,7 +4935,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)
{

View file

@ -2876,7 +2876,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
@ -16076,7 +16081,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
@ -16278,7 +16283,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
{ // MT_WAYPOINT
2001, // doomednum
S_NULL, // spawnstate
S_INVISIBLE, // spawnstate
1000, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
@ -16299,7 +16304,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
},

View file

@ -3552,6 +3552,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,

595
src/k_bheap.c Normal file
View file

@ -0,0 +1,595 @@
// 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"
/*--------------------------------------------------
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;
I_Assert(heap != NULL);
I_Assert(item != NULL);
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;
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));
(void)heap;
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)
{
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));
(void)heap;
{
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;
if (item1->indexchanged != NULL)
{
item1->indexchanged(item1->data, item1->heapindex);
}
if (item2->indexchanged != NULL)
{
item2->indexchanged(item2->data, item2->heapindex);
}
}
}
/*--------------------------------------------------
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;
I_Assert(item != NULL);
I_Assert(item->heapindex < (SIZE_MAX / 2));
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;
I_Assert(item != NULL);
I_Assert(item->heapindex < (SIZE_MAX / 2));
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;
I_Assert(item != NULL);
I_Assert(item->heapindex < (SIZE_MAX / 2));
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)
{
I_Assert(heap != NULL);
I_Assert(K_BHeapValid(heap));
I_Assert(item != NULL);
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)
{
I_Assert(heap != NULL);
I_Assert(K_BHeapValid(heap));
I_Assert(item != NULL);
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, updateindexfunc changeindexcallback)
See header file for description.
--------------------------------------------------*/
boolean K_BHeapPush(bheap_t *const heap, void *const item, UINT32 value, updateindexfunc changeindexcallback)
{
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
{
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)
{
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;
}
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++;
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;
if (returnitemstorage->indexchanged != NULL)
{
returnitemstorage->indexchanged(returnitemstorage->data, returnitemstorage->heapindex);
}
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_UpdateBHeapItemValue(bheapitem_t *const item, const UINT32 newvalue)
See header file for description.
--------------------------------------------------*/
boolean K_UpdateBHeapItemValue(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;
}
/*--------------------------------------------------
size_t K_BHeapContains(bheap_t *const heap, void *const data, size_t index)
See header file for description.
--------------------------------------------------*/
size_t K_BHeapContains(bheap_t *const heap, void *const data, size_t index)
{
size_t 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;
}
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;
}

153
src/k_bheap.h Normal file
View file

@ -0,0 +1,153 @@
// 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__
#include "doomdef.h"
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
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;
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, 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
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, updateindexfunc changeindexcallback);
/*--------------------------------------------------
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_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
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_UpdateBHeapItemValue(bheapitem_t *const item, const UINT32 newvalue);
/*--------------------------------------------------
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.
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
--------------------------------------------------*/
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

File diff suppressed because it is too large Load diff

View file

@ -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);

506
src/k_pathfind.c Normal file
View file

@ -0,0 +1,506 @@
// 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"
#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;
I_Assert(node != NULL);
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_NodeUpdateHeapIndex.\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;
size_t i = 0U;
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--)
{
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;
size_t i = 0U;
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--)
{
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;
I_Assert(path != NULL);
I_Assert(destinationnode != NULL);
{
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 * sizeof(pathfindnode_t), 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;
}

82
src/k_pathfind.h Normal file
View file

@ -0,0 +1,82 @@
// 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__
#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

1763
src/k_waypoint.c Normal file

File diff suppressed because it is too large Load diff

338
src/k_waypoint.h Normal file
View file

@ -0,0 +1,338 @@
// 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__
#include "doomtype.h"
#include "p_mobj.h"
#include "k_pathfind.h"
typedef struct waypoint_s
{
mobj_t *mobj;
struct waypoint_s **nextwaypoints;
struct waypoint_s **prevwaypoints;
UINT32 *nextwaypointdistances;
UINT32 *prevwaypointdistances;
size_t numnextwaypoints;
size_t numprevwaypoints;
} waypoint_t;
// 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)
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);
/*--------------------------------------------------
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)
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);
/*--------------------------------------------------
UINT32 K_GetCircuitLength(void)
Returns the circuit length, 0 on sprint maps.
Input Arguments:-
Return:-
The circuit length.
--------------------------------------------------*/
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);
/*--------------------------------------------------
waypoint_t *K_GetBestWaypointForMobj(mobj_t *const mobj)
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, or NULL if there were no matches
--------------------------------------------------*/
waypoint_t *K_GetBestWaypointForMobj(mobj_t *const mobj);
/*--------------------------------------------------
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)
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
/*--------------------------------------------------
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()
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.
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(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
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

View file

@ -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);
}

View file

@ -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"
@ -8444,14 +8445,31 @@ void A_JawzExplode(mobj_t *actor)
return;
}
static void SpawnSPBTrailRings(mobj_t *actor)
{
I_Assert(actor != NULL);
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;
}
}
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<<FRACBITS; // best player distance when seeking
angle_t pangle; // angle between us and the player
#ifdef HAVE_BLUA
if (LUA_CallAction("A_SPBChase", actor))
return;
@ -8463,9 +8481,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;
}
@ -8491,18 +8509,22 @@ 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;
// 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);
@ -8600,13 +8622,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
@ -8639,9 +8655,7 @@ void A_SPBChase(mobj_t *actor)
else if (actor->extravalue1 == 2) // MODE: WAIT...
{
actor->momx = actor->momy = actor->momz = 0; // Stoooop
// don't hurt players that have nothing to do with this:
actor->flags |= MF_NOCLIPTHING;
actor->cusval = -1; // Reset waypoint
if (actor->lastlook != -1
&& playeringame[actor->lastlook]
@ -8667,6 +8681,11 @@ void A_SPBChase(mobj_t *actor)
}
else // MODE: SEEKING
{
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
if (!player || !player->mo || player->mo->health <= 0 || player->kartstuff[k_respawn])
@ -8679,17 +8698,72 @@ 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;
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);
// Move along the waypoints until you get close enough
if (actor->cusval > -1 && actor->extravalue2 > 0)
{
// Previously set nextwaypoint
lastwaypoint = K_GetWaypointFromIndex((size_t)actor->cusval);
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.
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_GetBestWaypointForMobj(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;
// If the player is on a shortcut, use shortcuts. No escape.
if (K_GetWaypointIsShortcut(player->nextwaypoint))
{
useshortcuts = true;
}
nextwaypoint = K_GetNextWaypointToDestination(
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);
actor->extravalue2 = (INT32)K_GetWaypointHeapIndex(bestwaypoint); // save our last best, used above.
}
else
{
// continue straight ahead... Shouldn't happen.
hang = actor->angle;
vang = 0U;
}
{
// Smoothly rotate horz angle
@ -8698,13 +8772,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<<FRACBITS) - AngleFixed(input)) / 90) - FRACUNIT));
input = FixedAngle(AngleFixed(input)/4);
if (invert)
input = InvAngle(input);
actor->angle += input;
// Smoothly rotate vert angle
@ -8713,13 +8787,13 @@ 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<<FRACBITS) - AngleFixed(input)) / 90) - FRACUNIT));
input = FixedAngle(AngleFixed(input)/4);
if (invert)
input = InvAngle(input);
actor->movedir += input;
}
@ -8727,15 +8801,49 @@ 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));
if (dist <= (3072*actor->tracer->scale)) // Close enough to target?
// 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++)
{
S_StartSound(actor, actor->info->attacksound); // Siren sound; might not need this anymore, but I'm keeping it for now just for debugging.
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);
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;
}
}
// Finally, no matter what, the spb should not be able to be under the ground, or above the ceiling;
if (actor->z < actor->floorz)
actor->z = actor->floorz;
else if (actor->z > actor->ceilingz - actor->height)
actor->z = actor->ceilingz - actor->height;
return;
}
@ -10577,8 +10685,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);
}

View file

@ -1462,15 +1462,9 @@ 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))
if (circuitmap && special->health - player->starpostnum > 1)
{
// blatant reuse of a variable that's normally unused in circuit
if (!player->tossdelay)
@ -1491,13 +1485,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;

View file

@ -182,7 +182,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);

View file

@ -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;
}
//#define TELEPORTJANK
boolean P_DoSpring(mobj_t *spring, mobj_t *object)
@ -146,7 +256,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 TELEPORTJANK
#if 0
if (horizspeed && vertispeed) // Mimic SA
{
object->momx = object->momy = 0;
@ -897,12 +1007,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);
}
@ -1161,15 +1279,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);
}
@ -1309,7 +1435,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;
@ -1554,7 +1680,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
@ -1972,7 +2098,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));
@ -2039,6 +2165,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;
}
@ -2313,6 +2445,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;
@ -2752,6 +2887,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
@ -2759,8 +2896,13 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
#endif
floatok = false;
if (radius < MAXRADIUS/2)
radius = MAXRADIUS/2;
// 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) {
@ -2784,6 +2926,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.
@ -2956,6 +3101,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;
}

View file

@ -47,6 +47,7 @@ consvar_t cv_splats = {"splats", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0
actioncache_t actioncachehead;
static mobj_t *overlaycap = 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)
@ -1655,18 +1656,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
@ -6105,6 +6099,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_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
@ -6538,16 +6597,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!
@ -8315,7 +8383,10 @@ void P_MobjThinker(mobj_t *mobj)
if (p)
{
if (p->kartstuff[k_driftboost] > mobj->movecount)
{
; // reset animation
}
mobj->movecount = p->kartstuff[k_driftboost];
}
}
@ -9756,6 +9827,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);
@ -10436,6 +10513,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;
}
@ -10591,6 +10672,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
@ -11176,6 +11260,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);
@ -11392,11 +11479,10 @@ void P_MovePlayerToStarpost(INT32 playernum)
sector->ceilingheight;
if (mobj->player->kartstuff[k_starpostflip])
z = (p->starpostz<<FRACBITS) - FixedMul(128<<FRACBITS, mapobjectscale) - mobj->height;
z = (p->starpostz<<FRACBITS) - (128 * mapobjectscale) - mobj->height;
else
z = (p->starpostz<<FRACBITS) + FixedMul(128<<FRACBITS, mapobjectscale);
z = (p->starpostz<<FRACBITS) + (128 * mapobjectscale);
//z = (p->starpostz + 128) << FRACBITS; // reverse gravity exists, pls
mobj->player->kartstuff[k_starpostflip] = 0;
if (z < floor)
@ -11413,8 +11499,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))
@ -11723,6 +11807,26 @@ void P_SpawnMapThing(mapthing_t *mthing)
else
mthing->z = (INT16)(z>>FRACBITS);
}
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));
// 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;
@ -11969,6 +12073,69 @@ ML_NOCLIMB : Direction not controllable
if (mthing->angle >= 360)
mobj->tics += 7*(mthing->angle / 360) + 1; // starting delay
break;
case MT_WAYPOINT:
{
// Just like MT_SPINMACEPOINT, this now works here too!
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)
{
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
// 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 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);
P_SetTarget(&waypointcap, mobj);
break;
}
// SRB2Kart
case MT_BALLOON:
mobj->color = (1 + (mthing->angle % (MAXSKINCOLORS-1)));
@ -12104,13 +12271,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;
mobj->movecount = mthing->extrainfo;
P_SetTarget(&mobj->tracer, waypointcap);
P_SetTarget(&waypointcap, mobj);
}
else if (i == MT_SPIKE)
{
// Pop up spikes!

View file

@ -319,6 +319,9 @@ typedef struct mobj_s
struct mobj_s *hnext;
struct mobj_s *hprev;
// One last pointer for kart item lists
struct mobj_s *itnext;
INT32 health; // for player this is rings + 1
// Movement direction, movement generation (zig-zagging).
@ -437,12 +440,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);

View file

@ -279,6 +279,9 @@ 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);
WRITEUINT32(save_p, K_GetWaypointHeapIndex(players[i].nextwaypoint));
}
}
@ -447,6 +450,9 @@ 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);
players[i].nextwaypoint = (waypoint_t *)(size_t)READUINT32(save_p);
}
}
@ -951,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;
@ -1145,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;
@ -1153,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;
@ -1268,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);
@ -2145,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)
{
@ -2186,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
}
@ -3038,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;
@ -3066,6 +3092,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);
}
}
}
}
}

View file

@ -85,6 +85,7 @@
// SRB2Kart
#include "k_kart.h"
#include "k_pwrlv.h"
#include "k_waypoint.h"
//
// Map MD5, calculated on level load.
@ -3115,6 +3116,17 @@ boolean P_SetupLevel(boolean skipprecip)
if (loadprecip) // ugly hack for P_NetUnArchiveMisc (and P_LoadNetGame)
P_SpawnPrecipitation();
// 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_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
if (rendermode != render_soft && rendermode != render_none)
{

View file

@ -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)
{
@ -382,14 +484,35 @@ 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;
}
#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
}
}

View file

@ -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
@ -2089,6 +2057,188 @@ void P_SwitchWeather(UINT8 newWeather)
P_SpawnPrecipitation();
}
// Passed over the finish line forwards
static void K_HandleLapIncrement(player_t *player)
{
if (player)
{
if ((player->starpostnum == numstarposts) || (player->laps == 0))
{
size_t 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 (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);
}
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)
{
S_StartSound(player->mo, sfx_s26d);
}
}
}
// player went backwards over the line
static void K_HandleLapDecrement(player_t *player)
{
if (player)
{
if ((player->starpostnum == 0) && (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) && !(player->pflags & PF_HITFINISHLINE))
{
if (((line->flags & (ML_NOCLIMB)) && (side == 0))
|| (!(line->flags & (ML_NOCLIMB)) && (side == 1))) // crossed from behind to infront
{
K_HandleLapIncrement(player);
}
else
{
K_HandleLapDecrement(player);
}
player->pflags |= PF_HITFINISHLINE;
}
}
break;
default:
{
// Do nothing
}
break;
}
}
}
/** Gets an object.
*
* \param type Object type to look for.
@ -3621,10 +3771,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)
@ -3862,7 +4012,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;
@ -4213,113 +4363,8 @@ DoneSection2:
}
break;
case 10: // Finish Line
// SRB2kart - 150117
if (G_RaceGametype() && (player->starpostnum >= (numstarposts - (numstarposts/2)) || 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)")
{
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);
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
@ -4872,7 +4917,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;
}
@ -5731,9 +5776,9 @@ 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)
// Remove before release
CONS_Alert(CONS_WARNING, "Finish line sector type is deprecated.\n");
break;
}
}
@ -6664,6 +6709,15 @@ 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;
default:
break;
}
@ -6884,8 +6938,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.
@ -6960,8 +7014,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.
@ -7856,7 +7910,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])
{

View file

@ -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);

View file

@ -23,6 +23,7 @@
#include "lua_script.h"
#include "lua_hook.h"
#include "k_kart.h"
#include "k_waypoint.h"
// Object place
#include "m_cheat.h"
@ -172,6 +173,7 @@ void P_InitThinkers(void)
{
thinkercap.prev = thinkercap.next = &thinkercap;
waypointcap = NULL;
kitemcap = NULL;
}
//
@ -628,6 +630,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();
@ -736,6 +741,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.

View file

@ -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);
@ -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 //
@ -8176,12 +8091,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
@ -8414,26 +8323,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);
}

View file

@ -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));
}
}
*/