diff --git a/src/Sourcefile b/src/Sourcefile index e13a8a47f..c5e65c549 100644 --- a/src/Sourcefile +++ b/src/Sourcefile @@ -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 diff --git a/src/d_clisrv.c b/src/d_clisrv.c index f4203fc7d..94f4084f6 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -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; } diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 25c5c1fd6..6f086866c 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -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; } } diff --git a/src/d_netcmd.h b/src/d_netcmd.h index b9a33bb12..0c3c06f75 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -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]; diff --git a/src/d_player.h b/src/d_player.h index 1282b4ad8..45e97f00a 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -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 diff --git a/src/d_ticcmd.h b/src/d_ticcmd.h index da14ded9e..b4137c712 100644 --- a/src/d_ticcmd.h +++ b/src/d_ticcmd.h @@ -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) diff --git a/src/deh_tables.c b/src/deh_tables.c index 257a99d0d..b1c5b1843 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -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}, diff --git a/src/g_demo.c b/src/g_demo.c index f2ea59870..0bdae712a 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -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) diff --git a/src/g_game.c b/src/g_game.c index b1a2d075a..2f0d72dad 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -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); diff --git a/src/k_bot.cpp b/src/k_bot.cpp index f042699c9..6e50bc152 100644 --- a/src/k_bot.cpp +++ b/src/k_bot.cpp @@ -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), + 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(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(disttonext, distanceleft) * FRACUNIT); - predict->y += P_ReturnThrustY(nullptr, angletonext, std::min(disttonext, distanceleft) * FRACUNIT); + bd->predict.x += P_ReturnThrustX(nullptr, angletonext, std::min(disttonext, distanceleft) * FRACUNIT); + bd->predict.y += P_ReturnThrustY(nullptr, angletonext, std::min(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(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]; +} diff --git a/src/k_bot.h b/src/k_bot.h index 139b0d46e..b74722be0 100644 --- a/src/k_bot.h +++ b/src/k_bot.h @@ -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" diff --git a/src/k_botitem.cpp b/src/k_botitem.cpp index d4529bcc7..96da3b04c 100644 --- a/src/k_botitem.cpp +++ b/src/k_botitem.cpp @@ -24,52 +24,22 @@ #include "byteptr.h" #include "d_net.h" // nodetoplayer #include "k_kart.h" -#include "z_zone.h" #include "i_system.h" #include "p_maputl.h" -#include "d_ticcmd.h" #include "m_random.h" #include "r_things.h" // numskins #include "m_easing.h" -/*-------------------------------------------------- - static inline boolean K_ItemButtonWasDown(const player_t *player) - - Looks for players around the bot, and presses the item button - if there is one in range. - - Input Arguments:- - player - Bot to check. - - Return:- - true if the item button was pressed last tic, otherwise false. ---------------------------------------------------*/ -static inline boolean K_ItemButtonWasDown(const player_t *player) -{ - return (player->oldcmd.buttons & BT_ATTACK); -} - -/*-------------------------------------------------- - static boolean K_BotUseItemNearPlayer(const player_t *player, ticcmd_t *cmd, fixed_t radius) - - Looks for players around the bot, and presses the item button - if there is one in range. - - Input Arguments:- - player - Bot to compare against. - cmd - The bot's ticcmd. - radius - The radius to look for players in. - - Return:- - true if a player was found & we can press the item button, otherwise false. ---------------------------------------------------*/ -static boolean K_BotUseItemNearPlayer(const player_t *player, ticcmd_t *cmd, fixed_t radius) +// Looks for players around the bot, and presses the item button +// if there is one in range. +// Returns true if a player was found & we can press the item button, otherwise false. +static boolean K_BotUseItemNearPlayer(botdata_t *bd, const player_t *player, fixed_t radius) { ZoneScoped; UINT8 i; - if (K_ItemButtonWasDown(player) == true) + if (bd->itemwasdown) { return false; } @@ -101,7 +71,7 @@ static boolean K_BotUseItemNearPlayer(const player_t *player, ticcmd_t *cmd, fix if (dist <= radius) { - cmd->buttons |= BT_ATTACK; + bd->itemdown = true; return true; } } @@ -311,21 +281,10 @@ static boolean K_RivalBotAggression(const player_t *bot, const player_t *target) return false; } -/*-------------------------------------------------- - static void K_ItemConfirmForTarget(const player_t *bot, ticcmd_t *cmd, const player_t *target, UINT16 amount) - - Handles updating item confirm values for offense items. - - Input Arguments:- - bot - Bot to check. - cmd - Bot's ticcmd to edit. - target - Who the bot wants to attack. - amount - Amount to increase item confirm time by. - - Return:- - None ---------------------------------------------------*/ -static void K_ItemConfirmForTarget(const player_t *bot, ticcmd_t *cmd, const player_t *target, UINT16 amount) +// Handles updating item confirm values for offense items. +// target - Who the bot wants to attack. +// amount - Amount to increase item confirm time by. +static void K_ItemConfirmForTarget(botdata_t *bd, const player_t *bot, const player_t *target, UINT16 amount) { if (bot == NULL || target == NULL) { @@ -335,86 +294,55 @@ static void K_ItemConfirmForTarget(const player_t *bot, ticcmd_t *cmd, const pla if (K_RivalBotAggression(bot, target) == true) { // Double the rate when you're aggressive. - cmd->bot.itemconfirm += amount << 1; + bd->itemconfirm += amount << 1; } else { // Do as normal. - cmd->bot.itemconfirm += amount; + bd->itemconfirm += amount; } } -/*-------------------------------------------------- - static boolean K_BotGenericPressItem(const player_t *player, ticcmd_t *cmd, SINT8 dir) - Presses the item button & aim buttons for the bot. - - Input Arguments:- - player - Bot to do this for. - cmd - Bot's ticcmd to edit. - dir - Aiming direction: 1 for forwards, -1 for backwards, 0 for neutral. - - Return:- - true if we could press, false if not. ---------------------------------------------------*/ -static boolean K_BotGenericPressItem(const player_t *player, ticcmd_t *cmd, SINT8 dir) +// Presses the item button & aim buttons for the bot. +// dir - Aiming direction: 1 for forwards, -1 for backwards, 0 for neutral. +// Returns true if we could press, false if not. +static boolean K_BotGenericPressItem(botdata_t *bd, SINT8 dir) { ZoneScoped; - if (K_ItemButtonWasDown(player) == true) + if (bd->itemwasdown) { return false; } - cmd->throwdir = KART_FULLTURN * dir; - cmd->buttons |= BT_ATTACK; - //player->botvars.itemconfirm = 0; + bd->itemthrow = dir; + bd->itemdown = true; + //bd->itemconfirm = 0; return true; } -/*-------------------------------------------------- - static void K_BotItemGenericTap(const player_t *player, ticcmd_t *cmd) - - Item usage for generic items that you need to tap. - - Input Arguments:- - player - Bot to do this for. - cmd - Bot's ticcmd to edit. - - Return:- - None ---------------------------------------------------*/ -static void K_BotItemGenericTap(const player_t *player, ticcmd_t *cmd) +// Item usage for generic items that you need to tap. +static void K_BotItemGenericTap(botdata_t *bd) { ZoneScoped; - if (K_ItemButtonWasDown(player) == false) + if (!bd->itemwasdown) { - cmd->buttons |= BT_ATTACK; - //player->botvars.itemconfirm = 0; + bd->itemdown = true; + //bd->itemconfirm = 0; } } -/*-------------------------------------------------- - static boolean K_BotRevealsGenericTrap(const player_t *player, INT16 turnamt, boolean mine) - - Decides if a bot is ready to reveal their trap item or not. - - Input Arguments:- - player - Bot that has the banana. - turnamt - How hard they currently are turning. - mine - Set to true to handle Mine-specific behaviors. - - Return:- - true if we want the bot to reveal their banana, otherwise false. ---------------------------------------------------*/ -static boolean K_BotRevealsGenericTrap(const player_t *player, INT16 turnamt, boolean mine) +// Decides if a bot is ready to reveal their trap item or not. +// mine - Set to true to handle Mine-specific behaviors. +static boolean K_BotRevealsGenericTrap(botdata_t *bd, const player_t *player, boolean mine) { ZoneScoped; const fixed_t coneDist = FixedMul(1280 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); - if (abs(turnamt) >= KART_FULLTURN/2) + if (abs(bd->turnamt) >= KART_FULLTURN/2) { // DON'T reveal on turns, we can place bananas on turns whenever we have multiple to spare, // or if you missed your intentioned throw/place on a player. @@ -444,21 +372,9 @@ static boolean K_BotRevealsGenericTrap(const player_t *player, INT16 turnamt, bo return false; } -/*-------------------------------------------------- - static void K_BotItemGenericTrapShield(const player_t *player, ticcmd_t *cmd, INT16 turnamt, boolean mine) - - Item usage for Eggman shields. - - Input Arguments:- - player - Bot to do this for. - cmd - Bot's ticcmd to edit. - turnamt - How hard they currently are turning. - mine - Set to true to handle Mine-specific behaviors. - - Return:- - None ---------------------------------------------------*/ -static void K_BotItemGenericTrapShield(const player_t *player, ticcmd_t *cmd, INT16 turnamt, boolean mine) +// Item usage for Eggman shields. +// mine - Set to true to handle Mine-specific behaviors. +static void K_BotItemGenericTrapShield(botdata_t *bd, const player_t *player, boolean mine) { ZoneScoped; @@ -467,27 +383,16 @@ static void K_BotItemGenericTrapShield(const player_t *player, ticcmd_t *cmd, IN return; } - cmd->bot.itemconfirm++; + bd->itemconfirm++; - if (K_BotRevealsGenericTrap(player, turnamt, mine) || (player->botvars.itemconfirm > 5*TICRATE)) + if (K_BotRevealsGenericTrap(bd, player, mine) || (bd->itemconfirm > 5*TICRATE)) { - K_BotGenericPressItem(player, cmd, 0); + K_BotGenericPressItem(bd, 0); } } -/*-------------------------------------------------- - static void K_BotItemGenericOrbitShield(const player_t *player, ticcmd_t *cmd) - - Item usage for orbitting shields. - - Input Arguments:- - player - Bot to do this for. - cmd - Bot's ticcmd to edit. - - Return:- - None ---------------------------------------------------*/ -static void K_BotItemGenericOrbitShield(const player_t *player, ticcmd_t *cmd) +// Item usage for orbitting shields. +static void K_BotItemGenericOrbitShield(botdata_t *bd, const player_t *player) { ZoneScoped; @@ -496,22 +401,11 @@ static void K_BotItemGenericOrbitShield(const player_t *player, ticcmd_t *cmd) return; } - K_BotGenericPressItem(player, cmd, 0); + K_BotGenericPressItem(bd, 0); } -/*-------------------------------------------------- - static void K_BotItemSneaker(const player_t *player, ticcmd_t *cmd) - - Item usage for sneakers. - - Input Arguments:- - player - Bot to do this for. - cmd - Bot's ticcmd to edit. - - Return:- - None ---------------------------------------------------*/ -static void K_BotItemSneaker(player_t *player, ticcmd_t *cmd) +// Item usage for sneakers. +static void K_BotItemSneaker(botdata_t *bd, const player_t *player) { ZoneScoped; @@ -525,33 +419,22 @@ static void K_BotItemSneaker(player_t *player, ticcmd_t *cmd) || K_GetWaypointIsShortcut(player->nextwaypoint) == true // Going toward a shortcut! || player->speed < K_GetKartSpeed(player, false, true) / 2 // Being slowed down too much || player->speedboost > (FRACUNIT/8) // Have another type of boost (tethering) - || player->botvars.itemconfirm > 4*TICRATE) // Held onto it for too long + || bd->itemconfirm > 4*TICRATE) // Held onto it for too long { - if (player->sneakertimer == 0 && K_ItemButtonWasDown(player) == false) + if (player->sneakertimer == 0 && !bd->itemwasdown) { - cmd->buttons |= BT_ATTACK; - //player->botvars.itemconfirm = 2*TICRATE; + bd->itemdown = true; + //bd->itemconfirm = 2*TICRATE; } } else { - cmd->bot.itemconfirm++; + bd->itemconfirm++; } } -/*-------------------------------------------------- - static void K_BotItemRocketSneaker(player_t *player, ticcmd_t *cmd) - - Item usage for rocket sneakers. - - Input Arguments:- - player - Bot to do this for. - cmd - Bot's ticcmd to edit. - - Return:- - None ---------------------------------------------------*/ -static void K_BotItemRocketSneaker(const player_t *player, ticcmd_t *cmd) +// Item usage for rocket sneakers. +static void K_BotItemRocketSneaker(botdata_t *bd, const player_t *player) { ZoneScoped; @@ -561,34 +444,22 @@ static void K_BotItemRocketSneaker(const player_t *player, ticcmd_t *cmd) return; } - if (player->botvars.itemconfirm > TICRATE) + if (bd->itemconfirm > TICRATE) { - if (player->sneakertimer == 0 && K_ItemButtonWasDown(player) == false) + if (player->sneakertimer == 0 && !bd->itemwasdown) { - cmd->buttons |= BT_ATTACK; - //player->botvars.itemconfirm = 0; + bd->itemdown = true; + //bd->itemconfirm = 0; } } else { - cmd->bot.itemconfirm++; + bd->itemconfirm++; } } -/*-------------------------------------------------- - static void K_BotItemBanana(player_t *player, ticcmd_t *cmd, INT16 turnamt) - - Item usage for trap item throwing. - - Input Arguments:- - player - Bot to do this for. - cmd - Bot's ticcmd to edit. - turnamt - How hard they currently are turning. - - Return:- - None ---------------------------------------------------*/ -static void K_BotItemBanana(const player_t *player, ticcmd_t *cmd, INT16 turnamt) +// Item usage for trap item throwing. +static void K_BotItemBanana(botdata_t *bd, const player_t *player) { ZoneScoped; @@ -597,19 +468,19 @@ static void K_BotItemBanana(const player_t *player, ticcmd_t *cmd, INT16 turnamt boolean tryLookback = false; player_t *target = NULL; - cmd->bot.itemconfirm++; + bd->itemconfirm++; target = K_PlayerInCone(player, coneDist, 15, true); if (target != NULL) { - K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty); + K_ItemConfirmForTarget(bd, player, target, player->botvars.difficulty); throwdir = -1; tryLookback = true; } - if (abs(turnamt) >= KART_FULLTURN/2) + if (abs(bd->turnamt) >= KART_FULLTURN/2) { - cmd->bot.itemconfirm += player->botvars.difficulty / 2; + bd->itemconfirm += player->botvars.difficulty / 2; throwdir = -1; } else @@ -618,36 +489,24 @@ static void K_BotItemBanana(const player_t *player, ticcmd_t *cmd, INT16 turnamt if (target != NULL) { - K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty * 2); + K_ItemConfirmForTarget(bd, player, target, player->botvars.difficulty * 2); throwdir = 1; } } if (tryLookback == true && throwdir == -1) { - cmd->buttons |= BT_LOOKBACK; + bd->dolookback = true; } - if (player->botvars.itemconfirm > 10*TICRATE || player->bananadrag >= TICRATE) + if (bd->itemconfirm > 10*TICRATE || player->bananadrag >= TICRATE) { - K_BotGenericPressItem(player, cmd, throwdir); + K_BotGenericPressItem(bd, throwdir); } } -/*-------------------------------------------------- - static void K_BotItemMine(player_t *player, ticcmd_t *cmd, INT16 turnamt) - - Item usage for trap item throwing. - - Input Arguments:- - player - Bot to do this for. - cmd - Bot's ticcmd to edit. - turnamt - How hard they currently are turning. - - Return:- - None ---------------------------------------------------*/ -static void K_BotItemMine(const player_t *player, ticcmd_t *cmd, INT16 turnamt) +// Item usage for trap item throwing. +static void K_BotItemMine(botdata_t *bd, const player_t *player) { ZoneScoped; @@ -656,18 +515,18 @@ static void K_BotItemMine(const player_t *player, ticcmd_t *cmd, INT16 turnamt) boolean tryLookback = false; player_t *target = NULL; - cmd->bot.itemconfirm++; + bd->itemconfirm++; target = K_PlayerInCone(player, coneDist, 15, true); if (target != NULL) { - K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty); + K_ItemConfirmForTarget(bd, player, target, player->botvars.difficulty); throwdir = -1; } - if (abs(turnamt) >= KART_FULLTURN/2) + if (abs(bd->turnamt) >= KART_FULLTURN/2) { - cmd->bot.itemconfirm += player->botvars.difficulty / 2; + bd->itemconfirm += player->botvars.difficulty / 2; throwdir = -1; tryLookback = true; } @@ -676,82 +535,59 @@ static void K_BotItemMine(const player_t *player, ticcmd_t *cmd, INT16 turnamt) target = K_PlayerPredictThrow(player, 0); if (target != NULL) { - K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty * 2); + K_ItemConfirmForTarget(bd, player, target, player->botvars.difficulty * 2); throwdir = 0; } target = K_PlayerPredictThrow(player, 1); if (target != NULL) { - K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty * 2); + K_ItemConfirmForTarget(bd, player, target, player->botvars.difficulty * 2); throwdir = 1; } } if (tryLookback == true && throwdir == -1) { - cmd->buttons |= BT_LOOKBACK; + bd->dolookback = true; } - if (player->botvars.itemconfirm > 10*TICRATE || player->bananadrag >= TICRATE) + if (bd->itemconfirm > 10*TICRATE || player->bananadrag >= TICRATE) { - K_BotGenericPressItem(player, cmd, throwdir); + K_BotGenericPressItem(bd, throwdir); } } -/*-------------------------------------------------- - static void K_BotItemLandmine(player_t *player, ticcmd_t *cmd, INT16 turnamt) - - Item usage for landmine tossing. - - Input Arguments:- - player - Bot to do this for. - cmd - Bot's ticcmd to edit. - turnamt - How hard they currently are turning. - - Return:- - None ---------------------------------------------------*/ -static void K_BotItemLandmine(const player_t *player, ticcmd_t *cmd, INT16 turnamt) +// Item usage for landmine tossing. +static void K_BotItemLandmine(botdata_t *bd, const player_t *player) { ZoneScoped; const fixed_t coneDist = FixedMul(1280 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); player_t *target = NULL; - cmd->bot.itemconfirm++; + bd->itemconfirm++; - if (abs(turnamt) >= KART_FULLTURN/2) + if (abs(bd->turnamt) >= KART_FULLTURN/2) { - cmd->bot.itemconfirm += player->botvars.difficulty / 2; + bd->itemconfirm += player->botvars.difficulty / 2; } target = K_PlayerInCone(player, coneDist, 15, true); if (target != NULL) { - K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty); - cmd->buttons |= BT_LOOKBACK; + K_ItemConfirmForTarget(bd, player, target, player->botvars.difficulty); + bd->dolookback = true; } - if (player->botvars.itemconfirm > 10*TICRATE) + if (bd->itemconfirm > 10*TICRATE) { - K_BotGenericPressItem(player, cmd, -1); + K_BotGenericPressItem(bd, -1); } } -/*-------------------------------------------------- - static void K_BotItemEggman(player_t *player, ticcmd_t *cmd) - - Item usage for Eggman item throwing. - - Input Arguments:- - player - Bot to do this for. - cmd - Bot's ticcmd to edit. - - Return:- - None ---------------------------------------------------*/ -static void K_BotItemEggman(const player_t *player, ticcmd_t *cmd) +// Item usage for Eggman item throwing. +static void K_BotItemEggman(botdata_t *bd, const player_t *player) { ZoneScoped; @@ -761,37 +597,37 @@ static void K_BotItemEggman(const player_t *player, ticcmd_t *cmd) boolean tryLookback = false; player_t *target = NULL; - cmd->bot.itemconfirm++; + bd->itemconfirm++; target = K_PlayerPredictThrow(player, 0); if (target != NULL) { - K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty / 2); + K_ItemConfirmForTarget(bd, player, target, player->botvars.difficulty / 2); throwdir = 1; } target = K_PlayerInCone(player, coneDist, 15, true); if (target != NULL) { - K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty); + K_ItemConfirmForTarget(bd, player, target, player->botvars.difficulty); throwdir = -1; tryLookback = true; } if (stealth > 1 || player->itemroulette) { - cmd->bot.itemconfirm += player->botvars.difficulty * 4; + bd->itemconfirm += player->botvars.difficulty * 4; throwdir = -1; } if (tryLookback == true && throwdir == -1) { - cmd->buttons |= BT_LOOKBACK; + bd->dolookback = true; } - if (player->botvars.itemconfirm > 10*TICRATE || player->bananadrag >= TICRATE) + if (bd->itemconfirm > 10*TICRATE || player->bananadrag >= TICRATE) { - K_BotGenericPressItem(player, cmd, throwdir); + K_BotGenericPressItem(bd, throwdir); } } @@ -837,19 +673,8 @@ static boolean K_BotRevealsEggbox(const player_t *player) return false; } -/*-------------------------------------------------- - static void K_BotItemEggmanShield(const player_t *player, ticcmd_t *cmd) - - Item usage for Eggman shields. - - Input Arguments:- - player - Bot to do this for. - cmd - Bot's ticcmd to edit. - - Return:- - None ---------------------------------------------------*/ -static void K_BotItemEggmanShield(const player_t *player, ticcmd_t *cmd) +// Item usage for Eggman shields. +static void K_BotItemEggmanShield(botdata_t *bd, const player_t *player) { ZoneScoped; @@ -858,53 +683,31 @@ static void K_BotItemEggmanShield(const player_t *player, ticcmd_t *cmd) return; } - cmd->bot.itemconfirm++; + bd->itemconfirm++; - if (K_BotRevealsEggbox(player) == true || (player->botvars.itemconfirm > 20*TICRATE)) + if (K_BotRevealsEggbox(player) == true || (bd->itemconfirm > 20*TICRATE)) { - K_BotGenericPressItem(player, cmd, 0); + K_BotGenericPressItem(bd, 0); } } -/*-------------------------------------------------- - static void K_BotItemEggmanExplosion(const player_t *player, ticcmd_t *cmd) - - Item usage for Eggman explosions. - - Input Arguments:- - player - Bot to do this for. - cmd - Bot's ticcmd to edit. - - Return:- - None ---------------------------------------------------*/ -static void K_BotItemEggmanExplosion(const player_t *player, ticcmd_t *cmd) +// Item usage for Eggman explosions. +static void K_BotItemEggmanExplosion(botdata_t *bd, const player_t *player) { ZoneScoped; - if (player->position == 1) + if (player->position == 1 && bd->acceldown) { // Hey, we aren't gonna find anyone up here... // why don't we slow down a bit? :) - cmd->forwardmove /= 2; + bd->brakedown = true; } - K_BotUseItemNearPlayer(player, cmd, 128*player->mo->scale); + K_BotUseItemNearPlayer(bd, player, 128*player->mo->scale); } -/*-------------------------------------------------- - static void K_BotItemOrbinaut(const player_t *player, ticcmd_t *cmd) - - Item usage for Orbinaut throwing. - - Input Arguments:- - player - Bot to do this for. - cmd - Bot's ticcmd to edit. - - Return:- - None ---------------------------------------------------*/ -static void K_BotItemOrbinaut(player_t *player, ticcmd_t *cmd) +// Item usage for Orbinaut throwing. +static void K_BotItemOrbinaut(botdata_t *bd, const player_t *player) { ZoneScoped; @@ -921,12 +724,12 @@ static void K_BotItemOrbinaut(player_t *player, ticcmd_t *cmd) snipeMul = 3; // Confirm faster when you'll throw it with a bunch of extra speed!! } - cmd->bot.itemconfirm++; + bd->itemconfirm++; target = K_PlayerInCone(player, radius, 15, false); if (target != NULL) { - K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty * snipeMul); + K_ItemConfirmForTarget(bd, player, target, player->botvars.difficulty * snipeMul); throwdir = 1; } else @@ -935,7 +738,7 @@ static void K_BotItemOrbinaut(player_t *player, ticcmd_t *cmd) if (target != NULL) { - K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty); + K_ItemConfirmForTarget(bd, player, target, player->botvars.difficulty); throwdir = -1; tryLookback = true; } @@ -943,28 +746,17 @@ static void K_BotItemOrbinaut(player_t *player, ticcmd_t *cmd) if (tryLookback == true && throwdir == -1) { - cmd->buttons |= BT_LOOKBACK; + bd->dolookback = true; } - if (player->botvars.itemconfirm > 25*TICRATE) + if (bd->itemconfirm > 25*TICRATE) { - K_BotGenericPressItem(player, cmd, throwdir); + K_BotGenericPressItem(bd, throwdir); } } -/*-------------------------------------------------- - static void K_BotItemBallhog(const player_t *player, ticcmd_t *cmd) - - Item usage for Ballhog throwing. - - Input Arguments:- - player - Bot to do this for. - cmd - Bot's ticcmd to edit. - - Return:- - None ---------------------------------------------------*/ -static void K_BotItemBallhog(player_t *player, ticcmd_t *cmd) +// Item usage for Ballhog throwing. +static void K_BotItemBallhog(botdata_t *bd, const player_t *player) { ZoneScoped; @@ -985,7 +777,7 @@ static void K_BotItemBallhog(player_t *player, ticcmd_t *cmd) target = K_PlayerInCone(player, radius, 15, false); if (target != NULL) { - K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty * snipeMul); + K_ItemConfirmForTarget(bd, player, target, player->botvars.difficulty * snipeMul); throwdir = 1; } else @@ -994,7 +786,7 @@ static void K_BotItemBallhog(player_t *player, ticcmd_t *cmd) if (target != NULL) { - K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty); + K_ItemConfirmForTarget(bd, player, target, player->botvars.difficulty); throwdir = -1; tryLookback = true; } @@ -1002,7 +794,7 @@ static void K_BotItemBallhog(player_t *player, ticcmd_t *cmd) if (tryLookback == true && throwdir == -1) { - cmd->buttons |= BT_LOOKBACK; + bd->dolookback = true; } if (target != NULL) @@ -1017,30 +809,19 @@ static void K_BotItemBallhog(player_t *player, ticcmd_t *cmd) // If we've been waiting for too long though, then // we'll go for the full charge :) - cmd->bot.itemconfirm++; - hold = (player->botvars.itemconfirm > 10*TICRATE); + bd->itemconfirm++; + hold = (bd->itemconfirm > 10*TICRATE); } if (hold == true) { - cmd->throwdir = KART_FULLTURN * throwdir; - cmd->buttons |= BT_ATTACK; + bd->itemthrow = throwdir; + bd->itemdown = true; } } -/*-------------------------------------------------- - static void K_BotItemJawz(const player_t *player, ticcmd_t *cmd) - - Item usage for Jawz throwing. - - Input Arguments:- - player - Bot to do this for. - cmd - Bot's ticcmd to edit. - - Return:- - None ---------------------------------------------------*/ -static void K_BotItemJawz(player_t *player, ticcmd_t *cmd) +// Item usage for Jawz throwing. +static void K_BotItemJawz(botdata_t *bd, const player_t *player) { ZoneScoped; @@ -1058,12 +839,12 @@ static void K_BotItemJawz(player_t *player, ticcmd_t *cmd) snipeMul = 3; // Confirm faster when you'll throw it with a bunch of extra speed!! } - cmd->bot.itemconfirm++; + bd->itemconfirm++; target = K_PlayerInCone(player, radius, 15, true); if (target != NULL) { - K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty); + K_ItemConfirmForTarget(bd, player, target, player->botvars.difficulty); throwdir = -1; tryLookback = true; } @@ -1094,121 +875,71 @@ static void K_BotItemJawz(player_t *player, ticcmd_t *cmd) if (targettedAlready == false) { - K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty * snipeMul); + K_ItemConfirmForTarget(bd, player, target, player->botvars.difficulty * snipeMul); throwdir = 1; } } if (tryLookback == true && throwdir == -1) { - cmd->buttons |= BT_LOOKBACK; + bd->dolookback = true; } - if (player->botvars.itemconfirm > 25*TICRATE) + if (bd->itemconfirm > 25*TICRATE) { - K_BotGenericPressItem(player, cmd, throwdir); + K_BotGenericPressItem(bd, throwdir); } } -/*-------------------------------------------------- - static void K_BotItemLightning(const player_t *player, ticcmd_t *cmd) - - Item usage for Lightning Shield. - - Input Arguments:- - player - Bot to do this for. - cmd - Bot's ticcmd to edit. - - Return:- - None ---------------------------------------------------*/ -static void K_BotItemLightning(const player_t *player, ticcmd_t *cmd) +// Item usage for Lightning Shield. +static void K_BotItemLightning(botdata_t *bd, const player_t *player) { ZoneScoped; fixed_t radius = 192 * player->mo->scale; radius = Easing_Linear(FRACUNIT * player->botvars.difficulty / MAXBOTDIFFICULTY, 2*radius, radius); - if (K_BotUseItemNearPlayer(player, cmd, radius) == false) + if (K_BotUseItemNearPlayer(bd, player, radius) == false) { - if (player->botvars.itemconfirm > 10*TICRATE) + if (bd->itemconfirm > 10*TICRATE) { - K_BotGenericPressItem(player, cmd, 0); + K_BotGenericPressItem(bd, 0); } else { - cmd->bot.itemconfirm++; + bd->itemconfirm++; } } } -/*-------------------------------------------------- - static void K_BotItemBubble(const player_t *player, ticcmd_t *cmd) - - Item usage for Bubble Shield. - - Input Arguments:- - player - Bot to do this for. - cmd - Bot's ticcmd to edit. - - Return:- - None ---------------------------------------------------*/ -static void K_BotItemBubble(const player_t *player, ticcmd_t *cmd) +// Item usage for Bubble Shield. +static void K_BotItemBubble(botdata_t *bd, const player_t *player) { ZoneScoped; boolean hold = false; + // We are holding this thing too long, lets get rid of it. + if (bd->itemconfirm > 20*TICRATE) + { + bd->itemconfirm++; + + if (player->itemflags & IF_HOLDREADY) + { + bd->itemdown = true; + } + + return; + } + if (player->bubbleblowup <= 0) { - UINT8 i; - - cmd->bot.itemconfirm++; - if (player->bubblecool <= 0) { fixed_t radius = 192 * player->mo->scale; radius = Easing_Linear(FRACUNIT * player->botvars.difficulty / MAXBOTDIFFICULTY, 2*radius, radius); - for (i = 0; i < MAXPLAYERS; i++) - { - player_t *target = NULL; - fixed_t dist = INT32_MAX; - - if (!playeringame[i]) - { - continue; - } - - target = &players[i]; - - if (target->mo == NULL || P_MobjWasRemoved(target->mo) - || player == target || target->spectator - || target->flashing) - { - continue; - } - - dist = P_AproxDistance(P_AproxDistance( - player->mo->x - target->mo->x, - player->mo->y - target->mo->y), - (player->mo->z - target->mo->z) / 4 - ); - - if (dist <= radius) - { - hold = true; - break; - } - } - } - } - else if (player->bubbleblowup >= bubbletime) - { - if (player->botvars.itemconfirm > 10*TICRATE) - { - hold = true; + hold = K_GetBlockedBubbleItem(player, radius); } } else if (player->bubbleblowup < bubbletime) @@ -1218,77 +949,63 @@ static void K_BotItemBubble(const player_t *player, ticcmd_t *cmd) if (hold && (player->itemflags & IF_HOLDREADY)) { - cmd->buttons |= BT_ATTACK; + bd->itemdown = true; } + + bd->itemconfirm++; } -/*-------------------------------------------------- - static void K_BotItemFlame(const player_t *player, ticcmd_t *cmd) - - Item usage for Flame Shield. - - Input Arguments:- - player - Bot to do this for. - cmd - Bot's ticcmd to edit. - - Return:- - None ---------------------------------------------------*/ -static void K_BotItemFlame(const player_t *player, ticcmd_t *cmd) +// Item usage for Flame Shield. +static void K_BotItemFlame(botdata_t *bd, const player_t *player) { ZoneScoped; if (P_IsObjectOnGround(player->mo) == false) { + // Drain itemdelay as needed so theres no delay when landing. + if (bd->itemdelay) + { + bd->itemdelay--; + bd->itemconfirm = 0; + } + // Don't use while mid-air. return; } - if (player->botvars.itemconfirm > TICRATE) + if (player->botvars.difficulty < 6 || player->flametimer <= 2*TICRATE) { - if (player->botvars.difficulty >= 6 && player->flametimer <= 2*TICRATE) + // We aren't smart enough to use this properly. + // ...or we are doing the finishing blow. + bd->itemdown = true; + bd->itemdelay = 0; + bd->itemconfirm++; + return; + } + + if (!bd->itemdelay) + { + if (player->flamestore < FLAMESTOREMAX - TICRATE/4) { - cmd->buttons |= BT_ATTACK; - cmd->bot.itemconfirm = 0; + bd->itemdown = true; } - else if (player->flametimer > 0) + else { - if (player->botvars.difficulty < 6 || player->flamestore < FLAMESTOREMAX - TICRATE/4) - { - cmd->buttons |= BT_ATTACK; - cmd->bot.itemconfirm = 0; - } - else - { - UINT8 difficultyadjust = MAXBOTDIFFICULTY - player->botvars.difficulty; - cmd->bot.itemconfirm = (-TICRATE/2) - difficultyadjust; - } + UINT8 difficultyadjust = MAXBOTDIFFICULTY - player->botvars.difficulty; + bd->itemdelay = (TICRATE/2) + difficultyadjust; } + + bd->itemconfirm++; } else { - if (player->botvars.difficulty >= 6 && player->flametimer <= 2*TICRATE) - { - cmd->bot.itemconfirm = 0; - } - - cmd->bot.itemconfirm++; + bd->itemdelay--; + bd->itemconfirm = 0; } } -/*-------------------------------------------------- - static void K_BotItemRings(const player_t *player, ticcmd_t *cmd) - - Item usage for rings. - - Input Arguments:- - player - Bot to do this for. - cmd - Bot's ticcmd to edit. - - Return:- - None ---------------------------------------------------*/ -static void K_BotItemRings(player_t *player, ticcmd_t *cmd) +// Item usage for rings. +static void K_BotItemRings(botdata_t *bd, const player_t *player) { ZoneScoped; @@ -1300,7 +1017,7 @@ static void K_BotItemRings(player_t *player, ticcmd_t *cmd) return; } - if ((cmd->buttons & BT_ACCELERATE) == 0) + if (!bd->acceldown) { // Don't use rings if you're not trying to accelerate. return; @@ -1335,7 +1052,7 @@ static void K_BotItemRings(player_t *player, ticcmd_t *cmd) if (player->rings > saferingsval) { - cmd->buttons |= BT_ATTACK; + bd->itemdown = true; } } @@ -1350,7 +1067,7 @@ static void K_BotItemRings(player_t *player, ticcmd_t *cmd) Return:- Returns the useodds of this bot. --------------------------------------------------*/ -static UINT32 K_BotCalculateUseodds(player_t *player) +static UINT32 K_BotCalculateUseodds(const player_t *player) { UINT32 pdis = 0; UINT8 pingame = 0, bestbumper = 0; @@ -1396,24 +1113,13 @@ static UINT32 K_BotCalculateUseodds(player_t *player) return K_FindUseodds(player, 0, pdis, bestbumper, spbrush); } -/*-------------------------------------------------- - static void K_BotItemRouletteMash(const player_t *player, ticcmd_t *cmd) - - Item usage for item roulette mashing. - - Input Arguments:- - player - Bot to do this for. - cmd - Bot's ticcmd to edit. - - Return:- - None ---------------------------------------------------*/ -static void K_BotItemRouletteMash(player_t *player, ticcmd_t *cmd) +// Item usage for item roulette mashing. +static void K_BotItemRouletteMash(botdata_t *bd, const player_t *player) { boolean mash = false; UINT32 useodds = 0; - if (K_ItemButtonWasDown(player) == true) + if (bd->itemwasdown) { return; } @@ -1455,16 +1161,31 @@ static void K_BotItemRouletteMash(player_t *player, ticcmd_t *cmd) if (mash == true) { - cmd->buttons |= BT_ATTACK; + bd->itemdown = true; } } -/*-------------------------------------------------- - void K_BotItemUsage(const player_t *player, ticcmd_t *cmd, INT16 turnamt) +// Determines if we should use the Regular Item Delay Logic. +static boolean K_CanHandleItemDelay(botdata_t *bd, const player_t *player) +{ + if (!bd->itemdelay) + { + // We don't even have any. Don't bother. + return false; + } - See header file for description. ---------------------------------------------------*/ -void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt) + if (player->flametimer > 0) + { + // Flame Shield? We handle this there instead. + return false; + + } + + return true; +} + +// See header file for description. +void K_BotItemUsage(botdata_t *bd, const player_t *player) { ZoneScoped; @@ -1473,142 +1194,139 @@ void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt) if (player->rings > 0) { // Use rings! - K_BotItemRings(player, cmd); + K_BotItemRings(bd, player); } + return; + } + + if (K_CanHandleItemDelay(bd, player)) + { + bd->itemdelay--; + bd->itemconfirm = 0; + return; + } + + if (player->itemroulette) + { + // Mashing behaviors + K_BotItemRouletteMash(bd, player); + return; + } + + if (player->stealingtimer != 0) + return; + + if (player->eggmanexplode) + { + K_BotItemEggmanExplosion(bd, player); + } + else if (player->itemflags & IF_EGGMANOUT) + { + K_BotItemEggman(bd, player); + } + else if (player->rocketsneakertimer > 0) + { + K_BotItemRocketSneaker(bd, player); + } + else if (player->flametimer > 0) + { + K_BotItemFlame(bd, player); } else { - if (player->botvars.itemdelay) + switch (player->itemtype) { - return; - } + default: + if (player->itemtype != KITEM_NONE) + { + K_BotItemGenericTap(bd); + } - if (player->itemroulette) - { - // Mashing behaviors - K_BotItemRouletteMash(player, cmd); - return; - } - - if (player->stealingtimer == 0) - { - if (player->eggmanexplode) + //bd->itemconfirm = 0; + break; + case KITEM_INVINCIBILITY: + case KITEM_SPB: + case KITEM_GROW: + case KITEM_SHRINK: + case KITEM_SUPERRING: + K_BotItemGenericTap(bd); + break; + case KITEM_ROCKETSNEAKER: + if (player->rocketsneakertimer <= 0) { - K_BotItemEggmanExplosion(player, cmd); + K_BotItemGenericTap(bd); } - else if (player->itemflags & IF_EGGMANOUT) + break; + case KITEM_FLAMESHIELD: + if (player->flametimer <= 0) { - K_BotItemEggman(player, cmd); + K_BotItemGenericTap(bd); } - else if (player->rocketsneakertimer > 0) + break; + case KITEM_SNEAKER: + K_BotItemSneaker(bd, player); + break; + case KITEM_BANANA: + if (!(player->itemflags & IF_ITEMOUT)) { - K_BotItemRocketSneaker(player, cmd); - } - else if (player->flametimer > 0) - { - K_BotItemFlame(player, cmd); + K_BotItemGenericTrapShield(bd, player, false); } else { - switch (player->itemtype) - { - default: - if (player->itemtype != KITEM_NONE) - { - K_BotItemGenericTap(player, cmd); - } - - //player->botvars.itemconfirm = 0; - break; - case KITEM_INVINCIBILITY: - case KITEM_SPB: - case KITEM_GROW: - case KITEM_SHRINK: - case KITEM_SUPERRING: - K_BotItemGenericTap(player, cmd); - break; - case KITEM_ROCKETSNEAKER: - if (player->rocketsneakertimer <= 0) - { - K_BotItemGenericTap(player, cmd); - } - break; - case KITEM_FLAMESHIELD: - if (player->flametimer <= 0) - { - K_BotItemGenericTap(player, cmd); - } - break; - case KITEM_SNEAKER: - K_BotItemSneaker(player, cmd); - break; - case KITEM_BANANA: - if (!(player->itemflags & IF_ITEMOUT)) - { - K_BotItemGenericTrapShield(player, cmd, turnamt, false); - } - else - { - K_BotItemBanana(player, cmd, turnamt); - } - break; - case KITEM_EGGMAN: - K_BotItemEggmanShield(player, cmd); - break; - case KITEM_ORBINAUT: - if (!(player->itemflags & IF_ITEMOUT)) - { - K_BotItemGenericOrbitShield(player, cmd); - } - else if (player->position != 1) // Hold onto orbiting items when in 1st :) - { - K_BotItemOrbinaut(player, cmd); - } - break; - case KITEM_JAWZ: - if (!(player->itemflags & IF_ITEMOUT)) - { - K_BotItemGenericOrbitShield(player, cmd); - } - else if (player->position != 1) // Hold onto orbiting items when in 1st :) - { - K_BotItemJawz(player, cmd); - } - break; - case KITEM_MINE: - if (!(player->itemflags & IF_ITEMOUT)) - { - K_BotItemGenericTrapShield(player, cmd, turnamt, true); - } - else - { - K_BotItemMine(player, cmd, turnamt); - } - break; - case KITEM_LANDMINE: - case KITEM_HYUDORO: // Function re-use, as they have about the same usage. - K_BotItemLandmine(player, cmd, turnamt); - break; - case KITEM_BALLHOG: - K_BotItemBallhog(player, cmd); - break; - case KITEM_THUNDERSHIELD: - K_BotItemLightning(player, cmd); - break; - case KITEM_BUBBLESHIELD: - K_BotItemBubble(player, cmd); - break; - } + K_BotItemBanana(bd, player); } + break; + case KITEM_EGGMAN: + K_BotItemEggmanShield(bd, player); + break; + case KITEM_ORBINAUT: + if (!(player->itemflags & IF_ITEMOUT)) + { + K_BotItemGenericOrbitShield(bd, player); + } + else if (player->position != 1) // Hold onto orbiting items when in 1st :) + { + K_BotItemOrbinaut(bd, player); + } + break; + case KITEM_JAWZ: + if (!(player->itemflags & IF_ITEMOUT)) + { + K_BotItemGenericOrbitShield(bd, player); + } + else if (player->position != 1) // Hold onto orbiting items when in 1st :) + { + K_BotItemJawz(bd, player); + } + break; + case KITEM_MINE: + if (!(player->itemflags & IF_ITEMOUT)) + { + K_BotItemGenericTrapShield(bd, player, true); + } + else + { + K_BotItemMine(bd, player); + } + break; + case KITEM_LANDMINE: + case KITEM_HYUDORO: // Function re-use, as they have about the same usage. + K_BotItemLandmine(bd, player); + break; + case KITEM_BALLHOG: + K_BotItemBallhog(bd, player); + break; + case KITEM_THUNDERSHIELD: + K_BotItemLightning(bd, player); + break; + case KITEM_BUBBLESHIELD: + K_BotItemBubble(bd, player); + break; } } } -/*-------------------------------------------------- - void K_UpdateBotGameplayVarsItemUsage(player_t *player) - - See header file for description. ---------------------------------------------------*/ +// See header file for description. void K_UpdateBotGameplayVarsItemUsage(player_t *player) { if (player->itemflags & IF_USERINGS) @@ -1616,22 +1334,6 @@ void K_UpdateBotGameplayVarsItemUsage(player_t *player) return; } - if (player->botvars.itemdelay) - { - player->botvars.itemdelay--; - player->botvars.itemconfirm = 0; - return; - } - - if (player->cmd.bot.itemconfirm < 0 && abs(player->cmd.bot.itemconfirm) > player->botvars.itemconfirm) - { - player->botvars.itemconfirm = 0; - } - else - { - player->botvars.itemconfirm += player->cmd.bot.itemconfirm; - } - if (player->itemflags & IF_USERINGS) { ; @@ -1641,7 +1343,6 @@ void K_UpdateBotGameplayVarsItemUsage(player_t *player) if (player->itemroulette) { // Mashing behaviors - K_BotItemRouletteMash(player, &player->cmd); return; } diff --git a/src/k_botsearch.cpp b/src/k_botsearch.cpp index 3aa10d022..37ae6146d 100644 --- a/src/k_botsearch.cpp +++ b/src/k_botsearch.cpp @@ -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; +} diff --git a/src/k_collide.c b/src/k_collide.c index 4a42a5d70..cf32bd60a 100644 --- a/src/k_collide.c +++ b/src/k_collide.c @@ -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 diff --git a/src/k_grandprix.c b/src/k_grandprix.c index 74bfd4776..464655449 100644 --- a/src/k_grandprix.c +++ b/src/k_grandprix.c @@ -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; } } diff --git a/src/k_hud.c b/src/k_hud.c index 10efcb213..920931ebb 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -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.... diff --git a/src/k_kart.c b/src/k_kart.c index c136c01bf..ef7f8915d 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -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<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: diff --git a/src/k_kart.h b/src/k_kart.h index 4b16940a0..935c50966 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -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); diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 81d01d62b..9aeff1ce8 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -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}, diff --git a/src/lua_botvarslib.c b/src/lua_botvarslib.c new file mode 100644 index 000000000..cca32b86b --- /dev/null +++ b/src/lua_botvarslib.c @@ -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; +} diff --git a/src/lua_hook.h b/src/lua_hook.h index eb0db2a0f..8e715193a 100644 --- a/src/lua_hook.h +++ b/src/lua_hook.h @@ -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 diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c index 7c7c06a82..c1d059fb1 100644 --- a/src/lua_hooklib.c +++ b/src/lua_hooklib.c @@ -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; diff --git a/src/lua_libs.h b/src/lua_libs.h index d0d9f4b60..b261b6be8 100644 --- a/src/lua_libs.h +++ b/src/lua_libs.h @@ -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); diff --git a/src/lua_maplib.c b/src/lua_maplib.c index 2305a4d7a..4d3f050dd 100644 --- a/src/lua_maplib.c +++ b/src/lua_maplib.c @@ -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, §or->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, diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 9510da504..28d0c5593 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -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; diff --git a/src/lua_script.c b/src/lua_script.c index d8e680b54..f646db2ac 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -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 diff --git a/src/m_perfstats.c b/src/m_perfstats.c index 993b16ab7..335d9bdbc 100644 --- a/src/m_perfstats.c +++ b/src/m_perfstats.c @@ -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"); diff --git a/src/p_local.h b/src/p_local.h index 97154a8b1..20d8f3e1d 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -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) diff --git a/src/p_mobj.c b/src/p_mobj.c index 8a8a61f2e..de03ddcb6 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -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); diff --git a/src/p_saveg.c b/src/p_saveg.c index d64c624a6..d5089dc23 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -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); diff --git a/src/p_tick.c b/src/p_tick.c index 67e11ccdd..c51a5aa83 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -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) { diff --git a/src/p_user.c b/src/p_user.c index e637274c2..612229f0e 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -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]; } diff --git a/src/typedef.h b/src/typedef.h index 424858d4a..72323f202 100644 --- a/src/typedef.h +++ b/src/typedef.h @@ -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