Merge pull request 'Bot Refactor' (#82) from botrefactor into blankart-dev

Reviewed-on: https://codeberg.org/NepDisk/blankart/pulls/82
This commit is contained in:
NepDisk 2025-09-01 04:58:37 +02:00
commit 2a1d6396ab
33 changed files with 1752 additions and 1335 deletions

View file

@ -109,6 +109,7 @@ lua_maplib.c
lua_taglib.c
lua_polyobjlib.c
lua_blockmaplib.c
lua_botvarslib.c
lua_hudlib.c
lua_hudlib_drawlist.c
lua_followerlib.c

View file

@ -5638,8 +5638,6 @@ static void SV_Maketic(void)
// Moved here so bots and packetloss indication doesn't break....
G_MoveTiccmd(netcmds[maketic % BACKUPTICS], playercmds, MAXPLAYERS);
PS_ResetBotInfo();
for (i = 0; i < MAXPLAYERS; i++)
{
packetloss[i][maketic%PACKETMEASUREWINDOW] = false;
@ -5649,13 +5647,7 @@ static void SV_Maketic(void)
if (K_PlayerUsesBotMovement(&players[i]))
{
const precise_t t = I_GetPreciseTime();
K_BuildBotTiccmd(&players[i], &netcmds[maketic%BACKUPTICS][i]);
ps_bots[i].isBot = true;
ps_bots[i].total = I_GetPreciseTime() - t;
ps_botticcmd_time += ps_bots[i].total;
continue;
}

View file

@ -679,10 +679,6 @@ consvar_t cv_schedule = CVAR_INIT ("schedule", "On", CV_NETVAR|CV_CALL, CV_OnOff
consvar_t cv_automate = CVAR_INIT ("automate", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_test1 = CVAR_INIT ("test1", "200", CV_NETVAR|CV_FLOAT, CV_Signed, NULL);
consvar_t cv_test2 = CVAR_INIT ("test2", "40", CV_NETVAR|CV_FLOAT, CV_Signed, NULL);
consvar_t cv_test3 = CVAR_INIT ("test3", "18", CV_NETVAR|CV_FLOAT, CV_Signed, NULL);
char timedemo_name[256];
boolean timedemo_csv;
char timedemo_csv_id[256];
@ -3521,7 +3517,7 @@ static void Got_Pause(UINT8 **cp, INT32 playernum)
// Command for stuck characters in netgames, griefing, etc.
static void HandleRespawnCommand(UINT8 localplayer)
{
UINT8 buf[4];
UINT8 buf[1];
UINT8 *cp = buf;
if (!(gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_VOTING))
@ -3548,7 +3544,7 @@ static void HandleRespawnCommand(UINT8 localplayer)
return;
}
WRITEINT32(cp, g_localplayers[localplayer]);
WRITEUINT8(cp, g_localplayers[localplayer]);
SendNetXCmdForPlayer(localplayer, XD_RESPAWN, &buf, sizeof(buf));
}
@ -3575,10 +3571,13 @@ static void Command_Respawn4(void)
static void Got_Respawn(UINT8 **cp, INT32 playernum)
{
INT32 respawnplayer = READINT32(*cp);
INT32 respawnplayer = READUINT8(*cp);
// You can't respawn someone else. Nice try, there.
if (respawnplayer != playernum || P_PlayerInPain(&players[respawnplayer]) || spbplace == players[respawnplayer].position) // srb2kart: "|| (!(gametyperules & GTR_CIRCUIT))"
// Unless it's a bot. They can respawn whenever they feel like it, if the server says so.
if (players[respawnplayer].bot
? playernum != serverplayer
: respawnplayer != playernum || P_PlayerInPain(&players[respawnplayer]) || spbplace == players[respawnplayer].position)
{
CONS_Alert(CONS_WARNING, M_GetText("Illegal respawn command received from %s\n"), player_names[playernum]);
if (server)
@ -3593,7 +3592,7 @@ static void Got_Respawn(UINT8 **cp, INT32 playernum)
return;
P_DamageMobj(players[respawnplayer].mo, NULL, NULL, 1, DMG_INSTAKILL);
demo_extradata[playernum] |= DXD_RESPAWN;
demo_extradata[respawnplayer] |= DXD_RESPAWN;
}
}

View file

@ -237,8 +237,6 @@ extern consvar_t cv_director;
extern consvar_t cv_schedule;
extern consvar_t cv_test1, cv_test2, cv_test3;
extern char timedemo_name[256];
extern boolean timedemo_csv;
extern char timedemo_csv_id[256];

View file

@ -419,17 +419,6 @@ typedef enum
// for kickstartaccel
#define ACCEL_KICKSTART (TICRATE)
// Minimum percentage for a (non-auto) drift to begin.
#define BOTDRIFTPERCENT (10)
// Minimum turning percentage for an auto drift to begin.
#define DRIFTSTARTPCT (45)
#define BOTDRIFTTICS (2*TICRATE/3)
#define BOTDRIFTLOCKOUT (TICRATE/2)
#define MAXDRIFTSKILL (FRACUNIT/2)
typedef enum
{
BOT_STYLE_NORMAL,
@ -439,15 +428,6 @@ typedef enum
BOT_STYLE__MAX
} botStyle_e;
typedef enum
{
DRIFTSTATE_AUTO,
DRIFTSTATE_STARTING,
DRIFTSTATE_ACTIVE,
DRIFTSTATE_ENDING,
NUMDRIFTSTATES
} botdrift_t;
// player_t struct for all bot variables
struct botvars_t
{
@ -459,20 +439,6 @@ struct botvars_t
fixed_t rubberband; // Bot rubberband value
UINT16 controller; // Special bot controller linedef ID
tic_t itemdelay; // Delay before using item at all
tic_t itemconfirm; // When high enough, they will use their item
SINT8 turnconfirm; // Confirm turn direction
UINT32 respawnconfirm; // Confirm when respawn is needed.
// Drift-relevant data below:
fixed_t driftskill; // The bot's "skill" at drifts.
// Determines how soon a bot starts to drift.
botdrift_t driftstate; // Drifting state
SINT8 driftturn; // Drifting turn direction
tic_t drifttime; // Time spent drifting
tic_t driftlockout; // do not allow drifting for this many tics
};
struct sonicloopcamvars_t

View file

@ -57,7 +57,6 @@ typedef enum
#define TICCMD_RECEIVED (0x01) /* Actual tic recieved from client */
#define TICCMD_TYPING (0x02) /* chat window or console open */
#define TICCMD_KEYSTROKE (0x04) /* chat character input */
#define TICCMD_BOT (0x80) /* generated by bot, demos write bot variables */
#if defined(_MSC_VER)
#pragma pack(1)
@ -74,12 +73,6 @@ struct ticcmd_t
UINT16 buttons;
UINT8 latency; // Netgames: how many tics ago was this ticcmd generated from this player's end?
UINT8 flags;
struct
{
SINT8 turnconfirm;
SINT8 itemconfirm;
SINT8 respawnconfirm;
} bot;
} ATTRPACK;
#if defined(_MSC_VER)

View file

@ -27,11 +27,11 @@
#include "r_data.h" // patchalphastyle_t
#include "k_boss.h" // spottype_t (for lua)
#include "k_follower.h" // followermode_t (for lua)
#include "k_bot.h" // bot constants (for lua)
#include "g_input.h" // Game controls (for lua)
#include "k_kart.h" // awardscaledrings_t
#include "k_waypoint.h" // waypoint values (for lua)
#include "deh_tables.h"
#include "fastcmp.h"
@ -1553,6 +1553,60 @@ struct int_const_s const INT_CONST[] = {
{"SNEAKERTYPE_ROCKETSNEAKER", SNEAKERTYPE_ROCKETSNEAKER},
{"SNEAKERTYPE_WATERPANEL", SNEAKERTYPE_WATERPANEL},
// k_bot.h constants
{"MAXBOTDIFFICULTY",MAXBOTDIFFICULTY},
{"DIFFICULTBOT",DIFFICULTBOT},
{"BOTTURNCONFIRM",BOTTURNCONFIRM},
{"BOTSPINDASHCONFIRM",BOTSPINDASHCONFIRM},
{"BOTRESPAWNCONFIRM",BOTRESPAWNCONFIRM},
{"BOT_ITEM_DECISION_TIME",BOT_ITEM_DECISION_TIME},
{"BOTDRIFTPERCENT",BOTDRIFTPERCENT},
{"DRIFTSTARTPCT",DRIFTSTARTPCT},
{"BOTDRIFTTICS",BOTDRIFTTICS},
{"BOTDRIFTLOCKOUT",BOTDRIFTLOCKOUT},
{"MAXDRIFTSKILL",MAXDRIFTSKILL},
// botStyle_e
{"BOT_STYLE_NORMAL",BOT_STYLE_NORMAL},
{"BOT_STYLE_STAY",BOT_STYLE_STAY},
{"BOT_STYLE__MAX",BOT_STYLE__MAX},
// botItemPriority_e
/*
{"BOT_ITEM_PR__FALLBACK",BOT_ITEM_PR__FALLBACK},
{"BOT_ITEM_PR_NEUTRAL",BOT_ITEM_PR_NEUTRAL},
{"BOT_ITEM_PR_FRONTRUNNER",BOT_ITEM_PR_FRONTRUNNER},
{"BOT_ITEM_PR_SPEED",BOT_ITEM_PR_SPEED},
{"BOT_ITEM_PR__OVERRIDES",BOT_ITEM_PR__OVERRIDES},
{"BOT_ITEM_PR_RINGDEBT",BOT_ITEM_PR_RINGDEBT},
{"BOT_ITEM_PR_POWER",BOT_ITEM_PR_POWER},
{"BOT_ITEM_PR_SPB",BOT_ITEM_PR_SPB},
{"BOT_ITEM_PR__MAX",BOT_ITEM_PR__MAX},
*/
// textmapbotcontroller_t
{"TMBOT_NORUBBERBAND",TMBOT_NORUBBERBAND},
{"TMBOT_NOCONTROL",TMBOT_NOCONTROL},
{"TMBOT_FORCEDIR",TMBOT_FORCEDIR},
//{"TMBOT_FASTFALL",TMBOT_FASTFALL},
// textmapbottrick_t
/*
{"TMBOTTR_NONE",TMBOTTR_NONE},
{"TMBOTTR_LEFT",TMBOTTR_LEFT},
{"TMBOTTR_RIGHT",TMBOTTR_RIGHT},
{"TMBOTTR_UP",TMBOTTR_UP},
{"TMBOTTR_DOWN",TMBOTTR_DOWN},
*/
// botdrift_e
{"DRIFTSTATE_AUTO",DRIFTSTATE_AUTO},
{"DRIFTSTATE_STARTING",DRIFTSTATE_STARTING},
{"DRIFTSTATE_ACTIVE",DRIFTSTATE_ACTIVE},
{"DRIFTSTATE_ENDING",DRIFTSTATE_ENDING},
{"NUMDRIFTSTATES",NUMDRIFTSTATES},
// t_overlay_action_t
{"TOV_UNDEFINED",TOV_UNDEFINED},
{"TOV_STILL",TOV_STILL},

View file

@ -146,13 +146,8 @@ demoghost *ghosts = NULL;
#define ZT_AIMING 0x0040
#define ZT_LATENCY 0x0080
#define ZT_FLAGS 0x0100
#define ZT_BOT 0x8000
// Ziptics are UINT16 now, go nuts
#define ZT_BOT_TURN 0x0001
#define ZT_BOT_ITEM 0x0002
#define ZT_BOT_RESPAWN 0x0004
#define DEMOMARKER 0x80 // demobuf.end
UINT8 demo_extradata[MAXPLAYERS];
@ -398,16 +393,12 @@ static UINT8 *G_ReadZipTic(ticcmd_t *cmd, UINT8 *dp, UINT16 version)
if (ziptic & ZT_FLAGS)
cmd->flags = READUINT8(dp);
if (ziptic & ZT_BOT)
if (version < 0x000a && ziptic & 0x8000) // ZT_BOT
{
UINT16 botziptic = READUINT16(dp);
if (botziptic & ZT_BOT_TURN)
cmd->bot.turnconfirm = READSINT8(dp);
if (botziptic & ZT_BOT_ITEM)
cmd->bot.itemconfirm = READSINT8(dp);
if (botziptic & ZT_BOT_RESPAWN)
cmd->bot.respawnconfirm = READSINT8(dp);
if (botziptic & 1) READSINT8(dp);
if (botziptic & 2) READSINT8(dp);
if (botziptic & 4) READSINT8(dp);
}
return dp;
}
@ -1403,45 +1394,8 @@ void G_WriteDemoTiccmd(ticcmd_t *cmd, INT32 playernum)
ziptic |= ZT_FLAGS;
}
if (cmd->flags & TICCMD_BOT)
{
ziptic |= ZT_BOT;
}
WRITEUINT16(ziptic_p, ziptic);
if (ziptic & ZT_BOT)
{
UINT16 botziptic = 0;
UINT8 *botziptic_p;
botziptic_p = demobuf.p; // the ziptic, written at the end of this function
demobuf.p += 2;
if (cmd->bot.turnconfirm != oldcmd[playernum].bot.turnconfirm)
{
WRITESINT8(demobuf.p, cmd->bot.turnconfirm);
oldcmd[playernum].bot.turnconfirm = cmd->bot.turnconfirm;
botziptic |= ZT_BOT_TURN;
}
if (cmd->bot.itemconfirm != oldcmd[playernum].bot.itemconfirm)
{
WRITESINT8(demobuf.p, cmd->bot.itemconfirm);
oldcmd[playernum].bot.itemconfirm = cmd->bot.itemconfirm;
botziptic |= ZT_BOT_ITEM;
}
if (cmd->bot.respawnconfirm != oldcmd[playernum].bot.respawnconfirm)
{
WRITESINT8(demobuf.p, cmd->bot.respawnconfirm);
oldcmd[playernum].bot.respawnconfirm = cmd->bot.respawnconfirm;
botziptic |= ZT_BOT_RESPAWN;
}
WRITEUINT16(botziptic_p, botziptic);
}
// attention here for the ticcmd size!
// latest demos with mouse aiming byte in ticcmd
if (!(demoflags & DF_GHOST) && ziptic_p > demobuf.end - 9)

View file

@ -1363,13 +1363,6 @@ ticcmd_t *G_MoveTiccmd(ticcmd_t* dest, const ticcmd_t* src, const size_t n)
dest[i].buttons = (UINT16)SHORT(src[i].buttons);
dest[i].latency = src[i].latency;
dest[i].flags = src[i].flags;
if (dest[i].flags & TICCMD_BOT)
{
dest[i].bot.itemconfirm = src[i].bot.itemconfirm;
dest[i].bot.turnconfirm = src[i].bot.turnconfirm;
dest[i].bot.respawnconfirm = src[i].bot.respawnconfirm;
}
}
return dest;
}
@ -2417,6 +2410,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
boolean spectator;
boolean bot;
UINT8 botdifficulty;
botStyle_e style;
INT16 rings;
angle_t playerangleturn;
@ -2476,6 +2470,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
followitem = players[player].followitem;
bot = players[player].bot;
style = players[player].botvars.style;
botdifficulty = players[player].botvars.difficulty;
botdiffincrease = players[player].botvars.diffincrease;
@ -2638,6 +2633,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
p->totalring = totalring;
p->bot = bot;
p->botvars.style = style;
p->botvars.difficulty = botdifficulty;
p->rings = rings;
p->botvars.diffincrease = botdiffincrease;
@ -2668,9 +2664,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
p->botvars.rubberband = FRACUNIT;
p->botvars.controller = UINT16_MAX;
p->botvars.driftskill =
FixedMul(MAXDRIFTSKILL, K_BotDetermineDriftSkill(p));
p->botvars.driftstate = DRIFTSTATE_AUTO;
K_BotReborn(p);
if (follower)
P_RemoveMobj(follower);

View file

@ -42,10 +42,46 @@
#endif
#include "i_net.h" // doomcom
#include "blan/b_soc.h"
#include "v_video.h" // for debugging
consvar_t cv_forcebots = CVAR_INIT ("kartforcebots", "Off", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL);
consvar_t cv_botcontrol = CVAR_INIT ("kartbotcontrol", "On", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL);
botdata_t botdata[MAXPLAYERS];
void K_DrawBotDebugger(const player_t *player)
{
INT32 vflags = V_6WIDTHSPACE|V_ALLOWLOWERCASE|V_30TRANS;
botdata_t *bd = &botdata[player - players];
static const char *driftstates[] = {
"auto", // DRIFTSTATE_AUTO
"starting", // DRIFTSTATE_STARTING
"active", // DRIFTSTATE_ACTIVE
"ending", // DRIFTSTATE_ENDING
};
if (!cv_kartdebugbot.value || !player->bot)
return;
INT32 x1 = 26, x2 = 99, y = 92;
V_DrawThinString(x1, y+0, vflags, va("predict.x: %d", bd->predict.x/FRACUNIT));
V_DrawThinString(x2, y+0, vflags, va("predict.y: %d", bd->predict.y/FRACUNIT));
V_DrawThinString(x1, y+8, vflags, va("predict.radius: %d", bd->predict.radius/FRACUNIT));
V_DrawThinString(x2, y+8, vflags, va("predict.baseradius: %d", bd->predict.baseRadius/FRACUNIT));
V_DrawThinString(x1, y+16, vflags, va("itemconfirm: %d", bd->itemconfirm));
V_DrawThinString(x2, y+16, vflags, va("itemdelay: %d", bd->itemdelay));
V_DrawThinString(x1, y+24, vflags, va("turnconfirm: %d", bd->turnconfirm));
V_DrawThinString(x2, y+24, vflags, va("respawnconfirm: %d", bd->respawnconfirm));
V_DrawThinString(x1, y+32, vflags, va("driftstate: %s", driftstates[bd->driftstate]));
V_DrawThinString(x2, y+32, vflags|(bd->driftlockout ? V_ORANGEMAP : 0), va("driftlockout: %d", bd->driftlockout));
V_DrawThinString(x1, y+40, vflags, va("driftturn: %d", bd->driftturn));
V_DrawThinString(x2, y+40, vflags, va("drifttime: %d", bd->drifttime));
}
/*--------------------------------------------------
void K_SetNameForBot(UINT8 playerNum, const char *realname)
@ -132,6 +168,8 @@ void K_SetBot(UINT8 newplayernum, UINT16 skinnum, UINT8 difficulty, botStyle_e s
SetPlayerSkinByNum(newplayernum, skinnum);
LUA_HookPlayer(&players[newplayernum], HOOK(BotJoin));
for (UINT8 i = 0; i < PWRLV_NUMTYPES; i++)
{
clientpowerlevels[newplayernum][i] = 0;
@ -359,6 +397,17 @@ void K_UpdateMatchRaceBots(void)
--------------------------------------------------*/
boolean K_PlayerUsesBotMovement(const player_t *player)
{
// Lua can't override the podium sequence result, but it can
// override the following results:
{
UINT8 shouldOverride = LUA_HookPlayerForceResults(const_cast<player_t*>(player),
HOOK(PlayerUsesBotMovement));
if (shouldOverride == 1)
return true;
if (shouldOverride == 2)
return false;
}
if (player->bot)
return true;
@ -370,7 +419,7 @@ boolean K_PlayerUsesBotMovement(const player_t *player)
See header file for description.
--------------------------------------------------*/
boolean K_BotCanTakeCut(player_t *player)
boolean K_BotCanTakeCut(const player_t *player)
{
if (
(K_TripwirePassConditions(player) != TRIPWIRE_NONE || K_ApplyOffroad(player) == false)
@ -453,11 +502,11 @@ static fixed_t K_BotSpeedScaled(const player_t *player, fixed_t speed)
}
/*--------------------------------------------------
const botcontroller_t *K_GetBotController(const mobj_t *mobj)
botcontroller_t *K_GetBotController(const mobj_t *mobj)
See header file for description.
--------------------------------------------------*/
const botcontroller_t *K_GetBotController(mobj_t *mobj)
botcontroller_t *K_GetBotController(const mobj_t *mobj)
{
botcontroller_t *ret = nullptr;
@ -838,18 +887,8 @@ static fixed_t K_ScaleWPDistWithSlope(fixed_t disttonext, angle_t angletonext, c
return disttonext;
}
/*--------------------------------------------------
static botprediction_t *K_CreateBotPrediction(const player_t *player)
Calculates a point further along the track to attempt to drive towards.
Input Arguments:-
player - Player to compare.
Return:-
Bot prediction struct.
--------------------------------------------------*/
static botprediction_t *K_CreateBotPrediction(player_t *player)
// Calculates a point further along the track to attempt to drive towards.
static boolean K_CreateBotPrediction(botdata_t *bd, const player_t *player)
{
ZoneScoped;
@ -884,17 +923,14 @@ static botprediction_t *K_CreateBotPrediction(player_t *player)
boolean pathfindsuccess = false;
path_t pathtofinish = {0};
botprediction_t *predict = nullptr;
size_t i;
if (wp == nullptr || P_MobjWasRemoved(wp->mobj) == true)
{
// Can't do any of this if we don't have a waypoint.
return nullptr;
return false;
}
predict = static_cast<botprediction_t *>(Z_Calloc(sizeof(botprediction_t), PU_LEVEL, nullptr));
// Init defaults in case of pathfind failure
angletonext = R_PointToAngle2(prevwpmobj->x, prevwpmobj->y, wp->mobj->x, wp->mobj->y);
disttonext = P_AproxDistance(prevwpmobj->x - wp->mobj->x, prevwpmobj->y - wp->mobj->y);
@ -950,22 +986,22 @@ static botprediction_t *K_CreateBotPrediction(player_t *player)
// Set our predicted point's coordinates,
// and use the smallest radius of all of the waypoints in the chain!
predict->x = wp->mobj->x;
predict->y = wp->mobj->y;
bd->predict.x = wp->mobj->x;
bd->predict.y = wp->mobj->y;
predict->baseRadius = radius;
predict->radius = FixedMul(radiusScaled, radReduce);
bd->predict.baseRadius = radius;
bd->predict.radius = FixedMul(radiusScaled, radReduce);
// Set the prediction coordinates between the 2 waypoints if there's still distance left.
if (distanceleft > 0)
{
// Scaled with the leftover anglemul!
predict->x += P_ReturnThrustX(nullptr, angletonext, std::min<fixed_t>(disttonext, distanceleft) * FRACUNIT);
predict->y += P_ReturnThrustY(nullptr, angletonext, std::min<fixed_t>(disttonext, distanceleft) * FRACUNIT);
bd->predict.x += P_ReturnThrustX(nullptr, angletonext, std::min<fixed_t>(disttonext, distanceleft) * FRACUNIT);
bd->predict.y += P_ReturnThrustY(nullptr, angletonext, std::min<fixed_t>(disttonext, distanceleft) * FRACUNIT);
}
ps_bots[player - players].prediction += I_GetPreciseTime() - time;
return predict;
return true;
}
/*--------------------------------------------------
@ -980,19 +1016,18 @@ static botprediction_t *K_CreateBotPrediction(player_t *player)
Return:-
None
--------------------------------------------------*/
static void K_DrawPredictionDebug(botprediction_t *predict, const player_t *player)
static void K_DrawPredictionDebug(botdata_t *bd, const player_t *player)
{
mobj_t *debugMobj = nullptr;
angle_t sideAngle = ANGLE_MAX;
UINT8 i = UINT8_MAX;
I_Assert(predict != nullptr);
I_Assert(player != nullptr);
I_Assert(player->mo != nullptr && P_MobjWasRemoved(player->mo) == false);
sideAngle = player->mo->angle + ANGLE_90;
debugMobj = P_SpawnMobj(predict->x, predict->y, player->mo->z, MT_SPARK);
debugMobj = P_SpawnMobj(bd->predict.x, bd->predict.y, player->mo->z, MT_SPARK);
P_SetMobjState(debugMobj, S_THOK);
debugMobj->frame &= ~FF_TRANSMASK;
@ -1001,22 +1036,22 @@ static void K_DrawPredictionDebug(botprediction_t *predict, const player_t *play
debugMobj->color = SKINCOLOR_ORANGE;
P_SetScale(debugMobj, debugMobj->destscale * 2);
debugMobj->tics = 2;
debugMobj->tics = 1;
for (i = 0; i < 2; i++)
{
mobj_t *radiusMobj = nullptr;
fixed_t radiusX = predict->x, radiusY = predict->y;
fixed_t radiusX = bd->predict.x, radiusY = bd->predict.y;
if (i & 1)
{
radiusX -= FixedMul(predict->radius, FINECOSINE(sideAngle >> ANGLETOFINESHIFT));
radiusY -= FixedMul(predict->radius, FINESINE(sideAngle >> ANGLETOFINESHIFT));
radiusX -= FixedMul(bd->predict.radius, FINECOSINE(sideAngle >> ANGLETOFINESHIFT));
radiusY -= FixedMul(bd->predict.radius, FINESINE(sideAngle >> ANGLETOFINESHIFT));
}
else
{
radiusX += FixedMul(predict->radius, FINECOSINE(sideAngle >> ANGLETOFINESHIFT));
radiusY += FixedMul(predict->radius, FINESINE(sideAngle >> ANGLETOFINESHIFT));
radiusX += FixedMul(bd->predict.radius, FINECOSINE(sideAngle >> ANGLETOFINESHIFT));
radiusY += FixedMul(bd->predict.radius, FINESINE(sideAngle >> ANGLETOFINESHIFT));
}
radiusMobj = P_SpawnMobj(radiusX, radiusY, player->mo->z, MT_SPARK);
@ -1028,24 +1063,15 @@ static void K_DrawPredictionDebug(botprediction_t *predict, const player_t *play
radiusMobj->color = SKINCOLOR_YELLOW;
P_SetScale(debugMobj, debugMobj->destscale / 2);
radiusMobj->tics = 2;
radiusMobj->tics = 1;
}
}
/*--------------------------------------------------
fixed_t K_BotDetermineDriftSkill(player_t *player)
Calculates drift skill for a player based on stats.
Input Arguments:-
player - Player to get drift skill for.
Return:-
Calculated drift skill.
--------------------------------------------------*/
fixed_t K_BotDetermineDriftSkill(player_t *player)
// Calculates drift skill for a player based on ~~stats~~ difficulty.
static fixed_t K_BotDetermineDriftSkill(const player_t *player)
{
return ((FRACUNIT * (player->kartspeed + player->kartweight)) / 18);
return FRACUNIT/8 + (FRACUNIT * player->botvars.difficulty) / DIFFICULTBOT;
//return ((FRACUNIT * (player->kartspeed + player->kartweight)) / 18);
}
static void K_WaypointGetDirectionVector(waypoint_t *wp1, waypoint_t *wp2, vector3_t *a_o)
@ -1064,37 +1090,27 @@ static void K_WaypointGetDirectionVector(waypoint_t *wp1, waypoint_t *wp2, vecto
FV3_Normalize(a_o);
}
/*--------------------------------------------------
void K_BotSetDriftState(player_t *player, botdrift_t newstate, tic_t lockout)
See header file for description.
--------------------------------------------------*/
void K_BotSetDriftState(player_t *player, botdrift_t newstate, tic_t lockout)
// Changes a bot's drift state.
// Resets the drift timer if the old and new state are different.
// If lockout is non-zero, apply drift lockout for this many tics.
void K_BotSetDriftState(const player_t *player, botdrift_e newstate, tic_t lockout)
{
if (newstate != player->botvars.driftstate)
botdata_t *bd = &botdata[player - players];
if (newstate != bd->driftstate)
{
player->botvars.driftstate = newstate;
player->botvars.drifttime = 0;
bd->driftstate = newstate;
bd->drifttime = 0;
}
if (lockout > player->botvars.driftlockout)
player->botvars.driftlockout = lockout;
if (lockout > bd->driftlockout)
bd->driftlockout = lockout;
}
#define MINBOTDRIFT (KART_FULLTURN * 2) / 3 // 0.66
/*--------------------------------------------------
static INT32 K_BotStartDrift(player_t* player)
Begins and ends "forced" drifts on a per-waypoint basis.
Input Arguments:-
player - Player to begin the drift for.
Return:-
Override value for turn amount.
--------------------------------------------------*/
static void K_BotStartDrift(player_t* player)
// Begins and ends "forced" drifts on a per-waypoint basis.
static void K_BotStartDrift(botdata_t *bd, const player_t* player)
{
// Handle DRIFTING towards waypoints!
boolean shouldDrift;
@ -1116,17 +1132,16 @@ static void K_BotStartDrift(player_t* player)
return;
}
if (player->botvars.driftlockout)
if (bd->driftlockout)
{
// things are not working out in our favor
player->botvars.driftlockout--;
bd->driftlockout--;
return;
}
// check for waypoints ahead of us with drift settings, based on our current speed
path_t path = {0};
INT32 maxdist = FixedInt(cv_test1.value) + gamespeed*50;
maxdist = FixedMul(maxdist, speedfactor * (player->botvars.driftstate == DRIFTSTATE_ACTIVE ? 1 : 2));
INT32 maxdist = FixedMul(bd->driftmaxdist, speedfactor * (bd->driftstate == DRIFTSTATE_ACTIVE ? 1 : 2));
if (maxdist >= 0 && K_PathfindThruCircuit(player->currentwaypoint, maxdist, &path, false, false))
{
@ -1156,17 +1171,17 @@ static void K_BotStartDrift(player_t* player)
if (driftsetting == DRIFT_END)
{
if (player->botvars.driftstate != DRIFTSTATE_AUTO)
if (bd->driftstate != DRIFTSTATE_AUTO)
K_BotSetDriftState(player, DRIFTSTATE_ENDING, 0);
}
else if (driftsetting > DRIFT_NONE && driftsetting < DRIFT_END
&& player->botvars.driftstate == DRIFTSTATE_AUTO)
&& bd->driftstate == DRIFTSTATE_AUTO)
{
// Randomly decide to drift based on our skill at drifting,
// and how fast we're moving.
fixed_t driftpotential = P_RandomKey(MAXDRIFTSKILL);
fixed_t driftpotential = M_RandomKey(MAXDRIFTSKILL);
if ((driftpotential <= player->botvars.driftskill) &&
if ((driftpotential <= bd->driftskill) &&
(botDriftSpeed <= player->speed))
{
shouldDrift = true;
@ -1195,26 +1210,14 @@ static void K_BotStartDrift(player_t* player)
break;
}
player->botvars.driftturn = driftturn;
bd->driftturn = driftturn;
K_BotSetDriftState(player, DRIFTSTATE_STARTING, 0);
}
}
}
/*--------------------------------------------------
static INT32 K_HandleBotTrack(const player_t *player, ticcmd_t *cmd, botprediction_t *predict)
Determines inputs for standard track driving.
Input Arguments:-
player - Player to generate the ticcmd for.
cmd - The player's ticcmd to modify.
predict - Pointer to the bot's prediction.
Return:-
New value for turn amount.
--------------------------------------------------*/
static INT32 K_HandleBotTrack(player_t *player, ticcmd_t *cmd, botprediction_t *predict, angle_t destangle)
// Determines inputs for standard track driving.
static INT32 K_HandleBotTrack(botdata_t *bd, const player_t *player, angle_t destangle)
{
ZoneScoped;
@ -1225,15 +1228,13 @@ static INT32 K_HandleBotTrack(player_t *player, ticcmd_t *cmd, botprediction_t *
INT32 anglediff, anglediff2;
fixed_t speedfactor = FixedDiv(player->speed, K_GetKartSpeed(player, false, false));
I_Assert(predict != nullptr);
moveangle = player->mo->angle;
anglediff = AngleDeltaSigned(moveangle, destangle);
// line up for an incoming drift
if (player->botvars.driftstate == DRIFTSTATE_STARTING)
if (bd->driftstate == DRIFTSTATE_STARTING)
{
anglediff += FixedMul(ANG10-ANG2, speedfactor) * player->botvars.driftturn;
anglediff += FixedMul(ANG10-ANG2, speedfactor) * bd->driftturn;
}
if (anglediff < 0)
@ -1252,18 +1253,18 @@ static INT32 K_HandleBotTrack(player_t *player, ticcmd_t *cmd, botprediction_t *
if (anglediff > ANGLE_67h)
{
// Wrong way!
cmd->forwardmove = -MAXPLMOVE;
cmd->buttons |= BT_BRAKE;
bd->acceldown = false;
bd->brakedown = true;
}
else
{
const fixed_t playerwidth = (player->mo->radius * 2);
fixed_t realrad = predict->radius*3/4; // Remove a "safe" distance away from the edges of the road
fixed_t realrad = bd->predict.radius*3/4; // Remove a "safe" distance away from the edges of the road
fixed_t rad = realrad;
fixed_t dirdist = K_DistanceOfLineFromPoint(
player->mo->x, player->mo->y,
player->mo->x + FINECOSINE(moveangle >> ANGLETOFINESHIFT), player->mo->y + FINESINE(moveangle >> ANGLETOFINESHIFT),
predict->x, predict->y
bd->predict.x, bd->predict.y
);
if (realrad < playerwidth)
@ -1296,11 +1297,11 @@ static INT32 K_HandleBotTrack(player_t *player, ticcmd_t *cmd, botprediction_t *
}
// Full speed ahead!
cmd->buttons |= BT_ACCELERATE;
cmd->forwardmove = MAXPLMOVE;
bd->acceldown = true;
bd->brakedown = false;
if (dirdist <= rad
&& player->botvars.driftstate != DRIFTSTATE_STARTING) // steer towards waypoints when starting drift
&& bd->driftstate != DRIFTSTATE_STARTING) // steer towards waypoints when starting drift
{
// Going the right way, don't turn at all.
turnamt = 0;
@ -1310,11 +1311,11 @@ static INT32 K_HandleBotTrack(player_t *player, ticcmd_t *cmd, botprediction_t *
//INT32 mindriftamt = FixedMul(MINBOTDRIFT * (cv_kartspeed.value + 1), 2 * FRACUNIT);
// Start or continue a drift.
if (player->botvars.driftstate == DRIFTSTATE_ACTIVE || player->botvars.driftstate == DRIFTSTATE_ENDING)
if (bd->driftstate == DRIFTSTATE_ACTIVE || bd->driftstate == DRIFTSTATE_ENDING)
{
cmd->buttons |= BT_DRIFT;
bd->driftdown = true;
fixed_t angofs = K_GetKartSpeedFromStat(5 - (player->kartspeed - 5), false) * -player->botvars.driftturn;
fixed_t angofs = K_GetKartSpeedFromStat(5 - (player->kartspeed - 5), false) * -bd->driftturn;
// adjust for speed
angofs = FixedMul(angofs, speedfactor - (2-gamespeed)*FRACUNIT/4);
@ -1322,20 +1323,20 @@ static INT32 K_HandleBotTrack(player_t *player, ticcmd_t *cmd, botprediction_t *
fixed_t driftpower = angofs - FixedDiv(anglediff2, ANG1);
// arbitrary divider on the final driftpower
driftpower /= FixedInt(cv_test2.value);
driftpower /= bd->driftpowerdiv;
// brakedrift if we're steering too hard
if (abs(driftpower) >= FRACUNIT)
cmd->buttons |= BT_BRAKE;
bd->brakedown = true;
// get the raw turn value and "invert" it (higher weight needs harder steering!)
INT16 turnvalue = abs(K_GetKartTurnValue(player, KART_FULLTURN * (player->botvars.driftturn < 0 ? 1 : -1)));
INT16 turnvalue = abs(K_GetKartTurnValue(player, KART_FULLTURN * (bd->driftturn < 0 ? 1 : -1)));
turnvalue = 541 - (turnvalue - 541); // weight 5 = 541
turnamt = std::clamp(FixedMul(driftpower, turnvalue), -KART_FULLTURN, KART_FULLTURN);
}
/*
else if ((turnamt) && (player->botvars.driftstate == DRIFTSTATE_AUTO) &&
else if ((turnamt) && (bd->driftstate == DRIFTSTATE_AUTO) &&
(turnpower > FixedPercentage(DRIFTSTARTPCT)))
{
// TODO: Figure out a drift prediction system.
@ -1348,6 +1349,38 @@ static INT32 K_HandleBotTrack(player_t *player, ticcmd_t *cmd, botprediction_t *
#undef MINBOTDRIFT
static void K_IncrementBotRespawn(const player_t *player, UINT8 *respawn, const UINT8 respawnmax)
{
const fixed_t requireDist = (12*player->mo->scale) / FRACUNIT;
INT32 progress = player->distancetofinishprev - player->distancetofinish;
boolean exceptions = (
(leveltime < starttime)
|| player->flashing != 0
|| player->spinouttimer != 0
|| player->airtime > 3*TICRATE/2
|| (player->justbumped > 0 && player->justbumped < bumptime-1)
);
if (!exceptions && (progress < requireDist))
{
if (*respawn < respawnmax)
{
// Making no progress, start counting against you.
*respawn = *respawn + 1;
if (progress < -requireDist && *respawn < respawnmax)
{
// Making NEGATIVE progress? Start counting even harder.
*respawn = *respawn + 1;
}
}
}
else if (*respawn > 0)
{
// Playing normally.
*respawn = *respawn - 1;
}
}
/*--------------------------------------------------
static INT32 K_HandleBotReverse(const player_t *player, ticcmd_t *cmd, botprediction_t *predict)
@ -1361,7 +1394,7 @@ static INT32 K_HandleBotTrack(player_t *player, ticcmd_t *cmd, botprediction_t *
Return:-
New value for turn amount.
--------------------------------------------------*/
/*static INT32 K_HandleBotReverse(player_t *player, ticcmd_t *cmd, botprediction_t *predict, angle_t destangle)
/*static INT32 K_HandleBotReverse(const player_t *player, ticcmd_t *cmd, botprediction_t *predict, angle_t destangle)
{
ZoneScoped;
@ -1493,26 +1526,24 @@ static INT32 K_HandleBotTrack(player_t *player, ticcmd_t *cmd, botprediction_t *
return turnamt;
}*/
/*--------------------------------------------------
static void K_BuildBotTiccmdNormal(const player_t *player, ticcmd_t *cmd)
Build ticcmd for bots with a style of BOT_STYLE_NORMAL
--------------------------------------------------*/
static void K_BuildBotTiccmdNormal(player_t *player, ticcmd_t *cmd)
// updates server-sided bot logic
void K_BotTicker(const player_t *player)
{
precise_t t = 0;
botprediction_t *predict = nullptr;
auto predict_finally = srb2::finally([&predict]() { Z_Free(predict); });
angle_t destangle = 0;
INT32 turnamt = 0;
botdata_t *bd = &botdata[player - players];
bd->itemwasdown = bd->itemdown;
bd->acceldown = bd->brakedown = bd->driftdown = bd->itemdown = false;
bd->dolookback = false;
bd->itemthrow = 0;
if (!(gametyperules & GTR_BOTS) // No bot behaviors
|| K_GetNumWaypoints() == 0 // No waypoints
|| leveltime <= introtime // During intro camera
|| player->playerstate == PST_DEAD // Dead, respawning.
|| player->mo->scale <= 1) // Post-finish "death" animation
|| player->mo->scale <= 1 // Post-finish "death" animation
|| player->spectator) // spectating
{
// No need to do anything else.
return;
@ -1539,53 +1570,58 @@ static void K_BuildBotTiccmdNormal(player_t *player, ticcmd_t *cmd)
if (player->exiting)
{
//Bot finish
// TODO: Make bots spin around like a player would based on random chance
return;
}
if (player->botvars.respawnconfirm >= BOTRESPAWNCONFIRM)
// Is a bot not making any progress? Kill it and respawn at next waypoint.
K_IncrementBotRespawn(player, &bd->respawnconfirm, BOTRESPAWNCONFIRM);
if (bd->respawnconfirm > BOTRESPAWNCONFIRM - TICRATE)
{
// We want to respawn. Simply hold brake and stop here!
cmd->buttons &= ~BT_ACCELERATE|BT_DRIFT|BT_ATTACK;
bd->acceldown = false;
K_BotSetDriftState(player, DRIFTSTATE_AUTO, TICRATE);
if (player->speed > 0)
{
cmd->buttons |= (BT_BRAKE);
cmd->bot.respawnconfirm++;
}
bd->brakedown = true;
if ((player->speed < 10*FRACUNIT))
if (bd->respawnconfirm >= BOTRESPAWNCONFIRM || player->speed < 10*FRACUNIT)
{
cmd->bot.respawnconfirm = TICRATE;
// WHAT ARE YOU DOING??? RACE ALREADY!
// ...
// FINE, I'LL SEND A RESPAWN COMMAND ON YOUR BEHALF!
char buf[1], *cp = buf;
WRITEUINT8(cp, player - players);
SendNetXCmdForPlayer(consoleplayer, XD_RESPAWN, buf, sizeof(buf));
}
return;
}
else
{
cmd->bot.respawnconfirm = 0;
}
destangle = player->mo->angle;
// it's time to start predicting
memset(&bd->predict, 0, sizeof(bd->predict));
boolean forcedDir = false;
if (botController != nullptr && (botController->flags & TMBOT_FORCEDIR) == TMBOT_FORCEDIR)
{
const fixed_t dist = DEFAULT_WAYPOINT_RADIUS * player->mo->scale;
// Overwritten prediction
predict = static_cast<botprediction_t *>(Z_Calloc(sizeof(botprediction_t), PU_STATIC, nullptr));
predict->x = player->mo->x + FixedMul(dist, FINECOSINE(botController->forceAngle >> ANGLETOFINESHIFT));
predict->y = player->mo->y + FixedMul(dist, FINESINE(botController->forceAngle >> ANGLETOFINESHIFT));
predict->radius = (DEFAULT_WAYPOINT_RADIUS / 4) * mapobjectscale;
bd->predict = {
.x = player->mo->x + FixedMul(dist, FINECOSINE(botController->forceAngle >> ANGLETOFINESHIFT)),
.y = player->mo->y + FixedMul(dist, FINESINE(botController->forceAngle >> ANGLETOFINESHIFT)),
.radius = (DEFAULT_WAYPOINT_RADIUS / 4) * mapobjectscale,
};
forcedDir = true;
}
if (forcedDir == true)
{
destangle = R_PointToAngle2(player->mo->x, player->mo->y, predict->x, predict->y);
turnamt = K_HandleBotTrack(player, cmd, predict, destangle);
destangle = R_PointToAngle2(player->mo->x, player->mo->y, bd->predict.x, bd->predict.y);
turnamt = K_HandleBotTrack(bd, player, destangle);
}
else if (leveltime <= starttime)
{
@ -1594,210 +1630,69 @@ static void K_BuildBotTiccmdNormal(player_t *player, ticcmd_t *cmd)
if (player->botvars.difficulty > 4)
{
if (leveltime >= starttime-TICRATE-TICRATE/7+finaltiming)
{
cmd->buttons |= BT_ACCELERATE;
cmd->forwardmove = MAXPLMOVE;
}
bd->acceldown = true;
}
}
else
{
// Handle steering towards waypoints!
if (predict == nullptr)
// Create a prediction.
if (K_CreateBotPrediction(bd, player))
{
// Create a prediction.
predict = K_CreateBotPrediction(player);
}
if (predict != nullptr)
{
K_NudgePredictionTowardsObjects(predict, player);
destangle = R_PointToAngle2(player->mo->x, player->mo->y, predict->x, predict->y);
turnamt = K_HandleBotTrack(player, cmd, predict, destangle);
K_NudgePredictionTowardsObjects(bd, player);
destangle = R_PointToAngle2(player->mo->x, player->mo->y, bd->predict.x, bd->predict.y);
turnamt = K_HandleBotTrack(bd, player, destangle);
}
}
if (turnamt > KART_FULLTURN)
{
turnamt = KART_FULLTURN;
}
else if (turnamt < -KART_FULLTURN)
{
turnamt = -KART_FULLTURN;
}
bd->turnamt = turnamt;
if (player->exiting == 0)
{
// TODO: Allowing projectile items like orbinaut while e-braking would be nice, maybe just pass in the spindash variable?
t = I_GetPreciseTime();
K_BotItemUsage(player, cmd, turnamt);
precise_t t = I_GetPreciseTime();
K_BotItemUsage(bd, player);
ps_bots[player - players].item = I_GetPreciseTime() - t;
}
if (turnamt != 0)
if (turnamt > 0)
{
if (turnamt > KART_FULLTURN)
// Count up
if (bd->turnconfirm < BOTTURNCONFIRM)
{
turnamt = KART_FULLTURN;
bd->turnconfirm++;
}
else if (turnamt < -KART_FULLTURN)
}
else if (turnamt < 0)
{
// Count down
if (bd->turnconfirm > -BOTTURNCONFIRM)
{
turnamt = -KART_FULLTURN;
bd->turnconfirm--;
}
if (turnamt > 0)
}
else
{
// Back to neutral
if (bd->turnconfirm < 0)
{
// Count up
if (player->botvars.turnconfirm < BOTTURNCONFIRM)
{
cmd->bot.turnconfirm++;
}
bd->turnconfirm++;
}
else if (turnamt < 0)
else if (bd->turnconfirm > 0)
{
// Count down
if (player->botvars.turnconfirm > -BOTTURNCONFIRM)
{
cmd->bot.turnconfirm--;
}
}
else
{
// Back to neutral
if (player->botvars.turnconfirm < 0)
{
cmd->bot.turnconfirm++;
}
else if (player->botvars.turnconfirm > 0)
{
cmd->bot.turnconfirm--;
}
}
if (abs(player->botvars.turnconfirm) >= BOTTURNCONFIRM)
{
// You're commiting to your turn, you're allowed!
cmd->turning = turnamt;
if (P_CanPlayerTurn(player, cmd))
cmd->angle += K_GetKartTurnValue(player, turnamt);
bd->turnconfirm--;
}
}
// Free the prediction we made earlier
if (predict != nullptr)
{
if (cv_kartdebugbot.value != 0 && player - players == displayplayers[0] && !(paused || P_AutoPause()))
{
K_DrawPredictionDebug(predict, player);
}
}
}
/*--------------------------------------------------
void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd)
See header file for description.
--------------------------------------------------*/
void K_BuildBotTiccmd(
player_t *player, // annoyingly NOT const because of LUA_HookTiccmd... grumble grumble
ticcmd_t *cmd)
{
ZoneScoped;
// Remove any existing controls
memset(cmd, 0, sizeof(ticcmd_t));
if (player->mo == nullptr
|| player->spectator == true
|| G_GamestateUsesLevel() == false)
{
// Not in the level.
return;
}
cmd->angle = player->mo->angle >> TICCMD_REDUCE;
// Complete override of all ticcmd functionality.
// May add more hooks to individual pieces of bot ticcmd,
// but this should always be here so anyone can roll
// their own :)
if (LUA_HookTiccmd(player, cmd, HOOK(BotTiccmd)) == true)
{
cmd->flags |= TICCMD_BOT;
return;
}
cmd->flags |= TICCMD_BOT;
switch (player->botvars.style)
{
case BOT_STYLE_STAY:
{
// Hey, this one's pretty easy :P
break;
}
default:
{
K_BuildBotTiccmdNormal(player, cmd);
break;
}
}
}
static void K_IncrementBotRespawn(player_t *player, UINT32 *respawn, const UINT32 respawnmax)
{
const fixed_t requireDist = (12*player->mo->scale) / FRACUNIT;
INT32 progress = player->distancetofinishprev - player->distancetofinish;
boolean exceptions = (
(leveltime < starttime)
|| player->flashing != 0
|| player->spinouttimer != 0
|| player->airtime > 3*TICRATE/2
|| (player->justbumped > 0 && player->justbumped < bumptime-1)
);
if (!exceptions && (progress < requireDist))
{
if (*respawn < respawnmax)
{
// Making no progress, start counting against you.
*respawn = *respawn + 1;
if (progress < -requireDist && *respawn < respawnmax)
{
// Making NEGATIVE progress? Start counting even harder.
*respawn = *respawn + 1;
}
}
}
else if (*respawn > 0)
{
// Playing normally.
*respawn = *respawn - 1;
}
}
/*--------------------------------------------------
void K_UpdateBotGameplayVars(player_t *player);
See header file for description.
--------------------------------------------------*/
void K_UpdateBotGameplayVars(player_t *player)
{
if (gamestate != GS_LEVEL || !player->mo)
{
// Not in the level.
return;
}
player->botvars.rubberband = K_UpdateRubberband(player);
player->botvars.turnconfirm += player->cmd.bot.turnconfirm;
// Is a bot not making any progress? Kill it and respawn at next waypoint.
K_IncrementBotRespawn(player, &player->botvars.respawnconfirm, BOTRESPAWNCONFIRM);
if ((player->cmd.bot.respawnconfirm >= TICRATE) && (player->botvars.respawnconfirm >= BOTRESPAWNCONFIRM))
{
// Now a clean function! Neat, eh?
K_SetRespawnAtNextWaypoint(player);
// WHAT ARE YOU DOING??? RACE ALREADY!
P_DamageMobj(player->mo, NULL, NULL, 1, DMG_INSTAKILL);
player->botvars.respawnconfirm = 0;
}
else if (player->cmd.forwardmove < 0)
if (!bd->acceldown && bd->brakedown)
{
// stop drifting if we're reversing
K_BotSetDriftState(player, DRIFTSTATE_AUTO, BOTDRIFTLOCKOUT);
@ -1807,15 +1702,15 @@ void K_UpdateBotGameplayVars(player_t *player)
// Figure out if we need to drift.
// Drift-ending waypoints will kill the drift timer,
// so no need to worry about doing that ourselves.
K_BotStartDrift(player);
K_BotStartDrift(bd, player);
INT32 limit = FixedInt(cv_test3.value) - gamespeed*5;
INT32 dtime = ++player->botvars.drifttime;
INT32 limit = bd->driftstatedelay;
INT32 dtime = ++bd->drifttime;
// the faster we are going, the sooner we need to drift
fixed_t speedfactor = FixedDiv(player->speed, K_GetKartSpeed(player, false, false));
switch (player->botvars.driftstate)
switch (bd->driftstate)
{
case DRIFTSTATE_STARTING:
limit = std::max(0, limit - FixedMul(TICRATE/5, speedfactor));
@ -1838,5 +1733,123 @@ void K_UpdateBotGameplayVars(player_t *player)
}
}
if (cv_kartdebugbot.value != 0 && player - players == displayplayers[0] && !(paused || P_AutoPause()))
{
K_DrawPredictionDebug(bd, player);
}
}
/*--------------------------------------------------
void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd)
See header file for description.
--------------------------------------------------*/
void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd)
{
ZoneScoped;
botdata_t *bd = &botdata[player - players];
// Remove any existing controls
memset(cmd, 0, sizeof(ticcmd_t));
if (player->mo == nullptr
|| player->spectator == true
|| G_GamestateUsesLevel() == false)
{
// Not in the level.
return;
}
cmd->angle = player->mo->angle >> TICCMD_REDUCE;
// Complete override of all ticcmd functionality.
// May add more hooks to individual pieces of bot ticcmd,
// but this should always be here so anyone can roll
// their own :)
if (LUA_HookTiccmd(player, cmd, HOOK(BotTiccmd)) == true)
return;
if (player->botvars.style == BOT_STYLE_STAY)
{
// Hey, this one's pretty easy :P
return;
}
// BOT_STYLE_NORMAL is the only other style, so...
cmd->forwardmove = !!bd->acceldown * MAXPLMOVE - !!bd->brakedown * MAXPLMOVE/2;
if (bd->itemthrow != 0)
{
cmd->throwdir = bd->itemthrow * KART_FULLTURN;
cmd->buttons |= bd->itemthrow > 0 ? BT_FORWARD : BT_BACKWARD;
}
if (bd->acceldown)
cmd->buttons |= BT_ACCELERATE;
if (bd->brakedown)
cmd->buttons |= BT_BRAKE;
if (bd->driftdown)
cmd->buttons |= BT_DRIFT;
if (bd->itemdown)
cmd->buttons |= BT_ATTACK;
if (bd->dolookback)
cmd->buttons |= BT_LOOKBACK;
if (abs(bd->turnconfirm) >= BOTTURNCONFIRM)
{
// You're commiting to your turn, you're allowed!
cmd->turning = bd->turnamt;
if (P_CanPlayerTurn(player, cmd))
cmd->angle += K_GetKartTurnValue(player, bd->turnamt);
}
}
/*--------------------------------------------------
void K_UpdateBotGameplayVars(player_t *player);
See header file for description.
--------------------------------------------------*/
void K_UpdateBotGameplayVars(player_t *player)
{
if (gamestate != GS_LEVEL || !player->mo)
{
// Not in the level.
return;
}
player->botvars.rubberband = K_UpdateRubberband(player);
K_UpdateBotGameplayVarsItemUsage(player);
}
// resets some botdata stuff after respawning
void K_BotReborn(const player_t *player)
{
botdata_t *bd = &botdata[player - players];
memset(bd, 0, sizeof(*bd));
bd->driftstate = DRIFTSTATE_AUTO;
bd->driftskill = FixedMul(MAXDRIFTSKILL, K_BotDetermineDriftSkill(player));
// drift parameters, just fixed values for now
bd->driftmaxdist = 200 + gamespeed*50;
bd->driftpowerdiv = 40;
bd->driftstatedelay = TICRATE/2 - gamespeed*5;
}
void K_BotResetItemConfirm(const player_t *player, boolean setdelay)
{
botdata_t *bd = &botdata[player - players];
bd->itemconfirm = 0;
if (setdelay)
bd->itemdelay = TICRATE;
}
botdata_t *K_GetBotData(UINT8 num)
{
if (num < 0 || num >= MAXPLAYERS)
return NULL;
return &botdata[num];
}

View file

@ -44,15 +44,67 @@ extern consvar_t cv_botcontrol;
// How long it takes for a Lv.1 bot to decide to pick an item.
#define BOT_ITEM_DECISION_TIME (2*TICRATE)
// Point for bots to aim for
struct botprediction_t
// Minimum percentage for a (non-auto) drift to begin.
#define BOTDRIFTPERCENT (10)
// Minimum turning percentage for an auto drift to begin.
#define DRIFTSTARTPCT (45)
#define BOTDRIFTTICS (2*TICRATE/3)
#define BOTDRIFTLOCKOUT (TICRATE/2)
#define MAXDRIFTSKILL (FRACUNIT/2)
typedef enum
{
fixed_t x, y;
fixed_t radius, baseRadius;
DRIFTSTATE_AUTO,
DRIFTSTATE_STARTING,
DRIFTSTATE_ACTIVE,
DRIFTSTATE_ENDING,
NUMDRIFTSTATES
} ATTRPACK botdrift_e;
// unsynced bot variables, exclusively for use by bot logic and ticcmd generation
// nobody except the server cares about this stuff!
struct botdata_t
{
// Point for bots to aim for
struct
{
fixed_t x, y;
fixed_t radius, baseRadius;
} predict;
tic_t itemdelay; // Delay before using item at all
tic_t itemconfirm; // When high enough, they will use their item
SINT8 turnconfirm; // Confirm turn direction
UINT8 respawnconfirm; // Confirm when respawn is needed.
botdrift_e driftstate; // Drifting state
SINT8 driftturn; // Drifting turn direction
tic_t drifttime; // Time spent drifting
tic_t driftlockout; // do not allow drifting for this many tics
UINT16 driftmaxdist; // how far to look for drift waypoints
UINT16 driftpowerdiv; // arbitrary divider on drift turning
UINT16 driftstatedelay; // max time to wait between drift state changes
fixed_t driftskill; // The bot's "skill" at drifts.
// Determines how soon a bot starts to drift.
boolean acceldown, brakedown, driftdown, itemdown; // buttons
boolean dolookback; // look out behind you
boolean itemwasdown; // last tic's item button
SINT8 itemthrow; // throwdir
INT16 turnamt; // turning
};
// AVAILABLE FOR LUA
void K_BotTicker(const player_t *player);
void K_DrawBotDebugger(const player_t *player);
/*--------------------------------------------------
boolean K_PlayerUsesBotMovement(const player_t *player);
@ -70,7 +122,7 @@ boolean K_PlayerUsesBotMovement(const player_t *player);
/*--------------------------------------------------
boolean K_BotCanTakeCut(player_t *player);
boolean K_BotCanTakeCut(const player_t *player);
Tells if this bot is able to take shortcuts (currently unaffected by offroad,
or has certain items ready).
@ -82,11 +134,11 @@ boolean K_PlayerUsesBotMovement(const player_t *player);
true if able to take shortcuts, otherwise false.
--------------------------------------------------*/
boolean K_BotCanTakeCut(player_t *player);
boolean K_BotCanTakeCut(const player_t *player);
/*--------------------------------------------------
const botcontroller_t *K_GetBotController(mobj_t *mobj);
botcontroller_t *K_GetBotController(const mobj_t *mobj);
Retrieves the current bot controller values from
the player's current sector.
@ -98,7 +150,7 @@ boolean K_BotCanTakeCut(player_t *player);
Pointer to the sector's bot controller struct.
--------------------------------------------------*/
const botcontroller_t *K_GetBotController(mobj_t *mobj);
botcontroller_t *K_GetBotController(const mobj_t *mobj);
/*--------------------------------------------------
@ -261,7 +313,7 @@ UINT8 K_EggboxStealth(fixed_t x, fixed_t y);
/*--------------------------------------------------
boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t y)
boolean K_BotHatesThisSector(const 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.
@ -276,23 +328,10 @@ UINT8 K_EggboxStealth(fixed_t x, fixed_t y);
true if avoiding this sector, false otherwise.
--------------------------------------------------*/
boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t y);
boolean K_BotHatesThisSector(const player_t *player, sector_t *sec, fixed_t x, fixed_t y);
/*--------------------------------------------------
void K_NudgePredictionTowardsObjects(botprediction_t *predict, player_t *player);
Moves the bot's prediction, based on objects around the bot.
Input Arguments:-
predict - The bot's prediction to nudge.
player - Player to compare.
Return:-
None
--------------------------------------------------*/
void K_NudgePredictionTowardsObjects(botprediction_t *predict, const player_t *player);
// Moves the bot's prediction, based on objects around the bot.
void K_NudgePredictionTowardsObjects(botdata_t *bd, const player_t *player);
/*--------------------------------------------------
@ -310,6 +349,21 @@ void K_NudgePredictionTowardsObjects(botprediction_t *predict, const player_t *p
INT32 K_PositionBully(const player_t *player);
/*--------------------------------------------------
boolean K_GetBlockedBubbleItem(const player_t *player, , fixed_t radius)
Searches the blockmap for items to block with the Bubble Shield
Input Arguments:-
player - Bot to run this for.
radius - Radius around player to check for items.
Return:-
false if couldn't find anything, otherwise true to attempt blocking item.
--------------------------------------------------*/
boolean K_GetBlockedBubbleItem(const player_t *player, fixed_t radius);
/*--------------------------------------------------
void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd);
@ -359,54 +413,16 @@ void K_UpdateBotGameplayVarsItemUsage(player_t *player);
void K_UpdateBotGameplayVars(player_t *player);
// Item usage part of ticcmd generation.
void K_BotItemUsage(botdata_t *bd, const player_t *player);
/*--------------------------------------------------
void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt);
void K_BotReborn(const player_t *player);
Item usage part of ticcmd generation.
void K_BotResetItemConfirm(const player_t *player, boolean setdelay);
Input Arguments:-
player - Player to generate the ticcmd for.
cmd - The player's ticcmd to modify.
turnamt - How hard the bot is turning.
Return:-
None
--------------------------------------------------*/
void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt);
/*--------------------------------------------------
fixed_t K_BotDetermineDriftSkill(player_t *player)
Calculates drift skill for a player based on stats.
Input Arguments:-
player - Player to get drift skill for.
Return:-
Calculated drift skill.
--------------------------------------------------*/
fixed_t K_BotDetermineDriftSkill(player_t *player);
/*--------------------------------------------------
void K_BotSetDriftState(player_t *player, botdrift_t newstate, tic_t lockout)
Changes a bot's drift state.
Resets the drift timer if the old and new state are different.
Input Arguments:-
player - Player to set drift state for.
newstate - The new drift state.
lockout - If non-zero, apply drift lockout for this many tics.
Return:-
None
--------------------------------------------------*/
void K_BotSetDriftState(player_t *player, botdrift_t newstate, tic_t lockout);
void K_BotSetDriftState(const player_t *player, botdrift_e newstate, tic_t lockout);
botdata_t *K_GetBotData(UINT8 num);
#ifdef __cplusplus
} // extern "C"

File diff suppressed because it is too large Load diff

View file

@ -137,7 +137,7 @@ UINT8 K_EggboxStealth(fixed_t x, fixed_t y)
Return:-
true if avoiding this sector special, false otherwise.
--------------------------------------------------*/
static boolean K_BotHatesThisSectorsSpecial(player_t *player, sector_t *sec, const boolean flip)
static boolean K_BotHatesThisSectorsSpecial(const player_t *player, sector_t *sec, const boolean flip)
{
terrain_t *terrain = K_GetTerrainForFlatNum(flip ? sec->ceilingpic : sec->floorpic);
@ -174,7 +174,7 @@ static boolean K_BotHatesThisSectorsSpecial(player_t *player, sector_t *sec, con
See header file for description.
--------------------------------------------------*/
boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t y)
boolean K_BotHatesThisSector(const player_t *player, sector_t *sec, fixed_t x, fixed_t y)
{
const boolean flip = (player->mo->eflags & MFE_VERTICALFLIP);
fixed_t highestfloor = INT32_MAX;
@ -644,12 +644,8 @@ static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing)
return BMIT_CONTINUE;
}
/*--------------------------------------------------
void K_NudgePredictionTowardsObjects(botprediction_t *predict, const player_t *player)
See header file for description.
--------------------------------------------------*/
void K_NudgePredictionTowardsObjects(botprediction_t *predict, const player_t *player)
// See header file for description.
void K_NudgePredictionTowardsObjects(botdata_t *bd, const player_t *player)
{
ZoneScoped;
@ -672,20 +668,14 @@ void K_NudgePredictionTowardsObjects(botprediction_t *predict, const player_t *p
SINT8 gotoSide = -1;
UINT8 i;
if (predict == NULL)
{
ps_bots[player - players].nudge += I_GetPreciseTime() - time;
return;
}
distToPredict = R_PointToDist2(player->mo->x, player->mo->y, predict->x, predict->y);
angleToPredict = R_PointToAngle2(player->mo->x, player->mo->y, predict->x, predict->y);
distToPredict = R_PointToDist2(player->mo->x, player->mo->y, bd->predict.x, bd->predict.y);
angleToPredict = R_PointToAngle2(player->mo->x, player->mo->y, bd->predict.x, bd->predict.y);
radToPredict = distToPredict >> 1;
g_nudgeSearch.distancetocheck = FixedMul(radToPredict, radToPredict);
baseNudge = predict->baseRadius >> 3;
maxNudge = predict->baseRadius - baseNudge;
baseNudge = bd->predict.baseRadius >> 3;
maxNudge = bd->predict.baseRadius - baseNudge;
g_nudgeSearch.botmo = player->mo;
g_nudgeSearch.angle = angleToPredict;
@ -735,7 +725,7 @@ void K_NudgePredictionTowardsObjects(botprediction_t *predict, const player_t *p
avgDist = R_PointToDist2(
avgX, avgY,
predict->x, predict->y
bd->predict.x, bd->predict.y
);
// High handling characters dodge better
@ -748,14 +738,14 @@ void K_NudgePredictionTowardsObjects(botprediction_t *predict, const player_t *p
// Point away
nudgeDir = R_PointToAngle2(
avgX, avgY,
predict->x, predict->y
bd->predict.x, bd->predict.y
);
predict->x += FixedMul(nudgeDist, FINECOSINE(nudgeDir >> ANGLETOFINESHIFT));
predict->y += FixedMul(nudgeDist, FINESINE(nudgeDir >> ANGLETOFINESHIFT));
predict->radius = std::max(predict->radius - nudgeDist, baseNudge);
bd->predict.x += FixedMul(nudgeDist, FINECOSINE(nudgeDir >> ANGLETOFINESHIFT));
bd->predict.y += FixedMul(nudgeDist, FINESINE(nudgeDir >> ANGLETOFINESHIFT));
bd->predict.radius = std::max(bd->predict.radius - nudgeDist, baseNudge);
distToPredict = R_PointToDist2(player->mo->x, player->mo->y, predict->x, predict->y);
distToPredict = R_PointToDist2(player->mo->x, player->mo->y, bd->predict.x, bd->predict.y);
// Flip side, since we want to check for objects to steer towards on the side we're NOT dodging.
if (gotoSide == 0)
@ -797,7 +787,7 @@ void K_NudgePredictionTowardsObjects(botprediction_t *predict, const player_t *p
avgY = (g_nudgeSearch.gotoAvgY[gotoSide] / g_nudgeSearch.gotoObjs[gotoSide]) * mapobjectscale;
avgDist = R_PointToDist2(
predict->x, predict->y,
bd->predict.x, bd->predict.y,
avgX, avgY
);
@ -810,23 +800,23 @@ void K_NudgePredictionTowardsObjects(botprediction_t *predict, const player_t *p
if (avgDist <= nudgeDist)
{
predict->x = avgX;
predict->y = avgY;
predict->radius = baseNudge;
bd->predict.x = avgX;
bd->predict.y = avgY;
bd->predict.radius = baseNudge;
}
else
{
// Point towards
nudgeDir = R_PointToAngle2(
predict->x, predict->y,
bd->predict.x, bd->predict.y,
avgX, avgY
);
predict->x += FixedMul(nudgeDist, FINECOSINE(nudgeDir >> ANGLETOFINESHIFT));
predict->y += FixedMul(nudgeDist, FINESINE(nudgeDir >> ANGLETOFINESHIFT));
predict->radius = std::max(predict->radius - nudgeDist, baseNudge);
bd->predict.x += FixedMul(nudgeDist, FINECOSINE(nudgeDir >> ANGLETOFINESHIFT));
bd->predict.y += FixedMul(nudgeDist, FINESINE(nudgeDir >> ANGLETOFINESHIFT));
bd->predict.radius = std::max(bd->predict.radius - nudgeDist, baseNudge);
//distToPredict = R_PointToDist2(player->mo->x, player->mo->y, predict->x, predict->y);
//distToPredict = R_PointToDist2(player->mo->x, player->mo->y, bd->predict.x, bd->predict.y);
}
}
@ -991,3 +981,90 @@ INT32 K_PositionBully(const player_t *player)
return KART_FULLTURN;
}
static mobj_t *bubbleSource;
static fixed_t bubbleDist;
static boolean bubbleBlock;
static inline BlockItReturn_t PIT_BubbleShieldBlock(mobj_t *thing)
{
if (bubbleSource == NULL || P_MobjWasRemoved(bubbleSource))
{
// Invalid?
return BMIT_ABORT;
}
if (thing == NULL || P_MobjWasRemoved(thing))
{
// Invalid?
return BMIT_ABORT;
}
if (thing == bubbleSource)
{
// Don't block yourself!!
return BMIT_CONTINUE;
}
if (thing->player)
{
// We don't block players.
return BMIT_CONTINUE;
}
if (thing->health <= 0)
{
// Dead
return BMIT_CONTINUE;
}
if (!P_IsKartItem(thing->type) && !(thing->flags & MF_SHOOTABLE))
{
// Not a Item.
return BMIT_CONTINUE;
}
if (P_AproxDistance(P_AproxDistance(
bubbleSource->x - thing->x,
bubbleSource->y - thing->y),
(bubbleSource->z - thing->z) / 4) > bubbleDist)
{
// Too far away
return BMIT_CONTINUE;
}
#if 0
if (P_CheckSight(bubbleSource, thing) == false)
{
// Not in sight
return BMIT_CONTINUE;
}
#endif
bubbleBlock = true;
return BMIT_CONTINUE;
}
// Searches the blockmap for items to block with the Bubble Shield
boolean K_GetBlockedBubbleItem(const player_t *player, fixed_t radius)
{
INT32 bx, by, xl, xh, yl, yh;
bubbleSource = player->mo;
bubbleDist = radius;
bubbleBlock = false;
// Use blockmap to check for nearby harmful items.
yh = (unsigned)(bubbleSource->y + bubbleDist - bmaporgy)>>MAPBLOCKSHIFT;
yl = (unsigned)(bubbleSource->y - bubbleDist - bmaporgy)>>MAPBLOCKSHIFT;
xh = (unsigned)(bubbleSource->x + bubbleDist - bmaporgx)>>MAPBLOCKSHIFT;
xl = (unsigned)(bubbleSource->x - bubbleDist - bmaporgx)>>MAPBLOCKSHIFT;
BMBOUNDFIX (xl, xh, yl, yh);
for (by = yl; by <= yh; by++)
for (bx = xl; bx <= xh; bx++)
P_BlockThingsIterator(bx, by, PIT_BubbleShieldBlock);
return bubbleBlock;
}

View file

@ -554,9 +554,9 @@ boolean K_BubbleShieldReflect(mobj_t *t1, mobj_t *t2)
}
else
{
t2->momx = -6*t2->momx;
t2->momy = -6*t2->momy;
t2->momz = -6*t2->momz;
t2->momx = (-1*t2->momx)/2;
t2->momy = (-1*t2->momy)/2;
t2->momz = (-1*t2->momz)/2;
t2->angle += ANGLE_180;
}
if (t2->type == MT_JAWZ)
@ -598,7 +598,15 @@ boolean K_BubbleShieldCollide(mobj_t *t1, mobj_t *t2)
if (t2->flags & MF_SHOOTABLE)
{
P_DamageMobj(t2, t1, t1->target, 1, DMG_NORMAL);
boolean shootable = P_DamageMobj(t2, t1, t1->target, 1, DMG_NORMAL);
if (shootable)
{
// Drain my stuff please.
K_BubbleShieldCollideDrain(t1->target->player, t1);
}
return shootable;
}
// no interaction

View file

@ -19,7 +19,7 @@
#include "k_kart.h"
#include "m_random.h"
#include "r_things.h"
#include "lua_hook.h" // LUA_HookGPRankPoints
#include "lua_hook.h"
struct grandprixinfo grandprixinfo;
@ -641,6 +641,7 @@ void K_RetireBots(void)
K_SetNameForBot(i, skins[skinnum].realname);
bot->score = 0;
LUA_HookPlayer(bot, HOOK(BotJoin));
bot->pflags &= ~PF_NOCONTEST;
}
}

View file

@ -40,7 +40,7 @@
#include "m_random.h"
#include "g_party.h"
#include "h_timers.h"
#include "v_video.h"
#include "k_bot.h" // K_DrawBotDebugger
#define NUMPOSNUMS 10
#define NUMPOSFRAMES 7 // White, three blues, three reds
@ -5068,29 +5068,6 @@ static void K_DrawClusterDebugger(void)
}
}
static void K_DrawBotDebugger(void)
{
if (!cv_kartdebugbot.value || !stplyr->bot)
return;
if (stplyrnum != 0) // only for p1
return;
INT32 vflags = V_6WIDTHSPACE|V_ALLOWLOWERCASE;
static const char *driftstates[] = {
[DRIFTSTATE_AUTO] = "Auto",
[DRIFTSTATE_STARTING] = "Starting",
[DRIFTSTATE_ACTIVE] = "Active",
[DRIFTSTATE_ENDING] = "Ending",
};
V_DrawThinString(24, 100, vflags, va("Drift state: %s", driftstates[stplyr->botvars.driftstate]));
V_DrawThinString(24, 108, vflags|(stplyr->botvars.driftlockout ? V_ORANGEMAP : 0), va("Drift lockout: %d", stplyr->botvars.driftlockout));
V_DrawThinString(24, 116, vflags, va("Drift turn: %d", stplyr->botvars.driftturn));
V_DrawThinString(24, 124, vflags, va("Drift timer: %d", stplyr->botvars.drifttime));
}
void K_drawKartHUD(void)
{
boolean islonesome = false;
@ -5332,9 +5309,11 @@ void K_drawKartHUD(void)
}
K_DrawWaypointDebugger();
K_DrawBotDebugger();
K_DrawClusterDebugger();
K_DrawDirectorDebugger();
if (stplyrnum == 0)
K_DrawBotDebugger(stplyr);
}
// Thank you Haya....

View file

@ -385,10 +385,6 @@ void K_RegisterKartStuff(void)
CV_RegisterVar(&cv_itemtimers);
CV_RegisterVar(&cv_saltyhop);
CV_RegisterVar(&cv_test1);
CV_RegisterVar(&cv_test2);
CV_RegisterVar(&cv_test3);
CV_RegisterVar(&cv_naturalcamera);
}
@ -647,8 +643,7 @@ static void K_KartGetItemResult(player_t *player, SINT8 getitem)
if (getitem == KITEM_HYUDORO) // Hyudoro cooldown
hyubgone = 20*TICRATE;
player->botvars.itemdelay = TICRATE;
player->botvars.itemconfirm = 0;
K_BotResetItemConfirm(player, true);
player->itemtype = K_ItemResultToType(getitem);
UINT8 itemamount = K_ItemResultToAmount(getitem);
@ -1226,7 +1221,7 @@ INT32 K_KartGetLegacyItemOdds(UINT8 pos, SINT8 item, fixed_t clusterDist, fixed_
//{ SRB2kart Roulette Code - Distance Based, yes waypoints
UINT8 K_FindUseodds(player_t *player, fixed_t mashed, UINT32 pdis, UINT8 bestbumper, boolean spbrush)
UINT8 K_FindUseodds(const player_t *player, fixed_t mashed, UINT32 pdis, UINT8 bestbumper, boolean spbrush)
{
UINT8 i;
UINT8 useodds = 0;
@ -3440,7 +3435,7 @@ void K_MomentumToFacing(player_t *player)
player->mo->momy = FixedMul(player->mo->momy - player->cmomy, player->mo->friction) + player->cmomy;
}
boolean K_ApplyOffroad(player_t *player)
boolean K_ApplyOffroad(const player_t *player)
{
boolean sneakertimer = CANTCHAINOFFROAD ? (player->sneakertimer && player->realsneakertimer) : player->sneakertimer > 0;
@ -3452,7 +3447,7 @@ boolean K_ApplyOffroad(player_t *player)
return true;
}
fixed_t K_PlayerTripwireSpeedThreshold(player_t *player)
fixed_t K_PlayerTripwireSpeedThreshold(const player_t *player)
{
fixed_t required_speed = 2 * K_GetKartSpeed(player, false, false); // 200%
@ -3476,7 +3471,7 @@ fixed_t K_PlayerTripwireSpeedThreshold(player_t *player)
return required_speed;
}
tripwirepass_t K_TripwirePassConditions(player_t *player)
tripwirepass_t K_TripwirePassConditions(const player_t *player)
{
if (
player->invincibilitytimer ||
@ -3499,7 +3494,7 @@ tripwirepass_t K_TripwirePassConditions(player_t *player)
return TRIPWIRE_NONE;
}
boolean K_TripwirePass(player_t *player)
boolean K_TripwirePass(const player_t *player)
{
return (player->tripwirePass != TRIPWIRE_NONE);
}
@ -4164,7 +4159,7 @@ fixed_t K_GetKartSpeedFromStat(UINT8 kartspeed, boolean karmabomb)
return finalspeed;
}
fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower, boolean dorubberband)
fixed_t K_GetKartSpeed(const player_t *player, boolean doboostpower, boolean dorubberband)
{
boolean karmabomb = ((gametyperules & GTR_KARMA) && player->bumper <= 0);
fixed_t finalspeed;
@ -4204,7 +4199,7 @@ fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower, boolean dorubberb
return finalspeed;
}
fixed_t K_GetKartAccel(player_t *player)
fixed_t K_GetKartAccel(const player_t *player)
{
fixed_t k_accel = 32; // 36;
UINT8 kartspeed = player->kartspeed;
@ -4217,7 +4212,7 @@ fixed_t K_GetKartAccel(player_t *player)
return FixedMul(k_accel, FRACUNIT+player->accelboost);
}
UINT16 K_GetKartFlashing(player_t *player)
UINT16 K_GetKartFlashing(const player_t *player)
{
UINT16 tics = flashingtics;
@ -4230,7 +4225,7 @@ UINT16 K_GetKartFlashing(player_t *player)
return tics;
}
boolean K_PlayerShrinkCheat(player_t *player)
boolean K_PlayerShrinkCheat(const player_t *player)
{
if (cv_kartdebugshrink.value)
return true;
@ -4261,20 +4256,20 @@ void K_UpdateShrinkCheat(player_t *player)
}
}
boolean K_KartKickstart(player_t *player)
boolean K_KartKickstart(const player_t *player)
{
return ((player->pflags & PF_KICKSTARTACCEL)
&& (!K_PlayerUsesBotMovement(player))
&& (player->kickstartaccel >= ACCEL_KICKSTART));
}
UINT16 K_GetKartButtons(player_t *player)
UINT16 K_GetKartButtons(const player_t *player)
{
return (player->cmd.buttons |
(K_KartKickstart(player) ? BT_ACCELERATE : 0));
}
SINT8 K_GetForwardMove(player_t *player)
SINT8 K_GetForwardMove(const player_t *player)
{
SINT8 forwardmove = player->cmd.forwardmove;
@ -4305,7 +4300,7 @@ SINT8 K_GetForwardMove(player_t *player)
return forwardmove;
}
SINT8 K_GetSideMove(player_t *player)
SINT8 K_GetSideMove(const player_t *player)
{
SINT8 sidemove = player->cmd.sidemove;
@ -4325,7 +4320,7 @@ SINT8 K_GetSideMove(player_t *player)
return sidemove;
}
fixed_t K_GetNewSpeed(player_t *player)
fixed_t K_GetNewSpeed(const player_t *player)
{
const fixed_t accelmax = 4000;
const fixed_t p_speed = K_GetKartSpeed(player, true, true);
@ -4365,7 +4360,7 @@ fixed_t K_GetNewSpeed(player_t *player)
return finalspeed;
}
fixed_t K_3dKartMovement(player_t *player, boolean onground, SINT8 forwardmove)
fixed_t K_3dKartMovement(const player_t *player, boolean onground, SINT8 forwardmove)
{
fixed_t finalspeed = K_GetNewSpeed(player);
@ -4662,7 +4657,7 @@ INT32 K_ExplodePlayer(player_t *player, mobj_t *inflictor, mobj_t *source) // A
return ringburst;
}
boolean K_IsPlayerDamaged(player_t *player)
boolean K_IsPlayerDamaged(const player_t *player)
{
return ((player->spinouttimer > 0) || (player->flipovertimer > 0));
}
@ -9781,7 +9776,7 @@ static void K_UpdatePlayerWaypoints(player_t *const player)
player->pflags &= ~PF_TRUSTWAYPOINTS; // clear special exception
}
INT32 K_GetKartRingPower(player_t *player, boolean boosted)
INT32 K_GetKartRingPower(const player_t *player, boolean boosted)
{
fixed_t ringPower = ((9 - player->kartspeed) + (9 - player->kartweight)) * (FRACUNIT/2);
UINT16 finalPower = 0;
@ -9845,7 +9840,7 @@ boolean K_CheckPlayersRespawnColliding(INT32 playernum, fixed_t x, fixed_t y)
// countersteer is how strong the controls are telling us we are turning
// turndir is the direction the controls are telling us to turn, -1 if turning right and 1 if turning left
static INT16 K_GetKartDriftValue(player_t *player, fixed_t countersteer)
static INT16 K_GetKartDriftValue(const player_t *player, fixed_t countersteer)
{
INT16 basedrift, driftangle;
fixed_t driftweight = player->kartweight*14; // 12
@ -9872,7 +9867,7 @@ static INT16 K_GetKartDriftValue(player_t *player, fixed_t countersteer)
return basedrift + FixedMul(driftangle, countersteer);
}
INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue)
INT16 K_GetKartTurnValue(const player_t *player, INT16 turnvalue)
{
fixed_t p_topspeed = K_GetKartSpeed(player, false, false);
fixed_t p_curspeed = min(player->speed, p_topspeed * 2);
@ -9918,7 +9913,7 @@ INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue)
return turnvalue;
}
INT32 K_GetKartDriftSparkValue(player_t *player)
INT32 K_GetKartDriftSparkValue(const player_t *player)
{
UINT8 kartspeed = ((gametyperules & GTR_KARMA) && player->bumper <= 0)
? 1
@ -9926,7 +9921,7 @@ INT32 K_GetKartDriftSparkValue(player_t *player)
return (26*4 + kartspeed*2 + (9 - player->kartweight))*8;
}
INT32 K_GetKartDriftSparkValueForStage(player_t *player, UINT8 stage)
INT32 K_GetKartDriftSparkValueForStage(const player_t *player, UINT8 stage)
{
fixed_t mul = FRACUNIT;
@ -10850,7 +10845,7 @@ void K_StripOther(player_t *player)
}
}
SINT8 K_Sliptiding(player_t *player)
SINT8 K_Sliptiding(const player_t *player)
{
return player->drift ? 0 : player->aizdriftstrat;
}
@ -10897,7 +10892,7 @@ static void K_AirFailsafe(player_t *player)
//
// K_PlayerBaseFriction
//
fixed_t K_PlayerBaseFriction(player_t *player, fixed_t original)
fixed_t K_PlayerBaseFriction(const player_t *player, fixed_t original)
{
const fixed_t factor = FixedMul(
FixedDiv(FRACUNIT - original, FRACUNIT - ORIG_FRICTION),
@ -11023,7 +11018,7 @@ void K_SetTireGrease(player_t *player, tic_t tics)
player->tiregrease = tics;
}
boolean K_SlopeResistance(player_t *player)
boolean K_SlopeResistance(const player_t *player)
{
if (player->tiregrease)
return true;
@ -11165,7 +11160,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
if (ATTACK_IS_DOWN && player->eggmanexplode <= 3*TICRATE && player->eggmanexplode > 1)
{
player->eggmanexplode = 1;
player->botvars.itemconfirm = 0;
K_BotResetItemConfirm(player, false);
}
}
// Eggman Monitor throwing
@ -11177,7 +11172,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
K_PlayAttackTaunt(player->mo);
player->itemflags &= ~IF_EGGMANOUT;
K_UpdateHnextList(player, true);
player->botvars.itemconfirm = 0;
K_BotResetItemConfirm(player, false);
}
}
// Rocket Sneaker usage
@ -11191,7 +11186,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
player->rocketsneakertimer = 1;
else
player->rocketsneakertimer -= 2*TICRATE;
player->botvars.itemconfirm = 0;
K_BotResetItemConfirm(player, false);
}
}
// Flame Shield Usage
@ -11304,7 +11299,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
K_DoSneaker(player, SNEAKERTYPE_SNEAKER);
K_PlayBoostTaunt(player->mo);
player->itemamount--;
player->botvars.itemconfirm = 0;
K_BotResetItemConfirm(player, false);
}
break;
case KITEM_ROCKETSNEAKER:
@ -11338,7 +11333,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
P_SetTarget(&prev->hnext, mo);
prev = mo;
}
player->botvars.itemconfirm = 0;
K_BotResetItemConfirm(player, false);
}
break;
case KITEM_INVINCIBILITY:
@ -11347,7 +11342,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
K_DoInvincibility(player, K_GetInvincibilityTime(player));
K_PlayPowerGloatSound(player->mo);
player->itemamount--;
player->botvars.itemconfirm = 0;
K_BotResetItemConfirm(player, false);
}
break;
case KITEM_BANANA:
@ -11378,7 +11373,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
P_SetTarget(&prev->hnext, mo);
prev = mo;
}
player->botvars.itemconfirm = 0;
K_BotResetItemConfirm(player, false);
}
else if (ATTACK_IS_DOWN && (player->itemflags & IF_ITEMOUT)) // Banana x3 thrown
@ -11387,7 +11382,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
K_ThrowKartItem(player, false, MT_BANANA, -1, 0);
K_PlayAttackTaunt(player->mo);
K_UpdateHnextList(player, false);
player->botvars.itemconfirm = 0;
K_BotResetItemConfirm(player, false);
}
break;
case KITEM_EGGMAN:
@ -11408,7 +11403,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
P_SetTarget(&mo->target, player->mo);
P_SetTarget(&player->mo->hnext, mo);
}
player->botvars.itemconfirm = 0;
K_BotResetItemConfirm(player, false);
}
break;
case KITEM_ORBINAUT:
@ -11443,7 +11438,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
P_SetTarget(&prev->hnext, mo);
prev = mo;
}
player->botvars.itemconfirm = 0;
K_BotResetItemConfirm(player, false);
}
else if (ATTACK_IS_DOWN && (player->itemflags & IF_ITEMOUT)) // Orbinaut x3 thrown
{
@ -11451,7 +11446,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
K_ThrowKartItem(player, true, MT_ORBINAUT, 1, 0);
K_PlayAttackTaunt(player->mo);
K_UpdateHnextList(player, false);
player->botvars.itemconfirm = 0;
K_BotResetItemConfirm(player, false);
}
break;
case KITEM_JAWZ:
@ -11485,7 +11480,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
P_SetTarget(&prev->hnext, mo);
prev = mo;
}
player->botvars.itemconfirm = 0;
K_BotResetItemConfirm(player, false);
}
else if (ATTACK_IS_DOWN && HOLDING_ITEM && (player->itemflags & IF_ITEMOUT)) // Jawz thrown
{
@ -11496,7 +11491,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
K_ThrowKartItem(player, true, MT_JAWZ_DUD, -1, 0);
K_PlayAttackTaunt(player->mo);
K_UpdateHnextList(player, false);
player->botvars.itemconfirm = 0;
K_BotResetItemConfirm(player, false);
}
break;
case KITEM_MINE:
@ -11515,7 +11510,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
P_SetTarget(&mo->target, player->mo);
P_SetTarget(&player->mo->hnext, mo);
}
player->botvars.itemconfirm = 0;
K_BotResetItemConfirm(player, false);
}
else if (ATTACK_IS_DOWN && (player->itemflags & IF_ITEMOUT))
{
@ -11524,7 +11519,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
K_PlayAttackTaunt(player->mo);
player->itemflags &= ~IF_ITEMOUT;
K_UpdateHnextList(player, true);
player->botvars.itemconfirm = 0;
K_BotResetItemConfirm(player, false);
}
break;
case KITEM_LANDMINE:
@ -11533,7 +11528,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
player->itemamount--;
K_ThrowLandMine(player);
K_PlayAttackTaunt(player->mo);
player->botvars.itemconfirm = 0;
K_BotResetItemConfirm(player, false);
}
break;
case KITEM_BALLHOG:
@ -11542,7 +11537,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
player->itemamount--;
K_ThrowKartItem(player, true, MT_BALLHOG, 1, 0);
K_PlayAttackTaunt(player->mo);
player->botvars.itemconfirm = 0;
K_BotResetItemConfirm(player, false);
}
break;
case KITEM_SPB:
@ -11551,7 +11546,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
player->itemamount--;
K_ThrowKartItem(player, true, MT_SPB, 1, 0);
K_PlayAttackTaunt(player->mo);
player->botvars.itemconfirm = 0;
K_BotResetItemConfirm(player, false);
}
break;
case KITEM_GROW:
@ -11595,7 +11590,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
S_StartSound(player->mo, sfx_kc5a);
}
player->botvars.itemconfirm = 0;
K_BotResetItemConfirm(player, false);
}
break;
case KITEM_SHRINK:
@ -11604,7 +11599,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
K_DoShrink(player);
player->itemamount--;
K_PlayPowerGloatSound(player->mo);
player->botvars.itemconfirm = 0;
K_BotResetItemConfirm(player, false);
}
break;
case KITEM_THUNDERSHIELD:
@ -11629,7 +11624,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
// ...:dumbestass:
player->itemamount--;
K_PlayAttackTaunt(player->mo);
player->botvars.itemconfirm = 0;
K_BotResetItemConfirm(player, false);
}
}
break;
@ -11719,7 +11714,7 @@ bubbledeflate:
player->itemamount--;
K_DoHyudoroSteal(player); // yes. yes they do.
K_PlayAttackTaunt(player->mo);
player->botvars.itemconfirm = 0;
K_BotResetItemConfirm(player, false);
}
break;
case KITEM_POGOSPRING:
@ -11729,7 +11724,7 @@ bubbledeflate:
K_PlayBoostTaunt(player->mo);
K_DoPogoSpring(player->mo, 32<<FRACBITS, 2);
player->pogospring = 1;
player->botvars.itemconfirm = 0;
K_BotResetItemConfirm(player, false);
}
break;
case KITEM_SUPERRING:
@ -11737,7 +11732,7 @@ bubbledeflate:
{
player->itemamount--;
K_AwardScaledPlayerRings(player, ASR_SUPERRING);
player->botvars.itemconfirm = 0;
K_BotResetItemConfirm(player, false);
}
break;
case KITEM_KITCHENSINK:
@ -11756,7 +11751,7 @@ bubbledeflate:
P_SetTarget(&mo->target, player->mo);
P_SetTarget(&player->mo->hnext, mo);
}
player->botvars.itemconfirm = 0;
K_BotResetItemConfirm(player, false);
}
else if (ATTACK_IS_DOWN && HOLDING_ITEM && (player->itemflags & IF_ITEMOUT)) // Sink thrown
{
@ -11765,7 +11760,7 @@ bubbledeflate:
K_PlayAttackTaunt(player->mo);
player->itemflags &= ~IF_ITEMOUT;
K_UpdateHnextList(player, true);
player->botvars.itemconfirm = 0;
K_BotResetItemConfirm(player, false);
}
break;
case KITEM_SAD:
@ -11774,7 +11769,7 @@ bubbledeflate:
{
player->sadtimer = stealtime;
player->itemamount--;
player->botvars.itemconfirm = 0;
K_BotResetItemConfirm(player, false);
}
break;
default:

View file

@ -191,7 +191,7 @@ fixed_t K_GetKartGameSpeedScalar(SINT8 value);
extern consvar_t *KartItemCVars[NUMKARTRESULTS-1];
UINT8 K_FindUseodds(player_t *player, fixed_t mashed, UINT32 pdis, UINT8 bestbumper, boolean spbrush);
UINT8 K_FindUseodds(const player_t *player, fixed_t mashed, UINT32 pdis, UINT8 bestbumper, boolean spbrush);
INT32 K_FindLegacyUseodds(player_t *player, fixed_t mashed, INT32 pingame, INT32 bestbumper, boolean spbrush, boolean dontforcespb);
fixed_t K_ItemOddsScale(UINT8 numPlayers, boolean spbrush);
UINT32 K_ScaleItemDistance(UINT32 distance, UINT8 numPlayers, boolean spbrush);
@ -227,7 +227,7 @@ void K_BattleAwardHit(player_t *player, player_t *victim, mobj_t *inflictor, UIN
void K_SpinPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 type);
void K_FlipPlayer(player_t *player, mobj_t *inflictor, mobj_t *source);
INT32 K_ExplodePlayer(player_t *player, mobj_t *inflictor, mobj_t *source);
boolean K_IsPlayerDamaged(player_t *player);
boolean K_IsPlayerDamaged(const player_t *player);
void K_HandleBumperChanges(player_t *player, UINT8 prevBumpers);
void K_DestroyBumpers(player_t *player, UINT8 amount);
void K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount);
@ -263,13 +263,13 @@ void K_RemoveBubbleHealth(player_t *player, INT16 sub);
void K_RepairOrbitChain(mobj_t *orbit);
void K_CalculateBananaSlope(mobj_t *mobj, fixed_t x, fixed_t y, fixed_t z, fixed_t radius, fixed_t height, boolean flip, boolean player);
player_t *K_FindJawzTarget(mobj_t *actor, player_t *source);
INT32 K_GetKartRingPower(player_t *player, boolean boosted);
INT32 K_GetKartRingPower(const player_t *player, boolean boosted);
size_t K_NextRespawnWaypointIndex(waypoint_t *waypoint);
boolean K_CheckPlayersRespawnColliding(INT32 playernum, fixed_t x, fixed_t y);
void K_SetRespawnAtNextWaypoint(player_t * player);
INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue);
INT32 K_GetKartDriftSparkValue(player_t *player);
INT32 K_GetKartDriftSparkValueForStage(player_t *player, UINT8 stage);
INT16 K_GetKartTurnValue(const player_t *player, INT16 turnvalue);
INT32 K_GetKartDriftSparkValue(const player_t *player);
INT32 K_GetKartDriftSparkValueForStage(const player_t *player, UINT8 stage);
void K_SpawnDriftBoostExplosion(player_t *player, int stage);
void K_SpawnDriftElectricSparks(player_t *player);
INT32 K_GetDriftAngleOffset(player_t *player);
@ -284,31 +284,31 @@ void K_DropKitchenSink(player_t *player);
void K_StripItems(player_t *player);
void K_StripOther(player_t *player);
void K_MomentumToFacing(player_t *player);
boolean K_ApplyOffroad(player_t *player);
fixed_t K_PlayerTripwireSpeedThreshold(player_t *player);
tripwirepass_t K_TripwirePassConditions(player_t *player);
boolean K_TripwirePass(player_t *player);
boolean K_ApplyOffroad(const player_t *player);
fixed_t K_PlayerTripwireSpeedThreshold(const player_t *player);
tripwirepass_t K_TripwirePassConditions(const player_t *player);
boolean K_TripwirePass(const player_t *player);
boolean K_WaterRun(mobj_t *mobj);
void K_SpawnWaterTrail(mobj_t *mobj);
boolean K_ItemMobjAllowedtoWaterRun(mobj_t *item);
void K_SetTireGrease(player_t *player, tic_t tics);
boolean K_SlopeResistance(player_t *player);
boolean K_SlopeResistance(const player_t *player);
void K_SquishPlayer(player_t *player, mobj_t *inflictor, mobj_t *source);
void K_ApplyTripWire(player_t *player, tripwirestate_t state);
fixed_t K_GetKartSpeedFromStat(UINT8 kartspeed, boolean karmabomb);
fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower, boolean dorubberbanding);
fixed_t K_GetKartAccel(player_t *player);
UINT16 K_GetKartFlashing(player_t *player);
boolean K_PlayerShrinkCheat(player_t *player);
fixed_t K_GetKartSpeed(const player_t *player, boolean doboostpower, boolean dorubberbanding);
fixed_t K_GetKartAccel(const player_t *player);
UINT16 K_GetKartFlashing(const player_t *player);
boolean K_PlayerShrinkCheat(const player_t *player);
void K_UpdateShrinkCheat(player_t *player);
boolean K_KartKickstart(player_t *player);
UINT16 K_GetKartButtons(player_t *player);
SINT8 K_GetForwardMove(player_t *player);
SINT8 K_GetSideMove(player_t *player);
fixed_t K_GetNewSpeed(player_t *player);
fixed_t K_3dKartMovement(player_t *player, boolean onground, SINT8 fowardmove);
SINT8 K_Sliptiding(player_t *player);
fixed_t K_PlayerBaseFriction(player_t *player, fixed_t original);
boolean K_KartKickstart(const player_t *player);
UINT16 K_GetKartButtons(const player_t *player);
SINT8 K_GetForwardMove(const player_t *player);
SINT8 K_GetSideMove(const player_t *player);
fixed_t K_GetNewSpeed(const player_t *player);
fixed_t K_3dKartMovement(const player_t *player, boolean onground, SINT8 fowardmove);
SINT8 K_Sliptiding(const player_t *player);
fixed_t K_PlayerBaseFriction(const player_t *player, fixed_t original);
void K_MoveKartPlayer(player_t *player, boolean onground);
void K_CheckSpectateStatus(boolean considermapreset);
UINT8 K_GetInvincibilityItemFrame(void);

