(WIP) Rewrite bot code to be much more sane
Bot logic is now handled exclusively by the server, now in the actual game loop code, rather than during ticcmd generation. No TICCMD_BOT needed! botdata_t has been added to store any variables that clients don't need. Behavior differences: * Brakedrifting actually works now... to the detriment of the bots * Respawn triggers 1 second sooner (and actually causes braking) * No longer spams respawn when a track lacks waypoints * Turning might be less responsive? `botvars.turnconfirm += cmd.bot.turnconfirm` is gone... who knows * No longer affected by timescale Plus, no more outdated or copy-pasted comment blocks!
This commit is contained in:
parent
1593ad8595
commit
6feb25cefe
13 changed files with 693 additions and 1104 deletions
|
|
@ -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
|
||||
{
|
||||
|
|
@ -460,19 +440,8 @@ 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
|
||||
// Determines how soon a bot starts to drift.
|
||||
};
|
||||
|
||||
struct sonicloopcamvars_t
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
54
src/g_demo.c
54
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 < 0x0009 && 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)
|
||||
|
|
|
|||
10
src/g_game.c
10
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;
|
||||
}
|
||||
|
|
@ -2670,7 +2663,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
p->botvars.controller = UINT16_MAX;
|
||||
p->botvars.driftskill =
|
||||
FixedMul(MAXDRIFTSKILL, K_BotDetermineDriftSkill(p));
|
||||
p->botvars.driftstate = DRIFTSTATE_AUTO;
|
||||
|
||||
K_BotRespawn(p);
|
||||
|
||||
if (follower)
|
||||
P_RemoveMobj(follower);
|
||||
|
|
|
|||
600
src/k_bot.cpp
600
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)
|
||||
|
||||
|
|
@ -838,18 +874,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, player_t *player)
|
||||
{
|
||||
ZoneScoped;
|
||||
|
||||
|
|
@ -884,17 +910,14 @@ static botprediction_t *K_CreateBotPrediction(player_t *player)
|
|||
boolean pathfindsuccess = false;
|
||||
path_t pathtofinish = {0};
|
||||
|
||||
botprediction_t *predict = nullptr;
|
||||
size_t i;
|
||||
|
||||
if (wp == nullptr || P_MobjWasRemoved(wp->mobj) == true)
|
||||
{
|
||||
// Can't do any of this if we don't have a waypoint.
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
predict = static_cast<botprediction_t *>(Z_Calloc(sizeof(botprediction_t), PU_LEVEL, nullptr));
|
||||
|
||||
// Init defaults in case of pathfind failure
|
||||
angletonext = R_PointToAngle2(prevwpmobj->x, prevwpmobj->y, wp->mobj->x, wp->mobj->y);
|
||||
disttonext = P_AproxDistance(prevwpmobj->x - wp->mobj->x, prevwpmobj->y - wp->mobj->y);
|
||||
|
|
@ -950,22 +973,22 @@ static botprediction_t *K_CreateBotPrediction(player_t *player)
|
|||
|
||||
// Set our predicted point's coordinates,
|
||||
// and use the smallest radius of all of the waypoints in the chain!
|
||||
predict->x = wp->mobj->x;
|
||||
predict->y = wp->mobj->y;
|
||||
bd->predict.x = wp->mobj->x;
|
||||
bd->predict.y = wp->mobj->y;
|
||||
|
||||
predict->baseRadius = radius;
|
||||
predict->radius = FixedMul(radiusScaled, radReduce);
|
||||
bd->predict.baseRadius = radius;
|
||||
bd->predict.radius = FixedMul(radiusScaled, radReduce);
|
||||
|
||||
// Set the prediction coordinates between the 2 waypoints if there's still distance left.
|
||||
if (distanceleft > 0)
|
||||
{
|
||||
// Scaled with the leftover anglemul!
|
||||
predict->x += P_ReturnThrustX(nullptr, angletonext, std::min<fixed_t>(disttonext, distanceleft) * FRACUNIT);
|
||||
predict->y += P_ReturnThrustY(nullptr, angletonext, std::min<fixed_t>(disttonext, distanceleft) * FRACUNIT);
|
||||
bd->predict.x += P_ReturnThrustX(nullptr, angletonext, std::min<fixed_t>(disttonext, distanceleft) * FRACUNIT);
|
||||
bd->predict.y += P_ReturnThrustY(nullptr, angletonext, std::min<fixed_t>(disttonext, distanceleft) * FRACUNIT);
|
||||
}
|
||||
|
||||
ps_bots[player - players].prediction += I_GetPreciseTime() - time;
|
||||
return predict;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
|
|
@ -980,19 +1003,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 +1023,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,7 +1050,7 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1069,32 +1091,24 @@ static void K_WaypointGetDirectionVector(waypoint_t *wp1, waypoint_t *wp2, vecto
|
|||
|
||||
See header file for description.
|
||||
--------------------------------------------------*/
|
||||
void K_BotSetDriftState(player_t *player, botdrift_t newstate, tic_t lockout)
|
||||
void K_BotSetDriftState(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, player_t* player)
|
||||
{
|
||||
// Handle DRIFTING towards waypoints!
|
||||
boolean shouldDrift;
|
||||
|
|
@ -1116,17 +1130,17 @@ 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));
|
||||
maxdist = FixedMul(maxdist, speedfactor * (bd->driftstate == DRIFTSTATE_ACTIVE ? 1 : 2));
|
||||
|
||||
if (maxdist >= 0 && K_PathfindThruCircuit(player->currentwaypoint, maxdist, &path, false, false))
|
||||
{
|
||||
|
|
@ -1156,11 +1170,11 @@ 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.
|
||||
|
|
@ -1195,26 +1209,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, player_t *player, angle_t destangle)
|
||||
{
|
||||
ZoneScoped;
|
||||
|
||||
|
|
@ -1225,15 +1227,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 +1252,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 +1296,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 +1310,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);
|
||||
|
|
@ -1326,16 +1326,16 @@ static INT32 K_HandleBotTrack(player_t *player, ticcmd_t *cmd, botprediction_t *
|
|||
|
||||
// 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 +1348,38 @@ static INT32 K_HandleBotTrack(player_t *player, ticcmd_t *cmd, botprediction_t *
|
|||
|
||||
#undef MINBOTDRIFT
|
||||
|
||||
static void K_IncrementBotRespawn(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)
|
||||
|
||||
|
|
@ -1493,26 +1525,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(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 +1569,57 @@ 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;
|
||||
// Now a clean function! Neat, eh?
|
||||
K_SetRespawnAtNextWaypoint(player);
|
||||
|
||||
// WHAT ARE YOU DOING??? RACE ALREADY!
|
||||
P_DamageMobj(player->mo, NULL, NULL, 1, DMG_INSTAKILL);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
cmd->bot.respawnconfirm = 0;
|
||||
}
|
||||
|
||||
destangle = player->mo->angle;
|
||||
|
||||
// it's time to start predicting
|
||||
memset(&bd->predict, 0, sizeof(bd->predict));
|
||||
|
||||
boolean forcedDir = false;
|
||||
if (botController != nullptr && (botController->flags & TMBOT_FORCEDIR) == TMBOT_FORCEDIR)
|
||||
{
|
||||
const fixed_t dist = DEFAULT_WAYPOINT_RADIUS * player->mo->scale;
|
||||
|
||||
// Overwritten prediction
|
||||
predict = static_cast<botprediction_t *>(Z_Calloc(sizeof(botprediction_t), PU_STATIC, nullptr));
|
||||
|
||||
predict->x = player->mo->x + FixedMul(dist, FINECOSINE(botController->forceAngle >> ANGLETOFINESHIFT));
|
||||
predict->y = player->mo->y + FixedMul(dist, FINESINE(botController->forceAngle >> ANGLETOFINESHIFT));
|
||||
predict->radius = (DEFAULT_WAYPOINT_RADIUS / 4) * mapobjectscale;
|
||||
bd->predict = {
|
||||
.x = player->mo->x + FixedMul(dist, FINECOSINE(botController->forceAngle >> ANGLETOFINESHIFT)),
|
||||
.y = player->mo->y + FixedMul(dist, FINESINE(botController->forceAngle >> ANGLETOFINESHIFT)),
|
||||
.radius = (DEFAULT_WAYPOINT_RADIUS / 4) * mapobjectscale,
|
||||
};
|
||||
|
||||
forcedDir = true;
|
||||
}
|
||||
|
||||
if (forcedDir == true)
|
||||
{
|
||||
destangle = R_PointToAngle2(player->mo->x, player->mo->y, predict->x, predict->y);
|
||||
turnamt = K_HandleBotTrack(player, cmd, predict, destangle);
|
||||
destangle = R_PointToAngle2(player->mo->x, player->mo->y, bd->predict.x, bd->predict.y);
|
||||
turnamt = K_HandleBotTrack(bd, player, destangle);
|
||||
}
|
||||
else if (leveltime <= starttime)
|
||||
{
|
||||
|
|
@ -1594,210 +1628,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 +1700,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 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 +1731,108 @@ 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_BotRespawn(player_t *player)
|
||||
{
|
||||
botdata_t *bd = &botdata[player - players];
|
||||
memset(bd, 0, sizeof(*bd));
|
||||
bd->driftstate = DRIFTSTATE_AUTO;
|
||||
}
|
||||
|
||||
void K_BotResetItemConfirm(player_t *player, boolean setdelay)
|
||||
{
|
||||
botdata_t *bd = &botdata[player - players];
|
||||
bd->itemconfirm = 0;
|
||||
if (setdelay)
|
||||
bd->itemdelay = TICRATE;
|
||||
}
|
||||
|
|
|
|||
90
src/k_bot.h
90
src/k_bot.h
|
|
@ -44,15 +44,60 @@ 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
|
||||
|
||||
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(player_t *player);
|
||||
|
||||
void K_DrawBotDebugger(const player_t *player);
|
||||
|
||||
/*--------------------------------------------------
|
||||
boolean K_PlayerUsesBotMovement(const player_t *player);
|
||||
|
|
@ -278,21 +323,8 @@ UINT8 K_EggboxStealth(fixed_t x, fixed_t y);
|
|||
|
||||
boolean K_BotHatesThisSector(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);
|
||||
|
||||
|
||||
/*--------------------------------------------------
|
||||
|
|
@ -359,22 +391,12 @@ void K_UpdateBotGameplayVarsItemUsage(player_t *player);
|
|||
|
||||
void K_UpdateBotGameplayVars(player_t *player);
|
||||
|
||||
// Item usage part of ticcmd generation.
|
||||
void K_BotItemUsage(botdata_t *bd, player_t *player);
|
||||
|
||||
/*--------------------------------------------------
|
||||
void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt);
|
||||
void K_BotRespawn(player_t *player);
|
||||
|
||||
Item usage part of ticcmd generation.
|
||||
|
||||
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);
|
||||
void K_BotResetItemConfirm(player_t *player, boolean setdelay);
|
||||
|
||||
/*--------------------------------------------------
|
||||
fixed_t K_BotDetermineDriftSkill(player_t *player)
|
||||
|
|
@ -405,7 +427,7 @@ fixed_t K_BotDetermineDriftSkill(player_t *player);
|
|||
None
|
||||
--------------------------------------------------*/
|
||||
|
||||
void K_BotSetDriftState(player_t *player, botdrift_t newstate, tic_t lockout);
|
||||
void K_BotSetDriftState(player_t *player, botdrift_e newstate, tic_t lockout);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
29
src/k_hud.c
29
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....
|
||||
|
|
|
|||
57
src/k_kart.c
57
src/k_kart.c
|
|
@ -647,8 +647,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);
|
||||
|
|
@ -11161,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
|
||||
|
|
@ -11173,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
|
||||
|
|
@ -11187,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
|
||||
|
|
@ -11300,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:
|
||||
|
|
@ -11334,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:
|
||||
|
|
@ -11343,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:
|
||||
|
|
@ -11374,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
|
||||
|
|
@ -11383,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:
|
||||
|
|
@ -11404,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:
|
||||
|
|
@ -11439,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
|
||||
{
|
||||
|
|
@ -11447,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:
|
||||
|
|
@ -11481,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
|
||||
{
|
||||
|
|
@ -11492,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:
|
||||
|
|
@ -11511,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))
|
||||
{
|
||||
|
|
@ -11520,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:
|
||||
|
|
@ -11529,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:
|
||||
|
|
@ -11538,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:
|
||||
|
|
@ -11547,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:
|
||||
|
|
@ -11591,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:
|
||||
|
|
@ -11600,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:
|
||||
|
|
@ -11625,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;
|
||||
|
|
@ -11715,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:
|
||||
|
|
@ -11725,7 +11724,7 @@ bubbledeflate:
|
|||
K_PlayBoostTaunt(player->mo);
|
||||
K_DoPogoSpring(player->mo, 32<<FRACBITS, 2);
|
||||
player->pogospring = 1;
|
||||
player->botvars.itemconfirm = 0;
|
||||
K_BotResetItemConfirm(player, false);
|
||||
}
|
||||
break;
|
||||
case KITEM_SUPERRING:
|
||||
|
|
@ -11733,7 +11732,7 @@ bubbledeflate:
|
|||
{
|
||||
player->itemamount--;
|
||||
K_AwardScaledPlayerRings(player, ASR_SUPERRING);
|
||||
player->botvars.itemconfirm = 0;
|
||||
K_BotResetItemConfirm(player, false);
|
||||
}
|
||||
break;
|
||||
case KITEM_KITCHENSINK:
|
||||
|
|
@ -11752,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
|
||||
{
|
||||
|
|
@ -11761,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:
|
||||
|
|
@ -11770,7 +11769,7 @@ bubbledeflate:
|
|||
{
|
||||
player->sadtimer = stealtime;
|
||||
player->itemamount--;
|
||||
player->botvars.itemconfirm = 0;
|
||||
K_BotResetItemConfirm(player, false);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -401,15 +401,7 @@ static void P_NetArchivePlayers(savebuffer_t *save)
|
|||
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);
|
||||
|
|
@ -756,15 +748,7 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
|
|||
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);
|
||||
|
|
|
|||
|
|
@ -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,12 @@ 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)
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
if (playeringame[i] && players[i].bot && players[i].mo && !P_MobjWasRemoved(players[i].mo))
|
||||
K_BotTicker(&players[i]);
|
||||
|
||||
// Bosses have a punchy start, so no position.
|
||||
if (bossinfo.boss == true)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue