Merge branch 'bot-maint' into 'master'

Bot maintenance

See merge request KartKrew/Kart!489
This commit is contained in:
Sal 2021-12-12 19:54:38 +00:00
commit d7829334d3
11 changed files with 654 additions and 387 deletions

View file

@ -273,16 +273,94 @@ boolean K_PlayerUsesBotMovement(player_t *player)
--------------------------------------------------*/
boolean K_BotCanTakeCut(player_t *player)
{
if (!K_ApplyOffroad(player)
if (
#if 1
K_TripwirePass(player) == true
#else
K_ApplyOffroad(player) == false
#endif
|| player->itemtype == KITEM_SNEAKER
|| player->itemtype == KITEM_ROCKETSNEAKER
|| player->itemtype == KITEM_INVINCIBILITY
|| player->itemtype == KITEM_HYUDORO)
)
{
return true;
}
return false;
}
/*--------------------------------------------------
static line_t *K_FindBotController(mobj_t *mo)
Finds if any bot controller linedefs are tagged to the bot's sector.
Input Arguments:-
mo - The bot player's mobj.
Return:-
Linedef of the bot controller. NULL if it doesn't exist.
--------------------------------------------------*/
static line_t *K_FindBotController(mobj_t *mo)
{
msecnode_t *node;
ffloor_t *rover;
INT16 lineNum = -1;
mtag_t tag;
I_Assert(mo != NULL);
I_Assert(!P_MobjWasRemoved(mo));
for (node = mo->touching_sectorlist; node; node = node->m_sectorlist_next)
{
if (!node->m_sector)
{
continue;
}
tag = Tag_FGet(&node->m_sector->tags);
lineNum = P_FindSpecialLineFromTag(2004, tag, -1); // todo: needs to not use P_FindSpecialLineFromTag
if (lineNum != -1)
{
break;
}
for (rover = node->m_sector->ffloors; rover; rover = rover->next)
{
sector_t *rs = NULL;
if (!(rover->flags & FF_EXISTS))
{
continue;
}
if (mo->z > *rover->topheight || mo->z + mo->height < *rover->bottomheight)
{
continue;
}
rs = &sectors[rover->secnum];
tag = Tag_FGet(&rs->tags);
lineNum = P_FindSpecialLineFromTag(2004, tag, -1);
if (lineNum != -1)
{
break;
}
}
}
if (lineNum != -1)
{
return &lines[lineNum];
}
else
{
return NULL;
}
}
/*--------------------------------------------------
static UINT32 K_BotRubberbandDistance(player_t *player)
@ -346,6 +424,7 @@ fixed_t K_BotRubberband(player_t *player)
fixed_t rubberband = FRACUNIT;
fixed_t max, min;
player_t *firstplace = NULL;
line_t *botController = NULL;
UINT8 i;
if (player->exiting)
@ -354,6 +433,17 @@ fixed_t K_BotRubberband(player_t *player)
return FRACUNIT;
}
botController = K_FindBotController(player->mo);
if (botController != NULL)
{
// No Climb Flag: Disable rubberbanding
if (botController->flags & ML_NOCLIMB)
{
return FRACUNIT;
}
}
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator)
@ -430,8 +520,8 @@ fixed_t K_BotTopSpeedRubberband(player_t *player)
}
else
{
// Max at +10% for level 9 bots
rubberband = FRACUNIT + ((rubberband - FRACUNIT) / 10);
// Max at +20% for level 9 bots
rubberband = FRACUNIT + ((rubberband - FRACUNIT) / 5);
}
// Only allow you to go faster than your regular top speed if you're facing the right direction
@ -488,6 +578,14 @@ fixed_t K_BotFrictionRubberband(player_t *player, fixed_t frict)
return frict;
}
if (player->tiregrease > 0)
{
// This isn't great -- it means rubberbanding will slow down when they hit a spring
// But it's better than the opposite where they accelerate into hyperspace :V
// (would appreciate an actual fix though ... could try being additive instead of multiplicative)
return frict;
}
origFrict = FixedDiv(ORIG_FRICTION, FRACUNIT + (rubberband / 2));
if (frict == ORIG_FRICTION)
@ -567,14 +665,11 @@ static botprediction_t *K_CreateBotPrediction(player_t *player)
const INT16 handling = K_GetKartTurnValue(player, KART_FULLTURN); // Reduce prediction based on how fast you can turn
const INT16 normal = KART_FULLTURN; // "Standard" handling to compare to
const fixed_t distreduce = K_BotReducePrediction(player);
const fixed_t radreduce = min(distreduce + FRACUNIT/4, FRACUNIT);
const tic_t futuresight = (TICRATE * normal) / max(1, handling); // How far ahead into the future to try and predict
const fixed_t speed = max(P_AproxDistance(player->mo->momx, player->mo->momy), K_GetKartSpeed(player, false) / 4);
const fixed_t speed = P_AproxDistance(player->mo->momx, player->mo->momy);
const INT32 startDist = (768 * mapobjectscale) / FRACUNIT;
const INT32 distance = ((FixedMul(speed, distreduce) / FRACUNIT) * futuresight) + startDist;
const INT32 distance = ((speed / FRACUNIT) * futuresight) + startDist;
botprediction_t *predict = Z_Calloc(sizeof(botprediction_t), PU_STATIC, NULL);
waypoint_t *wp = player->nextwaypoint;
@ -583,6 +678,9 @@ static botprediction_t *K_CreateBotPrediction(player_t *player)
fixed_t smallestradius = INT32_MAX;
angle_t angletonext = ANGLE_MAX;
// Halves radius when encountering a wall on your way to your destination.
fixed_t radreduce = FRACUNIT;
size_t nwp;
size_t i;
@ -595,7 +693,7 @@ static botprediction_t *K_CreateBotPrediction(player_t *player)
{
predict->x = wp->mobj->x;
predict->y = wp->mobj->y;
predict->radius = FixedMul(wp->mobj->radius, radreduce);
predict->radius = wp->mobj->radius;
return predict;
}
@ -635,12 +733,12 @@ static botprediction_t *K_CreateBotPrediction(player_t *player)
for (i = 0; i < wp->numnextwaypoints; i++)
{
if (!K_GetWaypointIsEnabled(wp->nextwaypoints[i]))
if (K_GetWaypointIsEnabled(wp->nextwaypoints[i]) == false)
{
continue;
}
if (K_GetWaypointIsShortcut(wp->nextwaypoints[i]) && !K_BotCanTakeCut(player))
if (K_GetWaypointIsShortcut(wp->nextwaypoints[i]) == true && K_BotCanTakeCut(player) == false)
{
continue;
}
@ -673,6 +771,13 @@ static botprediction_t *K_CreateBotPrediction(player_t *player)
disttonext = (INT32)wp->nextwaypointdistances[nwp];
if (P_TraceBotTraversal(player->mo, wp->nextwaypoints[nwp]->mobj) == false)
{
// If we can't get a direct path to this waypoint, we don't want to check much further...
disttonext *= 2;
radreduce = FRACUNIT/2;
}
if (disttonext > distanceleft)
{
break;
@ -761,7 +866,7 @@ static UINT8 K_TrySpindash(player_t *player)
return 0;
}
if (speedDiff < (3 * baseAccel / 4))
if (speedDiff < (baseAccel / 4))
{
if (player->botvars.spindashconfirm < BOTSPINDASHCONFIRM)
{
@ -797,70 +902,6 @@ static UINT8 K_TrySpindash(player_t *player)
return 0;
}
/*--------------------------------------------------
static INT16 K_FindBotController(mobj_t *mo)
Finds if any bot controller linedefs are tagged to the bot's sector.
Input Arguments:-
mo - The bot player's mobj.
Return:-
Line number of the bot controller. -1 if it doesn't exist.
--------------------------------------------------*/
static INT16 K_FindBotController(mobj_t *mo)
{
msecnode_t *node;
ffloor_t *rover;
INT16 lineNum = -1;
mtag_t tag;
I_Assert(mo != NULL);
I_Assert(!P_MobjWasRemoved(mo));
for (node = mo->touching_sectorlist; node; node = node->m_sectorlist_next)
{
if (!node->m_sector)
{
continue;
}
tag = Tag_FGet(&node->m_sector->tags);
lineNum = P_FindSpecialLineFromTag(2004, tag, -1); // todo: needs to not use P_FindSpecialLineFromTag
if (lineNum != -1)
{
return lineNum;
}
for (rover = node->m_sector->ffloors; rover; rover = rover->next)
{
sector_t *rs = NULL;
if (!(rover->flags & FF_EXISTS))
{
continue;
}
if (mo->z > *rover->topheight || mo->z + mo->height < *rover->bottomheight)
{
continue;
}
rs = &sectors[rover->secnum];
tag = Tag_FGet(&rs->tags);
lineNum = P_FindSpecialLineFromTag(2004, tag, -1);
if (lineNum != -1)
{
return lineNum;
}
}
}
return -1;
}
/*--------------------------------------------------
static void K_DrawPredictionDebug(botprediction_t *predict, player_t *player)
@ -925,6 +966,50 @@ static void K_DrawPredictionDebug(botprediction_t *predict, player_t *player)
}
}
/*--------------------------------------------------
static void K_BotTrick(player_t *player, ticcmd_t *cmd, line_t *botController)
Determines inputs for trick panels.
Input Arguments:-
player - Player to generate the ticcmd for.
cmd - The player's ticcmd to modify.
botController - Linedef for the bot controller.
Return:-
None
--------------------------------------------------*/
static void K_BotTrick(player_t *player, ticcmd_t *cmd, line_t *botController)
{
// Trick panel state -- do nothing until a controller line is found, in which case do a trick.
if (botController == NULL)
{
return;
}
if (player->trickpanel == 1)
{
INT32 type = (sides[botController->sidenum[0]].rowoffset / FRACUNIT);
// Y Offset: Trick type
switch (type)
{
case 1:
cmd->turning = KART_FULLTURN;
break;
case 2:
cmd->turning = -KART_FULLTURN;
break;
case 3:
cmd->buttons |= BT_FORWARD;
break;
case 4:
cmd->buttons |= BT_BACKWARD;
break;
}
}
}
/*--------------------------------------------------
void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd)
@ -936,7 +1021,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd)
boolean trySpindash = true;
UINT8 spindash = 0;
INT32 turnamt = 0;
INT16 botController = -1;
line_t *botController = NULL;
// Can't build a ticcmd if we aren't spawned...
if (!player->mo)
@ -959,7 +1044,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd)
}
// Complete override of all ticcmd functionality
if (LUAh_BotTiccmd(player, cmd))
if (LUAh_BotTiccmd(player, cmd) == true)
{
return;
}
@ -968,30 +1053,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd)
if (player->trickpanel != 0)
{
// Trick panel state -- do nothing until a controller line is found, in which case do a trick.
if (player->trickpanel == 1 && botController != -1)
{
line_t *controllerLine = &lines[botController];
INT32 type = (sides[controllerLine->sidenum[0]].rowoffset / FRACUNIT);
// Y Offset: Trick type
switch (type)
{
case 1:
cmd->turning = KART_FULLTURN;
break;
case 2:
cmd->turning = -KART_FULLTURN;
break;
case 3:
cmd->buttons |= BT_FORWARD;
break;
case 4:
cmd->buttons |= BT_BACKWARD;
break;
}
}
K_BotTrick(player, cmd, botController);
// Don't do anything else.
return;
@ -1000,20 +1062,19 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd)
if ((player->nextwaypoint != NULL
&& player->nextwaypoint->mobj != NULL
&& !P_MobjWasRemoved(player->nextwaypoint->mobj))
|| (botController != -1))
|| (botController != NULL))
{
// Handle steering towards waypoints!
SINT8 turnsign = 0;
angle_t destangle, moveangle, angle;
INT16 anglediff;
if (botController != -1)
if (botController != NULL && (botController->flags & ML_EFFECT1))
{
const fixed_t dist = (player->mo->radius * 4);
line_t *controllerLine = &lines[botController];
// X Offset: Movement direction
destangle = FixedAngle(sides[controllerLine->sidenum[0]].textureoffset);
destangle = FixedAngle(sides[botController->sidenum[0]].textureoffset);
// Overwritten prediction
predict = Z_Calloc(sizeof(botprediction_t), PU_STATIC, NULL);
@ -1112,18 +1173,6 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd)
// Don't turn at all
turnamt = 0;
}
else
{
// Make minor adjustments
turnamt /= 4;
}
}
if (anglediff > 60)
{
// Actually, don't go too fast...
cmd->forwardmove /= 2;
cmd->buttons |= BT_BRAKE;
}
}
}

View file

@ -15,6 +15,7 @@
#include "k_waypoint.h"
#include "d_player.h"
#include "r_defs.h"
// Maximum value of botvars.difficulty
#define MAXBOTDIFFICULTY 9
@ -185,19 +186,22 @@ UINT8 K_EggboxStealth(fixed_t x, fixed_t y);
/*--------------------------------------------------
fixed_t K_BotReducePrediction(player_t *player);
boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t y)
Finds walls nearby the specified player, to create a multiplier
to pull bot predictions back by.
Tells us if a bot will play more careful around
this sector. Checks FOFs in the sector, as well.
Input Arguments:-
player - Player to compare.
player - Player to check against.
sec - Sector to check against.
x - Linedef cross X position, for slopes
y - Linedef cross Y position, for slopes
Return:-
Multiplier in fixed point scale.
true if avoiding this sector, false otherwise.
--------------------------------------------------*/
fixed_t K_BotReducePrediction(player_t *player);
boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t y);
/*--------------------------------------------------
@ -219,8 +223,8 @@ void K_NudgePredictionTowardsObjects(botprediction_t *predict, player_t *player)
/*--------------------------------------------------
void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd);
Gives a multiplier for a bot's rubberbanding. Meant to be used for top speed,
acceleration, and handling.
Creates a bot's ticcmd, looking at its surroundings to
try and figure out what it should do.
Input Arguments:-
player - Player to generate the ticcmd for.

View file

@ -86,7 +86,7 @@ static boolean K_BotUseItemNearPlayer(player_t *player, ticcmd_t *cmd, fixed_t r
}
/*--------------------------------------------------
static boolean K_PlayerNearSpot(player_t *player, fixed_t x, fixed_t y, fixed_t radius)
static player_t *K_PlayerNearSpot(player_t *player, fixed_t x, fixed_t y, fixed_t radius)
Looks for players around a specified x/y coordinate.
@ -97,9 +97,9 @@ static boolean K_BotUseItemNearPlayer(player_t *player, ticcmd_t *cmd, fixed_t r
radius - The radius to look for players in.
Return:-
true if a player was found around the coordinate, otherwise false.
The player we found, NULL if nothing was found.
--------------------------------------------------*/
static boolean K_PlayerNearSpot(player_t *player, fixed_t x, fixed_t y, fixed_t radius)
static player_t *K_PlayerNearSpot(player_t *player, fixed_t x, fixed_t y, fixed_t radius)
{
UINT8 i;
@ -129,15 +129,15 @@ static boolean K_PlayerNearSpot(player_t *player, fixed_t x, fixed_t y, fixed_t
if (dist <= radius)
{
return true;
return target;
}
}
return false;
return NULL;
}
/*--------------------------------------------------
static boolean K_PlayerPredictThrow(player_t *player, UINT8 extra)
static player_t *K_PlayerPredictThrow(player_t *player, UINT8 extra)
Looks for players around the predicted coordinates of their thrown item.
@ -146,9 +146,9 @@ static boolean K_PlayerNearSpot(player_t *player, fixed_t x, fixed_t y, fixed_t
extra - Extra throwing distance, for aim forward on mines.
Return:-
true if a player was found around the coordinate, otherwise false.
The player we're trying to throw at, NULL if none was found.
--------------------------------------------------*/
static boolean K_PlayerPredictThrow(player_t *player, UINT8 extra)
static player_t *K_PlayerPredictThrow(player_t *player, UINT8 extra)
{
const fixed_t dist = (30 + (extra * 10)) * player->mo->scale;
const UINT32 airtime = FixedDiv(dist + player->mo->momz, gravity);
@ -159,7 +159,7 @@ static boolean K_PlayerPredictThrow(player_t *player, UINT8 extra)
}
/*--------------------------------------------------
static boolean K_PlayerInCone(player_t *player, UINT16 cone, boolean flip)
static player_t *K_PlayerInCone(player_t *player, UINT16 cone, boolean flip)
Looks for players in the .
@ -172,7 +172,7 @@ static boolean K_PlayerPredictThrow(player_t *player, UINT8 extra)
Return:-
true if a player was found in the cone, otherwise false.
--------------------------------------------------*/
static boolean K_PlayerInCone(player_t *player, fixed_t radius, UINT16 cone, boolean flip)
static player_t *K_PlayerInCone(player_t *player, fixed_t radius, UINT16 cone, boolean flip)
{
UINT8 i;
@ -222,22 +222,96 @@ static boolean K_PlayerInCone(player_t *player, fixed_t radius, UINT16 cone, boo
{
if (ad >= 180-cone)
{
return true;
return target;
}
}
else
{
if (ad <= cone)
{
return true;
return target;
}
}
}
}
return NULL;
}
/*--------------------------------------------------
static boolean K_RivalBotAggression(player_t *bot, player_t *target)
Returns if a bot is a rival & wants to be aggressive to a player.
Input Arguments:-
bot - Bot to check.
target - Who the bot wants to attack.
Return:-
false if not the rival. false if the target is another bot. Otherwise, true.
--------------------------------------------------*/
static boolean K_RivalBotAggression(player_t *bot, player_t *target)
{
if (bot == NULL || target == NULL)
{
// Invalid.
return false;
}
if (bot->bot == false)
{
// lol
return false;
}
if (bot->botvars.rival == false)
{
// Not the rival, we aren't self-aware.
return false;
}
if (target->bot == false)
{
// This bot knows that the real threat is the player.
return true;
}
// Calling them your friends is misleading, but you'll at least spare them.
return false;
}
/*--------------------------------------------------
static void K_ItemConfirmForTarget(player_t *bot, player_t *target, UINT16 amount)
Handles updating item confirm values for offense items.
Input Arguments:-
bot - Bot to check.
target - Who the bot wants to attack.
amount - Amount to increase item confirm time by.
Return:-
None
--------------------------------------------------*/
static void K_ItemConfirmForTarget(player_t *bot, player_t *target, UINT16 amount)
{
if (bot == NULL || target == NULL)
{
return;
}
if (K_RivalBotAggression(bot, target) == true)
{
// Double the rate when you're aggressive.
bot->botvars.itemconfirm += amount << 1;
}
else
{
// Do as normal.
bot->botvars.itemconfirm += amount;
}
}
/*--------------------------------------------------
static boolean K_BotGenericPressItem(player_t *player, ticcmd_t *cmd, SINT8 dir)
@ -316,21 +390,21 @@ static boolean K_BotRevealsGenericTrap(player_t *player, INT16 turnamt, boolean
}
// Check the predicted throws.
if (K_PlayerPredictThrow(player, 0))
if (K_PlayerPredictThrow(player, 0) != NULL)
{
return true;
}
if (mine)
{
if (K_PlayerPredictThrow(player, 1))
if (K_PlayerPredictThrow(player, 1) != NULL)
{
return true;
}
}
// Check your behind.
if (K_PlayerInCone(player, player->mo->radius * 16, 10, true))
if (K_PlayerInCone(player, player->mo->radius * 16, 10, true) != NULL)
{
return true;
}
@ -447,7 +521,6 @@ static void K_BotItemRocketSneaker(player_t *player, ticcmd_t *cmd)
}
}
/*--------------------------------------------------
static void K_BotItemBanana(player_t *player, ticcmd_t *cmd, INT16 turnamt)
@ -464,9 +537,17 @@ static void K_BotItemRocketSneaker(player_t *player, ticcmd_t *cmd)
static void K_BotItemBanana(player_t *player, ticcmd_t *cmd, INT16 turnamt)
{
SINT8 throwdir = -1;
player_t *target = NULL;
player->botvars.itemconfirm++;
target = K_PlayerInCone(player, player->mo->radius * 16, 10, true);
if (target != NULL)
{
K_ItemConfirmForTarget(player, target, player->botvars.difficulty);
throwdir = -1;
}
if (abs(turnamt) >= KART_FULLTURN/2)
{
player->botvars.itemconfirm += player->botvars.difficulty / 2;
@ -474,19 +555,15 @@ static void K_BotItemBanana(player_t *player, ticcmd_t *cmd, INT16 turnamt)
}
else
{
if (K_PlayerPredictThrow(player, 0))
target = K_PlayerPredictThrow(player, 0);
if (target != NULL)
{
player->botvars.itemconfirm += player->botvars.difficulty * 2;
K_ItemConfirmForTarget(player, target, player->botvars.difficulty * 2);
throwdir = 1;
}
}
if (K_PlayerInCone(player, player->mo->radius * 16, 10, true))
{
player->botvars.itemconfirm += player->botvars.difficulty;
throwdir = -1;
}
if (player->botvars.itemconfirm > 2*TICRATE || player->bananadrag >= TICRATE)
{
K_BotGenericPressItem(player, cmd, throwdir);
@ -509,12 +586,14 @@ static void K_BotItemBanana(player_t *player, ticcmd_t *cmd, INT16 turnamt)
static void K_BotItemMine(player_t *player, ticcmd_t *cmd, INT16 turnamt)
{
SINT8 throwdir = 0;
player_t *target = NULL;
player->botvars.itemconfirm++;
if (K_PlayerInCone(player, player->mo->radius * 16, 10, true))
target = K_PlayerInCone(player, player->mo->radius * 16, 10, true);
if (target != NULL)
{
player->botvars.itemconfirm += player->botvars.difficulty;
K_ItemConfirmForTarget(player, target, player->botvars.difficulty);
throwdir = -1;
}
@ -525,27 +604,63 @@ static void K_BotItemMine(player_t *player, ticcmd_t *cmd, INT16 turnamt)
}
else
{
if (K_PlayerPredictThrow(player, 0))
target = K_PlayerPredictThrow(player, 0);
if (target != NULL)
{
player->botvars.itemconfirm += player->botvars.difficulty * 2;
K_ItemConfirmForTarget(player, target, player->botvars.difficulty * 2);
throwdir = 0;
}
if (K_PlayerPredictThrow(player, 1))
target = K_PlayerPredictThrow(player, 1);
if (target != NULL)
{
player->botvars.itemconfirm += player->botvars.difficulty * 2;
K_ItemConfirmForTarget(player, target, player->botvars.difficulty * 2);
throwdir = 1;
}
}
if (player->botvars.itemconfirm > 2*TICRATE || player->bananadrag >= TICRATE)
{
K_BotGenericPressItem(player, cmd, throwdir);
}
}
/*--------------------------------------------------
static void K_BotItemLandmine(player_t *player, ticcmd_t *cmd, INT16 turnamt)
Item usage for landmine tossing.
Input Arguments:-
player - Bot to do this for.
cmd - Bot's ticcmd to edit.
turnamt - How hard they currently are turning.
Return:-
None
--------------------------------------------------*/
static void K_BotItemLandmine(player_t *player, ticcmd_t *cmd, INT16 turnamt)
{
player_t *target = NULL;
player->botvars.itemconfirm++;
if (abs(turnamt) >= KART_FULLTURN/2)
{
player->botvars.itemconfirm += player->botvars.difficulty / 2;
}
target = K_PlayerInCone(player, player->mo->radius * 16, 10, true);
if (target != NULL)
{
K_ItemConfirmForTarget(player, target, player->botvars.difficulty);
}
if (player->botvars.itemconfirm > 2*TICRATE)
{
K_BotGenericPressItem(player, cmd, -1);
}
}
/*--------------------------------------------------
static void K_BotItemEggman(player_t *player, ticcmd_t *cmd)
@ -562,18 +677,21 @@ static void K_BotItemEggman(player_t *player, ticcmd_t *cmd)
{
const UINT8 stealth = K_EggboxStealth(player->mo->x, player->mo->y);
SINT8 throwdir = -1;
player_t *target = NULL;
player->botvars.itemconfirm++;
if (K_PlayerPredictThrow(player, 0))
target = K_PlayerPredictThrow(player, 0);
if (target != NULL)
{
player->botvars.itemconfirm += player->botvars.difficulty / 2;
K_ItemConfirmForTarget(player, target, player->botvars.difficulty / 2);
throwdir = 1;
}
if (K_PlayerInCone(player, player->mo->radius * 16, 10, true))
target = K_PlayerInCone(player, player->mo->radius * 16, 10, true);
if (target != NULL)
{
player->botvars.itemconfirm += player->botvars.difficulty;
K_ItemConfirmForTarget(player, target, player->botvars.difficulty);
throwdir = -1;
}
@ -603,6 +721,7 @@ static void K_BotItemEggman(player_t *player, ticcmd_t *cmd)
static boolean K_BotRevealsEggbox(player_t *player)
{
const UINT8 stealth = K_EggboxStealth(player->mo->x, player->mo->y);
player_t *target = NULL;
// This is a stealthy spot for an eggbox, lets reveal it!
if (stealth > 1)
@ -611,13 +730,15 @@ static boolean K_BotRevealsEggbox(player_t *player)
}
// Check the predicted throws.
if (K_PlayerPredictThrow(player, 0))
target = K_PlayerPredictThrow(player, 0);
if (target != NULL)
{
return true;
}
// Check your behind.
if (K_PlayerInCone(player, player->mo->radius * 16, 10, true))
target = K_PlayerInCone(player, player->mo->radius * 16, 10, true);
if (target != NULL)
{
return true;
}
@ -644,7 +765,7 @@ static void K_BotItemEggmanShield(player_t *player, ticcmd_t *cmd)
return;
}
if (K_BotRevealsEggbox(player) || (player->botvars.itemconfirm++ > 20*TICRATE))
if (K_BotRevealsEggbox(player) == true || (player->botvars.itemconfirm++ > 20*TICRATE))
{
K_BotGenericPressItem(player, cmd, 0);
}
@ -666,8 +787,9 @@ static void K_BotItemEggmanExplosion(player_t *player, ticcmd_t *cmd)
{
if (player->position == 1)
{
// Hey, we aren't gonna find anyone up here...
// why don't we slow down a bit? :)
cmd->forwardmove /= 2;
cmd->buttons |= BT_BRAKE;
}
K_BotUseItemNearPlayer(player, cmd, 128*player->mo->scale);
@ -690,23 +812,32 @@ static void K_BotItemOrbinaut(player_t *player, ticcmd_t *cmd)
const fixed_t topspeed = K_GetKartSpeed(player, false);
fixed_t radius = (player->mo->radius * 32);
SINT8 throwdir = -1;
UINT8 snipeMul = 2;
player_t *target = NULL;
if (player->speed > topspeed)
{
radius = FixedMul(radius, FixedDiv(player->speed, topspeed));
snipeMul = 3; // Confirm faster when you'll throw it with a bunch of extra speed!!
}
player->botvars.itemconfirm++;
if (K_PlayerInCone(player, radius, 10, false))
target = K_PlayerInCone(player, radius, 10, false);
if (target != NULL)
{
player->botvars.itemconfirm += player->botvars.difficulty * 2;
K_ItemConfirmForTarget(player, target, player->botvars.difficulty * snipeMul);
throwdir = 1;
}
else if (K_PlayerInCone(player, radius, 10, true))
{
player->botvars.itemconfirm += player->botvars.difficulty;
throwdir = -1;
target = K_PlayerInCone(player, radius, 10, true);
if (target != NULL)
{
K_ItemConfirmForTarget(player, target, player->botvars.difficulty);
throwdir = -1;
}
}
if (player->botvars.itemconfirm > 5*TICRATE)
@ -732,24 +863,54 @@ static void K_BotItemJawz(player_t *player, ticcmd_t *cmd)
const fixed_t topspeed = K_GetKartSpeed(player, false);
fixed_t radius = (player->mo->radius * 32);
SINT8 throwdir = 1;
UINT8 snipeMul = 2;
INT32 lastTarg = player->lastjawztarget;
player_t *target = NULL;
if (player->speed > topspeed)
{
radius = FixedMul(radius, FixedDiv(player->speed, topspeed));
snipeMul = 3; // Confirm faster when you'll throw it with a bunch of extra speed!!
}
player->botvars.itemconfirm++;
if (K_PlayerInCone(player, radius, 10, true))
target = K_PlayerInCone(player, radius, 10, true);
if (target != NULL)
{
player->botvars.itemconfirm += player->botvars.difficulty;
K_ItemConfirmForTarget(player, target, player->botvars.difficulty);
throwdir = -1;
}
if (player->lastjawztarget != -1)
if (lastTarg != -1
&& playeringame[lastTarg] == true
&& players[lastTarg].spectator == false
&& players[lastTarg].mo != NULL
&& P_MobjWasRemoved(players[lastTarg].mo) == false)
{
player->botvars.itemconfirm += player->botvars.difficulty * 2;
throwdir = 1;
mobj_t *targMo = players[lastTarg].mo;
mobj_t *mobj = NULL, *next = NULL;
boolean targettedAlready = false;
target = &players[lastTarg];
// Make sure no other Jawz are targetting this player.
for (mobj = kitemcap; mobj; mobj = next)
{
next = mobj->itnext;
if (mobj->type == MT_JAWZ && mobj->target == targMo)
{
targettedAlready = true;
break;
}
}
if (targettedAlready == false)
{
K_ItemConfirmForTarget(player, target, player->botvars.difficulty * snipeMul);
throwdir = 1;
}
}
if (player->botvars.itemconfirm > 5*TICRATE)
@ -772,7 +933,7 @@ static void K_BotItemJawz(player_t *player, ticcmd_t *cmd)
--------------------------------------------------*/
static void K_BotItemThunder(player_t *player, ticcmd_t *cmd)
{
if (!K_BotUseItemNearPlayer(player, cmd, 192*player->mo->scale))
if (K_BotUseItemNearPlayer(player, cmd, 192*player->mo->scale) == false)
{
if (player->botvars.itemconfirm > 10*TICRATE)
{
@ -1036,7 +1197,6 @@ void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt)
K_BotItemSneaker(player, cmd);
break;
case KITEM_BANANA:
case KITEM_LANDMINE:
if (!(player->pflags & PF_ITEMOUT))
{
K_BotItemGenericTrapShield(player, cmd, turnamt, false);
@ -1081,6 +1241,9 @@ void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt)
K_BotItemMine(player, cmd, turnamt);
}
break;
case KITEM_LANDMINE:
K_BotItemLandmine(player, cmd, turnamt);
break;
case KITEM_THUNDERSHIELD:
K_BotItemThunder(player, cmd);
break;

View file

@ -122,7 +122,7 @@ UINT8 K_EggboxStealth(fixed_t x, fixed_t y)
}
}
return (globalsmuggle.randomitems * globalsmuggle.eggboxes);
return (globalsmuggle.randomitems * (globalsmuggle.eggboxes + 1));
}
/*--------------------------------------------------
@ -162,21 +162,11 @@ static boolean K_BotHatesThisSectorsSpecial(player_t *player, sector_t *sec)
}
/*--------------------------------------------------
static boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t y)
boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t y)
Tells us if a bot will play more careful around
this sector. Checks FOFs in the sector, as well.
Input Arguments:-
player - Player to check against.
sec - Sector to check against.
x - Linedef cross X position, for slopes
y - Linedef cross Y position, for slopes
Return:-
true if avoiding this sector, false otherwise.
See header file for description.
--------------------------------------------------*/
static boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t y)
boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t y)
{
const boolean flip = (player->mo->eflags & MFE_VERTICALFLIP);
INT32 specialflag = 0;
@ -257,171 +247,6 @@ static boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x,
return K_BotHatesThisSectorsSpecial(player, bestsector);
}
/*--------------------------------------------------
static boolean K_FindBlockingWalls(line_t *line)
Blockmap search function.
Reels the bot prediction back in based on solid walls
or other obstacles surrounding the bot.
Input Arguments:-
line - Linedef passed in from iteration.
Return:-
true continues searching, false ends the search early.
--------------------------------------------------*/
static boolean K_FindBlockingWalls(line_t *line)
{
// Condensed version of PIT_CheckLine
const fixed_t maxstepmove = FixedMul(MAXSTEPMOVE, mapobjectscale);
fixed_t maxstep = maxstepmove;
fixed_t linedist = INT32_MAX;
INT32 lineside = 0;
vertex_t pos;
if (!globalsmuggle.botmo || P_MobjWasRemoved(globalsmuggle.botmo) || !globalsmuggle.botmo->player)
{
return false;
}
if (line->polyobj && !(line->polyobj->flags & POF_SOLID))
{
return true;
}
if (tmbbox[BOXRIGHT] <= line->bbox[BOXLEFT] || tmbbox[BOXLEFT] >= line->bbox[BOXRIGHT]
|| tmbbox[BOXTOP] <= line->bbox[BOXBOTTOM] || tmbbox[BOXBOTTOM] >= line->bbox[BOXTOP])
{
return true;
}
if (P_BoxOnLineSide(tmbbox, line) != -1)
{
return true;
}
lineside = P_PointOnLineSide(globalsmuggle.botmo->x, globalsmuggle.botmo->y, line);
// one sided line
if (!line->backsector)
{
if (lineside)
{
// don't hit the back side
return true;
}
goto blocked;
}
if ((line->flags & ML_IMPASSABLE) || (line->flags & ML_BLOCKPLAYERS))
{
goto blocked;
}
// set openrange, opentop, openbottom
P_LineOpening(line, globalsmuggle.botmo);
if (globalsmuggle.botmo->player->waterskip)
maxstep += maxstepmove;
if (P_MobjTouchingSectorSpecial(globalsmuggle.botmo, 1, 13, false))
maxstep <<= 1;
else if (P_MobjTouchingSectorSpecial(globalsmuggle.botmo, 1, 12, false))
maxstep = 0;
if ((openrange < globalsmuggle.botmo->height) // doesn't fit
|| (opentop - globalsmuggle.botmo->z < globalsmuggle.botmo->height) // mobj is too high
|| (openbottom - globalsmuggle.botmo->z > maxstep)) // too big a step up
{
goto blocked;
}
// Treat damage sectors like walls
P_ClosestPointOnLine(globalsmuggle.botmo->x, globalsmuggle.botmo->y, line, &pos);
if (lineside)
{
if (K_BotHatesThisSector(globalsmuggle.botmo->player, line->frontsector, pos.x, pos.y))
goto blocked;
}
else
{
if (K_BotHatesThisSector(globalsmuggle.botmo->player, line->backsector, pos.x, pos.y))
goto blocked;
}
// We weren't blocked!
return true;
blocked:
linedist = K_DistanceOfLineFromPoint(line->v1->x, line->v1->y, line->v2->x, line->v2->y, globalsmuggle.botmo->x, globalsmuggle.botmo->y);
linedist -= (globalsmuggle.botmo->radius * 8); // Maintain a reasonable distance away from it
if (linedist > globalsmuggle.distancetocheck)
{
return true;
}
if (linedist <= 0)
{
globalsmuggle.closestlinedist = 0;
return false;
}
if (linedist < globalsmuggle.closestlinedist)
{
globalsmuggle.closestlinedist = linedist;
}
return true;
}
/*--------------------------------------------------
fixed_t K_BotReducePrediction(player_t *player)
See header file for description.
--------------------------------------------------*/
fixed_t K_BotReducePrediction(player_t *player)
{
INT32 xl, xh, yl, yh, bx, by;
globalsmuggle.botmo = player->mo;
globalsmuggle.distancetocheck = (player->mo->radius * 32);
globalsmuggle.closestlinedist = INT32_MAX;
tmx = player->mo->x;
tmy = player->mo->y;
xl = (unsigned)(tmx - globalsmuggle.distancetocheck - bmaporgx)>>MAPBLOCKSHIFT;
xh = (unsigned)(tmx + globalsmuggle.distancetocheck - bmaporgx)>>MAPBLOCKSHIFT;
yl = (unsigned)(tmy - globalsmuggle.distancetocheck - bmaporgy)>>MAPBLOCKSHIFT;
yh = (unsigned)(tmy + globalsmuggle.distancetocheck - bmaporgy)>>MAPBLOCKSHIFT;
BMBOUNDFIX(xl, xh, yl, yh);
tmbbox[BOXTOP] = tmy + globalsmuggle.distancetocheck;
tmbbox[BOXBOTTOM] = tmy - globalsmuggle.distancetocheck;
tmbbox[BOXRIGHT] = tmx + globalsmuggle.distancetocheck;
tmbbox[BOXLEFT] = tmx - globalsmuggle.distancetocheck;
// Check for lines that the bot might collide with
for (bx = xl; bx <= xh; bx++)
{
for (by = yl; by <= yh; by++)
{
P_BlockLinesIterator(bx, by, K_FindBlockingWalls);
}
}
if (globalsmuggle.closestlinedist == INT32_MAX)
{
return FRACUNIT;
}
return (FRACUNIT/2) + (FixedDiv(globalsmuggle.closestlinedist, globalsmuggle.distancetocheck) / 2);
}
/*--------------------------------------------------
static void K_AddAttackObject(mobj_t *thing, UINT8 side, UINT8 weight)

View file

@ -8634,8 +8634,6 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
boolean HOLDING_ITEM = (player->pflags & (PF_ITEMOUT|PF_EGGMANOUT));
boolean NO_HYUDORO = (player->stealingtimer == 0);
player->pflags &= ~PF_HITFINISHLINE;
if (!player->exiting)
{
if (player->oldposition < player->position) // But first, if you lost a place,

View file

@ -404,6 +404,8 @@ boolean P_IsLineBlocking(const line_t *ld, const mobj_t *thing);
boolean P_IsLineTripWire(const line_t *ld);
boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y);
boolean P_CheckCameraPosition(fixed_t x, fixed_t y, camera_t *thiscam);
fixed_t P_BaseStepUp(void);
fixed_t P_GetThingStepUp(mobj_t *thing);
boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff);
boolean P_Move(mobj_t *actor, fixed_t speed);
boolean P_SetOrigin(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z);
@ -413,6 +415,7 @@ void P_BouncePlayerMove(mobj_t *mo);
void P_BounceMove(mobj_t *mo);
boolean P_CheckSight(mobj_t *t1, mobj_t *t2);
boolean P_TraceBlockingLines(mobj_t *t1, mobj_t *t2);
boolean P_TraceBotTraversal(mobj_t *t1, mobj_t *t2);
void P_CheckHoopPosition(mobj_t *hoopthing, fixed_t x, fixed_t y, fixed_t z, fixed_t radius);
boolean P_CheckSector(sector_t *sector, boolean crunch);

View file

@ -2431,6 +2431,42 @@ static boolean P_WaterStepUp(mobj_t *thing)
P_WaterRunning(thing);
}
fixed_t P_BaseStepUp(void)
{
return FixedMul(MAXSTEPMOVE, mapobjectscale);
}
fixed_t P_GetThingStepUp(mobj_t *thing)
{
const fixed_t maxstepmove = P_BaseStepUp();
fixed_t maxstep = maxstepmove;
if (thing->type == MT_SKIM)
{
// Skim special (not needed for kart?)
return 0;
}
if (P_WaterStepUp(thing) == true)
{
// Add some extra stepmove when waterskipping
maxstep += maxstepmove;
}
if (P_MobjTouchingSectorSpecial(thing, 1, 13, false))
{
// If using type Section1:13, double the maxstep.
maxstep <<= 1;
}
else if (P_MobjTouchingSectorSpecial(thing, 1, 12, false))
{
// If using type Section1:12, no maxstep. For short walls, like Egg Zeppelin
maxstep = 0;
}
return maxstep;
}
//
// P_TryMove
// Attempt to move to a new position.
@ -2492,21 +2528,7 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
if (!(thing->flags & MF_NOCLIP))
{
//All things are affected by their scale.
const fixed_t maxstepmove = FixedMul(MAXSTEPMOVE, mapobjectscale);
fixed_t maxstep = maxstepmove;
if (thing->player && P_WaterStepUp(thing))
maxstep += maxstepmove; // Add some extra stepmove when waterskipping
// If using type Section1:13, double the maxstep.
if (P_MobjTouchingSectorSpecial(thing, 1, 13, false))
maxstep <<= 1;
// If using type Section1:12, no maxstep. For short walls, like Egg Zeppelin
else if (P_MobjTouchingSectorSpecial(thing, 1, 12, false))
maxstep = 0;
if (thing->type == MT_SKIM)
maxstep = 0;
fixed_t maxstep = P_GetThingStepUp(thing);
if (tmceilingz - tmfloorz < thing->height)
{
@ -2743,7 +2765,7 @@ boolean P_SceneryTryMove(mobj_t *thing, fixed_t x, fixed_t y)
if (!(thing->flags & MF_NOCLIP))
{
const fixed_t maxstep = FixedMul(MAXSTEPMOVE, mapobjectscale);
const fixed_t maxstep = P_BaseStepUp();
if (tmceilingz - tmfloorz < thing->height)
return false; // doesn't fit
@ -3205,7 +3227,7 @@ static boolean PTR_LineIsBlocking(line_t *li)
if (opentop - slidemo->z < slidemo->height)
return true; // mobj is too high
if (openbottom - slidemo->z > FixedMul(MAXSTEPMOVE, mapobjectscale))
if (openbottom - slidemo->z > P_GetThingStepUp(slidemo))
return true; // too big a step up
return false;

View file

@ -4095,7 +4095,7 @@ boolean P_BossTargetPlayer(mobj_t *actor, boolean closest)
player = &players[actor->lastlook];
if (player->bot || player->spectator)
if (player->spectator)
continue; // ignore notarget
if (!player->mo || P_MobjWasRemoved(player->mo))

View file

@ -18,6 +18,9 @@
#include "r_main.h"
#include "r_state.h"
#include "k_bot.h" // K_BotHatesThisSector
#include "k_kart.h" // K_TripwirePass
//
// P_CheckSight
//
@ -572,7 +575,7 @@ static boolean P_CrossBlockingSubsector(size_t num, register traceblocking_t *tb
if (P_IsLineBlocking(line, tb->compareThing) == true)
{
// This line will block us
// This line will always block us
return false;
}
}
@ -656,3 +659,192 @@ boolean P_TraceBlockingLines(mobj_t *t1, mobj_t *t2)
// the head node is the last node output
return P_CrossBSPNodeBlocking((INT32)numnodes - 1, &tb);
}
//
// ANOTHER version, this time for bot traversal.
// (TODO: since we have so many versions of this function, the differences
// should maybe just be a function var that gets called?)
//
static boolean P_CrossBotTraversalSubsector(size_t num, register traceblocking_t *tb)
{
seg_t *seg;
INT32 count;
#ifdef RANGECHECK
if (num >= numsubsectors)
I_Error("P_CrossBotTraversalSubsector: ss %s with numss = %s\n", sizeu1(num), sizeu2(numsubsectors));
#endif
// haleyjd 02/23/06: this assignment should be after the above check
seg = segs + subsectors[num].firstline;
for (count = subsectors[num].numlines; --count >= 0; seg++) // check lines
{
line_t *line = seg->linedef;
divline_t divl;
const vertex_t *v1,*v2;
fixed_t maxstep = INT32_MAX;
if (seg->glseg)
continue;
// already checked other side?
if (line->validcount == validcount)
continue;
line->validcount = validcount;
// OPTIMIZE: killough 4/20/98: Added quick bounding-box rejection test
if (line->bbox[BOXLEFT ] > tb->bbox[BOXRIGHT ] ||
line->bbox[BOXRIGHT ] < tb->bbox[BOXLEFT ] ||
line->bbox[BOXBOTTOM] > tb->bbox[BOXTOP ] ||
line->bbox[BOXTOP] < tb->bbox[BOXBOTTOM])
continue;
v1 = line->v1;
v2 = line->v2;
// line isn't crossed?
if (P_DivlineSide(v1->x, v1->y, &tb->strace) ==
P_DivlineSide(v2->x, v2->y, &tb->strace))
continue;
// stop because it is not two sided anyway
if (!(line->flags & ML_TWOSIDED))
return false;
divl.dx = v2->x - (divl.x = v1->x);
divl.dy = v2->y - (divl.y = v1->y);
// line isn't crossed?
if (P_DivlineSide(tb->strace.x, tb->strace.y, &divl) ==
P_DivlineSide(tb->t2x, tb->t2y, &divl))
continue;
if (P_IsLineBlocking(line, tb->compareThing) == true)
{
// This line will always block us
return false;
}
// set openrange, opentop, openbottom
P_LineOpening(line, tb->compareThing);
maxstep = P_GetThingStepUp(tb->compareThing);
if ((openrange < tb->compareThing->height) // doesn't fit
|| (opentop - tb->compareThing->z < tb->compareThing->height) // mobj is too high
|| (openbottom - tb->compareThing->z > maxstep)) // too big a step up
{
// This line situationally blocks us
return false;
}
// Treat damage sectors like walls
if (tb->compareThing->player != NULL)
{
boolean alreadyHates = K_BotHatesThisSector(tb->compareThing->player, tb->compareThing->subsector->sector, tb->compareThing->x, tb->compareThing->y);
if (alreadyHates == false)
{
INT32 lineside = 0;
vertex_t pos;
P_ClosestPointOnLine(tb->compareThing->x, tb->compareThing->y, line, &pos);
lineside = P_PointOnLineSide(tb->compareThing->x, tb->compareThing->y, line);
if (K_BotHatesThisSector(tb->compareThing->player, ((lineside == 1) ? line->frontsector : line->backsector), pos.x, pos.y))
{
// This line does not block us, but we don't want to be in it.
return false;
}
}
if (P_IsLineTripWire(line) == true && K_TripwirePass(tb->compareThing->player) == false)
{
// Can't go through trip wire.
return false;
}
}
}
// passed the subsector ok
return true;
}
static boolean P_CrossBSPNodeBotTraversal(INT32 bspnum, register traceblocking_t *tb)
{
while (!(bspnum & NF_SUBSECTOR))
{
register node_t *bsp = nodes + bspnum;
INT32 side = P_DivlineSide(tb->strace.x,tb->strace.y,(divline_t *)bsp)&1;
if (side == P_DivlineSide(tb->t2x, tb->t2y, (divline_t *) bsp))
bspnum = bsp->children[side]; // doesn't touch the other side
else // the partition plane is crossed here
{
if (!P_CrossBSPNodeBotTraversal(bsp->children[side], tb))
return false; // cross the starting side
else
bspnum = bsp->children[side^1]; // cross the ending side
}
}
return P_CrossBotTraversalSubsector((bspnum == -1 ? 0 : bspnum & ~NF_SUBSECTOR), tb);
}
boolean P_TraceBotTraversal(mobj_t *t1, mobj_t *t2)
{
const sector_t *s1, *s2;
size_t pnum;
traceblocking_t tb;
// First check for trivial rejection.
if (!t1 || !t2)
return false;
I_Assert(!P_MobjWasRemoved(t1));
I_Assert(!P_MobjWasRemoved(t2));
if (!t1->subsector || !t2->subsector
|| !t1->subsector->sector || !t2->subsector->sector)
return false;
s1 = t1->subsector->sector;
s2 = t2->subsector->sector;
pnum = (s1-sectors)*numsectors + (s2-sectors);
if (rejectmatrix != NULL)
{
// Check in REJECT table.
if (rejectmatrix[pnum>>3] & (1 << (pnum&7))) // can't possibly be connected
return false;
}
// killough 11/98: shortcut for melee situations
// same subsector? obviously visible
// haleyjd 02/23/06: can't do this if there are polyobjects in the subsec
if (!t1->subsector->polyList &&
t1->subsector == t2->subsector)
return true;
validcount++;
tb.strace.dx = (tb.t2x = t2->x) - (tb.strace.x = t1->x);
tb.strace.dy = (tb.t2y = t2->y) - (tb.strace.y = t1->y);
if (t1->x > t2->x)
tb.bbox[BOXRIGHT] = t1->x, tb.bbox[BOXLEFT] = t2->x;
else
tb.bbox[BOXRIGHT] = t2->x, tb.bbox[BOXLEFT] = t1->x;
if (t1->y > t2->y)
tb.bbox[BOXTOP] = t1->y, tb.bbox[BOXBOTTOM] = t2->y;
else
tb.bbox[BOXTOP] = t2->y, tb.bbox[BOXBOTTOM] = t1->y;
tb.compareThing = t1;
// the head node is the last node output
return P_CrossBSPNodeBotTraversal((INT32)numnodes - 1, &tb);
}

View file

@ -2502,6 +2502,8 @@ static void P_ConsiderAllGone(void)
//
static void P_DeathThink(player_t *player)
{
boolean playerGone = false;
player->deltaviewheight = 0;
if (player->deadtimer < INT32_MAX)
@ -2522,7 +2524,19 @@ static void P_DeathThink(player_t *player)
K_KartPlayerHUDUpdate(player);
if (player->lives > 0 && !(player->pflags & PF_NOCONTEST) && player->deadtimer > TICRATE)
if (player->pflags & PF_NOCONTEST)
{
playerGone = true;
}
else if (player->bot == false)
{
if (G_GametypeUsesLives() == true && player->lives == 0)
{
playerGone = true;
}
}
if (playerGone == false && player->deadtimer > TICRATE)
{
player->playerstate = PST_REBORN;
}
@ -4222,9 +4236,6 @@ void P_PlayerThink(player_t *player)
ticcmd_t *cmd;
const size_t playeri = (size_t)(player - players);
player->old_drawangle = player->drawangle;
player->old_viewrollangle = player->viewrollangle;
#ifdef PARANOIA
if (!player->mo)
I_Error("p_playerthink: players[%s].mo == NULL", sizeu1(playeri));
@ -4237,6 +4248,11 @@ void P_PlayerThink(player_t *player)
player->playerstate = PST_DEAD;
}
player->old_drawangle = player->drawangle;
player->old_viewrollangle = player->viewrollangle;
player->pflags &= ~PF_HITFINISHLINE;
if (player->awayviewmobj && P_MobjWasRemoved(player->awayviewmobj))
{
P_SetTarget(&player->awayviewmobj, NULL); // remove awayviewmobj asap if invalid

View file

@ -311,11 +311,6 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum)
}
player->skincolor = newcolor = skin->prefcolor;
if (player->bot && botingame)
{
botskin = (UINT8)(skinnum + 1);
botcolor = skin->prefcolor;
}
}
#endif