View file

@ -38,6 +38,8 @@
#include "p_spec.h" // P_StartQuake
#include "i_system.h" // I_GetPreciseTime, I_GetPrecisePrecision
#include "v_video.h"
#include "k_terrain.h"
#include "k_bot.h"
#include "lua_script.h"
#include "lua_libs.h"
@ -45,8 +47,6 @@
#include "taglist.h" // P_FindSpecialLineFromTag
#include "lua_hook.h" // hook_cmd_running errors
#include "k_terrain.h"
#define NOHUD if (hud_running)\
return luaL_error(L, "HUD rendering code should not call this function!");\
else if (hook_cmd_running)\
@ -230,15 +230,21 @@ static const struct {
{META_LUABANKS, "luabanks[]"},
{META_ACTIVATOR, "activator_t"},
{META_FOLLOWER, "follower_t"},
{META_SONICLOOPVARS, "sonicloopvars_t"},
{META_SONICLOOPCAMVARS, "sonicloopcamvars_t"},
{META_BOTVARS, "botvars_t"},
{META_BOTCONTROLLER, "botcontroller_t"},
{META_BOTDATA, "botdata_t"},
{META_SPLASH, "t_splash_t"},
{META_FOOTSTEP, "t_footstep_t"},
{META_OVERLAY, "t_overlay_t"},
{META_TERRAIN, "terrain_t"},
{META_SONICLOOPVARS, "sonicloopvars_t"},
{META_SONICLOOPCAMVARS, "sonicloopcamvars_t"},
{NULL, NULL}
};
@ -2484,7 +2490,6 @@ static int lib_rSetPlayerSkin(lua_State *L)
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
INT32 i = -1, j = -1;
NOHUD
INLEVEL
if (!player)
return LUA_ErrInvalid(L, "player_t");
@ -4344,6 +4349,148 @@ static int lib_kAwardScaledPlayerRings(lua_State *L)
return 0;
}
static int lib_kPlayerUsesBotMovement(lua_State *L)
{
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
INLEVEL
if (!player)
return LUA_ErrInvalid(L, "player_t");
lua_pushboolean(L, K_PlayerUsesBotMovement(player));
return 1;
}
static int lib_kBotCanTakeCut(lua_State *L)
{
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
INLEVEL
if (!player)
return LUA_ErrInvalid(L, "player_t");
lua_pushboolean(L, K_BotCanTakeCut(player));
return 1;
}
static int lib_kGetBotController(lua_State *L)
{
mobj_t *mobj = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
INLEVEL
if (!mobj)
return LUA_ErrInvalid(L, "mobj_t");
botcontroller_t *botController = K_GetBotController(mobj);
if (botController != NULL)
LUA_PushUserdata(L, botController, META_BOTCONTROLLER);
else
lua_pushnil(L);
return 1;
}
static int lib_kBotMapModifier(lua_State *L)
{
INLEVEL
lua_pushfixed(L, K_BotMapModifier());
return 1;
}
static int lib_kBotRubberband(lua_State *L)
{
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
INLEVEL
if (!player)
return LUA_ErrInvalid(L, "player_t");
lua_pushfixed(L, K_BotRubberband(player));
return 1;
}
static int lib_kUpdateRubberband(lua_State *L)
{
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
INLEVEL
if (!player)
return LUA_ErrInvalid(L, "player_t");
lua_pushfixed(L, K_UpdateRubberband(player));
return 1;
}
static int lib_kDistanceOfLineFromPoint(lua_State *L)
{
fixed_t v1x = luaL_checkfixed(L, 1);
fixed_t v1y = luaL_checkfixed(L, 2);
fixed_t v2x = luaL_checkfixed(L, 3);
fixed_t v2y = luaL_checkfixed(L, 4);
fixed_t cx = luaL_checkfixed(L, 5);
fixed_t cy = luaL_checkfixed(L, 6);
lua_pushfixed(L, K_DistanceOfLineFromPoint(v1x, v1y, v2x, v2y, cx, cy));
return 1;
}
static int lib_kAddBot(lua_State *L)
{
INT32 skinid = -1;
UINT8 difficulty = luaL_checkinteger(L, 2);
botStyle_e style = luaL_checkinteger(L, 3);
UINT8 newplayernum = 0;
// Copypaste of libd_getSprite2Patch, but fails loudly on each fail state instead.
// get skin first!
if (lua_isnumber(L, 1)) // find skin by number
{
skinid = lua_tonumber(L, 1);
if (skinid < 0 || skinid >= numskins)
return luaL_error(L, "skin number %d out of range (0 - %d)", skinid, numskins-1);
}
else // find skin by name
{
const char *name = luaL_checkstring(L, 1);
skinid = R_SkinAvailable(name);
if (skinid == -1)
return luaL_error(L, "could not find skin %s by name", name);
}
INLEVEL
boolean success = K_AddBot(skinid, difficulty, style, &newplayernum);
lua_pushboolean(L, success);
if (success)
LUA_PushUserdata(L, &players[newplayernum - 1], META_PLAYER);
else
lua_pushnil(L);
return 2;
}
static int lib_kSetNameForBot(lua_State *L)
{
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
const char *realname = luaL_checkstring(L, 2);
if (!player)
return LUA_ErrInvalid(L, "player_t");
if (!player->bot)
return luaL_error(L, "You may only change bot names.");
K_SetNameForBot(player-players, realname);
return 0;
}
static int lib_kRemoveBot(lua_State *L)
{
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
INLEVEL
if (!player)
return LUA_ErrInvalid(L, "player_t");
if (!player->bot)
return luaL_error(L, "You may only remove bots.");
CL_RemovePlayer(player-players, KR_LEAVE);
return 0;
}
static int lib_kNextRespawnWaypointIndex(lua_State *L)
{
waypoint_t *waypoint = *((waypoint_t **)luaL_checkudata(L, 1, META_WAYPOINT));
@ -5141,6 +5288,19 @@ static luaL_Reg lib[] = {
// I_System
{"I_GetPreciseTime",lib_iGetPreciseTime},
// k_bot
{"K_PlayerUsesBotMovement", lib_kPlayerUsesBotMovement},
{"K_BotCanTakeCut", lib_kBotCanTakeCut},
{"K_GetBotController", lib_kGetBotController},
{"K_BotMapModifier", lib_kBotMapModifier},
{"K_BotRubberband", lib_kBotRubberband},
{"K_UpdateRubberband", lib_kUpdateRubberband},
{"K_DistanceOfLineFromPoint", lib_kDistanceOfLineFromPoint},
{"K_AddBot", lib_kAddBot},
{"K_SetNameForBot", lib_kSetNameForBot},
// Lua-only function to allow safely removing bots.
{"K_RemoveBot", lib_kRemoveBot},
// k_terrain
{"K_AffectingTerrainActive", lib_kAffectingTerrainActive},
{"K_GetDefaultTerrain", lib_kGetDefaultTerrain},

398
src/lua_botvarslib.c Normal file
View file

@ -0,0 +1,398 @@
// DR. ROBOTNIK'S RING RACERS
//-----------------------------------------------------------------------------
// Copyright (C) 2024 by Kart Krew.
// Copyright (C) 2020 by Sonic Team Junior.
// Copyright (C) 2016 by John "JTE" Muniz.
//
// 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 lua_botvarslib.c
/// \brief player botvars structure library for Lua scripting
#include "doomdef.h"
#include "fastcmp.h"
#include "lua_script.h"
#include "lua_libs.h"
#include "k_bot.h"
enum botvars {
botvars_valid = 0,
botvars_style,
botvars_difficulty,
botvars_diffincrease,
botvars_rival,
botvars_rubberband,
};
static const char *const botvars_opt[] = {
"valid",
"style",
"difficulty",
"diffincrease",
"rival",
"rubberband",
NULL
};
#define UNIMPLEMENTED luaL_error(L, LUA_QL("botvars_t") " field " LUA_QS " is not implemented for Lua and cannot be accessed.", follower_opt[field])
static int botvars_get(lua_State *L)
{
botvars_t *botvars = *((botvars_t **)luaL_checkudata(L, 1, META_BOTVARS));
enum botvars field = luaL_checkoption(L, 2, NULL, botvars_opt);
// This is a property that always exists in a player.
I_Assert(botvars != NULL);
switch (field)
{
case botvars_valid:
lua_pushboolean(L, botvars != NULL);
break;
case botvars_style:
lua_pushinteger(L, botvars->style);
break;
case botvars_difficulty:
lua_pushinteger(L, botvars->difficulty);
break;
case botvars_diffincrease:
lua_pushinteger(L, botvars->diffincrease);
break;
case botvars_rival:
lua_pushboolean(L, botvars->rival);
break;
case botvars_rubberband:
lua_pushfixed(L, botvars->rubberband);
break;
}
return 1;
}
#define NOSET luaL_error(L, LUA_QL("botvars_t") " field " LUA_QS " should not be set directly.", botvars_opt[field])
static int botvars_set(lua_State *L)
{
botvars_t *botvars = *((botvars_t **)luaL_checkudata(L, 1, META_BOTVARS));
enum botvars field = luaL_checkoption(L, 2, botvars_opt[0], botvars_opt);
// This is a property that always exists in a player.
I_Assert(botvars != NULL);
INLEVEL
switch(field)
{
case botvars_valid:
return NOSET;
case botvars_style:
botvars->style = luaL_checkinteger(L, 3);
break;
case botvars_difficulty:
botvars->difficulty = luaL_checkinteger(L, 3);
break;
case botvars_diffincrease:
botvars->diffincrease = luaL_checkinteger(L, 3);
break;
case botvars_rival:
botvars->rival = luaL_checkboolean(L, 3);
break;
case botvars_rubberband:
botvars->rubberband = luaL_checkfixed(L, 3);
break;
}
return 0;
}
#undef NOSET
enum botdata {
botdata_valid,
botdata_itemdelay,
botdata_itemconfirm,
botdata_turnconfirm,
botdata_respawnconfirm,
botdata_driftstate,
botdata_driftturn,
botdata_drifttime,
botdata_driftlockout,
botdata_driftmaxdist,
botdata_driftpowerdiv,
botdata_driftstatedelay,
botdata_driftskill,
botdata_acceldown,
botdata_brakedown,
botdata_driftdown,
botdata_itemdown,
botdata_dolookback,
botdata_itemwasdown,
botdata_itemthrow,
botdata_turnamt,
};
static const char *const botdata_opt[] = {
"valid",
"itemdelay",
"itemconfirm",
"turnconfirm",
"respawnconfirm",
"driftstate",
"driftturn",
"drifttime",
"driftlockout",
"driftmaxdist",
"driftpowerdiv",
"driftstatedelay",
"driftskill",
"acceldown",
"brakedown",
"driftdown",
"itemdown",
"dolookback",
"itemwasdown",
"itemthrow",
"turnamt",
NULL
};
static int botdata_get(lua_State *L)
{
botdata_t *botdata = *((botdata_t **)luaL_checkudata(L, 1, META_BOTDATA));
enum botdata field = luaL_checkoption(L, 2, NULL, botdata_opt);
// it's a static array... i think...
I_Assert(botdata != NULL);
switch (field)
{
case botdata_valid:
lua_pushboolean(L, botdata != NULL);
return 1;
case botdata_itemdelay:
lua_pushinteger(L, botdata->itemdelay);
return 1;
case botdata_itemconfirm:
lua_pushinteger(L, botdata->itemconfirm);
return 1;
case botdata_turnconfirm:
lua_pushinteger(L, botdata->turnconfirm);
return 1;
case botdata_respawnconfirm:
lua_pushinteger(L, botdata->respawnconfirm);
return 1;
case botdata_driftstate:
lua_pushinteger(L, botdata->driftstate);
return 1;
case botdata_driftturn:
lua_pushinteger(L, botdata->driftturn);
return 1;
case botdata_drifttime:
lua_pushinteger(L, botdata->drifttime);
return 1;
case botdata_driftlockout:
lua_pushinteger(L, botdata->driftlockout);
return 1;
case botdata_driftmaxdist:
lua_pushinteger(L, botdata->driftmaxdist);
return 1;
case botdata_driftpowerdiv:
lua_pushinteger(L, botdata->driftpowerdiv);
return 1;
case botdata_driftstatedelay:
lua_pushinteger(L, botdata->driftstatedelay);
return 1;
case botdata_driftskill:
lua_pushfixed(L, botdata->driftskill);
return 1;
case botdata_acceldown:
lua_pushboolean(L, botdata->acceldown);
return 1;
case botdata_brakedown:
lua_pushboolean(L, botdata->brakedown);
return 1;
case botdata_driftdown:
lua_pushboolean(L, botdata->driftdown);
return 1;
case botdata_itemdown:
lua_pushboolean(L, botdata->itemdown);
return 1;
case botdata_dolookback:
lua_pushboolean(L, botdata->dolookback);
return 1;
case botdata_itemwasdown:
lua_pushboolean(L, botdata->itemwasdown);
return 1;
case botdata_itemthrow:
lua_pushinteger(L, botdata->itemthrow);
return 1;
case botdata_turnamt:
lua_pushinteger(L, botdata->turnamt);
return 1;
}
}
#define NOSET luaL_error(L, LUA_QL("botdata_t") " field " LUA_QS " should not be set directly.", botdata_opt[field])
static int botdata_set(lua_State *L)
{
botdata_t *botdata = *((botdata_t **)luaL_checkudata(L, 1, META_BOTDATA));
enum botdata field = luaL_checkoption(L, 2, NULL, botdata_opt);
I_Assert(botdata != NULL);
switch (field)
{
case botdata_valid:
return NOSET;
case botdata_itemdelay:
botdata->itemdelay = luaL_checkinteger(L, 3);
return 0;
case botdata_itemconfirm:
botdata->itemconfirm = luaL_checkinteger(L, 3);
return 0;
case botdata_turnconfirm:
botdata->turnconfirm = luaL_checkinteger(L, 3);
return 0;
case botdata_respawnconfirm:
botdata->respawnconfirm = luaL_checkinteger(L, 3);
return 0;
case botdata_driftstate:
botdata->driftstate = luaL_checkinteger(L, 3);
return 0;
case botdata_driftturn:
botdata->driftturn = luaL_checkinteger(L, 3);
return 0;
case botdata_drifttime:
botdata->drifttime = luaL_checkinteger(L, 3);
return 0;
case botdata_driftlockout:
botdata->driftlockout = luaL_checkinteger(L, 3);
return 0;
case botdata_driftmaxdist:
botdata->driftmaxdist = luaL_checkinteger(L, 3);
return 0;
case botdata_driftpowerdiv:
botdata->driftpowerdiv = luaL_checkinteger(L, 3);
return 0;
case botdata_driftstatedelay:
botdata->driftstatedelay = luaL_checkinteger(L, 3);
return 0;
case botdata_driftskill:
botdata->driftskill = luaL_checkfixed(L, 3);
return 0;
case botdata_acceldown:
botdata->acceldown = luaL_checkboolean(L, 3);
return 0;
case botdata_brakedown:
botdata->brakedown = luaL_checkboolean(L, 3);
return 0;
case botdata_driftdown:
botdata->driftdown = luaL_checkboolean(L, 3);
return 0;
case botdata_itemdown:
botdata->itemdown = luaL_checkboolean(L, 3);
return 0;
case botdata_dolookback:
botdata->dolookback = luaL_checkboolean(L, 3);
return 0;
case botdata_itemwasdown:
botdata->itemwasdown = luaL_checkboolean(L, 3);
return 0;
case botdata_itemthrow:
botdata->itemthrow = luaL_checkinteger(L, 3);
return 0;
case botdata_turnamt:
botdata->turnamt = luaL_checkinteger(L, 3);
return 0;
}
}
#undef NOSET
static int lib_iterateBotData(lua_State *L)
{
INT32 i;
if (lua_gettop(L) < 2)
{
lua_pushcfunction(L, lib_iterateBotData);
return 1;
}
lua_settop(L, 2);
lua_remove(L, 1); // state is unused.
if (!lua_isnil(L, 1))
i = (INT32)(*((botdata_t **)luaL_checkudata(L, 1, META_BOTDATA)) - K_GetBotData(0)) + 1;
else
i = 0;
if (i < MAXPLAYERS)
{
LUA_PushUserdata(L, K_GetBotData(i), META_BOTDATA);
return 1;
}
return 0;
}
static int lib_getBotData(lua_State *L)
{
INT32 i;
if (lua_type(L, 2) == LUA_TNUMBER)
{
i = luaL_checkinteger(L, 2);
if (i < 0 || i >= MAXPLAYERS)
return luaL_error(L, "botdata[] index %d out of range (0 - %d)", i, MAXPLAYERS-1);
LUA_PushUserdata(L, K_GetBotData(i), META_BOTDATA);
return 1;
}
if (fastcmp(luaL_checkstring(L, 2), "iterate"))
{
lua_pushcfunction(L, lib_iterateBotData);
return 1;
}
return 0;
}
static int lib_numBotData(lua_State *L)
{
lua_pushinteger(L, MAXPLAYERS);
return 1;
}
int LUA_BotVarsLib(lua_State *L)
{
luaL_newmetatable(L, META_BOTVARS);
lua_pushcfunction(L, botvars_get);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, botvars_set);
lua_setfield(L, -2, "__newindex");
lua_pop(L,1);
luaL_newmetatable(L, META_BOTDATA);
lua_pushcfunction(L, botdata_get);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, botdata_set);
lua_setfield(L, -2, "__newindex");
lua_pop(L,1);
lua_newuserdata(L, 0);
lua_createtable(L, 0, 2);
lua_pushcfunction(L, lib_getBotData);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, lib_numBotData);
lua_setfield(L, -2, "__len");
lua_setmetatable(L, -2);
lua_setglobal(L, "botdata");
return 0;
}

View file

@ -82,6 +82,8 @@ automatically.
X (PlayerCmd),/* building the player's ticcmd struct */\
X (MusicChange),\
X (VoteThinker),/* Y_VoteTicker */\
X (PlayerUsesBotMovement),/* K_PlayerUsesBotMovement */\
X (BotJoin),\
X (GPRankPoints),/* K_CalculateGPRankPoints */\
X (AddonLoaded),\
@ -136,6 +138,7 @@ void LUA_HookInt(INT32 integer, int hook);
void LUA_HookGamemap(int hook);
void LUA_HookBool(boolean value, int hook);
int LUA_HookPlayer(player_t *, int hook);
int LUA_HookPlayerForceResults(player_t *, int hook);
int LUA_HookTiccmd(player_t *, ticcmd_t *, int hook);
int LUA_HookKey(event_t *event, int hook); // Hooks for key events

View file

@ -676,6 +676,17 @@ int LUA_HookPlayer(player_t *player, int hook_type)
return hook.status;
}
int LUA_HookPlayerForceResults(player_t *player, int hook_type)
{
Hook_State hook;
if (prepare_hook(&hook, 0, hook_type))
{
LUA_PushUserdata(gL, player, META_PLAYER);
call_hooks(&hook, 1, res_force);
}
return hook.status;
}
int LUA_HookTiccmd(player_t *player, ticcmd_t *cmd, int hook_type)
{
Hook_State hook;

View file

@ -107,14 +107,18 @@ extern lua_State *gL;
#define META_FOLLOWER "FOLLOWER_T*"
#define META_WAYPOINT "WAYPOINT_T*"
#define META_SONICLOOPVARS "SONICLOOPVARS_T*"
#define META_SONICLOOPCAMVARS "SONICLOOPCAMVARS_T*"
#define META_BOTVARS "BOTVARS_T*"
#define META_BOTCONTROLLER "BOTCONTROLLER_T*"
#define META_BOTDATA "BOTDATA_T*"
#define META_SPLASH "T_SPLASH_T*"
#define META_FOOTSTEP "T_FOOTSTEP_T*"
#define META_OVERLAY "T_OVERLAY_T*"
#define META_TERRAIN "TERRAIN_T*"
#define META_SONICLOOPVARS "SONICLOOPVARS_T*"
#define META_SONICLOOPCAMVARS "SONICLOOPCAMVARS_T*"
boolean luaL_checkboolean(lua_State *L, int narg);
int LUA_EnumLib(lua_State *L);
@ -134,6 +138,7 @@ int LUA_PolyObjLib(lua_State *L);
int LUA_BlockmapLib(lua_State *L);
int LUA_HudLib(lua_State *L);
int LUA_FollowerLib(lua_State *L);
int LUA_BotVarsLib(lua_State *L);
int LUA_TerrainLib(lua_State *L);
int LUA_WaypointLib(lua_State *L);

View file

@ -57,6 +57,7 @@ enum sector_e {
sector_triggerer,
sector_friction,
sector_gravity,
sector_botcontroller,
sector_action,
sector_args,
sector_stringargs,
@ -91,6 +92,7 @@ static const char *const sector_opt[] = {
"triggerer",
"friction",
"gravity",
"botcontroller",
"action",
"args"
"stringargs",
@ -409,6 +411,20 @@ static const char *const activator_opt[] = {
"sector",
"po",
NULL};
enum botcontroller_e {
botcontroller_valid = 0,
//botcontroller_trick,
botcontroller_flags,
botcontroller_forceangle,
};
static const char *const botcontroller_opt[] = {
"valid",
//"trick",
"flags",
"forceangle",
NULL};
static const char *const array_opt[] ={"iterate",NULL};
static const char *const valid_opt[] ={"valid",NULL};
@ -767,6 +783,9 @@ static int sector_get(lua_State *L)
case sector_gravity: // gravity
lua_pushfixed(L, sector->gravity);
return 1;
case sector_botcontroller: // botController
LUA_PushUserdata(L, &sector->botController, META_BOTCONTROLLER);
return 1;
case sector_action: // action
lua_pushinteger(L, (INT16)sector->action);
return 1;
@ -2690,6 +2709,49 @@ static int activator_get(lua_State *L)
return 0;
}
/////////////////////
// botcontroller_t //
/////////////////////
static int botcontroller_get(lua_State *L)
{
botcontroller_t *botcontroller = *((botcontroller_t **)luaL_checkudata(L, 1, META_BOTCONTROLLER));
enum botcontroller_e field = luaL_checkoption(L, 2, botcontroller_opt[0], botcontroller_opt);
if (!botcontroller)
{
if (field == botcontroller_valid) {
lua_pushboolean(L, false);
return 1;
}
return luaL_error(L, "accessed botcontroller_t doesn't exist anymore.");
}
switch (field)
{
case botcontroller_valid:
lua_pushboolean(L, true);
return 1;
//case botcontroller_trick:
//lua_pushinteger(L, botcontroller->trick);
//return 1;
case botcontroller_flags:
lua_pushinteger(L, botcontroller->flags);
return 1;
case botcontroller_forceangle:
lua_pushangle(L, botcontroller->forceAngle);
return 1;
default:
break;
}
return 0;
}
int LUA_MapLib(lua_State *L)
{
luaL_newmetatable(L, META_SECTORLINES);
@ -2856,6 +2918,12 @@ int LUA_MapLib(lua_State *L)
lua_pushcfunction(L, activator_get);
lua_setfield(L, -2, "__index");
lua_pop(L, 1);
luaL_newmetatable(L, META_BOTCONTROLLER);
lua_pushcfunction(L, botcontroller_get);
lua_setfield(L, -2, "__index");
lua_pop(L, 1);
LUA_PushTaggableObjectArray(L, "sectors",
lib_iterateSectors,

View file

@ -345,6 +345,7 @@ enum player_e
player_spectator,
player_spectatewait,
player_bot,
player_botvars,
player_jointime,
player_spectatorreentry,
player_grieftime,
@ -539,6 +540,7 @@ static const char *const player_opt[] = {
"spectator",
"spectatewait",
"bot",
"botvars",
"jointime",
"spectatorreentry",
"grieftime",
@ -1135,6 +1137,9 @@ static int player_get(lua_State *L)
case player_bot:
lua_pushboolean(L, plr->bot);
break;
case player_botvars:
LUA_PushUserdata(L, &plr->botvars, META_BOTVARS);
break;
case player_jointime:
lua_pushinteger(L, plr->jointime);
break;
@ -1786,6 +1791,8 @@ static int player_set(lua_State *L)
break;
case player_bot:
return NOSET;
case player_botvars:
return NOSET;
case player_jointime:
plr->jointime = (tic_t)luaL_checkinteger(L, 3);
break;

View file

@ -60,6 +60,7 @@ static lua_CFunction liblist[] = {
LUA_BlockmapLib, // blockmap stuff
LUA_HudLib, // HUD stuff
LUA_FollowerLib, // follower_t, followers[]
LUA_BotVarsLib, // botvars_t
LUA_TerrainLib, // t_splash_t, t_footstep_t, t_overlay_t, terrain_t
LUA_WaypointLib, // waypoint_t
NULL

View file

@ -370,10 +370,14 @@ static void M_DrawTickStats(void)
{0}
};
perfstatrow_t bot_time_row[] = {
{"botcmd ", "Bot logic: ", &ps_botticcmd_time},
{0}
};
perfstatrow_t extra_thinker_time_row[] = {
{"lthinkf", "LUAh_ThinkFrame:", &ps_lua_thinkframe_time},
{"acs ", "ACS_Tick: ", &ps_acs_time},
{"botcmd ", "Bot logic: ", &ps_botticcmd_time},
{"other ", "Other: ", &extratime},
{0}
};
@ -417,6 +421,7 @@ static void M_DrawTickStats(void)
perfstatcol_t tictime_col = {20, 20, V_YELLOWMAP, tictime_row};
perfstatcol_t thinker_time_col = {24, 24, V_YELLOWMAP, thinker_time_row};
perfstatcol_t detailed_thinker_time_col = {28, 28, V_YELLOWMAP, detailed_thinker_time_row};
perfstatcol_t bot_time_col = {24, 24, V_YELLOWMAP, bot_time_row};
perfstatcol_t extra_thinker_time_col = {24, 24, V_YELLOWMAP, extra_thinker_time_row};
perfstatcol_t thinkercount_col = {90, 115, V_BLUEMAP, thinkercount_row};
@ -462,6 +467,8 @@ static void M_DrawTickStats(void)
M_DrawPerfTiming(&tictime_col);
M_DrawPerfTiming(&thinker_time_col);
M_DrawPerfTiming(&detailed_thinker_time_col);
if (server)
M_DrawPerfTiming(&bot_time_col);
M_DrawPerfTiming(&extra_thinker_time_col);
draw_row = 10;
@ -504,7 +511,11 @@ void M_DrawPerfStats(void)
}
else if (cv_perfstats.value == PS_BOT) // bot ticcmd
{
if (vid.width < 640 || vid.height < 400) // low resolution
if (!server)
{
V_DrawThinString(30, 30, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, "Not applicable to clients");
}
else if (vid.width < 640 || vid.height < 400) // low resolution
{
// it's not gonna fit very well..
V_DrawThinString(30, 30, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, "Not available for resolutions below 640x400");

View file

@ -174,13 +174,13 @@ void P_CalcChasePostImg(player_t *player, camera_t *thiscam);
boolean P_CanPlayerTurn(player_t *player, ticcmd_t *cmd);
boolean P_PlayerInPain(player_t *player);
boolean P_PlayerInPain(const player_t *player);
void P_ResetPlayer(player_t *player);
boolean P_PlayerCanDamage(player_t *player, mobj_t *thing);
fixed_t P_GetPlayerHeight(player_t *player);
fixed_t P_GetPlayerSpinHeight(player_t *player);
boolean P_IsLocalPlayer(player_t *player);
boolean P_IsLocalPlayer(const player_t *player);
boolean P_IsMachineLocalPlayer(player_t *player);
boolean P_IsDisplayPlayer(player_t *player);
@ -189,12 +189,12 @@ angle_t P_GetLocalAngle(player_t *player);
void P_ForceLocalAngle(player_t *player, angle_t angle, boolean interp);
boolean P_PlayerFullbright(player_t *player);
boolean P_IsObjectInGoop(mobj_t *mo);
boolean P_IsObjectOnGround(mobj_t *mo);
boolean P_IsObjectOnGroundIn(mobj_t *mo, sector_t *sec);
boolean P_IsObjectOnRealGround(mobj_t *mo, sector_t *sec); // SRB2Kart
boolean P_IsObjectInGoop(const mobj_t *mo);
boolean P_IsObjectOnGround(const mobj_t *mo);
boolean P_IsObjectOnGroundIn(const mobj_t *mo, const sector_t *sec);
boolean P_IsObjectOnRealGround(const mobj_t *mo, const sector_t *sec); // SRB2Kart
#define P_IsObjectFlipped(o) (((o)->eflags & MFE_VERTICALFLIP) == MFE_VERTICALFLIP)
boolean P_InQuicksand(mobj_t *mo);
boolean P_InQuicksand(const mobj_t *mo);
boolean P_PlayerHitFloor(player_t *player, boolean fromAir);
void P_SetObjectMomZ(mobj_t *mo, fixed_t value, boolean relative);
@ -328,13 +328,13 @@ void P_SceneryThinker(mobj_t *mobj);
// This does not need to be added to Lua.
// To test it in Lua, check mobj.valid
FUNCINLINE static ATTRINLINE boolean P_MobjWasRemoved(mobj_t *mobj)
FUNCINLINE static ATTRINLINE boolean P_MobjWasRemoved(const mobj_t *mobj)
{
return mobj == NULL || mobj->thinker.function.acp1 != (actionf_p1)P_MobjThinker;
}
fixed_t P_MobjFloorZ(mobj_t *mobj, sector_t *sector, sector_t *boundsec, fixed_t x, fixed_t y, line_t *line, boolean lowest, boolean perfect);
fixed_t P_MobjCeilingZ(mobj_t *mobj, sector_t *sector, sector_t *boundsec, fixed_t x, fixed_t y, line_t *line, boolean lowest, boolean perfect);
fixed_t P_MobjFloorZ(const mobj_t *mobj, const sector_t *sector, const sector_t *boundsec, fixed_t x, fixed_t y, const line_t *line, boolean lowest, boolean perfect);
fixed_t P_MobjCeilingZ(const mobj_t *mobj, const sector_t *sector, const sector_t *boundsec, fixed_t x, fixed_t y, const line_t *line, boolean lowest, boolean perfect);
#define P_GetFloorZ(mobj, sector, x, y, line) P_MobjFloorZ(mobj, sector, NULL, x, y, line, false, false)
#define P_GetCeilingZ(mobj, sector, x, y, line) P_MobjCeilingZ(mobj, sector, NULL, x, y, line, true, false)
#define P_GetFOFTopZ(mobj, sector, fof, x, y, line) P_MobjCeilingZ(mobj, sectors + fof->secnum, sector, x, y, line, false, false)

View file

@ -635,7 +635,7 @@ boolean P_InsideANonSolidFFloor(mobj_t *mobj, ffloor_t *rover)
// Supply boundsec ONLY when checking for specials! It should be the "in-level" sector, and sector the control sector (if separate).
// If set, then this function will iterate through boundsec's linedefs to find the highest contact point on the slope. Non-special-checking
// usage will handle that later.
static fixed_t HighestOnLine(fixed_t radius, fixed_t x, fixed_t y, line_t *line, pslope_t *slope, boolean actuallylowest)
static fixed_t HighestOnLine(fixed_t radius, fixed_t x, fixed_t y, const line_t *line, const pslope_t *slope, boolean actuallylowest)
{
// Alright, so we're sitting on a line that contains our slope sector, and need to figure out the highest point we're touching...
// The solution is simple! Get the line's vertices, and pull each one in along its line until it touches the object's bounding box
@ -733,7 +733,7 @@ static fixed_t HighestOnLine(fixed_t radius, fixed_t x, fixed_t y, line_t *line,
);
}
fixed_t P_MobjFloorZ(mobj_t *mobj, sector_t *sector, sector_t *boundsec, fixed_t x, fixed_t y, line_t *line, boolean lowest, boolean perfect)
fixed_t P_MobjFloorZ(const mobj_t *mobj, const sector_t *sector, const sector_t *boundsec, fixed_t x, fixed_t y, const line_t *line, boolean lowest, boolean perfect)
{
I_Assert(mobj != NULL);
I_Assert(sector != NULL);
@ -810,7 +810,7 @@ fixed_t P_MobjFloorZ(mobj_t *mobj, sector_t *sector, sector_t *boundsec, fixed_t
return sector->floorheight;
}
fixed_t P_MobjCeilingZ(mobj_t *mobj, sector_t *sector, sector_t *boundsec, fixed_t x, fixed_t y, line_t *line, boolean lowest, boolean perfect)
fixed_t P_MobjCeilingZ(const mobj_t *mobj, const sector_t *sector, const sector_t *boundsec, fixed_t x, fixed_t y, const line_t *line, boolean lowest, boolean perfect)
{
I_Assert(mobj != NULL);
I_Assert(sector != NULL);

View file

@ -396,20 +396,12 @@ static void P_NetArchivePlayers(savebuffer_t *save)
// botvars_t
WRITEUINT8(save->p, players[i].bot);
WRITEUINT8(save->p, players[i].botvars.style);
WRITEUINT8(save->p, players[i].botvars.difficulty);
WRITEUINT8(save->p, players[i].botvars.diffincrease);
WRITEUINT8(save->p, players[i].botvars.rival);
WRITEFIXED(save->p, players[i].botvars.rubberband);
WRITEUINT16(save->p, players[i].botvars.controller);
WRITEUINT32(save->p, players[i].botvars.itemdelay);
WRITEUINT32(save->p, players[i].botvars.itemconfirm);
WRITESINT8(save->p, players[i].botvars.turnconfirm);
WRITEUINT32(save->p, players[i].botvars.respawnconfirm);
WRITEFIXED(save->p, players[i].botvars.driftskill);
WRITEINT32(save->p, players[i].botvars.driftstate);
WRITESINT8(save->p, players[i].botvars.driftturn);
WRITEUINT32(save->p, players[i].botvars.drifttime);
WRITEUINT32(save->p, players[i].botvars.driftlockout);
WRITEFIXED(save->p, players[i].outrun);
WRITEUINT8(save->p, players[i].outruntime);
@ -751,20 +743,12 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
// botvars_t
players[i].bot = READUINT8(save->p);
players[i].botvars.style = READUINT8(save->p);
players[i].botvars.difficulty = READUINT8(save->p);
players[i].botvars.diffincrease = READUINT8(save->p);
players[i].botvars.rival = (boolean)READUINT8(save->p);
players[i].botvars.rubberband = READFIXED(save->p);
players[i].botvars.controller = READUINT16(save->p);
players[i].botvars.itemdelay = READUINT32(save->p);
players[i].botvars.itemconfirm = READUINT32(save->p);
players[i].botvars.turnconfirm = READSINT8(save->p);
players[i].botvars.respawnconfirm = READUINT32(save->p);
players[i].botvars.driftskill = READFIXED(save->p);
players[i].botvars.driftstate = READINT32(save->p);
players[i].botvars.driftturn = READSINT8(save->p);
players[i].botvars.drifttime = READUINT32(save->p);
players[i].botvars.driftlockout = READUINT32(save->p);
players[i].outrun = READFIXED(save->p);
players[i].outruntime = READUINT8(save->p);

View file

@ -40,6 +40,7 @@
#include "k_waypoint.h"
#include "k_director.h"
#include "acs/interface.h"
#include "k_bot.h" // K_BotTicker
#ifdef PARANOIA
#include "deh_tables.h" // MOBJTYPE_LIST
@ -767,6 +768,26 @@ void P_Ticker(boolean run)
if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo))
P_PlayerAfterThink(&players[i]);
// run all the bot tickers
if (server)
{
PS_ResetBotInfo();
for (i = 0; i < MAXPLAYERS; i++)
{
player_t *player = &players[i];
if (playeringame[i] && player->mo && !P_MobjWasRemoved(player->mo) && K_PlayerUsesBotMovement(player))
{
const precise_t t = I_GetPreciseTime();
K_BotTicker(player);
ps_bots[i].total = I_GetPreciseTime() - t;
ps_bots[i].isBot = true;
ps_botticcmd_time += ps_bots[i].total;
}
}
}
// Bosses have a punchy start, so no position.
if (bossinfo.boss == true)
{

View file

@ -465,7 +465,7 @@ UINT8 P_FindHighestLap(void)
// Is player in pain??
// Checks for painstate and flashing, if both found return true
//
boolean P_PlayerInPain(player_t *player)
boolean P_PlayerInPain(const player_t *player)
{
if (K_IsPlayerDamaged(player) || player->squishedtimer)
return true;
@ -890,7 +890,7 @@ void P_RestoreMusic(player_t *player)
// Returns true if the object is inside goop water.
// (Spectators and objects otherwise without gravity cannot have goop gravity!)
//
boolean P_IsObjectInGoop(mobj_t *mo)
boolean P_IsObjectInGoop(const mobj_t *mo)
{
if (mo->player && mo->player->spectator)
return false;
@ -908,7 +908,7 @@ boolean P_IsObjectInGoop(mobj_t *mo)
// on the ground. Takes reverse
// gravity and FOFs into account.
//
boolean P_IsObjectOnGround(mobj_t *mo)
boolean P_IsObjectOnGround(const mobj_t *mo)
{
if (P_IsObjectInGoop(mo))
{
@ -953,7 +953,7 @@ boolean P_IsObjectOnGround(mobj_t *mo)
// on the ground in a specific sector. Takes reverse
// gravity and FOFs into account.
//
boolean P_IsObjectOnGroundIn(mobj_t *mo, sector_t *sec)
boolean P_IsObjectOnGroundIn(const mobj_t *mo, const sector_t *sec)
{
ffloor_t *rover;
@ -1040,7 +1040,7 @@ boolean P_IsObjectOnGroundIn(mobj_t *mo, sector_t *sec)
// Really simple, but personally I think it's also incredibly helpful. I think this is fine in p_user.c
// -- Sal
boolean P_IsObjectOnRealGround(mobj_t *mo, sector_t *sec)
boolean P_IsObjectOnRealGround(const mobj_t *mo, const sector_t *sec)
{
// Is the object in reverse gravity?
if (mo->eflags & MFE_VERTICALFLIP)
@ -1132,7 +1132,7 @@ boolean P_IsMachineLocalPlayer(player_t *player)
// on the local machine
// (or simulated party)
//
boolean P_IsLocalPlayer(player_t *player)
boolean P_IsLocalPlayer(const player_t *player)
{
if (player == NULL)
{
@ -1400,7 +1400,7 @@ boolean P_PlayerHitFloor(player_t *player, boolean fromAir)
return clipmomz;
}
boolean P_InQuicksand(mobj_t *mo) // Returns true if you are in quicksand
boolean P_InQuicksand(const mobj_t *mo) // Returns true if you are in quicksand
{
sector_t *sector = mo->subsector->sector;
fixed_t topheight, bottomheight;
@ -2934,6 +2934,12 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
}
else if (P_IsMachineLocalPlayer(player))
{
// Players-turned-bots outside of end of race contexts (Lua)
// don't update their local camera angle, so it should be updated
// somewhere - I choose here because it makes the most sense.
if (K_PlayerUsesBotMovement(player) && !player->bot)
P_ForceLocalAngle(player, mo->angle, true);
focusangle = localangle[num];
focusaiming = localaiming[num];
}

View file

@ -164,7 +164,7 @@ TYPEDEF (bheap_t);
TYPEDEF (weakspot_t);
// k_bot.h
TYPEDEF (botprediction_t);
TYPEDEF (botdata_t);
TYPEDEF (botcontroller_t);
// k_brightmap.h