mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
Merge branch 'bot-nerfs' into 'master'
Track-based bot difficulty See merge request KartKrew/Kart!1482
This commit is contained in:
commit
d0b16f2025
9 changed files with 608 additions and 70 deletions
|
|
@ -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
|
||||
|
||||
|
|
|
|||
25
src/k_bot.h
25
src/k_bot.h
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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++;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
26
src/k_kart.c
26
src/k_kart.c
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 = §ors[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;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue