Improve general bot handling

- Their waypoint radius gets thinner quicker
- Waypoint distance is scaled with each ones' floor slope, instead of done once at the end of the prediction.
- Prediction is pulled back further when it goes through a wall
- Prediction starts earlier and goes less far
- Bots will brake at shallower angles (allows them to brake-turn more often)
- K_AddDodgeObject and K_AddAttackObject now adjust based on the radius of the object
- Fixed K_AddDodgeObject adding to the goto objects instead of avoid objects
- Optimized blockmap search size for K_FindObjectsForNudging
- Current waypoint is no longer cleared each frame
This commit is contained in:
Sally Coolatta 2023-05-10 22:25:24 -04:00
parent 7d4720b1a6
commit a8af5b7616
3 changed files with 129 additions and 72 deletions

View file

@ -639,7 +639,7 @@ fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, fixed_t
static fixed_t K_GetBotWaypointRadius(waypoint_t *const waypoint)
{
static const fixed_t maxReduce = FRACUNIT/32;
static const angle_t maxDelta = ANGLE_45;
static const angle_t maxDelta = ANGLE_22h;
fixed_t radius = waypoint->mobj->radius;
fixed_t reduce = FRACUNIT;
@ -678,6 +678,35 @@ static fixed_t K_GetBotWaypointRadius(waypoint_t *const waypoint)
return FixedMul(radius, reduce);
}
static fixed_t K_ScaleWPDistWithSlope(fixed_t disttonext, angle_t angletonext, const pslope_t *slope, SINT8 flip)
{
if (slope == NULL)
{
return disttonext;
}
if ((slope->flags & SL_NOPHYSICS) == 0 && abs(slope->zdelta) >= FRACUNIT/21)
{
// Displace the prediction to go with the slope physics.
fixed_t slopeMul = FRACUNIT;
angle_t angle = angletonext - slope->xydirection;
if (flip * slope->zdelta < 0)
{
angle ^= ANGLE_180;
}
// Going uphill: 0
// Going downhill: FRACUNIT*2
slopeMul = FRACUNIT + FINECOSINE(angle >> ANGLETOFINESHIFT);
// Range: 0.25 to 1.75
return FixedMul(disttonext, (FRACUNIT >> 2) + ((slopeMul * 3) >> 2));
}
return disttonext;
}
/*--------------------------------------------------
static botprediction_t *K_CreateBotPrediction(player_t *player)
@ -701,8 +730,8 @@ static botprediction_t *K_CreateBotPrediction(player_t *player)
const tic_t futuresight = (TICRATE * KART_FULLTURN) / max(1, handling); // How far ahead into the future to try and predict
const fixed_t speed = K_BotSpeedScaled(player, P_AproxDistance(player->mo->momx, player->mo->momy));
const INT32 startDist = (DEFAULT_WAYPOINT_RADIUS * 2 * mapobjectscale) / FRACUNIT;
const INT32 maxDist = startDist * 4; // This function gets very laggy when it goes far distances, and going too far isn't very helpful anyway.
const INT32 startDist = 0; //(DEFAULT_WAYPOINT_RADIUS * mapobjectscale) / FRACUNIT;
const INT32 maxDist = (DEFAULT_WAYPOINT_RADIUS * 3 * mapobjectscale) / FRACUNIT; // This function gets very laggy when it goes far distances, and going too far isn't very helpful anyway.
const INT32 distance = min(((speed / FRACUNIT) * (INT32)futuresight) + startDist, maxDist);
// Halves radius when encountering a wall on your way to your destination.
@ -712,6 +741,8 @@ static botprediction_t *K_CreateBotPrediction(player_t *player)
fixed_t smallestradius = INT32_MAX;
angle_t angletonext = ANGLE_MAX;
INT32 disttonext = INT32_MAX;
INT32 distscaled = INT32_MAX;
pslope_t *nextslope = player->mo->standingslope;
waypoint_t *wp = player->nextwaypoint;
mobj_t *prevwpmobj = player->mo;
@ -721,15 +752,25 @@ static botprediction_t *K_CreateBotPrediction(player_t *player)
boolean pathfindsuccess = false;
path_t pathtofinish = {0};
botprediction_t *predict = Z_Calloc(sizeof(botprediction_t), PU_STATIC, NULL);
botprediction_t *predict = NULL;
size_t i;
if (wp == NULL || P_MobjWasRemoved(wp->mobj) == true)
{
// Can't do any of this if we don't have a waypoint.
return NULL;
}
predict = Z_Calloc(sizeof(botprediction_t), PU_STATIC, NULL);
// 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) / FRACUNIT;
disttonext = P_AproxDistance(prevwpmobj->x - wp->mobj->x, prevwpmobj->y - wp->mobj->y);
nextslope = wp->mobj->standingslope;
distscaled = K_ScaleWPDistWithSlope(disttonext, angletonext, nextslope, P_MobjFlip(wp->mobj)) / FRACUNIT;
pathfindsuccess = K_PathfindThruCircuit(
player->nextwaypoint, (unsigned)distanceleft,
wp, (unsigned)distanceleft,
&pathtofinish,
useshortcuts, huntbackwards
);
@ -753,12 +794,14 @@ static botprediction_t *K_CreateBotPrediction(player_t *player)
}
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) / FRACUNIT;
disttonext = P_AproxDistance(prevwpmobj->x - wp->mobj->x, prevwpmobj->y - wp->mobj->y);
nextslope = wp->mobj->standingslope;
distscaled = K_ScaleWPDistWithSlope(disttonext, angletonext, nextslope, P_MobjFlip(wp->mobj)) / FRACUNIT;
if (P_TraceBotTraversal(player->mo, wp->mobj) == false)
{
// If we can't get a direct path to this waypoint, predict less.
distanceleft /= 2;
// If we can't get a direct path to this waypoint, reduce our prediction drastically.
distscaled *= 4;
radreduce = FRACUNIT >> 1;
}
@ -768,7 +811,7 @@ static botprediction_t *K_CreateBotPrediction(player_t *player)
smallestradius = radius;
}
distanceleft -= disttonext;
distanceleft -= distscaled;
if (distanceleft <= 0)
{
@ -794,25 +837,6 @@ static botprediction_t *K_CreateBotPrediction(player_t *player)
predict->y += P_ReturnThrustY(NULL, angletonext, min(disttonext, distanceleft) * FRACUNIT);
}
if (player->mo->standingslope != NULL)
{
const pslope_t *slope = player->mo->standingslope;
if (!(slope->flags & SL_NOPHYSICS) && abs(slope->zdelta) >= FRACUNIT/21)
{
// Displace the prediction to go against the slope physics.
angle_t angle = slope->xydirection;
if (P_MobjFlip(player->mo) * slope->zdelta < 0)
{
angle ^= ANGLE_180;
}
predict->x -= P_ReturnThrustX(NULL, angle, startDist * abs(slope->zdelta));
predict->y -= P_ReturnThrustY(NULL, angle, startDist * abs(slope->zdelta));
}
}
ps_bots[player - players].prediction += I_GetPreciseTime() - time;
return predict;
}
@ -1109,7 +1133,7 @@ static INT32 K_HandleBotTrack(player_t *player, ticcmd_t *cmd, botprediction_t *
anglediff = abs(anglediff);
turnamt = KART_FULLTURN * turnsign;
if (anglediff > ANGLE_90)
if (anglediff > ANGLE_67h)
{
// Wrong way!
cmd->forwardmove = -MAXPLMOVE;
@ -1502,18 +1526,13 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd)
if (predict == NULL)
{
// Create a prediction.
if (player->nextwaypoint != NULL
&& player->nextwaypoint->mobj != NULL
&& !P_MobjWasRemoved(player->nextwaypoint->mobj))
{
predict = K_CreateBotPrediction(player);
K_NudgePredictionTowardsObjects(predict, player);
destangle = R_PointToAngle2(player->mo->x, player->mo->y, predict->x, predict->y);
}
predict = K_CreateBotPrediction(player);
}
if (predict != NULL)
{
K_NudgePredictionTowardsObjects(predict, player);
destangle = R_PointToAngle2(player->mo->x, player->mo->y, predict->x, predict->y);
turnamt = K_HandleBotTrack(player, cmd, predict, destangle);
}
cmd->buttons &= ~(BT_ACCELERATE|BT_BRAKE);
@ -1539,18 +1558,13 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd)
if (predict == NULL)
{
// Create a prediction.
if (player->nextwaypoint != NULL
&& player->nextwaypoint->mobj != NULL
&& !P_MobjWasRemoved(player->nextwaypoint->mobj))
{
predict = K_CreateBotPrediction(player);
K_NudgePredictionTowardsObjects(predict, player);
destangle = R_PointToAngle2(player->mo->x, player->mo->y, predict->x, predict->y);
}
predict = K_CreateBotPrediction(player);
}
if (predict != NULL)
{
K_NudgePredictionTowardsObjects(predict, player);
destangle = R_PointToAngle2(player->mo->x, player->mo->y, predict->x, predict->y);
turnamt = K_HandleBotTrack(player, cmd, predict, destangle);
}
}
@ -1561,18 +1575,13 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd)
if (predict == NULL)
{
// Create a prediction.
if (player->nextwaypoint != NULL
&& player->nextwaypoint->mobj != NULL
&& !P_MobjWasRemoved(player->nextwaypoint->mobj))
{
predict = K_CreateBotPrediction(player);
K_NudgePredictionTowardsObjects(predict, player);
destangle = R_PointToAngle2(player->mo->x, player->mo->y, predict->x, predict->y);
}
predict = K_CreateBotPrediction(player);
}
if (predict != NULL)
{
K_NudgePredictionTowardsObjects(predict, player);
destangle = R_PointToAngle2(player->mo->x, player->mo->y, predict->x, predict->y);
turnamt = K_HandleBotTrack(player, cmd, predict, destangle);
}
}

View file

@ -271,6 +271,8 @@ boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t
--------------------------------------------------*/
static void K_AddAttackObject(mobj_t *thing, UINT8 side, UINT8 weight)
{
fixed_t x, y;
angle_t a, dir;
UINT8 i;
I_Assert(side <= 1);
@ -280,10 +282,21 @@ static void K_AddAttackObject(mobj_t *thing, UINT8 side, UINT8 weight)
return;
}
x = thing->x;
y = thing->y;
a = R_PointToAngle2(globalsmuggle.botmo->x, globalsmuggle.botmo->y, x, y);
dir = a + (side ? -ANGLE_90 : ANGLE_90);
x += FixedMul(thing->radius, FINECOSINE(dir >> ANGLETOFINESHIFT));
y += FixedMul(thing->radius, FINESINE(dir >> ANGLETOFINESHIFT));
x /= mapobjectscale;
y /= mapobjectscale;
for (i = 0; i < weight; i++)
{
globalsmuggle.gotoAvgX[side] += thing->x / mapobjectscale;
globalsmuggle.gotoAvgY[side] += thing->y / mapobjectscale;
globalsmuggle.gotoAvgX[side] += x;
globalsmuggle.gotoAvgY[side] += y;
globalsmuggle.gotoObjs[side]++;
}
}
@ -303,6 +316,8 @@ static void K_AddAttackObject(mobj_t *thing, UINT8 side, UINT8 weight)
--------------------------------------------------*/
static void K_AddDodgeObject(mobj_t *thing, UINT8 side, UINT8 weight)
{
fixed_t x, y;
angle_t a, dir;
UINT8 i;
I_Assert(side <= 1);
@ -312,11 +327,22 @@ static void K_AddDodgeObject(mobj_t *thing, UINT8 side, UINT8 weight)
return;
}
x = thing->x;
y = thing->y;
a = R_PointToAngle2(globalsmuggle.botmo->x, globalsmuggle.botmo->y, x, y);
dir = a + (side ? -ANGLE_90 : ANGLE_90);
x += FixedMul(thing->radius, FINECOSINE(dir >> ANGLETOFINESHIFT));
y += FixedMul(thing->radius, FINESINE(dir >> ANGLETOFINESHIFT));
x /= mapobjectscale;
y /= mapobjectscale;
for (i = 0; i < weight; i++)
{
globalsmuggle.gotoAvgX[side] += thing->x / mapobjectscale;
globalsmuggle.gotoAvgY[side] += thing->y / mapobjectscale;
globalsmuggle.gotoObjs[side]++;
globalsmuggle.avoidAvgX[side] += x;
globalsmuggle.avoidAvgY[side] += y;
globalsmuggle.avoidObjs[side]++;
}
}
@ -366,7 +392,7 @@ static boolean K_PlayerAttackSteer(mobj_t *thing, UINT8 side, UINT8 weight, bool
--------------------------------------------------*/
static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing)
{
INT16 anglediff;
INT16 angledelta, anglediff;
fixed_t fulldist;
angle_t destangle, angle, predictangle;
UINT8 side = 0;
@ -404,15 +430,15 @@ static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing)
if (angle < ANGLE_180)
{
anglediff = AngleFixed(angle)>>FRACBITS;
angledelta = AngleFixed(angle)>>FRACBITS;
}
else
{
anglediff = 360-(AngleFixed(angle)>>FRACBITS);
angledelta = 360-(AngleFixed(angle)>>FRACBITS);
side = 1;
}
anglediff = abs(anglediff);
anglediff = abs(angledelta);
switch (thing->type)
{
@ -638,23 +664,40 @@ void K_NudgePredictionTowardsObjects(botprediction_t *predict, player_t *player)
INT32 xl, xh, yl, yh, bx, by;
fixed_t distToPredict = R_PointToDist2(player->mo->x, player->mo->y, predict->x, predict->y);
fixed_t distToPredict = 0;
angle_t angleToPredict = 0;
fixed_t avgX = 0, avgY = 0;
fixed_t avgDist = 0;
const fixed_t baseNudge = predict->radius;
fixed_t maxNudge = distToPredict;
fixed_t baseNudge = 0;
fixed_t maxNudge = 0;
fixed_t nudgeDist = 0;
angle_t nudgeDir = 0;
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);
globalsmuggle.distancetocheck = distToPredict >> 1;
baseNudge = predict->radius * 2;
maxNudge = distToPredict;
globalsmuggle.botmo = player->mo;
globalsmuggle.predict = predict;
globalsmuggle.distancetocheck = distToPredict;
// silly variable reuse
avgX = globalsmuggle.botmo->x + FixedMul(globalsmuggle.distancetocheck, FINECOSINE(angleToPredict >> ANGLETOFINESHIFT));
avgY = globalsmuggle.botmo->y + FixedMul(globalsmuggle.distancetocheck, FINESINE(angleToPredict >> ANGLETOFINESHIFT));
for (i = 0; i < 2; i++)
{
@ -665,10 +708,10 @@ void K_NudgePredictionTowardsObjects(botprediction_t *predict, player_t *player)
globalsmuggle.avoidObjs[i] = 0;
}
xl = (unsigned)(globalsmuggle.botmo->x - globalsmuggle.distancetocheck - bmaporgx)>>MAPBLOCKSHIFT;
xh = (unsigned)(globalsmuggle.botmo->x + globalsmuggle.distancetocheck - bmaporgx)>>MAPBLOCKSHIFT;
yl = (unsigned)(globalsmuggle.botmo->y - globalsmuggle.distancetocheck - bmaporgy)>>MAPBLOCKSHIFT;
yh = (unsigned)(globalsmuggle.botmo->y + globalsmuggle.distancetocheck - bmaporgy)>>MAPBLOCKSHIFT;
xl = (unsigned)(avgX - (globalsmuggle.distancetocheck + MAXRADIUS) - bmaporgx)>>MAPBLOCKSHIFT;
xh = (unsigned)(avgX + (globalsmuggle.distancetocheck + MAXRADIUS) - bmaporgx)>>MAPBLOCKSHIFT;
yl = (unsigned)(avgY - (globalsmuggle.distancetocheck + MAXRADIUS) - bmaporgy)>>MAPBLOCKSHIFT;
yh = (unsigned)(avgY + (globalsmuggle.distancetocheck + MAXRADIUS) - bmaporgy)>>MAPBLOCKSHIFT;
BMBOUNDFIX(xl, xh, yl, yh);

View file

@ -8352,7 +8352,12 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player)
boolean updaterespawn = false;
// Our current waypoint.
player->currentwaypoint = bestwaypoint = waypoint;
bestwaypoint = waypoint;
if (bestwaypoint != NULL)
{
player->currentwaypoint = bestwaypoint;
}
// check the waypoint's location in relation to the player
// If it's generally in front, it's fine, otherwise, use the best next/previous waypoint.