Merge branch 'bot-nerfs' into 'master'

Track-based bot difficulty

See merge request KartKrew/Kart!1482
This commit is contained in:
Oni 2023-09-23 03:28:43 +00:00
commit d0b16f2025
9 changed files with 608 additions and 70 deletions

View file

@ -449,6 +449,25 @@ static line_t *K_FindBotController(mobj_t *mo)
}
}
/*--------------------------------------------------
fixed_t K_BotMapModifier(void)
See header file for description.
--------------------------------------------------*/
fixed_t K_BotMapModifier(void)
{
constexpr INT32 complexity_scale = 10000;
constexpr INT32 modifier_max = FRACUNIT * 2;
const fixed_t complexity_value = std::clamp<fixed_t>(
FixedDiv(K_GetTrackComplexity(), complexity_scale),
-modifier_max,
modifier_max * 2
);
return FRACUNIT + complexity_value;
}
/*--------------------------------------------------
static UINT32 K_BotRubberbandDistance(player_t *player)
@ -465,7 +484,7 @@ static UINT32 K_BotRubberbandDistance(player_t *player)
{
const UINT32 spacing = FixedDiv(640 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)) / FRACUNIT;
const UINT8 portpriority = player - players;
UINT8 pos = 0;
UINT8 pos = 1;
UINT8 i;
if (player->botvars.rival)
@ -509,15 +528,22 @@ static UINT32 K_BotRubberbandDistance(player_t *player)
--------------------------------------------------*/
fixed_t K_BotRubberband(player_t *player)
{
constexpr fixed_t rubberdeltabase = FRACUNIT / 4; // +/- x0.25
// Lv. 1: x0.50 avg
// Lv. 9: x1.50 avg
const fixed_t difficultyEase = ((player->botvars.difficulty - 1) * FRACUNIT) / (DIFFICULTBOT - 1);
const fixed_t rubberavg = Easing_Linear(difficultyEase, FRACUNIT / 2, FRACUNIT * 3 / 2);
// Lv. 1: x0.35 min
// Lv. 9: x1.35 min
const fixed_t rubbermin = Easing_Linear(difficultyEase, FRACUNIT * 35 / 100, FRACUNIT * 135 / 100);
const fixed_t rubberdeltamin = FixedMul(rubberdeltabase, K_BotMapModifier());
const fixed_t rubbermin = std::max<fixed_t>(rubberavg - rubberdeltamin, FRACUNIT/3);
// Lv. 1: x1.0 max
// Lv. 1: x0.65 max
// Lv. 9: x1.65 max
const fixed_t rubbermax = Easing_Linear(difficultyEase, FRACUNIT, FRACUNIT * 165 / 100);
const fixed_t rubberdeltamax = FixedMul(rubberdeltabase, K_BotMapModifier());
const fixed_t rubbermax = std::min<fixed_t>(rubberavg - rubberdeltamax, FRACUNIT*3);
fixed_t rubberband = FRACUNIT >> 1;
player_t *firstplace = nullptr;
@ -550,6 +576,12 @@ fixed_t K_BotRubberband(player_t *player)
continue;
}
// Don't rubberband to ourselves...
if (player == &players[i])
{
continue;
}
#if 0
// Only rubberband up to players.
if (players[i].bot)
@ -566,15 +598,7 @@ fixed_t K_BotRubberband(player_t *player)
if (firstplace != nullptr)
{
// Lv. 1: 5120 units
// Lv. 9: 320 units
const fixed_t spacing = FixedDiv(
std::max<fixed_t>(
80 * mapobjectscale,
Easing_Linear(difficultyEase, 5120 * mapobjectscale, 320 * mapobjectscale)
),
K_GetKartGameSpeedScalar(gamespeed)
) / FRACUNIT;
const fixed_t spacing = FixedDiv(2560 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)) / FRACUNIT;
const UINT32 wanteddist = firstplace->distancetofinish + K_BotRubberbandDistance(player);
const INT32 distdiff = player->distancetofinish - wanteddist;
@ -745,7 +769,7 @@ static botprediction_t *K_CreateBotPrediction(player_t *player)
const precise_t time = I_GetPreciseTime();
// Stair janking makes it harder to steer, so attempt to steer harder.
const UINT8 jankDiv = (player->stairjank > 0) ? 2 : 1;
const UINT8 jankDiv = (player->stairjank > 0) ? 4 : 1;
const INT16 handling = K_GetKartTurnValue(player, KART_FULLTURN) / jankDiv; // Reduce prediction based on how fast you can turn

View file

@ -26,10 +26,10 @@ extern "C" {
#endif
// Maximum value of botvars.difficulty
#define MAXBOTDIFFICULTY 13
#define MAXBOTDIFFICULTY (13)
// Level of a "difficult" bot. The max bot level was increased, but this keeps all of the same calculations.
#define DIFFICULTBOT 9
#define DIFFICULTBOT (9)
// How many tics in a row do you need to turn in this direction before we'll let you turn.
// Made it as small as possible without making it look like the bots are twitching constantly.
@ -51,7 +51,6 @@ struct botprediction_t
fixed_t radius, baseRadius;
};
// AVAILABLE FOR LUA
@ -86,6 +85,26 @@ boolean K_PlayerUsesBotMovement(player_t *player);
boolean K_BotCanTakeCut(player_t *player);
/*--------------------------------------------------
fixed_t K_BotMapModifier(void);
Gives a multiplier, based on the track complexity.
Track complexity is a measure of how easy it is for
the bots to continuously rubberband. This is used
to make the rubberbanding and other difficulty
adjustments feel roughly the same between wildly
different layouts.
Input Arguments:-
N/A
Return:-
A multiplier in fixed point scale, between 0.0 and 2.0.
--------------------------------------------------*/
fixed_t K_BotMapModifier(void);
/*--------------------------------------------------
fixed_t K_BotRubberband(player_t *player);

View file

@ -1449,11 +1449,16 @@ static void K_BotItemInstashield(player_t *player, ticcmd_t *cmd)
if (dist <= radius)
{
// Use it!!
cmd->buttons |= BT_ATTACK;
break;
K_ItemConfirmForTarget(player, target, player->botvars.difficulty * 2);
}
}
if (player->botvars.itemconfirm > 10*TICRATE)
{
// Use it!!
cmd->buttons |= BT_ATTACK;
player->botvars.itemconfirm = 0;
}
}
/*--------------------------------------------------
@ -1494,7 +1499,7 @@ static void K_BotItemRouletteMash(player_t *player, ticcmd_t *cmd)
player->itemRoulette.itemList[ player->itemRoulette.index ]
);
if (player->botvars.roulettePriority == currentPriority)
if (player->botvars.roulettePriority == currentPriority)
{
// This is the item we want! Start timing!
player->botvars.itemconfirm++;

View file

@ -28,6 +28,7 @@
#include "r_things.h" // numskins
#include "p_slopes.h" // P_GetZAt
#include "m_perfstats.h"
#include "k_objects.h"
/*--------------------------------------------------
static BlockItReturn_t K_FindEggboxes(mobj_t *thing)
@ -477,7 +478,7 @@ static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing)
if (P_CanPickupItem(g_nudgeSearch.botmo->player, 1))
{
K_AddAttackObject(thing, side, 10);
K_AddAttackObject(thing, side, ((thing->extravalue1 < RINGBOX_TIME) ? 10 : 20));
}
break;
case MT_EGGMANITEM:
@ -493,11 +494,11 @@ static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing)
if (stealth >= requiredstealth)
{
K_AddAttackObject(thing, side, 10);
K_AddAttackObject(thing, side, 20);
}
else
{
K_AddDodgeObject(thing, side, 10);
K_AddDodgeObject(thing, side, 20);
}
}
break;

View file

@ -667,18 +667,25 @@ void K_RetireBots(void)
grabskins[usableskins] = MAXSKINS;
}
if (!grandprixinfo.gp) // Sure, let's let this happen all the time :)
if (grandprixinfo.gp) // Sure, let's let this happen all the time :)
{
newDifficulty = cv_kartbot.value;
if (grandprixinfo.masterbots == true)
{
newDifficulty = MAXBOTDIFFICULTY;
}
else
{
const UINT8 startingdifficulty = K_BotStartingDifficulty(grandprixinfo.gamespeed);
newDifficulty = startingdifficulty - 4;
if (roundqueue.size > 0)
{
newDifficulty += roundqueue.roundnum;
}
}
}
else
{
const UINT8 startingdifficulty = K_BotStartingDifficulty(grandprixinfo.gamespeed);
newDifficulty = startingdifficulty - 4;
if (roundqueue.size > 0)
{
newDifficulty += roundqueue.roundnum;
}
newDifficulty = cv_kartbot.value;
}
if (newDifficulty > MAXBOTDIFFICULTY)

View file

@ -3345,8 +3345,9 @@ fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower, boolean dorubberb
if (K_PlayerUsesBotMovement(player))
{
// Increase bot speed by 1-10% depending on difficulty
fixed_t add = (player->botvars.difficulty * (FRACUNIT/10)) / DIFFICULTBOT;
// Increase bot speed by 0-10% depending on difficulty
const fixed_t modifier = K_BotMapModifier();
fixed_t add = ((player->botvars.difficulty-1) * FixedMul(FRACUNIT / 10, modifier)) / (DIFFICULTBOT-1);
finalspeed = FixedMul(finalspeed, FRACUNIT + add);
if (player->bot && player->botvars.rival)
@ -9098,15 +9099,23 @@ void K_UpdateDistanceFromFinishLine(player_t *const player)
INT32 K_GetKartRingPower(player_t *player, boolean boosted)
{
INT32 ringPower = ((9 - player->kartspeed) + (9 - player->kartweight)) / 2;
fixed_t ringPower = ((9 - player->kartspeed) + (9 - player->kartweight)) * (FRACUNIT/2);
if (boosted == true && K_PlayerUsesBotMovement(player))
{
// Double for Lv. 9
ringPower += (player->botvars.difficulty * ringPower) / DIFFICULTBOT;
// x2.0 for Lv. 9
const fixed_t modifier = K_BotMapModifier();
fixed_t add = ((player->botvars.difficulty-1) * modifier) / (DIFFICULTBOT-1);
ringPower = FixedMul(ringPower, FRACUNIT + add);
if (player->botvars.rival == true)
{
// x2.0 for Rival
ringPower = FixedMul(ringPower, 2*FRACUNIT);
}
}
return ringPower;
return ringPower / FRACUNIT;
}
// Returns false if this player being placed here causes them to collide with any other player
@ -10644,10 +10653,11 @@ fixed_t K_PlayerBaseFriction(player_t *player, fixed_t original)
frict -= FixedMul(FRACUNIT >> 5, factor);
// Bots gain more traction as they rubberband.
if (player->botvars.rubberband > FRACUNIT)
fixed_t traction_value = FixedMul(player->botvars.rubberband, max(FRACUNIT, K_BotMapModifier()));
if (traction_value > FRACUNIT)
{
const fixed_t extraFriction = FixedMul(FRACUNIT >> 5, factor);
const fixed_t mul = player->botvars.rubberband - FRACUNIT;
const fixed_t mul = traction_value - FRACUNIT;
frict -= FixedMul(extraFriction, mul);
}
}

View file

@ -21,6 +21,8 @@
#include "g_game.h"
#include "p_slopes.h"
#include "cxxutil.hpp"
// The number of sparkles per waypoint connection in the waypoint visualisation
static const UINT32 SPARKLES_PER_CONNECTION = 16U;
@ -30,12 +32,16 @@ static const UINT32 SPARKLES_PER_CONNECTION = 16U;
#define CLOSEDSET_BASE_SIZE (256U)
#define NODESARRAY_BASE_SIZE (256U)
static waypoint_t *waypointheap = NULL;
static waypoint_t *waypointheap = NULL;
static waypoint_t *firstwaypoint = NULL;
static waypoint_t *finishline = NULL;
static waypoint_t *startingwaypoint = NULL;
static UINT32 circuitlength = 0U;
#define BASE_TRACK_COMPLEXITY (-5000) // Arbritrary, vibes-based value
static INT32 trackcomplexity = 0;
static size_t numwaypoints = 0U;
static size_t numwaypointmobjs = 0U;
static size_t baseopensetsize = OPENSET_BASE_SIZE;
@ -53,6 +59,16 @@ waypoint_t *K_GetFinishLineWaypoint(void)
return finishline;
}
/*--------------------------------------------------
waypoint_t *K_GetStartingWaypoint(void)
See header file for description.
--------------------------------------------------*/
waypoint_t *K_GetStartingWaypoint(void)
{
return startingwaypoint;
}
/*--------------------------------------------------
boolean K_GetWaypointIsFinishline(waypoint_t *waypoint)
@ -268,6 +284,16 @@ UINT32 K_GetCircuitLength(void)
return circuitlength;
}
/*--------------------------------------------------
INT32 K_GetTrackComplexity(void)
See header file for description.
--------------------------------------------------*/
INT32 K_GetTrackComplexity(void)
{
return trackcomplexity;
}
/*--------------------------------------------------
waypoint_t *K_GetClosestWaypointToMobj(mobj_t *const mobj)
@ -1909,13 +1935,34 @@ static UINT32 K_SetupCircuitLength(void)
// line places people correctly relative to each other
if ((mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE) == LF_SECTIONRACE)
{
circuitlength = 0U;
path_t bestsprintpath = {0};
auto sprint_finally = srb2::finally([&bestsprintpath]() { Z_Free(bestsprintpath.array); });
const boolean useshortcuts = false;
const boolean huntbackwards = true;
const UINT32 traveldist = UINT32_MAX - UINT16_MAX; // Go as far back as possible. Not exactly UINT32_MAX to avoid possible overflow.
boolean pathfindsuccess = K_PathfindThruCircuit(
finishline, traveldist,
&bestsprintpath,
useshortcuts, huntbackwards
);
circuitlength = bestsprintpath.totaldist;
if (pathfindsuccess == true)
{
startingwaypoint = (waypoint_t *)bestsprintpath.array[ bestsprintpath.numnodes - 1 ].nodedata;
}
}
else
{
// Create a fake finishline waypoint, then try and pathfind to the finishline from it
waypoint_t fakefinishline = *finishline;
path_t bestcircuitpath = {0};
auto circuit_finally = srb2::finally([&bestcircuitpath]() { Z_Free(bestcircuitpath.array); });
const boolean useshortcuts = false;
const boolean huntbackwards = false;
@ -1923,7 +1970,12 @@ static UINT32 K_SetupCircuitLength(void)
circuitlength = bestcircuitpath.totaldist;
Z_Free(bestcircuitpath.array);
if (finishline->numnextwaypoints > 0)
{
// TODO: Implementing a version of the fakefinishline hack for
// this instead would be the most ideal
startingwaypoint = finishline->nextwaypoints[0];
}
}
return circuitlength;
@ -2127,8 +2179,7 @@ static waypoint_t *K_SetupWaypoint(mobj_t *const mobj)
}
else
{
CONS_Alert(
CONS_WARNING, "Waypoint with ID %d has no next waypoint.\n", K_GetWaypointID(thiswaypoint));
CONS_Debug(DBG_SETUP, "Waypoint with ID %d has no next waypoint.\n", K_GetWaypointID(thiswaypoint));
}
}
else
@ -2214,6 +2265,412 @@ static void K_FreeWaypoints(void)
K_ClearWaypoints();
}
namespace
{
/*--------------------------------------------------
BlockItReturn_t K_TrackWaypointNearOffroad(line_t *line)
Blockmap iteration function to check in an extra radius
around a waypoint to find any solid walls around it.
--------------------------------------------------*/
static fixed_t g_track_wp_x = INT32_MAX;
static fixed_t g_track_wp_y = INT32_MAX;
static fixed_t g_track_wp_radius = INT32_MAX;
static BlockItReturn_t K_TrackWaypointNearOffroad(line_t *line)
{
fixed_t dist = INT32_MAX;
vertex_t v = {0};
P_ClosestPointOnLine(
g_track_wp_x, g_track_wp_y,
line,
&v
);
dist = R_PointToDist2(
g_track_wp_x, g_track_wp_y,
v.x, v.y
);
const fixed_t buffer = FixedMul(mobjinfo[MT_PLAYER].radius * 2, mapobjectscale) * 3;
dist -= buffer;
if (dist <= 0) // line gets crossed
{
if ((line->flags & (ML_TWOSIDED|ML_IMPASSABLE|ML_BLOCKPLAYERS|ML_MIDSOLID)) == ML_TWOSIDED)
{
// double-sided, and no blocking flags -- it's not a wall
const INT32 side = P_PointOnLineSide(g_track_wp_x, g_track_wp_y, line);
const sector_t *sec = side ? line->frontsector : line->backsector;
if (sec != nullptr && (sec->damagetype == SD_DEATHPIT || sec->damagetype == SD_INSTAKILL))
{
// force kill sectors to be more complex
return BMIT_STOP;
}
}
else
{
// actually is a wall
return BMIT_ABORT;
}
}
// not crossed, or not a wall
return BMIT_CONTINUE;
}
/*--------------------------------------------------
boolean K_SneakerPanelOverlap(struct sneakerpanel &panelA, struct sneakerpanel &panelB)
Returns whenever or not a sneaker panel sector / thing overlap
--------------------------------------------------*/
struct complexity_sneaker_s
{
fixed_t bbox[4];
//std::vector<sector_t *> sectors;
//std::vector<mapthing_t *> things;
complexity_sneaker_s(sector_t *sec)
{
M_ClearBox(bbox);
for (size_t i = 0; i < sec->linecount; i++)
{
line_t *const ld = sec->lines[i];
M_AddToBox(bbox, ld->bbox[BOXRIGHT], ld->bbox[BOXTOP]);
M_AddToBox(bbox, ld->bbox[BOXLEFT], ld->bbox[BOXBOTTOM]);
}
}
complexity_sneaker_s(mapthing_t *mt)
{
M_ClearBox(bbox);
fixed_t x = mt->x << FRACBITS;
fixed_t y = mt->y << FRACBITS;
fixed_t radius = FixedMul(FixedMul(mobjinfo[MT_SNEAKERPANEL].radius, mt->scale), mapobjectscale);
M_AddToBox(bbox, x - radius, y - radius);
M_AddToBox(bbox, x + radius, y + radius);
}
};
static boolean K_SneakerPanelOverlap(complexity_sneaker_s &panelA, complexity_sneaker_s &panelB)
{
const fixed_t overlap_extra = 528 * mapobjectscale; // merge ones this close together
const fixed_t a_width_half = (panelA.bbox[BOXRIGHT] - panelA.bbox[BOXLEFT]) / 2;
const fixed_t a_height_half = (panelA.bbox[BOXTOP] - panelA.bbox[BOXBOTTOM]) / 2;
const fixed_t a_x = panelA.bbox[BOXLEFT] + a_width_half;
const fixed_t a_y = panelA.bbox[BOXBOTTOM] + a_height_half;
const fixed_t b_width_half = (panelB.bbox[BOXRIGHT] - panelB.bbox[BOXLEFT]) / 2;
const fixed_t b_height_half = (panelB.bbox[BOXTOP] - panelB.bbox[BOXBOTTOM]) / 2;
const fixed_t b_x = panelB.bbox[BOXLEFT] + b_width_half;
const fixed_t b_y = panelB.bbox[BOXBOTTOM] + b_height_half;
const fixed_t dx = b_x - a_x;
const fixed_t px = (b_width_half - a_width_half) - abs(dx);
if (px <= -overlap_extra)
{
return false;
}
const fixed_t dy = b_y - a_y;
const fixed_t py = (b_height_half - a_height_half) - abs(dy);
if (py <= -overlap_extra)
{
return false;
}
return true;
}
/*--------------------------------------------------
INT32 K_CalculateTrackComplexity(void)
Sets the value of trackcomplexity. This value accumulates all of the
turn angle deltas to get an idea of how complicated the map is.
--------------------------------------------------*/
static INT32 K_CalculateTrackComplexity(void)
{
const boolean huntbackwards = false;
const boolean useshortcuts = false;
boolean pathfindsuccess = false;
path_t path = {0};
trackcomplexity = BASE_TRACK_COMPLEXITY;
if (startingwaypoint == NULL || finishline == NULL)
{
return trackcomplexity;
}
pathfindsuccess = K_PathfindToWaypoint(
startingwaypoint, finishline,
&path,
useshortcuts, huntbackwards
);
if (pathfindsuccess == true)
{
auto path_finally = srb2::finally([&path]() { Z_Free(path.array); });
for (size_t i = 1; i < path.numnodes-1; i++)
{
waypoint_t *const start = (waypoint_t *)path.array[ i - 1 ].nodedata;
waypoint_t *const mid = (waypoint_t *)path.array[ i ].nodedata;
waypoint_t *const end = (waypoint_t *)path.array[ i + 1 ].nodedata;
const fixed_t start_mid_dist = R_PointToDist2(
start->mobj->x, start->mobj->y,
mid->mobj->x, mid->mobj->y
);
const fixed_t mid_end_dist = R_PointToDist2(
mid->mobj->x, mid->mobj->y,
end->mobj->x, end->mobj->y
);
const angle_t start_mid_angle = R_PointToAngle2(
start->mobj->x, start->mobj->y,
mid->mobj->x, mid->mobj->y
);
const angle_t mid_end_angle = R_PointToAngle2(
mid->mobj->x, mid->mobj->y,
end->mobj->x, end->mobj->y
);
const angle_t start_mid_pitch = R_PointToAngle2(
0, start->mobj->z,
start_mid_dist, mid->mobj->z
);
const angle_t mid_end_pitch = R_PointToAngle2(
0, mid->mobj->z,
mid_end_dist, end->mobj->z
);
const fixed_t avg_radius = (start->mobj->radius + mid->mobj->radius + end->mobj->radius) / 3;
const fixed_t base_scale = DEFAULT_WAYPOINT_RADIUS * mapobjectscale;
// Reduce complexity with wider turns.
fixed_t radius_factor = FixedDiv(
base_scale,
std::max<fixed_t>(
1,
avg_radius
)
);
radius_factor = FRACUNIT + ((radius_factor - FRACUNIT) / 2); // reduce how much it's worth
// Reduce complexity with wider spaced waypoints.
fixed_t dist_factor = FixedDiv(
base_scale,
std::max<fixed_t>(
1,
start_mid_dist + mid_end_dist
)
);
fixed_t wall_factor = FRACUNIT;
constexpr fixed_t minimum_turn = 10 * FRACUNIT; // If the delta is lower than this, it's practically a straight-away.
fixed_t delta = AngleFixed(
AngleDelta(
start_mid_angle,
mid_end_angle
)
) - minimum_turn;
if (delta < 0)
{
dist_factor = FixedDiv(FRACUNIT, std::max(1, dist_factor));
radius_factor = FixedDiv(FRACUNIT, std::max(1, radius_factor));
}
else
{
// Weight turns hard enough
delta = FixedMul(delta, delta);
// Reduce turn complexity in walled maps.
wall_factor = FRACUNIT;
g_track_wp_x = mid->mobj->x;
g_track_wp_y = mid->mobj->y;
g_track_wp_radius = mid->mobj->radius;
const fixed_t searchRadius = /*g_track_wp_radius +*/ MAXRADIUS;
INT32 xl, xh, yl, yh;
INT32 bx, by;
const fixed_t c = FixedMul(g_track_wp_radius, FINECOSINE((start_mid_angle + ANGLE_90) >> ANGLETOFINESHIFT));
const fixed_t s = FixedMul(g_track_wp_radius, FINESINE((start_mid_angle + ANGLE_90) >> ANGLETOFINESHIFT));
validcount++; // used to make sure we only process a line once
xl = (unsigned)((g_track_wp_x + c - searchRadius) - bmaporgx)>>MAPBLOCKSHIFT;
xh = (unsigned)((g_track_wp_x + c + searchRadius) - bmaporgx)>>MAPBLOCKSHIFT;
yl = (unsigned)((g_track_wp_y + s - searchRadius) - bmaporgy)>>MAPBLOCKSHIFT;
yh = (unsigned)((g_track_wp_y + s + searchRadius) - bmaporgy)>>MAPBLOCKSHIFT;
BMBOUNDFIX(xl, xh, yl, yh);
for (bx = xl; bx <= xh; bx++)
{
for (by = yl; by <= yh; by++)
{
if (P_BlockLinesIterator(bx, by, K_TrackWaypointNearOffroad) == false)
{
wall_factor /= 4;
bx = xh + 1;
by = yh + 1;
}
}
}
validcount++; // used to make sure we only process a line once
xl = (unsigned)((g_track_wp_x - c - searchRadius) - bmaporgx)>>MAPBLOCKSHIFT;
xh = (unsigned)((g_track_wp_x - c + searchRadius) - bmaporgx)>>MAPBLOCKSHIFT;
yl = (unsigned)((g_track_wp_y - s - searchRadius) - bmaporgy)>>MAPBLOCKSHIFT;
yh = (unsigned)((g_track_wp_y - s + searchRadius) - bmaporgy)>>MAPBLOCKSHIFT;
BMBOUNDFIX(xl, xh, yl, yh);
for (bx = xl; bx <= xh; bx++)
{
for (by = yl; by <= yh; by++)
{
if (P_BlockLinesIterator(bx, by, K_TrackWaypointNearOffroad) == false)
{
wall_factor /= 4;
bx = xh + 1;
by = yh + 1;
}
}
}
}
fixed_t pitch_delta = AngleFixed(
AngleDelta(
start_mid_pitch,
mid_end_pitch
)
);
constexpr fixed_t minimum_drop = 45 * FRACUNIT; // If the delta is lower than this, it's probably just a slope.
if (pitch_delta > minimum_drop)
{
// bonus complexity for drop-off / ramp
delta += FixedMul(pitch_delta, FRACUNIT + (pitch_delta - minimum_drop));
}
delta = FixedMul(delta, FixedMul(FixedMul(dist_factor, radius_factor), wall_factor));
CONS_Printf(
"TURN [%d]: r: %.2f, d: %.2f, w: %.2f, r*d*w: %.2f, DELTA: %d\n",
i,
FixedToFloat(radius_factor),
FixedToFloat(dist_factor),
FixedToFloat(wall_factor),
FixedToFloat(FixedMul(FixedMul(dist_factor, radius_factor), wall_factor)),
(delta / FRACUNIT)
);
trackcomplexity += (delta / FRACUNIT);
}
std::vector<complexity_sneaker_s> sneaker_panels;
for (size_t i = 0; i < numsectors; i++)
{
sector_t *const sec = &sectors[i];
if (sec->linecount == 0)
{
continue;
}
terrain_t *terrain_f = K_GetTerrainForFlatNum(sec->floorpic);
terrain_t *terrain_c = K_GetTerrainForFlatNum(sec->ceilingpic);
if ((terrain_f != nullptr && (terrain_f->flags & TRF_SNEAKERPANEL))
|| (terrain_c != nullptr && (terrain_c->flags & TRF_SNEAKERPANEL)))
{
complexity_sneaker_s new_panel(sec);
boolean create_new = true;
for (size_t j = 0; j < sec->linecount; j++)
{
line_t *const ld = sec->lines[j];
M_AddToBox(new_panel.bbox, ld->bbox[BOXRIGHT], ld->bbox[BOXTOP]);
M_AddToBox(new_panel.bbox, ld->bbox[BOXLEFT], ld->bbox[BOXBOTTOM]);
}
for (auto &panel : sneaker_panels)
{
if (K_SneakerPanelOverlap(new_panel, panel) == true)
{
// merge together
M_AddToBox(panel.bbox, new_panel.bbox[BOXRIGHT], new_panel.bbox[BOXTOP]);
M_AddToBox(panel.bbox, new_panel.bbox[BOXLEFT], new_panel.bbox[BOXBOTTOM]);
//panel.sectors.push_back(sec);
create_new = false;
break;
}
}
if (create_new == true)
{
//new_panel.sectors.push_back(sec);
sneaker_panels.push_back(new_panel);
}
}
}
for (size_t i = 0; i < nummapthings; i++)
{
mapthing_t *const mt = &mapthings[i];
if (mt->type != mobjinfo[MT_SNEAKERPANEL].doomednum)
{
continue;
}
complexity_sneaker_s new_panel(mt);
boolean create_new = true;
for (auto &panel : sneaker_panels)
{
if (K_SneakerPanelOverlap(new_panel, panel) == true)
{
// merge together
M_AddToBox(panel.bbox, new_panel.bbox[BOXRIGHT], new_panel.bbox[BOXTOP]);
M_AddToBox(panel.bbox, new_panel.bbox[BOXLEFT], new_panel.bbox[BOXBOTTOM]);
create_new = false;
break;
}
}
if (create_new == true)
{
sneaker_panels.push_back(new_panel);
}
}
CONS_Printf("Num sneaker panel sets: %d\n", sneaker_panels.size());
trackcomplexity -= sneaker_panels.size() * 1250;
CONS_Printf(" ** COMPLEXITY: %d\n", trackcomplexity);
}
return trackcomplexity;
}
}; // namespace
/*--------------------------------------------------
boolean K_SetupWaypointList(void)
@ -2263,6 +2720,11 @@ boolean K_SetupWaypointList(void)
CONS_Alert(CONS_ERROR, "Circuit track waypoints do not form a circuit.\n");
}
if (startingwaypoint != NULL)
{
K_CalculateTrackComplexity();
}
setupsuccessful = true;
}
}
@ -2281,9 +2743,11 @@ void K_ClearWaypoints(void)
waypointheap = NULL;
firstwaypoint = NULL;
finishline = NULL;
startingwaypoint = NULL;
numwaypoints = 0U;
numwaypointmobjs = 0U;
circuitlength = 0U;
trackcomplexity = 0U;
}
/*--------------------------------------------------

View file

@ -55,6 +55,21 @@ struct waypoint_t
waypoint_t *K_GetFinishLineWaypoint(void);
/*--------------------------------------------------
waypoint_t *K_GetStartingWaypoint(void);
Return the waypoint farthest from the finish line.
Input Arguments:-
None
Return:-
The waypoint that is being used as the startingwaypoint.
--------------------------------------------------*/
waypoint_t *K_GetStartingWaypoint(void);
/*--------------------------------------------------
boolean K_GetWaypointIsFinishline(waypoint_t *waypoint)
@ -170,9 +185,26 @@ waypoint_t *K_GetWaypointFromID(INT32 waypointID);
Return:-
The circuit length.
--------------------------------------------------*/
UINT32 K_GetCircuitLength(void);
/*--------------------------------------------------
INT32 K_GetTrackComplexity(void)
Returns the track complexity values. This depends
on how many turns the map has, and is used for
bot code to determine their rubberbanding.
Input Arguments:-
Return:-
The track complexity value.
--------------------------------------------------*/
INT32 K_GetTrackComplexity(void);
/*--------------------------------------------------
waypoint_t *K_GetClosestWaypointToMobj(mobj_t *const mobj)

View file

@ -1249,31 +1249,7 @@ static mobj_t *InitSpecialUFO(waypoint_t *start)
mobj_t *Obj_CreateSpecialUFO(void)
{
waypoint_t *finishWaypoint = K_GetFinishLineWaypoint();
waypoint_t *startWaypoint = NULL;
if (finishWaypoint != NULL)
{
const boolean huntbackwards = true;
const boolean useshortcuts = false;
const UINT32 traveldist = INT32_MAX; // Go as far back as possible. Not UINT32_MAX to avoid possible overflow.
boolean pathfindsuccess = false;
path_t pathtofinish = {0};
pathfindsuccess = K_PathfindThruCircuit(
finishWaypoint, traveldist,
&pathtofinish,
useshortcuts, huntbackwards
);
if (pathfindsuccess == true)
{
startWaypoint = (waypoint_t *)pathtofinish.array[ pathtofinish.numnodes - 1 ].nodedata;
Z_Free(pathtofinish.array);
}
}
return InitSpecialUFO(startWaypoint);
return InitSpecialUFO(K_GetStartingWaypoint());
}
UINT32 K_GetSpecialUFODistance(void)