mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
Merge remote-tracking branch 'origin/master' into kill-whitespace
This commit is contained in:
commit
dda610fbd4
32 changed files with 5249 additions and 581 deletions
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
15
src/g_game.c
15
src/g_game.c
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
13
src/info.c
13
src/info.c
|
|
@ -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
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -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
595
src/k_bheap.c
Normal 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
153
src/k_bheap.h
Normal 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
|
||||
958
src/k_kart.c
958
src/k_kart.c
File diff suppressed because it is too large
Load diff
|
|
@ -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
506
src/k_pathfind.c
Normal 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
82
src/k_pathfind.h
Normal 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
1763
src/k_waypoint.c
Normal file
File diff suppressed because it is too large
Load diff
338
src/k_waypoint.h
Normal file
338
src/k_waypoint.h
Normal 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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
168
src/p_enemy.c
168
src/p_enemy.c
|
|
@ -24,6 +24,7 @@
|
|||
#include "i_video.h"
|
||||
#include "lua_hook.h"
|
||||
#include "k_kart.h" // SRB2kart
|
||||
#include "k_waypoint.h"
|
||||
|
||||
#ifdef HW3SOUND
|
||||
#include "hardware/hw3sound.h"
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
189
src/p_map.c
189
src/p_map.c
|
|
@ -68,6 +68,20 @@ line_t *ceilingline;
|
|||
// that is, for any line which is 'solid'
|
||||
line_t *blockingline;
|
||||
|
||||
// Mostly re-ported from DOOM Legacy
|
||||
// Keep track of special lines as they are hit, process them when the move is valid
|
||||
static size_t *spechit = NULL;
|
||||
static size_t spechit_max = 0U;
|
||||
static size_t numspechit = 0U;
|
||||
|
||||
// Need a intermediate buffer for P_TryMove because it performs multiple moves
|
||||
// the lines put into spechit will be moved into here after each checkposition,
|
||||
// then and duplicates will be removed before processing
|
||||
static size_t *spechitint = NULL;
|
||||
static size_t spechitint_max = 0U;
|
||||
static size_t numspechitint = 0U;
|
||||
|
||||
|
||||
msecnode_t *sector_list = NULL;
|
||||
mprecipsecnode_t *precipsector_list = NULL;
|
||||
camera_t *mapcampointer;
|
||||
|
|
@ -81,6 +95,8 @@ camera_t *mapcampointer;
|
|||
//
|
||||
boolean P_TeleportMove(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z)
|
||||
{
|
||||
numspechit = 0U;
|
||||
|
||||
// the move is ok,
|
||||
// so link the thing into its new position
|
||||
P_UnsetThingPosition(thing);
|
||||
|
|
@ -113,6 +129,100 @@ boolean P_TeleportMove(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z)
|
|||
// MOVEMENT ITERATOR FUNCTIONS
|
||||
// =========================================================================
|
||||
|
||||
// For our intermediate buffer, remove any duplicate entries by adding each one to
|
||||
// a temprary buffer if it's not already in there, copy the temporary buffer back over the intermediate afterwards
|
||||
static void spechitint_removedups(void)
|
||||
{
|
||||
// Only needs to be run if there's more than 1 line crossed
|
||||
if (numspechitint > 1U)
|
||||
{
|
||||
boolean valueintemp = false;
|
||||
size_t i = 0U, j = 0U;
|
||||
size_t numspechittemp = 0U;
|
||||
size_t *spechittemp = Z_Calloc(numspechitint * sizeof(size_t), PU_STATIC, NULL);
|
||||
|
||||
// Fill the hashtable
|
||||
for (i = 0U; i < numspechitint; i++)
|
||||
{
|
||||
valueintemp = false;
|
||||
for (j = 0; j < numspechittemp; j++)
|
||||
{
|
||||
if (spechitint[i] == spechittemp[j])
|
||||
{
|
||||
valueintemp = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!valueintemp)
|
||||
{
|
||||
spechittemp[numspechittemp] = spechitint[i];
|
||||
numspechittemp++;
|
||||
}
|
||||
}
|
||||
|
||||
// The hash table now IS the result we want to send back
|
||||
// easiest way to handle this is a memcpy
|
||||
if (numspechittemp != numspechitint)
|
||||
{
|
||||
memcpy(spechitint, spechittemp, numspechittemp * sizeof(size_t));
|
||||
numspechitint = numspechittemp;
|
||||
}
|
||||
|
||||
Z_Free(spechittemp);
|
||||
}
|
||||
}
|
||||
|
||||
// copy the contents of spechit into the end of spechitint
|
||||
static void spechitint_copyinto(void)
|
||||
{
|
||||
if (numspechit > 0U)
|
||||
{
|
||||
if (numspechitint + numspechit >= spechitint_max)
|
||||
{
|
||||
spechitint_max = spechitint_max + numspechit;
|
||||
spechitint = Z_Realloc(spechitint, spechitint_max * sizeof(size_t), PU_STATIC, NULL);
|
||||
}
|
||||
|
||||
memcpy(&spechitint[numspechitint], spechit, numspechit * sizeof(size_t));
|
||||
numspechitint += numspechit;
|
||||
}
|
||||
}
|
||||
|
||||
static void add_spechit(line_t *ld)
|
||||
{
|
||||
if (numspechit >= spechit_max)
|
||||
{
|
||||
spechit_max = spechit_max ? spechit_max * 2U : 16U;
|
||||
spechit = Z_Realloc(spechit, spechit_max * sizeof(size_t), PU_STATIC, NULL);
|
||||
}
|
||||
|
||||
spechit[numspechit] = ld - lines;
|
||||
numspechit++;
|
||||
}
|
||||
|
||||
static boolean P_SpecialIsLinedefCrossType(UINT16 ldspecial)
|
||||
{
|
||||
boolean linedefcrossspecial = false;
|
||||
|
||||
switch (ldspecial)
|
||||
{
|
||||
case 2001: // Finish line
|
||||
{
|
||||
linedefcrossspecial = true;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
linedefcrossspecial = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return linedefcrossspecial;
|
||||
}
|
||||
|
||||
//#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;
|
||||
}
|
||||
|
||||
|
|
|
|||
216
src/p_mobj.c
216
src/p_mobj.c
|
|
@ -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!
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
163
src/p_sight.c
163
src/p_sight.c
|
|
@ -2,7 +2,7 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 1993-1996 by id Software, Inc.
|
||||
// Copyright (C) 1998-2000 by DooM Legacy Team.
|
||||
// Copyright (C) 1999-2018 by Sonic Team Junior.
|
||||
// Copyright (C) 1999-2020 by Sonic Team Junior.
|
||||
//
|
||||
// This program is free software distributed under the
|
||||
// terms of the GNU General Public License, version 2.
|
||||
|
|
@ -14,6 +14,7 @@
|
|||
#include "doomdef.h"
|
||||
#include "doomstat.h"
|
||||
#include "p_local.h"
|
||||
#include "p_slopes.h"
|
||||
#include "r_main.h"
|
||||
#include "r_state.h"
|
||||
|
||||
|
|
@ -103,12 +104,20 @@ static fixed_t P_InterceptVector2(divline_t *v2, divline_t *v1)
|
|||
static boolean P_CrossSubsecPolyObj(polyobj_t *po, register los_t *los)
|
||||
{
|
||||
size_t i;
|
||||
sector_t *polysec;
|
||||
|
||||
if (!(po->flags & POF_RENDERALL))
|
||||
return true; // the polyobject isn't visible, so we can ignore it
|
||||
|
||||
polysec = po->lines[0]->backsector;
|
||||
|
||||
for (i = 0; i < po->numLines; ++i)
|
||||
{
|
||||
line_t *line = po->lines[i];
|
||||
divline_t divl;
|
||||
const vertex_t *v1,*v2;
|
||||
fixed_t frac;
|
||||
fixed_t topslope, bottomslope;
|
||||
|
||||
// already checked other side?
|
||||
if (line->validcount == validcount)
|
||||
|
|
@ -140,7 +149,22 @@ static boolean P_CrossSubsecPolyObj(polyobj_t *po, register los_t *los)
|
|||
continue;
|
||||
|
||||
// stop because it is not two sided
|
||||
return false;
|
||||
//if (!(po->flags & POF_TESTHEIGHT))
|
||||
//return false;
|
||||
|
||||
frac = P_InterceptVector2(&los->strace, &divl);
|
||||
|
||||
// get slopes of top and bottom of this polyobject line
|
||||
topslope = FixedDiv(polysec->ceilingheight - los->sightzstart , frac);
|
||||
bottomslope = FixedDiv(polysec->floorheight - los->sightzstart , frac);
|
||||
|
||||
if (topslope >= los->topslope && bottomslope <= los->bottomslope)
|
||||
return false; // view completely blocked
|
||||
|
||||
// TODO: figure out if it's worth considering partially blocked cases or not?
|
||||
// maybe to adjust los's top/bottom slopes if needed
|
||||
//if (los->topslope <= los->bottomslope)
|
||||
//return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -193,6 +217,15 @@ static boolean P_CrossSubsector(size_t num, register los_t *los)
|
|||
const sector_t *front, *back;
|
||||
const vertex_t *v1,*v2;
|
||||
fixed_t frac;
|
||||
fixed_t frontf, backf, frontc, backc;
|
||||
#ifdef ESLOPE
|
||||
fixed_t fracx, fracy;
|
||||
#endif
|
||||
|
||||
/* SRB2Kart doesn't have this?
|
||||
if (seg->glseg)
|
||||
continue;
|
||||
*/
|
||||
|
||||
// already checked other side?
|
||||
if (line->validcount == validcount)
|
||||
|
|
@ -227,36 +260,51 @@ static boolean P_CrossSubsector(size_t num, register los_t *los)
|
|||
if (!(line->flags & ML_TWOSIDED))
|
||||
return false;
|
||||
|
||||
// calculate fractional intercept (how far along we are divided by how far we are from t2)
|
||||
frac = P_InterceptVector2(&los->strace, &divl);
|
||||
|
||||
front = seg->frontsector;
|
||||
back = seg->backsector;
|
||||
#ifdef ESLOPE
|
||||
// calculate position at intercept
|
||||
fracx = los->strace.x + FixedMul(los->strace.dx, frac);
|
||||
fracy = los->strace.y + FixedMul(los->strace.dy, frac);
|
||||
// calculate sector heights
|
||||
frontf = (front->f_slope) ? P_GetZAt(front->f_slope, fracx, fracy) : front->floorheight;
|
||||
frontc = (front->c_slope) ? P_GetZAt(front->c_slope, fracx, fracy) : front->ceilingheight;
|
||||
backf = (back->f_slope) ? P_GetZAt(back->f_slope, fracx, fracy) : back->floorheight;
|
||||
backc = (back->c_slope) ? P_GetZAt(back->c_slope, fracx, fracy) : back->ceilingheight;
|
||||
#else
|
||||
frontf = front->floorheight;
|
||||
frontc = front->ceilingheight;
|
||||
backf = back->floorheight;
|
||||
backc = back->ceilingheight;
|
||||
#endif
|
||||
// crosses a two sided line
|
||||
// no wall to block sight with?
|
||||
if ((front = seg->frontsector)->floorheight ==
|
||||
(back = seg->backsector)->floorheight &&
|
||||
front->ceilingheight == back->ceilingheight)
|
||||
if (frontf == backf && frontc == backc
|
||||
&& !front->ffloors & !back->ffloors) // (and no FOFs)
|
||||
continue;
|
||||
|
||||
// possible occluder
|
||||
// because of ceiling height differences
|
||||
popentop = front->ceilingheight < back->ceilingheight ?
|
||||
front->ceilingheight : back->ceilingheight ;
|
||||
popentop = min(frontc, backc);
|
||||
|
||||
// because of floor height differences
|
||||
popenbottom = front->floorheight > back->floorheight ?
|
||||
front->floorheight : back->floorheight ;
|
||||
popenbottom = max(frontf, backf);
|
||||
|
||||
// quick test for totally closed doors
|
||||
if (popenbottom >= popentop)
|
||||
return false;
|
||||
|
||||
frac = P_InterceptVector2(&los->strace, &divl);
|
||||
|
||||
if (front->floorheight != back->floorheight)
|
||||
if (frontf != backf)
|
||||
{
|
||||
fixed_t slope = FixedDiv(popenbottom - los->sightzstart , frac);
|
||||
if (slope > los->bottomslope)
|
||||
los->bottomslope = slope;
|
||||
}
|
||||
|
||||
if (front->ceilingheight != back->ceilingheight)
|
||||
if (frontc != backc)
|
||||
{
|
||||
fixed_t slope = FixedDiv(popentop - los->sightzstart , frac);
|
||||
if (slope < los->topslope)
|
||||
|
|
@ -265,6 +313,58 @@ static boolean P_CrossSubsector(size_t num, register los_t *los)
|
|||
|
||||
if (los->topslope <= los->bottomslope)
|
||||
return false;
|
||||
|
||||
// Monster Iestyn: check FOFs!
|
||||
if (front->ffloors || back->ffloors)
|
||||
{
|
||||
ffloor_t *rover;
|
||||
fixed_t topslope, bottomslope;
|
||||
fixed_t topz, bottomz;
|
||||
// check front sector's FOFs first
|
||||
for (rover = front->ffloors; rover; rover = rover->next)
|
||||
{
|
||||
if (!(rover->flags & FF_EXISTS)
|
||||
|| !(rover->flags & FF_RENDERSIDES) || rover->flags & FF_TRANSLUCENT)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef ESLOPE
|
||||
topz = (*rover->t_slope) ? P_GetZAt(*rover->t_slope, fracx, fracy) : *rover->topheight;
|
||||
bottomz = (*rover->b_slope) ? P_GetZAt(*rover->b_slope, fracx, fracy) : *rover->bottomheight;
|
||||
#else
|
||||
topz = *rover->topheight;
|
||||
bottomz = *rover->bottomheight;
|
||||
#endif
|
||||
topslope = FixedDiv(topz - los->sightzstart , frac);
|
||||
bottomslope = FixedDiv(bottomz - los->sightzstart , frac);
|
||||
if (topslope >= los->topslope && bottomslope <= los->bottomslope)
|
||||
return false; // view completely blocked
|
||||
}
|
||||
// check back sector's FOFs as well
|
||||
for (rover = back->ffloors; rover; rover = rover->next)
|
||||
{
|
||||
if (!(rover->flags & FF_EXISTS)
|
||||
|| !(rover->flags & FF_RENDERSIDES) || rover->flags & FF_TRANSLUCENT)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef ESLOPE
|
||||
topz = (*rover->t_slope) ? P_GetZAt(*rover->t_slope, fracx, fracy) : *rover->topheight;
|
||||
bottomz = (*rover->b_slope) ? P_GetZAt(*rover->b_slope, fracx, fracy) : *rover->bottomheight;
|
||||
#else
|
||||
topz = *rover->topheight;
|
||||
bottomz = *rover->bottomheight;
|
||||
#endif
|
||||
topslope = FixedDiv(topz - los->sightzstart , frac);
|
||||
bottomslope = FixedDiv(bottomz - los->sightzstart , frac);
|
||||
if (topslope >= los->topslope && bottomslope <= los->bottomslope)
|
||||
return false; // view completely blocked
|
||||
}
|
||||
// TODO: figure out if it's worth considering partially blocked cases or not?
|
||||
// maybe to adjust los's top/bottom slopes if needed
|
||||
}
|
||||
}
|
||||
|
||||
// passed the subsector ok
|
||||
|
|
@ -375,6 +475,8 @@ boolean P_CheckSight(mobj_t *t1, mobj_t *t2)
|
|||
if (s1 == s2) // Both sectors are the same.
|
||||
{
|
||||
ffloor_t *rover;
|
||||
fixed_t topz1, bottomz1; // top, bottom heights at t1's position
|
||||
fixed_t topz2, bottomz2; // likewise but for t2
|
||||
|
||||
for (rover = s1->ffloors; rover; rover = rover->next)
|
||||
{
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
380
src/p_spec.c
380
src/p_spec.c
|
|
@ -1705,41 +1705,6 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
|
|||
if (!(ALL7EMERALDS(emeralds)))
|
||||
return false;
|
||||
}
|
||||
else if (GETSECSPECIAL(caller->special, 2) == 7) // SRB2Kart: reusing for Race Lap executor
|
||||
{
|
||||
UINT8 lap;
|
||||
|
||||
if (actor && actor->player && triggerline->flags & ML_EFFECT4)
|
||||
{
|
||||
/*if (maptol & TOL_NIGHTS)
|
||||
lap = actor->player->mare;
|
||||
else*/
|
||||
lap = actor->player->laps;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*if (maptol & TOL_NIGHTS)
|
||||
lap = P_FindLowestMare();
|
||||
else*/
|
||||
lap = P_FindLowestLap();
|
||||
}
|
||||
|
||||
if (triggerline->flags & ML_NOCLIMB) // Need higher than or equal to
|
||||
{
|
||||
if (lap < (sides[triggerline->sidenum[0]].textureoffset >> FRACBITS))
|
||||
return false;
|
||||
}
|
||||
else if (triggerline->flags & ML_BLOCKMONSTERS) // Need lower than or equal to
|
||||
{
|
||||
if (lap > (sides[triggerline->sidenum[0]].textureoffset >> FRACBITS))
|
||||
return false;
|
||||
}
|
||||
else // Need equal to
|
||||
{
|
||||
if (lap != (sides[triggerline->sidenum[0]].textureoffset >> FRACBITS))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// If we were not triggered by a sector type especially for the purpose,
|
||||
// a Linedef Executor linedef trigger is not handling sector triggers properly, return.
|
||||
|
||||
|
|
@ -1937,15 +1902,17 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
|
|||
else
|
||||
// These special types work only once
|
||||
if (specialtype == 302 // Once
|
||||
|| specialtype == 304 // Ring count - Once
|
||||
|| specialtype == 307 // Character ability - Once
|
||||
|| specialtype == 308 // Race only - Once
|
||||
|| specialtype == 315 // No of pushables - Once
|
||||
|| specialtype == 318 // Unlockable trigger - Once
|
||||
|| specialtype == 320 // Unlockable - Once
|
||||
|| specialtype == 321 || specialtype == 322 // Trigger on X calls - Continuous + Each Time
|
||||
|| specialtype == 328 // Encore Load
|
||||
|| specialtype == 399) // Level Load
|
||||
|| specialtype == 304 // Ring count - Once
|
||||
|| specialtype == 307 // Character ability - Once
|
||||
|| specialtype == 308 // Race only - Once
|
||||
|| specialtype == 315 // No of pushables - Once
|
||||
|| specialtype == 318 // Unlockable trigger - Once
|
||||
|| specialtype == 320 // Unlockable - Once
|
||||
|| specialtype == 321 || specialtype == 322 // Trigger on X calls - Continuous + Each Time
|
||||
|| specialtype == 328 // Encore Load
|
||||
|| specialtype == 399 // Level Load
|
||||
|| specialtype == 2002 // SRB2Kart Race Lap
|
||||
)
|
||||
triggerline->special = 0; // Clear it out
|
||||
|
||||
return true;
|
||||
|
|
@ -1981,6 +1948,7 @@ void P_LinedefExecute(INT16 tag, mobj_t *actor, sector_t *caller)
|
|||
if (lines[masterline].special == 313
|
||||
|| lines[masterline].special == 399
|
||||
|| lines[masterline].special == 328
|
||||
|| lines[masterline].special == 2002 // SRB2Kart race lap trigger
|
||||
// Each-time executors handle themselves, too
|
||||
|| lines[masterline].special == 301 // Each time
|
||||
|| lines[masterline].special == 306 // Character ability - Each time
|
||||
|
|
@ -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])
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
10
src/p_tick.c
10
src/p_tick.c
|
|
@ -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.
|
||||
|
|
|
|||
126
src/p_user.c
126
src/p_user.c
|
|
@ -1253,7 +1253,7 @@ void P_RestoreMusic(player_t *player)
|
|||
#if 0
|
||||
// Event - Final Lap
|
||||
// Still works for GME, but disabled for consistency
|
||||
if (G_RaceGametype() && player->laps >= (UINT8)(cv_numlaps.value - 1))
|
||||
if (G_RaceGametype() && player->laps >= (UINT8)(cv_numlaps.value))
|
||||
S_SpeedMusic(1.2f);
|
||||
#endif
|
||||
S_ChangeMusicEx(mapmusname, mapmusflags, true, mapmusposition, 0, 0);
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue