mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
Use srb2::finally on bot predictions
I'm not sure exactly where the bot code leaks, other than knowing its the prediction for sure (because the prediction is the only memory bots ever allocate), but srb2::finally is cool and lets me not care :)
This commit is contained in:
parent
79c4074499
commit
bfecf45110
5 changed files with 177 additions and 171 deletions
|
|
@ -125,7 +125,7 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32
|
||||||
k_waypoint.cpp
|
k_waypoint.cpp
|
||||||
k_pathfind.c
|
k_pathfind.c
|
||||||
k_bheap.c
|
k_bheap.c
|
||||||
k_bot.c
|
k_bot.cpp
|
||||||
k_botitem.c
|
k_botitem.c
|
||||||
k_botsearch.c
|
k_botsearch.c
|
||||||
k_grandprix.c
|
k_grandprix.c
|
||||||
|
|
|
||||||
|
|
@ -4056,10 +4056,10 @@ static void Got_AddBot(UINT8 **p, INT32 playernum)
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean SV_AddWaitingPlayers(SINT8 node, UINT8 *availabilities,
|
static boolean SV_AddWaitingPlayers(SINT8 node, UINT8 *availabilities,
|
||||||
const char *name, uint8_t *key, UINT16 *pwr,
|
const char *name, uint8_t *key, UINT16 *pwr,
|
||||||
const char *name2, uint8_t *key2, UINT16 *pwr2,
|
const char *name2, uint8_t *key2, UINT16 *pwr2,
|
||||||
const char *name3, uint8_t *key3, UINT16 *pwr3,
|
const char *name3, uint8_t *key3, UINT16 *pwr3,
|
||||||
const char *name4, uint8_t *key4, UINT16 *pwr4)
|
const char *name4, uint8_t *key4, UINT16 *pwr4)
|
||||||
{
|
{
|
||||||
INT32 n, newplayernum, i;
|
INT32 n, newplayernum, i;
|
||||||
UINT8 buf[4 + MAXPLAYERNAME + PUBKEYLENGTH + MAXAVAILABILITY + sizeof(((serverplayer_t *)0)->powerlevels)];
|
UINT8 buf[4 + MAXPLAYERNAME + PUBKEYLENGTH + MAXAVAILABILITY + sizeof(((serverplayer_t *)0)->powerlevels)];
|
||||||
|
|
@ -4168,6 +4168,85 @@ const char *name4, uint8_t *key4, UINT16 *pwr4)
|
||||||
return newplayer;
|
return newplayer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*--------------------------------------------------
|
||||||
|
boolean K_AddBotFromServer(UINT8 skin, UINT8 difficulty, botStyle_e style, UINT8 *p)
|
||||||
|
|
||||||
|
See header file for description.
|
||||||
|
--------------------------------------------------*/
|
||||||
|
boolean K_AddBotFromServer(UINT8 skin, UINT8 difficulty, botStyle_e style, UINT8 *p)
|
||||||
|
{
|
||||||
|
UINT8 newplayernum = *p;
|
||||||
|
|
||||||
|
// search for a free playernum
|
||||||
|
// we can't use playeringame since it is not updated here
|
||||||
|
for (; newplayernum < MAXPLAYERS; newplayernum++)
|
||||||
|
{
|
||||||
|
UINT8 n;
|
||||||
|
|
||||||
|
for (n = 0; n < MAXNETNODES; n++)
|
||||||
|
{
|
||||||
|
if (nodetoplayer[n] == newplayernum
|
||||||
|
|| nodetoplayer2[n] == newplayernum
|
||||||
|
|| nodetoplayer3[n] == newplayernum
|
||||||
|
|| nodetoplayer4[n] == newplayernum)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n == MAXNETNODES)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; newplayernum < MAXPLAYERS; newplayernum++)
|
||||||
|
{
|
||||||
|
if (playeringame[newplayernum] == false)
|
||||||
|
{
|
||||||
|
// free player slot
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newplayernum >= MAXPLAYERS)
|
||||||
|
{
|
||||||
|
// nothing is free
|
||||||
|
*p = MAXPLAYERS;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (server)
|
||||||
|
{
|
||||||
|
UINT8 buf[4];
|
||||||
|
UINT8 *buf_p = buf;
|
||||||
|
|
||||||
|
WRITEUINT8(buf_p, newplayernum);
|
||||||
|
|
||||||
|
if (skin > numskins)
|
||||||
|
{
|
||||||
|
skin = numskins;
|
||||||
|
}
|
||||||
|
|
||||||
|
WRITEUINT8(buf_p, skin);
|
||||||
|
|
||||||
|
if (difficulty < 1)
|
||||||
|
{
|
||||||
|
difficulty = 1;
|
||||||
|
}
|
||||||
|
else if (difficulty > MAXBOTDIFFICULTY)
|
||||||
|
{
|
||||||
|
difficulty = MAXBOTDIFFICULTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
WRITEUINT8(buf_p, difficulty);
|
||||||
|
WRITEUINT8(buf_p, style);
|
||||||
|
|
||||||
|
SendNetXCmd(XD_ADDBOT, buf, buf_p - buf);
|
||||||
|
DEBFILE(va("Server added bot %d\n", newplayernum));
|
||||||
|
}
|
||||||
|
|
||||||
|
// use the next free slot (we can't put playeringame[newplayernum] = true here)
|
||||||
|
*p = newplayernum+1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void CL_AddSplitscreenPlayer(void)
|
void CL_AddSplitscreenPlayer(void)
|
||||||
{
|
{
|
||||||
if (cl_mode == CL_CONNECTED)
|
if (cl_mode == CL_CONNECTED)
|
||||||
|
|
|
||||||
|
|
@ -588,6 +588,27 @@ void SV_StartSinglePlayerServer(INT32 dogametype, boolean donetgame);
|
||||||
boolean SV_SpawnServer(void);
|
boolean SV_SpawnServer(void);
|
||||||
void SV_StopServer(void);
|
void SV_StopServer(void);
|
||||||
void SV_ResetServer(void);
|
void SV_ResetServer(void);
|
||||||
|
|
||||||
|
/*--------------------------------------------------
|
||||||
|
boolean K_AddBotFromServer(UINT8 skin, UINT8 difficulty, botStyle_e style, UINT8 *newplayernum);
|
||||||
|
|
||||||
|
Adds a new bot, using a server-sided packet sent to all clients.
|
||||||
|
Using regular K_AddBot wherever possible is better, but this is kept
|
||||||
|
as a back-up measure if this is the only option.
|
||||||
|
|
||||||
|
Input Arguments:-
|
||||||
|
skin - Skin number that the bot will use.
|
||||||
|
difficulty - Difficulty level this bot will use.
|
||||||
|
style - Bot style to spawn this bot with, see botStyle_e.
|
||||||
|
newplayernum - Pointer to the last valid player slot number.
|
||||||
|
Is a pointer so that this function can be called multiple times to add more than one bot.
|
||||||
|
|
||||||
|
Return:-
|
||||||
|
true if a bot can be added via a packet later, otherwise false.
|
||||||
|
--------------------------------------------------*/
|
||||||
|
|
||||||
|
boolean K_AddBotFromServer(UINT8 skin, UINT8 difficulty, botStyle_e style, UINT8 *p);
|
||||||
|
|
||||||
void CL_AddSplitscreenPlayer(void);
|
void CL_AddSplitscreenPlayer(void);
|
||||||
void CL_RemoveSplitscreenPlayer(UINT8 p);
|
void CL_RemoveSplitscreenPlayer(UINT8 p);
|
||||||
void CL_Reset(void);
|
void CL_Reset(void);
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,19 @@
|
||||||
// SONIC ROBO BLAST 2 KART
|
// DR. ROBOTNIK'S RING RACERS
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Copyright (C) 2018-2020 by Sally "TehRealSalt" Cochenour
|
// Copyright (C) by Sally "TehRealSalt" Cochenour
|
||||||
// Copyright (C) 2018-2020 by Kart Krew
|
// Copyright (C) by Kart Krew
|
||||||
//
|
//
|
||||||
// This program is free software distributed under the
|
// This program is free software distributed under the
|
||||||
// terms of the GNU General Public License, version 2.
|
// terms of the GNU General Public License, version 2.
|
||||||
// See the 'LICENSE' file for more details.
|
// See the 'LICENSE' file for more details.
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
/// \file k_bot.c
|
/// \file k_bot.cpp
|
||||||
/// \brief Bot logic & ticcmd generation code
|
/// \brief Bot logic & ticcmd generation code
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "cxxutil.hpp"
|
||||||
|
|
||||||
#include "doomdef.h"
|
#include "doomdef.h"
|
||||||
#include "d_player.h"
|
#include "d_player.h"
|
||||||
#include "g_game.h"
|
#include "g_game.h"
|
||||||
|
|
@ -116,85 +120,6 @@ boolean K_AddBot(UINT8 skin, UINT8 difficulty, botStyle_e style, UINT8 *p)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*--------------------------------------------------
|
|
||||||
boolean K_AddBotFromServer(UINT8 skin, UINT8 difficulty, botStyle_e style, UINT8 *p)
|
|
||||||
|
|
||||||
See header file for description.
|
|
||||||
--------------------------------------------------*/
|
|
||||||
boolean K_AddBotFromServer(UINT8 skin, UINT8 difficulty, botStyle_e style, UINT8 *p)
|
|
||||||
{
|
|
||||||
UINT8 newplayernum = *p;
|
|
||||||
|
|
||||||
// search for a free playernum
|
|
||||||
// we can't use playeringame since it is not updated here
|
|
||||||
for (; newplayernum < MAXPLAYERS; newplayernum++)
|
|
||||||
{
|
|
||||||
UINT8 n;
|
|
||||||
|
|
||||||
for (n = 0; n < MAXNETNODES; n++)
|
|
||||||
{
|
|
||||||
if (nodetoplayer[n] == newplayernum
|
|
||||||
|| nodetoplayer2[n] == newplayernum
|
|
||||||
|| nodetoplayer3[n] == newplayernum
|
|
||||||
|| nodetoplayer4[n] == newplayernum)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (n == MAXNETNODES)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (; newplayernum < MAXPLAYERS; newplayernum++)
|
|
||||||
{
|
|
||||||
if (playeringame[newplayernum] == false)
|
|
||||||
{
|
|
||||||
// free player slot
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newplayernum >= MAXPLAYERS)
|
|
||||||
{
|
|
||||||
// nothing is free
|
|
||||||
*p = MAXPLAYERS;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (server)
|
|
||||||
{
|
|
||||||
UINT8 buf[4];
|
|
||||||
UINT8 *buf_p = buf;
|
|
||||||
|
|
||||||
WRITEUINT8(buf_p, newplayernum);
|
|
||||||
|
|
||||||
if (skin > numskins)
|
|
||||||
{
|
|
||||||
skin = numskins;
|
|
||||||
}
|
|
||||||
|
|
||||||
WRITEUINT8(buf_p, skin);
|
|
||||||
|
|
||||||
if (difficulty < 1)
|
|
||||||
{
|
|
||||||
difficulty = 1;
|
|
||||||
}
|
|
||||||
else if (difficulty > MAXBOTDIFFICULTY)
|
|
||||||
{
|
|
||||||
difficulty = MAXBOTDIFFICULTY;
|
|
||||||
}
|
|
||||||
|
|
||||||
WRITEUINT8(buf_p, difficulty);
|
|
||||||
WRITEUINT8(buf_p, style);
|
|
||||||
|
|
||||||
SendNetXCmd(XD_ADDBOT, buf, buf_p - buf);
|
|
||||||
DEBFILE(va("Server added bot %d\n", newplayernum));
|
|
||||||
}
|
|
||||||
|
|
||||||
// use the next free slot (we can't put playeringame[newplayernum] = true here)
|
|
||||||
*p = newplayernum+1;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*--------------------------------------------------
|
/*--------------------------------------------------
|
||||||
void K_UpdateMatchRaceBots(void)
|
void K_UpdateMatchRaceBots(void)
|
||||||
|
|
||||||
|
|
@ -204,7 +129,7 @@ void K_UpdateMatchRaceBots(void)
|
||||||
{
|
{
|
||||||
const UINT8 defaultbotskin = R_BotDefaultSkin();
|
const UINT8 defaultbotskin = R_BotDefaultSkin();
|
||||||
const UINT8 difficulty = cv_kartbot.value;
|
const UINT8 difficulty = cv_kartbot.value;
|
||||||
UINT8 pmax = min((dedicated ? MAXPLAYERS-1 : MAXPLAYERS), cv_maxconnections.value);
|
UINT8 pmax = std::min<UINT8>((dedicated ? MAXPLAYERS-1 : MAXPLAYERS), static_cast<UINT8>(cv_maxconnections.value));
|
||||||
UINT8 numplayers = 0;
|
UINT8 numplayers = 0;
|
||||||
UINT8 numbots = 0;
|
UINT8 numbots = 0;
|
||||||
UINT8 numwaiting = 0;
|
UINT8 numwaiting = 0;
|
||||||
|
|
@ -222,7 +147,7 @@ void K_UpdateMatchRaceBots(void)
|
||||||
|
|
||||||
if (cv_maxplayers.value > 0)
|
if (cv_maxplayers.value > 0)
|
||||||
{
|
{
|
||||||
pmax = min(pmax, cv_maxplayers.value);
|
pmax = std::min<UINT8>(pmax, static_cast<UINT8>(cv_maxplayers.value));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < MAXPLAYERS; i++)
|
for (i = 0; i < MAXPLAYERS; i++)
|
||||||
|
|
@ -429,7 +354,7 @@ static fixed_t K_BotSpeedScaled(player_t *player, fixed_t speed)
|
||||||
result = FixedMul(result, moveFactor);
|
result = FixedMul(result, moveFactor);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (player->mo->standingslope != NULL)
|
if (player->mo->standingslope != nullptr)
|
||||||
{
|
{
|
||||||
const pslope_t *slope = player->mo->standingslope;
|
const pslope_t *slope = player->mo->standingslope;
|
||||||
|
|
||||||
|
|
@ -462,7 +387,7 @@ static fixed_t K_BotSpeedScaled(player_t *player, fixed_t speed)
|
||||||
mo - The bot player's mobj.
|
mo - The bot player's mobj.
|
||||||
|
|
||||||
Return:-
|
Return:-
|
||||||
Linedef of the bot controller. NULL if it doesn't exist.
|
Linedef of the bot controller. nullptr if it doesn't exist.
|
||||||
--------------------------------------------------*/
|
--------------------------------------------------*/
|
||||||
static line_t *K_FindBotController(mobj_t *mo)
|
static line_t *K_FindBotController(mobj_t *mo)
|
||||||
{
|
{
|
||||||
|
|
@ -471,7 +396,7 @@ static line_t *K_FindBotController(mobj_t *mo)
|
||||||
INT16 lineNum = -1;
|
INT16 lineNum = -1;
|
||||||
mtag_t tag;
|
mtag_t tag;
|
||||||
|
|
||||||
I_Assert(mo != NULL);
|
I_Assert(mo != nullptr);
|
||||||
I_Assert(!P_MobjWasRemoved(mo));
|
I_Assert(!P_MobjWasRemoved(mo));
|
||||||
|
|
||||||
for (node = mo->touching_sectorlist; node; node = node->m_sectorlist_next)
|
for (node = mo->touching_sectorlist; node; node = node->m_sectorlist_next)
|
||||||
|
|
@ -491,7 +416,7 @@ static line_t *K_FindBotController(mobj_t *mo)
|
||||||
|
|
||||||
for (rover = node->m_sector->ffloors; rover; rover = rover->next)
|
for (rover = node->m_sector->ffloors; rover; rover = rover->next)
|
||||||
{
|
{
|
||||||
sector_t *rs = NULL;
|
sector_t *rs = nullptr;
|
||||||
|
|
||||||
if (!(rover->fofflags & FOF_EXISTS))
|
if (!(rover->fofflags & FOF_EXISTS))
|
||||||
{
|
{
|
||||||
|
|
@ -520,7 +445,7 @@ static line_t *K_FindBotController(mobj_t *mo)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return NULL;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -595,7 +520,7 @@ fixed_t K_BotRubberband(player_t *player)
|
||||||
const fixed_t rubbermax = Easing_Linear(difficultyEase, FRACUNIT, FRACUNIT * 165 / 100);
|
const fixed_t rubbermax = Easing_Linear(difficultyEase, FRACUNIT, FRACUNIT * 165 / 100);
|
||||||
|
|
||||||
fixed_t rubberband = FRACUNIT >> 1;
|
fixed_t rubberband = FRACUNIT >> 1;
|
||||||
player_t *firstplace = NULL;
|
player_t *firstplace = nullptr;
|
||||||
size_t i = SIZE_MAX;
|
size_t i = SIZE_MAX;
|
||||||
|
|
||||||
if (player->exiting)
|
if (player->exiting)
|
||||||
|
|
@ -608,7 +533,7 @@ fixed_t K_BotRubberband(player_t *player)
|
||||||
{
|
{
|
||||||
const line_t *botController = &lines[player->botvars.controller];
|
const line_t *botController = &lines[player->botvars.controller];
|
||||||
|
|
||||||
if (botController != NULL)
|
if (botController != nullptr)
|
||||||
{
|
{
|
||||||
// Disable rubberbanding
|
// Disable rubberbanding
|
||||||
if (botController->args[1] & TMBOT_NORUBBERBAND)
|
if (botController->args[1] & TMBOT_NORUBBERBAND)
|
||||||
|
|
@ -633,18 +558,18 @@ fixed_t K_BotRubberband(player_t *player)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (firstplace == NULL || players[i].distancetofinish < firstplace->distancetofinish)
|
if (firstplace == nullptr || players[i].distancetofinish < firstplace->distancetofinish)
|
||||||
{
|
{
|
||||||
firstplace = &players[i];
|
firstplace = &players[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (firstplace != NULL)
|
if (firstplace != nullptr)
|
||||||
{
|
{
|
||||||
// Lv. 1: 5120 units
|
// Lv. 1: 5120 units
|
||||||
// Lv. 9: 320 units
|
// Lv. 9: 320 units
|
||||||
const fixed_t spacing = FixedDiv(
|
const fixed_t spacing = FixedDiv(
|
||||||
max(
|
std::max<fixed_t>(
|
||||||
80 * mapobjectscale,
|
80 * mapobjectscale,
|
||||||
Easing_Linear(difficultyEase, 5120 * mapobjectscale, 320 * mapobjectscale)
|
Easing_Linear(difficultyEase, 5120 * mapobjectscale, 320 * mapobjectscale)
|
||||||
),
|
),
|
||||||
|
|
@ -759,7 +684,7 @@ static void K_GetBotWaypointRadius(waypoint_t *const waypoint, fixed_t *smallest
|
||||||
waypoint->mobj->x, waypoint->mobj->y
|
waypoint->mobj->x, waypoint->mobj->y
|
||||||
);
|
);
|
||||||
|
|
||||||
delta = max(delta, AngleDelta(nextAngle, prevAngle));
|
delta = std::max<angle_t>(delta, AngleDelta(nextAngle, prevAngle));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -771,13 +696,13 @@ static void K_GetBotWaypointRadius(waypoint_t *const waypoint, fixed_t *smallest
|
||||||
reduce = FixedDiv(delta, maxDelta);
|
reduce = FixedDiv(delta, maxDelta);
|
||||||
reduce = FRACUNIT + FixedMul(reduce, maxReduce - FRACUNIT);
|
reduce = FRACUNIT + FixedMul(reduce, maxReduce - FRACUNIT);
|
||||||
|
|
||||||
*smallestRadius = min(*smallestRadius, radius);
|
*smallestRadius = std::min<fixed_t>(*smallestRadius, radius);
|
||||||
*smallestScaled = min(*smallestScaled, FixedMul(radius, reduce));
|
*smallestScaled = std::min<fixed_t>(*smallestScaled, FixedMul(radius, reduce));
|
||||||
}
|
}
|
||||||
|
|
||||||
static fixed_t K_ScaleWPDistWithSlope(fixed_t disttonext, angle_t angletonext, const pslope_t *slope, SINT8 flip)
|
static fixed_t K_ScaleWPDistWithSlope(fixed_t disttonext, angle_t angletonext, const pslope_t *slope, SINT8 flip)
|
||||||
{
|
{
|
||||||
if (slope == NULL)
|
if (slope == nullptr)
|
||||||
{
|
{
|
||||||
return disttonext;
|
return disttonext;
|
||||||
}
|
}
|
||||||
|
|
@ -824,12 +749,12 @@ static botprediction_t *K_CreateBotPrediction(player_t *player)
|
||||||
|
|
||||||
const INT16 handling = K_GetKartTurnValue(player, KART_FULLTURN) / jankDiv; // Reduce prediction based on how fast you can turn
|
const INT16 handling = K_GetKartTurnValue(player, KART_FULLTURN) / jankDiv; // Reduce prediction based on how fast you can turn
|
||||||
|
|
||||||
const tic_t futuresight = (TICRATE * KART_FULLTURN) / max(1, handling); // How far ahead into the future to try and predict
|
const tic_t futuresight = (TICRATE * KART_FULLTURN) / std::max<INT16>(1, handling); // How far ahead into the future to try and predict
|
||||||
const fixed_t speed = K_BotSpeedScaled(player, P_AproxDistance(player->mo->momx, player->mo->momy));
|
const fixed_t speed = K_BotSpeedScaled(player, P_AproxDistance(player->mo->momx, player->mo->momy));
|
||||||
|
|
||||||
const INT32 startDist = 0; //(DEFAULT_WAYPOINT_RADIUS * mapobjectscale) / FRACUNIT;
|
const INT32 startDist = 0; //(DEFAULT_WAYPOINT_RADIUS * mapobjectscale) / FRACUNIT;
|
||||||
const INT32 maxDist = (DEFAULT_WAYPOINT_RADIUS * 3 * mapobjectscale) / FRACUNIT; // This function gets very laggy when it goes far distances, and going too far isn't very helpful anyway.
|
const INT32 maxDist = (DEFAULT_WAYPOINT_RADIUS * 3 * mapobjectscale) / FRACUNIT; // This function gets very laggy when it goes far distances, and going too far isn't very helpful anyway.
|
||||||
const INT32 distance = min(((speed / FRACUNIT) * (INT32)futuresight) + startDist, maxDist);
|
const INT32 distance = std::min<INT32>(((speed / FRACUNIT) * static_cast<INT32>(futuresight)) + startDist, maxDist);
|
||||||
|
|
||||||
// Halves radius when encountering a wall on your way to your destination.
|
// Halves radius when encountering a wall on your way to your destination.
|
||||||
fixed_t radReduce = FRACUNIT;
|
fixed_t radReduce = FRACUNIT;
|
||||||
|
|
@ -851,16 +776,16 @@ static botprediction_t *K_CreateBotPrediction(player_t *player)
|
||||||
boolean pathfindsuccess = false;
|
boolean pathfindsuccess = false;
|
||||||
path_t pathtofinish = {0};
|
path_t pathtofinish = {0};
|
||||||
|
|
||||||
botprediction_t *predict = NULL;
|
botprediction_t *predict = nullptr;
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
if (wp == NULL || P_MobjWasRemoved(wp->mobj) == true)
|
if (wp == nullptr || P_MobjWasRemoved(wp->mobj) == true)
|
||||||
{
|
{
|
||||||
// Can't do any of this if we don't have a waypoint.
|
// Can't do any of this if we don't have a waypoint.
|
||||||
return NULL;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
predict = Z_Calloc(sizeof(botprediction_t), PU_STATIC, NULL);
|
predict = static_cast<botprediction_t *>(Z_Calloc(sizeof(botprediction_t), PU_LEVEL, nullptr));
|
||||||
|
|
||||||
// Init defaults in case of pathfind failure
|
// Init defaults in case of pathfind failure
|
||||||
angletonext = R_PointToAngle2(prevwpmobj->x, prevwpmobj->y, wp->mobj->x, wp->mobj->y);
|
angletonext = R_PointToAngle2(prevwpmobj->x, prevwpmobj->y, wp->mobj->x, wp->mobj->y);
|
||||||
|
|
@ -927,8 +852,8 @@ static botprediction_t *K_CreateBotPrediction(player_t *player)
|
||||||
if (distanceleft > 0)
|
if (distanceleft > 0)
|
||||||
{
|
{
|
||||||
// Scaled with the leftover anglemul!
|
// Scaled with the leftover anglemul!
|
||||||
predict->x += P_ReturnThrustX(NULL, angletonext, min(disttonext, distanceleft) * FRACUNIT);
|
predict->x += P_ReturnThrustX(nullptr, angletonext, std::min<fixed_t>(disttonext, distanceleft) * FRACUNIT);
|
||||||
predict->y += P_ReturnThrustY(NULL, angletonext, min(disttonext, distanceleft) * FRACUNIT);
|
predict->y += P_ReturnThrustY(nullptr, angletonext, std::min<fixed_t>(disttonext, distanceleft) * FRACUNIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
ps_bots[player - players].prediction += I_GetPreciseTime() - time;
|
ps_bots[player - players].prediction += I_GetPreciseTime() - time;
|
||||||
|
|
@ -976,7 +901,7 @@ static UINT8 K_TrySpindash(player_t *player)
|
||||||
{
|
{
|
||||||
INT32 boosthold = starttime - K_GetSpindashChargeTime(player);
|
INT32 boosthold = starttime - K_GetSpindashChargeTime(player);
|
||||||
|
|
||||||
boosthold -= (DIFFICULTBOT - min(DIFFICULTBOT, player->botvars.difficulty)) * difficultyModifier;
|
boosthold -= (DIFFICULTBOT - std::min<UINT8>(DIFFICULTBOT, player->botvars.difficulty)) * difficultyModifier;
|
||||||
|
|
||||||
if (leveltime >= (unsigned)boosthold)
|
if (leveltime >= (unsigned)boosthold)
|
||||||
{
|
{
|
||||||
|
|
@ -996,7 +921,7 @@ static UINT8 K_TrySpindash(player_t *player)
|
||||||
|
|
||||||
// Release quicker the higher the difficulty is.
|
// Release quicker the higher the difficulty is.
|
||||||
// Sounds counter-productive, but that's actually the best strategy after the race has started.
|
// Sounds counter-productive, but that's actually the best strategy after the race has started.
|
||||||
chargingPoint -= min(DIFFICULTBOT, player->botvars.difficulty) * difficultyModifier;
|
chargingPoint -= std::min<UINT8>(DIFFICULTBOT, player->botvars.difficulty) * difficultyModifier;
|
||||||
|
|
||||||
if (player->spindash > chargingPoint)
|
if (player->spindash > chargingPoint)
|
||||||
{
|
{
|
||||||
|
|
@ -1022,7 +947,7 @@ static UINT8 K_TrySpindash(player_t *player)
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
|
|
||||||
if (K_SlopeResistance(player) == false && player->mo->standingslope != NULL)
|
if (K_SlopeResistance(player) == false && player->mo->standingslope != nullptr)
|
||||||
{
|
{
|
||||||
const pslope_t *slope = player->mo->standingslope;
|
const pslope_t *slope = player->mo->standingslope;
|
||||||
|
|
||||||
|
|
@ -1120,13 +1045,13 @@ static boolean K_TryRingShooter(player_t *player)
|
||||||
--------------------------------------------------*/
|
--------------------------------------------------*/
|
||||||
static void K_DrawPredictionDebug(botprediction_t *predict, player_t *player)
|
static void K_DrawPredictionDebug(botprediction_t *predict, player_t *player)
|
||||||
{
|
{
|
||||||
mobj_t *debugMobj = NULL;
|
mobj_t *debugMobj = nullptr;
|
||||||
angle_t sideAngle = ANGLE_MAX;
|
angle_t sideAngle = ANGLE_MAX;
|
||||||
UINT8 i = UINT8_MAX;
|
UINT8 i = UINT8_MAX;
|
||||||
|
|
||||||
I_Assert(predict != NULL);
|
I_Assert(predict != nullptr);
|
||||||
I_Assert(player != NULL);
|
I_Assert(player != nullptr);
|
||||||
I_Assert(player->mo != NULL && P_MobjWasRemoved(player->mo) == false);
|
I_Assert(player->mo != nullptr && P_MobjWasRemoved(player->mo) == false);
|
||||||
|
|
||||||
sideAngle = player->mo->angle + ANGLE_90;
|
sideAngle = player->mo->angle + ANGLE_90;
|
||||||
|
|
||||||
|
|
@ -1143,7 +1068,7 @@ static void K_DrawPredictionDebug(botprediction_t *predict, player_t *player)
|
||||||
|
|
||||||
for (i = 0; i < 2; i++)
|
for (i = 0; i < 2; i++)
|
||||||
{
|
{
|
||||||
mobj_t *radiusMobj = NULL;
|
mobj_t *radiusMobj = nullptr;
|
||||||
fixed_t radiusX = predict->x, radiusY = predict->y;
|
fixed_t radiusX = predict->x, radiusY = predict->y;
|
||||||
|
|
||||||
if (i & 1)
|
if (i & 1)
|
||||||
|
|
@ -1186,7 +1111,7 @@ static void K_DrawPredictionDebug(botprediction_t *predict, player_t *player)
|
||||||
static void K_BotTrick(player_t *player, ticcmd_t *cmd, const line_t *botController)
|
static void K_BotTrick(player_t *player, ticcmd_t *cmd, const line_t *botController)
|
||||||
{
|
{
|
||||||
// Trick panel state -- do nothing until a controller line is found, in which case do a trick.
|
// Trick panel state -- do nothing until a controller line is found, in which case do a trick.
|
||||||
if (botController == NULL)
|
if (botController == nullptr)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -1232,7 +1157,7 @@ static angle_t K_BotSmoothLanding(player_t *player, angle_t destangle)
|
||||||
angle_t newAngle = destangle;
|
angle_t newAngle = destangle;
|
||||||
boolean air = !P_IsObjectOnGround(player->mo);
|
boolean air = !P_IsObjectOnGround(player->mo);
|
||||||
angle_t steepVal = air ? STUMBLE_STEEP_VAL_AIR : STUMBLE_STEEP_VAL;
|
angle_t steepVal = air ? STUMBLE_STEEP_VAL_AIR : STUMBLE_STEEP_VAL;
|
||||||
angle_t slopeSteep = max(AngleDelta(player->mo->pitch, 0), AngleDelta(player->mo->roll, 0));
|
angle_t slopeSteep = std::max<angle_t>(AngleDelta(player->mo->pitch, 0), AngleDelta(player->mo->roll, 0));
|
||||||
|
|
||||||
if (slopeSteep > steepVal)
|
if (slopeSteep > steepVal)
|
||||||
{
|
{
|
||||||
|
|
@ -1284,7 +1209,7 @@ static INT32 K_HandleBotTrack(player_t *player, ticcmd_t *cmd, botprediction_t *
|
||||||
angle_t moveangle;
|
angle_t moveangle;
|
||||||
INT32 anglediff;
|
INT32 anglediff;
|
||||||
|
|
||||||
I_Assert(predict != NULL);
|
I_Assert(predict != nullptr);
|
||||||
|
|
||||||
destangle = K_BotSmoothLanding(player, destangle);
|
destangle = K_BotSmoothLanding(player, destangle);
|
||||||
|
|
||||||
|
|
@ -1329,7 +1254,7 @@ static INT32 K_HandleBotTrack(player_t *player, ticcmd_t *cmd, botprediction_t *
|
||||||
// This makes predictions into turns a little nicer
|
// This makes predictions into turns a little nicer
|
||||||
// Facing 90 degrees away from the predicted point gives you 0 radius
|
// Facing 90 degrees away from the predicted point gives you 0 radius
|
||||||
rad = FixedMul(rad,
|
rad = FixedMul(rad,
|
||||||
FixedDiv(max(0, ANGLE_90 - anglediff), ANGLE_90)
|
FixedDiv(std::max<angle_t>(0, ANGLE_90 - anglediff), ANGLE_90)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Become more precise the slower you're moving
|
// Become more precise the slower you're moving
|
||||||
|
|
@ -1384,21 +1309,21 @@ static INT32 K_HandleBotReverse(player_t *player, ticcmd_t *cmd, botprediction_t
|
||||||
angle_t moveangle, angle;
|
angle_t moveangle, angle;
|
||||||
INT16 anglediff, momdiff;
|
INT16 anglediff, momdiff;
|
||||||
|
|
||||||
if (predict != NULL)
|
if (predict != nullptr)
|
||||||
{
|
{
|
||||||
// TODO: Should we reverse through bot controllers?
|
// TODO: Should we reverse through bot controllers?
|
||||||
return K_HandleBotTrack(player, cmd, predict, destangle);
|
return K_HandleBotTrack(player, cmd, predict, destangle);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (player->nextwaypoint == NULL
|
if (player->nextwaypoint == nullptr
|
||||||
|| player->nextwaypoint->mobj == NULL
|
|| player->nextwaypoint->mobj == nullptr
|
||||||
|| P_MobjWasRemoved(player->nextwaypoint->mobj))
|
|| P_MobjWasRemoved(player->nextwaypoint->mobj))
|
||||||
{
|
{
|
||||||
// No data available...
|
// No data available...
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((player->nextwaypoint->prevwaypoints != NULL)
|
if ((player->nextwaypoint->prevwaypoints != nullptr)
|
||||||
&& (player->nextwaypoint->numprevwaypoints > 0U))
|
&& (player->nextwaypoint->numprevwaypoints > 0U))
|
||||||
{
|
{
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
@ -1470,7 +1395,7 @@ static INT32 K_HandleBotReverse(player_t *player, ticcmd_t *cmd, botprediction_t
|
||||||
{
|
{
|
||||||
fixed_t slopeMul = FRACUNIT;
|
fixed_t slopeMul = FRACUNIT;
|
||||||
|
|
||||||
if (player->mo->standingslope != NULL)
|
if (player->mo->standingslope != nullptr)
|
||||||
{
|
{
|
||||||
const pslope_t *slope = player->mo->standingslope;
|
const pslope_t *slope = player->mo->standingslope;
|
||||||
|
|
||||||
|
|
@ -1543,7 +1468,7 @@ static void K_BotPodiumTurning(player_t *player, ticcmd_t *cmd)
|
||||||
--------------------------------------------------*/
|
--------------------------------------------------*/
|
||||||
static void K_BuildBotPodiumTiccmd(player_t *player, ticcmd_t *cmd)
|
static void K_BuildBotPodiumTiccmd(player_t *player, ticcmd_t *cmd)
|
||||||
{
|
{
|
||||||
if (player->currentwaypoint == NULL)
|
if (player->currentwaypoint == nullptr)
|
||||||
{
|
{
|
||||||
// We've reached the end of our path.
|
// We've reached the end of our path.
|
||||||
// Simply stop moving.
|
// Simply stop moving.
|
||||||
|
|
@ -1573,12 +1498,15 @@ static void K_BuildBotPodiumTiccmd(player_t *player, ticcmd_t *cmd)
|
||||||
static void K_BuildBotTiccmdNormal(player_t *player, ticcmd_t *cmd)
|
static void K_BuildBotTiccmdNormal(player_t *player, ticcmd_t *cmd)
|
||||||
{
|
{
|
||||||
precise_t t = 0;
|
precise_t t = 0;
|
||||||
botprediction_t *predict = NULL;
|
|
||||||
|
botprediction_t *predict = nullptr;
|
||||||
|
auto predict_finally = srb2::finally([predict]() { Z_Free(predict); });
|
||||||
|
|
||||||
boolean trySpindash = true;
|
boolean trySpindash = true;
|
||||||
angle_t destangle = 0;
|
angle_t destangle = 0;
|
||||||
UINT8 spindash = 0;
|
UINT8 spindash = 0;
|
||||||
INT32 turnamt = 0;
|
INT32 turnamt = 0;
|
||||||
const line_t *botController = player->botvars.controller != UINT16_MAX ? &lines[player->botvars.controller] : NULL;
|
const line_t *botController = player->botvars.controller != UINT16_MAX ? &lines[player->botvars.controller] : nullptr;
|
||||||
|
|
||||||
if (!(gametyperules & GTR_BOTS) // No bot behaviors
|
if (!(gametyperules & GTR_BOTS) // No bot behaviors
|
||||||
|| K_GetNumWaypoints() == 0 // No waypoints
|
|| K_GetNumWaypoints() == 0 // No waypoints
|
||||||
|
|
@ -1618,7 +1546,7 @@ static void K_BuildBotTiccmdNormal(player_t *player, ticcmd_t *cmd)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (botController != NULL && (botController->args[1] & TMBOT_NOCONTROL))
|
if (botController != nullptr && (botController->args[1] & TMBOT_NOCONTROL))
|
||||||
{
|
{
|
||||||
// Disable bot controls entirely.
|
// Disable bot controls entirely.
|
||||||
return;
|
return;
|
||||||
|
|
@ -1626,7 +1554,7 @@ static void K_BuildBotTiccmdNormal(player_t *player, ticcmd_t *cmd)
|
||||||
|
|
||||||
destangle = player->mo->angle;
|
destangle = player->mo->angle;
|
||||||
|
|
||||||
if (botController != NULL && (botController->args[1] & TMBOT_FORCEDIR))
|
if (botController != nullptr && (botController->args[1] & TMBOT_FORCEDIR))
|
||||||
{
|
{
|
||||||
const fixed_t dist = DEFAULT_WAYPOINT_RADIUS * player->mo->scale;
|
const fixed_t dist = DEFAULT_WAYPOINT_RADIUS * player->mo->scale;
|
||||||
|
|
||||||
|
|
@ -1634,14 +1562,14 @@ static void K_BuildBotTiccmdNormal(player_t *player, ticcmd_t *cmd)
|
||||||
destangle = FixedAngle(botController->args[2] * FRACUNIT);
|
destangle = FixedAngle(botController->args[2] * FRACUNIT);
|
||||||
|
|
||||||
// Overwritten prediction
|
// Overwritten prediction
|
||||||
predict = Z_Calloc(sizeof(botprediction_t), PU_STATIC, NULL);
|
predict = static_cast<botprediction_t *>(Z_Calloc(sizeof(botprediction_t), PU_STATIC, nullptr));
|
||||||
|
|
||||||
predict->x = player->mo->x + FixedMul(dist, FINECOSINE(destangle >> ANGLETOFINESHIFT));
|
predict->x = player->mo->x + FixedMul(dist, FINECOSINE(destangle >> ANGLETOFINESHIFT));
|
||||||
predict->y = player->mo->y + FixedMul(dist, FINESINE(destangle >> ANGLETOFINESHIFT));
|
predict->y = player->mo->y + FixedMul(dist, FINESINE(destangle >> ANGLETOFINESHIFT));
|
||||||
predict->radius = (DEFAULT_WAYPOINT_RADIUS / 4) * mapobjectscale;
|
predict->radius = (DEFAULT_WAYPOINT_RADIUS / 4) * mapobjectscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (leveltime <= starttime && finishBeamLine != NULL)
|
if (leveltime <= starttime && finishBeamLine != nullptr)
|
||||||
{
|
{
|
||||||
// Handle POSITION!!
|
// Handle POSITION!!
|
||||||
const fixed_t distBase = 480*mapobjectscale;
|
const fixed_t distBase = 480*mapobjectscale;
|
||||||
|
|
@ -1684,13 +1612,13 @@ static void K_BuildBotTiccmdNormal(player_t *player, ticcmd_t *cmd)
|
||||||
if (bullyTurn == INT32_MAX)
|
if (bullyTurn == INT32_MAX)
|
||||||
{
|
{
|
||||||
// No one to bully, just go for a spindash as anyone.
|
// No one to bully, just go for a spindash as anyone.
|
||||||
if (predict == NULL)
|
if (predict == nullptr)
|
||||||
{
|
{
|
||||||
// Create a prediction.
|
// Create a prediction.
|
||||||
predict = K_CreateBotPrediction(player);
|
predict = K_CreateBotPrediction(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (predict != NULL)
|
if (predict != nullptr)
|
||||||
{
|
{
|
||||||
K_NudgePredictionTowardsObjects(predict, player);
|
K_NudgePredictionTowardsObjects(predict, player);
|
||||||
destangle = R_PointToAngle2(player->mo->x, player->mo->y, predict->x, predict->y);
|
destangle = R_PointToAngle2(player->mo->x, player->mo->y, predict->x, predict->y);
|
||||||
|
|
@ -1716,13 +1644,13 @@ static void K_BuildBotTiccmdNormal(player_t *player, ticcmd_t *cmd)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Too far away, we need to just drive up.
|
// Too far away, we need to just drive up.
|
||||||
if (predict == NULL)
|
if (predict == nullptr)
|
||||||
{
|
{
|
||||||
// Create a prediction.
|
// Create a prediction.
|
||||||
predict = K_CreateBotPrediction(player);
|
predict = K_CreateBotPrediction(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (predict != NULL)
|
if (predict != nullptr)
|
||||||
{
|
{
|
||||||
K_NudgePredictionTowardsObjects(predict, player);
|
K_NudgePredictionTowardsObjects(predict, player);
|
||||||
destangle = R_PointToAngle2(player->mo->x, player->mo->y, predict->x, predict->y);
|
destangle = R_PointToAngle2(player->mo->x, player->mo->y, predict->x, predict->y);
|
||||||
|
|
@ -1733,13 +1661,13 @@ static void K_BuildBotTiccmdNormal(player_t *player, ticcmd_t *cmd)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Handle steering towards waypoints!
|
// Handle steering towards waypoints!
|
||||||
if (predict == NULL)
|
if (predict == nullptr)
|
||||||
{
|
{
|
||||||
// Create a prediction.
|
// Create a prediction.
|
||||||
predict = K_CreateBotPrediction(player);
|
predict = K_CreateBotPrediction(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (predict != NULL)
|
if (predict != nullptr)
|
||||||
{
|
{
|
||||||
K_NudgePredictionTowardsObjects(predict, player);
|
K_NudgePredictionTowardsObjects(predict, player);
|
||||||
destangle = R_PointToAngle2(player->mo->x, player->mo->y, predict->x, predict->y);
|
destangle = R_PointToAngle2(player->mo->x, player->mo->y, predict->x, predict->y);
|
||||||
|
|
@ -1821,14 +1749,12 @@ static void K_BuildBotTiccmdNormal(player_t *player, ticcmd_t *cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free the prediction we made earlier
|
// Free the prediction we made earlier
|
||||||
if (predict != NULL)
|
if (predict != nullptr)
|
||||||
{
|
{
|
||||||
if (cv_kartdebugbots.value != 0 && player - players == displayplayers[0])
|
if (cv_kartdebugbots.value != 0 && player - players == displayplayers[0])
|
||||||
{
|
{
|
||||||
K_DrawPredictionDebug(predict, player);
|
K_DrawPredictionDebug(predict, player);
|
||||||
}
|
}
|
||||||
|
|
||||||
Z_Free(predict);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1842,7 +1768,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd)
|
||||||
// Remove any existing controls
|
// Remove any existing controls
|
||||||
memset(cmd, 0, sizeof(ticcmd_t));
|
memset(cmd, 0, sizeof(ticcmd_t));
|
||||||
|
|
||||||
if (player->mo == NULL
|
if (player->mo == nullptr
|
||||||
|| player->spectator == true
|
|| player->spectator == true
|
||||||
|| G_GamestateUsesLevel() == false)
|
|| G_GamestateUsesLevel() == false)
|
||||||
{
|
{
|
||||||
30
src/k_bot.h
30
src/k_bot.h
|
|
@ -1,7 +1,7 @@
|
||||||
// SONIC ROBO BLAST 2 KART
|
// DR. ROBOTNIK'S RING RACERS
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Copyright (C) 2018-2020 by Sally "TehRealSalt" Cochenour
|
// Copyright (C) by Sally "TehRealSalt" Cochenour
|
||||||
// Copyright (C) 2018-2020 by Kart Krew
|
// Copyright (C) by Kart Krew
|
||||||
//
|
//
|
||||||
// This program is free software distributed under the
|
// This program is free software distributed under the
|
||||||
// terms of the GNU General Public License, version 2.
|
// terms of the GNU General Public License, version 2.
|
||||||
|
|
@ -45,7 +45,8 @@ extern "C" {
|
||||||
#define BOT_ITEM_DECISION_TIME (2*TICRATE)
|
#define BOT_ITEM_DECISION_TIME (2*TICRATE)
|
||||||
|
|
||||||
// Point for bots to aim for
|
// Point for bots to aim for
|
||||||
struct botprediction_t {
|
struct botprediction_t
|
||||||
|
{
|
||||||
fixed_t x, y;
|
fixed_t x, y;
|
||||||
fixed_t radius, baseRadius;
|
fixed_t radius, baseRadius;
|
||||||
};
|
};
|
||||||
|
|
@ -180,27 +181,6 @@ boolean K_AddBot(UINT8 skin, UINT8 difficulty, botStyle_e style, UINT8 *p);
|
||||||
void K_SetBot(UINT8 newplayernum, UINT8 skinnum, UINT8 difficulty, botStyle_e style);
|
void K_SetBot(UINT8 newplayernum, UINT8 skinnum, UINT8 difficulty, botStyle_e style);
|
||||||
|
|
||||||
|
|
||||||
/*--------------------------------------------------
|
|
||||||
boolean K_AddBotFromServer(UINT8 skin, UINT8 difficulty, botStyle_e style, UINT8 *newplayernum);
|
|
||||||
|
|
||||||
Adds a new bot, using a server-sided packet sent to all clients.
|
|
||||||
Using regular K_AddBot wherever possible is better, but this is kept
|
|
||||||
as a back-up measure if this is the only option.
|
|
||||||
|
|
||||||
Input Arguments:-
|
|
||||||
skin - Skin number that the bot will use.
|
|
||||||
difficulty - Difficulty level this bot will use.
|
|
||||||
style - Bot style to spawn this bot with, see botStyle_e.
|
|
||||||
newplayernum - Pointer to the last valid player slot number.
|
|
||||||
Is a pointer so that this function can be called multiple times to add more than one bot.
|
|
||||||
|
|
||||||
Return:-
|
|
||||||
true if a bot can be added via a packet later, otherwise false.
|
|
||||||
--------------------------------------------------*/
|
|
||||||
|
|
||||||
boolean K_AddBotFromServer(UINT8 skin, UINT8 difficulty, botStyle_e style, UINT8 *p);
|
|
||||||
|
|
||||||
|
|
||||||
/*--------------------------------------------------
|
/*--------------------------------------------------
|
||||||
void K_UpdateMatchRaceBots(void);
|
void K_UpdateMatchRaceBots(void);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue