mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
Bots: Decide what item they want without using RNG
Makes Lv.1 a lot less aggressive, and Lv.MAX slightly more aggressive. But the main advantage is simply that they are deterministic again and I can :D
This commit is contained in:
parent
77ab86ab34
commit
d367bacc39
6 changed files with 263 additions and 6 deletions
|
|
@ -333,6 +333,9 @@ struct botvars_t
|
|||
|
||||
tic_t spindashconfirm; // When high enough, they will try spindashing
|
||||
UINT32 respawnconfirm; // When high enough, they will use Ring Shooter
|
||||
|
||||
UINT8 roulettePriority; // What items to go for on the roulette
|
||||
tic_t rouletteTimeout; // If it takes too long to decide, try lowering priority until we find something valid.
|
||||
};
|
||||
|
||||
// player_t struct for round-specific condition tracking
|
||||
|
|
@ -399,6 +402,23 @@ struct itemroulette_t
|
|||
boolean eggman;
|
||||
};
|
||||
|
||||
// enum for bot item priorities
|
||||
typedef enum
|
||||
{
|
||||
BOT_ITEM_PR__FALLBACK, // Priority decrement fallback -- end the bot's roulette asap
|
||||
BOT_ITEM_PR_NEUTRAL, // Default priority
|
||||
BOT_ITEM_PR_FRONTRUNNER,
|
||||
BOT_ITEM_PR_SPEED,
|
||||
// Priorities beyond this point are explicitly
|
||||
// used when any item from their priority group
|
||||
// exists in the roulette at all.
|
||||
BOT_ITEM_PR__OVERRIDES,
|
||||
BOT_ITEM_PR_RINGDEBT = BOT_ITEM_PR__OVERRIDES,
|
||||
BOT_ITEM_PR_POWER,
|
||||
BOT_ITEM_PR_SPB,
|
||||
BOT_ITEM_PR__MAX
|
||||
} botItemPriority_e;
|
||||
|
||||
// player_t struct for loop state
|
||||
typedef struct {
|
||||
fixed_t radius;
|
||||
|
|
|
|||
22
src/k_bot.h
22
src/k_bot.h
|
|
@ -37,6 +37,9 @@ extern "C" {
|
|||
// How many tics without being able to make progress before we'll let you respawn.
|
||||
#define BOTRESPAWNCONFIRM (5*TICRATE)
|
||||
|
||||
// How long it takes for a Lv.1 bot to decide to pick an item.
|
||||
#define BOT_ITEM_DECISION_TIME (3*TICRATE)
|
||||
|
||||
// Point for bots to aim for
|
||||
struct botprediction_t {
|
||||
fixed_t x, y;
|
||||
|
|
@ -277,6 +280,25 @@ void K_UpdateBotGameplayVars(player_t *player);
|
|||
|
||||
void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt);
|
||||
|
||||
|
||||
/*--------------------------------------------------
|
||||
void K_BotPickItemPriority(player_t *player)
|
||||
|
||||
Sets a bot's desired item classification
|
||||
based on what they're ,
|
||||
and delays based on their difficulty
|
||||
intended to be run when starting a roulette.
|
||||
|
||||
Input Arguments:-
|
||||
player - Bot to set the item classification for.
|
||||
|
||||
Return:-
|
||||
N/A
|
||||
--------------------------------------------------*/
|
||||
|
||||
void K_BotPickItemPriority(player_t *player);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
|
|
|||
132
src/k_botitem.c
132
src/k_botitem.c
|
|
@ -1394,16 +1394,55 @@ static void K_BotItemRings(player_t *player, ticcmd_t *cmd)
|
|||
--------------------------------------------------*/
|
||||
static void K_BotItemRouletteMash(player_t *player, ticcmd_t *cmd)
|
||||
{
|
||||
// 12 tics late for Lv.1, frame-perfect for Lv.MAX
|
||||
const tic_t confirmTime = (MAXBOTDIFFICULTY - player->botvars.difficulty);
|
||||
|
||||
if (K_ItemButtonWasDown(player) == true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Would be nice to implement smarter behavior
|
||||
// for selecting items.
|
||||
if (player->botvars.roulettePriority == BOT_ITEM_PR__FALLBACK)
|
||||
{
|
||||
// No items were part of our list, so set immediately.
|
||||
player->botvars.itemconfirm = confirmTime + 1;
|
||||
}
|
||||
else if (player->botvars.itemconfirm > 0)
|
||||
{
|
||||
// Delaying our reaction time a bit...
|
||||
player->botvars.itemconfirm++;
|
||||
}
|
||||
else
|
||||
{
|
||||
botItemPriority_e currentPriority = K_GetBotItemPriority(
|
||||
player->itemRoulette.itemList[ player->itemRoulette.index ]
|
||||
);
|
||||
|
||||
if (player->botvars.roulettePriority == currentPriority)
|
||||
{
|
||||
// This is the item we want! Start timing!
|
||||
player->botvars.itemconfirm++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not the time we want... if we take too long,
|
||||
// reduce priority until we get to a valid one.
|
||||
player->botvars.rouletteTimeout++;
|
||||
|
||||
if (player->botvars.rouletteTimeout > player->itemRoulette.itemListLen * player->itemRoulette.speed)
|
||||
{
|
||||
player->botvars.roulettePriority--;
|
||||
player->botvars.rouletteTimeout = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (player->botvars.itemconfirm > confirmTime)
|
||||
{
|
||||
// We've waited out our reaction time -- press the button now!
|
||||
cmd->buttons |= BT_ATTACK;
|
||||
}
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt)
|
||||
|
|
@ -1568,3 +1607,92 @@ void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
void K_BotPickItemPriority(player_t *player)
|
||||
|
||||
See header file for description.
|
||||
--------------------------------------------------*/
|
||||
void K_BotPickItemPriority(player_t *player)
|
||||
{
|
||||
const fixed_t closeDistance = FixedMul(1280 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed));
|
||||
size_t i;
|
||||
|
||||
// Roulette reaction time. This is how long to wait before considering items.
|
||||
// Takes 3 seconds for Lv.1, is instant for Lv.MAX
|
||||
player->botvars.itemdelay = ((MAXBOTDIFFICULTY - player->botvars.difficulty) * BOT_ITEM_DECISION_TIME) / (MAXBOTDIFFICULTY - 1);
|
||||
player->botvars.itemconfirm = 0;
|
||||
|
||||
// Set neutral items by default.
|
||||
player->botvars.roulettePriority = BOT_ITEM_PR_NEUTRAL;
|
||||
player->botvars.rouletteTimeout = 0;
|
||||
|
||||
// Check for items that are extremely high priority.
|
||||
for (i = 0; i < player->itemRoulette.itemListLen; i++)
|
||||
{
|
||||
botItemPriority_e priority = K_GetBotItemPriority( player->itemRoulette.itemList[i] );
|
||||
|
||||
if (priority < BOT_ITEM_PR__OVERRIDES)
|
||||
{
|
||||
// Not high enough to override.
|
||||
continue;
|
||||
}
|
||||
|
||||
player->botvars.roulettePriority = max( player->botvars.roulettePriority, priority );
|
||||
}
|
||||
|
||||
if (player->botvars.roulettePriority >= BOT_ITEM_PR__OVERRIDES)
|
||||
{
|
||||
// Selected a priority in the loop above.
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
player_t *other = NULL;
|
||||
fixed_t distance = INT32_MAX;
|
||||
|
||||
if (playeringame[i] == false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
other = &players[i];
|
||||
if (other->spectator == true || P_MobjWasRemoved(other->mo) == true)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
distance = P_AproxDistance(
|
||||
P_AproxDistance(
|
||||
other->mo->x - player->mo->x,
|
||||
other->mo->y - player->mo->y
|
||||
),
|
||||
other->mo->z - player->mo->z
|
||||
);
|
||||
|
||||
if (distance < closeDistance)
|
||||
{
|
||||
// A player is relatively close.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == MAXPLAYERS)
|
||||
{
|
||||
// Players are nearby, stay as neutral priority.
|
||||
return;
|
||||
}
|
||||
|
||||
// Players are far away enough to give you breathing room.
|
||||
if (player->position == 1)
|
||||
{
|
||||
// Frontrunning, so pick frontrunner items!
|
||||
player->botvars.roulettePriority = BOT_ITEM_PR_FRONTRUNNER;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Behind, so pick speed items!
|
||||
player->botvars.roulettePriority = BOT_ITEM_PR_SPEED;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -262,6 +262,71 @@ boolean K_ItemSingularity(kartitems_t item)
|
|||
}
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
botItemPriority_e K_GetBotItemPriority(kartitems_t type)
|
||||
|
||||
See header file for description.
|
||||
--------------------------------------------------*/
|
||||
botItemPriority_e K_GetBotItemPriority(kartitems_t type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case KITEM_SPB:
|
||||
{
|
||||
// Items that are intended to improve the game balance for everyone.
|
||||
return BOT_ITEM_PR_SPB;
|
||||
}
|
||||
case KITEM_INVINCIBILITY:
|
||||
case KITEM_GROW:
|
||||
case KITEM_SHRINK:
|
||||
case KITEM_LIGHTNINGSHIELD:
|
||||
case KITEM_BUBBLESHIELD:
|
||||
case KITEM_FLAMESHIELD:
|
||||
{
|
||||
// Items that drastically improve your own defense and/or speed.
|
||||
return BOT_ITEM_PR_POWER;
|
||||
}
|
||||
case KITEM_SUPERRING:
|
||||
{
|
||||
// Items that get you out of ring debt.
|
||||
return BOT_ITEM_PR_RINGDEBT;
|
||||
}
|
||||
case KITEM_SNEAKER:
|
||||
case KITEM_ROCKETSNEAKER:
|
||||
case KITEM_GARDENTOP:
|
||||
case KITEM_POGOSPRING:
|
||||
{
|
||||
// Used when not in 1st place and relatively far from players.
|
||||
// Items that give you speed with no protection.
|
||||
return BOT_ITEM_PR_SPEED;
|
||||
}
|
||||
case KITEM_HYUDORO:
|
||||
case KITEM_LANDMINE:
|
||||
case KITEM_DROPTARGET:
|
||||
case KITEM_EGGMAN:
|
||||
case KITEM_GACHABOM:
|
||||
{
|
||||
// Used when in 1st place and relatively far from players.
|
||||
// Typically attack items that don't give you protection.
|
||||
return BOT_ITEM_PR_FRONTRUNNER;
|
||||
}
|
||||
case KITEM_ORBINAUT:
|
||||
case KITEM_BALLHOG:
|
||||
case KITEM_JAWZ:
|
||||
case KITEM_BANANA:
|
||||
case KITEM_MINE:
|
||||
{
|
||||
// Used in all other instances (close to other players, no priority override)
|
||||
// Typically attack items that give you protection.
|
||||
return BOT_ITEM_PR_NEUTRAL;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return BOT_ITEM_PR__FALLBACK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
static fixed_t K_ItemOddsScale(UINT8 playerCount)
|
||||
|
||||
|
|
@ -1351,9 +1416,10 @@ void K_StartItemRoulette(player_t *const player)
|
|||
|
||||
K_FillItemRouletteData(player, roulette);
|
||||
|
||||
// Make the bots select their item after a little while.
|
||||
// One of the few instances of bot RNG, would be nice to remove it.
|
||||
player->botvars.itemdelay = P_RandomRange(PR_UNDEFINED, TICRATE, TICRATE*3);
|
||||
if (K_PlayerUsesBotMovement(player) == true)
|
||||
{
|
||||
K_BotPickItemPriority(player);
|
||||
}
|
||||
|
||||
// Prevent further duplicates of items that
|
||||
// are intended to only have one out at a time.
|
||||
|
|
|
|||
|
|
@ -57,6 +57,23 @@ boolean K_ItemEnabled(kartitems_t item);
|
|||
boolean K_ItemSingularity(kartitems_t item);
|
||||
|
||||
|
||||
/*--------------------------------------------------
|
||||
botItemPriority_e K_GetBotItemPriority(kartitems_t type)
|
||||
|
||||
Returns an item's priority value, which
|
||||
bots use to determine what kind of item they
|
||||
want when the roulette is started.
|
||||
|
||||
Input Arguments:-
|
||||
item - The item to check.
|
||||
|
||||
Return:-
|
||||
The item's priority type.
|
||||
--------------------------------------------------*/
|
||||
|
||||
botItemPriority_e K_GetBotItemPriority(kartitems_t type);
|
||||
|
||||
|
||||
/*--------------------------------------------------
|
||||
INT32 K_KartGetItemOdds(const player_t *player, itemroulette_t *const roulette, UINT8 pos, kartitems_t item);
|
||||
|
||||
|
|
|
|||
|
|
@ -444,6 +444,8 @@ static void P_NetArchivePlayers(savebuffer_t *save)
|
|||
WRITESINT8(save->p, players[i].botvars.turnconfirm);
|
||||
WRITEUINT32(save->p, players[i].botvars.spindashconfirm);
|
||||
WRITEUINT32(save->p, players[i].botvars.respawnconfirm);
|
||||
WRITEUINT8(save->p, players[i].botvars.roulettePriority);
|
||||
WRITEUINT32(save->p, players[i].botvars.rouletteTimeout);
|
||||
|
||||
// itemroulette_t
|
||||
WRITEUINT8(save->p, players[i].itemRoulette.active);
|
||||
|
|
@ -827,6 +829,8 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
|
|||
players[i].botvars.turnconfirm = READSINT8(save->p);
|
||||
players[i].botvars.spindashconfirm = READUINT32(save->p);
|
||||
players[i].botvars.respawnconfirm = READUINT32(save->p);
|
||||
players[i].botvars.roulettePriority = READUINT8(save->p);
|
||||
players[i].botvars.rouletteTimeout = READUINT32(save->p);
|
||||
|
||||
// itemroulette_t
|
||||
players[i].itemRoulette.active = (boolean)READUINT8(save->p);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue