mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2026-01-01 04:33:02 +00:00
There were a few remaining cases of bot ticcmd generation editing player structures directly. Fix all of this and make as much of it pass const player pointers so this physically can't be allowed to happen ever again. Appears to improve bot sync in netgames & demos bot support, but I have not tested extensively.
1854 lines
44 KiB
C
1854 lines
44 KiB
C
// SONIC ROBO BLAST 2 KART
|
|
//-----------------------------------------------------------------------------
|
|
// Copyright (C) 2018-2020 by Sally "TehRealSalt" Cochenour
|
|
// Copyright (C) 2018-2020 by Kart Krew
|
|
//
|
|
// This program is free software distributed under the
|
|
// terms of the GNU General Public License, version 2.
|
|
// See the 'LICENSE' file for more details.
|
|
//-----------------------------------------------------------------------------
|
|
/// \file k_botitem.c
|
|
/// \brief Bot item usage logic
|
|
|
|
#include "doomdef.h"
|
|
#include "d_player.h"
|
|
#include "g_game.h"
|
|
#include "r_main.h"
|
|
#include "p_local.h"
|
|
#include "k_bot.h"
|
|
#include "lua_hook.h"
|
|
#include "byteptr.h"
|
|
#include "d_net.h" // nodetoplayer
|
|
#include "k_kart.h"
|
|
#include "z_zone.h"
|
|
#include "i_system.h"
|
|
#include "p_maputl.h"
|
|
#include "d_ticcmd.h"
|
|
#include "m_random.h"
|
|
#include "r_things.h" // numskins
|
|
#include "k_roulette.h"
|
|
|
|
/*--------------------------------------------------
|
|
static inline boolean K_ItemButtonWasDown(const player_t *player)
|
|
|
|
Looks for players around the bot, and presses the item button
|
|
if there is one in range.
|
|
|
|
Input Arguments:-
|
|
player - Bot to check.
|
|
|
|
Return:-
|
|
true if the item button was pressed last tic, otherwise false.
|
|
--------------------------------------------------*/
|
|
static inline boolean K_ItemButtonWasDown(const player_t *player)
|
|
{
|
|
return (player->oldcmd.buttons & BT_ATTACK);
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static boolean K_BotUseItemNearPlayer(const player_t *player, ticcmd_t *cmd, fixed_t radius)
|
|
|
|
Looks for players around the bot, and presses the item button
|
|
if there is one in range.
|
|
|
|
Input Arguments:-
|
|
player - Bot to compare against.
|
|
cmd - The bot's ticcmd.
|
|
radius - The radius to look for players in.
|
|
|
|
Return:-
|
|
true if a player was found & we can press the item button, otherwise false.
|
|
--------------------------------------------------*/
|
|
static boolean K_BotUseItemNearPlayer(const player_t *player, ticcmd_t *cmd, fixed_t radius)
|
|
{
|
|
UINT8 i;
|
|
|
|
if (K_ItemButtonWasDown(player) == true)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
{
|
|
player_t *target = NULL;
|
|
fixed_t dist = INT32_MAX;
|
|
|
|
if (!playeringame[i])
|
|
{
|
|
continue;
|
|
}
|
|
|
|
target = &players[i];
|
|
|
|
if (target->mo == NULL || P_MobjWasRemoved(target->mo)
|
|
|| player == target || target->spectator
|
|
|| target->flashing)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
dist = P_AproxDistance(P_AproxDistance(
|
|
player->mo->x - target->mo->x,
|
|
player->mo->y - target->mo->y),
|
|
(player->mo->z - target->mo->z) / 4
|
|
);
|
|
|
|
if (dist <= radius)
|
|
{
|
|
cmd->buttons |= BT_ATTACK;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static player_t *K_PlayerNearSpot(const player_t *player, fixed_t x, fixed_t y, fixed_t radius)
|
|
|
|
Looks for players around a specified x/y coordinate.
|
|
|
|
Input Arguments:-
|
|
player - Bot to compare against.
|
|
x - X coordinate to look around.
|
|
y - Y coordinate to look around.
|
|
radius - The radius to look for players in.
|
|
|
|
Return:-
|
|
The player we found, NULL if nothing was found.
|
|
--------------------------------------------------*/
|
|
static player_t *K_PlayerNearSpot(const player_t *player, fixed_t x, fixed_t y, fixed_t radius)
|
|
{
|
|
UINT8 i;
|
|
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
{
|
|
player_t *target = NULL;
|
|
fixed_t dist = INT32_MAX;
|
|
|
|
if (!playeringame[i])
|
|
{
|
|
continue;
|
|
}
|
|
|
|
target = &players[i];
|
|
|
|
if (target->mo == NULL || P_MobjWasRemoved(target->mo)
|
|
|| player == target || target->spectator
|
|
|| target->flashing)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
dist = P_AproxDistance(
|
|
x - target->mo->x,
|
|
y - target->mo->y
|
|
);
|
|
|
|
if (dist <= radius)
|
|
{
|
|
return target;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static player_t *K_PlayerPredictThrow(const player_t *player, UINT8 extra)
|
|
|
|
Looks for players around the predicted coordinates of their thrown item.
|
|
|
|
Input Arguments:-
|
|
player - Bot to compare against.
|
|
extra - Extra throwing distance, for aim forward on mines.
|
|
|
|
Return:-
|
|
The player we're trying to throw at, NULL if none was found.
|
|
--------------------------------------------------*/
|
|
static player_t *K_PlayerPredictThrow(const player_t *player, UINT8 extra)
|
|
{
|
|
const fixed_t dist = (30 + (extra * 10)) * player->mo->scale;
|
|
const UINT32 airtime = FixedDiv(dist + player->mo->momz, gravity);
|
|
const fixed_t throwspeed = FixedMul(82 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed));
|
|
const fixed_t estx = player->mo->x + P_ReturnThrustX(NULL, player->mo->angle, (throwspeed + player->speed) * airtime);
|
|
const fixed_t esty = player->mo->y + P_ReturnThrustY(NULL, player->mo->angle, (throwspeed + player->speed) * airtime);
|
|
return K_PlayerNearSpot(player, estx, esty, player->mo->radius * 2);
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static player_t *K_PlayerInCone(const player_t *player, UINT16 cone, boolean flip)
|
|
|
|
Looks for players in the .
|
|
|
|
Input Arguments:-
|
|
player - Bot to compare against.
|
|
radius - How far away the targets can be.
|
|
cone - Size of cone, in degrees as an integer.
|
|
flip - If true, look behind. Otherwise, check in front of the player.
|
|
|
|
Return:-
|
|
true if a player was found in the cone, otherwise false.
|
|
--------------------------------------------------*/
|
|
static player_t *K_PlayerInCone(const player_t *player, fixed_t radius, UINT16 cone, boolean flip)
|
|
{
|
|
UINT8 i;
|
|
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
{
|
|
player_t *target = NULL;
|
|
fixed_t dist = INT32_MAX;
|
|
|
|
if (!playeringame[i])
|
|
{
|
|
continue;
|
|
}
|
|
|
|
target = &players[i];
|
|
|
|
if (target->mo == NULL || P_MobjWasRemoved(target->mo)
|
|
|| player == target || target->spectator
|
|
|| target->flashing
|
|
|| !P_CheckSight(player->mo, target->mo))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
dist = P_AproxDistance(P_AproxDistance(
|
|
player->mo->x - target->mo->x,
|
|
player->mo->y - target->mo->y),
|
|
(player->mo->z - target->mo->z) / 4
|
|
);
|
|
|
|
if (dist <= radius)
|
|
{
|
|
angle_t a = player->mo->angle - R_PointToAngle2(player->mo->x, player->mo->y, target->mo->x, target->mo->y);
|
|
INT16 ad = 0;
|
|
|
|
if (a < ANGLE_180)
|
|
{
|
|
ad = AngleFixed(a)>>FRACBITS;
|
|
}
|
|
else
|
|
{
|
|
ad = 360-(AngleFixed(a)>>FRACBITS);
|
|
}
|
|
|
|
ad = abs(ad);
|
|
|
|
if (flip)
|
|
{
|
|
if (ad >= 180-cone)
|
|
{
|
|
return target;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (ad <= cone)
|
|
{
|
|
return target;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static boolean K_RivalBotAggression(const player_t *bot, const player_t *target)
|
|
|
|
Returns if a bot is a rival & wants to be aggressive to a player.
|
|
|
|
Input Arguments:-
|
|
bot - Bot to check.
|
|
target - Who the bot wants to attack.
|
|
|
|
Return:-
|
|
false if not the rival. false if the target is another bot. Otherwise, true.
|
|
--------------------------------------------------*/
|
|
static boolean K_RivalBotAggression(const player_t *bot, const player_t *target)
|
|
{
|
|
if (bot == NULL || target == NULL)
|
|
{
|
|
// Invalid.
|
|
return false;
|
|
}
|
|
|
|
if (bot->bot == false)
|
|
{
|
|
// lol
|
|
return false;
|
|
}
|
|
|
|
if (bot->botvars.rival == false)
|
|
{
|
|
// Not the rival, we aren't self-aware.
|
|
return false;
|
|
}
|
|
|
|
if (target->bot == false)
|
|
{
|
|
// This bot knows that the real threat is the player.
|
|
return true;
|
|
}
|
|
|
|
// Calling them your friends is misleading, but you'll at least spare them.
|
|
return false;
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static void K_ItemConfirmForTarget(const player_t *bot, ticcmd_t *cmd, const player_t *target, UINT16 amount)
|
|
|
|
Handles updating item confirm values for offense items.
|
|
|
|
Input Arguments:-
|
|
bot - Bot to check.
|
|
cmd - Bot's ticcmd to edit.
|
|
target - Who the bot wants to attack.
|
|
amount - Amount to increase item confirm time by.
|
|
|
|
Return:-
|
|
None
|
|
--------------------------------------------------*/
|
|
static void K_ItemConfirmForTarget(const player_t *bot, ticcmd_t *cmd, const player_t *target, UINT16 amount)
|
|
{
|
|
if (bot == NULL || target == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (K_RivalBotAggression(bot, target) == true)
|
|
{
|
|
// Double the rate when you're aggressive.
|
|
cmd->bot.itemconfirm += amount << 1;
|
|
}
|
|
else
|
|
{
|
|
// Do as normal.
|
|
cmd->bot.itemconfirm += amount;
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static boolean K_BotGenericPressItem(const player_t *player, ticcmd_t *cmd, SINT8 dir)
|
|
|
|
Presses the item button & aim buttons for the bot.
|
|
|
|
Input Arguments:-
|
|
player - Bot to do this for.
|
|
cmd - Bot's ticcmd to edit.
|
|
dir - Aiming direction: 1 for forwards, -1 for backwards, 0 for neutral.
|
|
|
|
Return:-
|
|
true if we could press, false if not.
|
|
--------------------------------------------------*/
|
|
static boolean K_BotGenericPressItem(const player_t *player, ticcmd_t *cmd, SINT8 dir)
|
|
{
|
|
if (K_ItemButtonWasDown(player) == true)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
cmd->throwdir = KART_FULLTURN * dir;
|
|
cmd->buttons |= BT_ATTACK;
|
|
//player->botvars.itemconfirm = 0;
|
|
return true;
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static void K_BotItemGenericTap(const player_t *player, ticcmd_t *cmd)
|
|
|
|
Item usage for generic items that you need to tap.
|
|
|
|
Input Arguments:-
|
|
player - Bot to do this for.
|
|
cmd - Bot's ticcmd to edit.
|
|
|
|
Return:-
|
|
None
|
|
--------------------------------------------------*/
|
|
static void K_BotItemGenericTap(const player_t *player, ticcmd_t *cmd)
|
|
{
|
|
if (K_ItemButtonWasDown(player) == false)
|
|
{
|
|
cmd->buttons |= BT_ATTACK;
|
|
//player->botvars.itemconfirm = 0;
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static boolean K_BotRevealsGenericTrap(const player_t *player, INT16 turnamt, boolean mine)
|
|
|
|
Decides if a bot is ready to reveal their trap item or not.
|
|
|
|
Input Arguments:-
|
|
player - Bot that has the banana.
|
|
turnamt - How hard they currently are turning.
|
|
mine - Set to true to handle Mine-specific behaviors.
|
|
|
|
Return:-
|
|
true if we want the bot to reveal their banana, otherwise false.
|
|
--------------------------------------------------*/
|
|
static boolean K_BotRevealsGenericTrap(const player_t *player, INT16 turnamt, boolean mine)
|
|
{
|
|
const fixed_t coneDist = FixedMul(1280 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed));
|
|
|
|
if (abs(turnamt) >= KART_FULLTURN/2)
|
|
{
|
|
// DON'T reveal on turns, we can place bananas on turns whenever we have multiple to spare,
|
|
// or if you missed your intentioned throw/place on a player.
|
|
return false;
|
|
}
|
|
|
|
// Check the predicted throws.
|
|
if (K_PlayerPredictThrow(player, 0) != NULL)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (mine)
|
|
{
|
|
if (K_PlayerPredictThrow(player, 1) != NULL)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Check your behind.
|
|
if (K_PlayerInCone(player, coneDist, 15, true) != NULL)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static void K_BotItemGenericTrapShield(const player_t *player, ticcmd_t *cmd, INT16 turnamt, boolean mine)
|
|
|
|
Item usage for Eggman shields.
|
|
|
|
Input Arguments:-
|
|
player - Bot to do this for.
|
|
cmd - Bot's ticcmd to edit.
|
|
turnamt - How hard they currently are turning.
|
|
mine - Set to true to handle Mine-specific behaviors.
|
|
|
|
Return:-
|
|
None
|
|
--------------------------------------------------*/
|
|
static void K_BotItemGenericTrapShield(const player_t *player, ticcmd_t *cmd, INT16 turnamt, boolean mine)
|
|
{
|
|
if (player->itemflags & IF_ITEMOUT)
|
|
{
|
|
return;
|
|
}
|
|
|
|
cmd->bot.itemconfirm++;
|
|
|
|
if (K_BotRevealsGenericTrap(player, turnamt, mine) || (player->botvars.itemconfirm > 5*TICRATE))
|
|
{
|
|
K_BotGenericPressItem(player, cmd, 0);
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static void K_BotItemGenericOrbitShield(const player_t *player, ticcmd_t *cmd)
|
|
|
|
Item usage for orbitting shields.
|
|
|
|
Input Arguments:-
|
|
player - Bot to do this for.
|
|
cmd - Bot's ticcmd to edit.
|
|
|
|
Return:-
|
|
None
|
|
--------------------------------------------------*/
|
|
static void K_BotItemGenericOrbitShield(const player_t *player, ticcmd_t *cmd)
|
|
{
|
|
if (player->itemflags & IF_ITEMOUT)
|
|
{
|
|
return;
|
|
}
|
|
|
|
K_BotGenericPressItem(player, cmd, 0);
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static void K_BotItemSneaker(const player_t *player, ticcmd_t *cmd)
|
|
|
|
Item usage for sneakers.
|
|
|
|
Input Arguments:-
|
|
player - Bot to do this for.
|
|
cmd - Bot's ticcmd to edit.
|
|
|
|
Return:-
|
|
None
|
|
--------------------------------------------------*/
|
|
static void K_BotItemSneaker(const player_t *player, ticcmd_t *cmd)
|
|
{
|
|
if (P_IsObjectOnGround(player->mo) == false)
|
|
{
|
|
// Don't use while mid-air.
|
|
return;
|
|
}
|
|
|
|
if ((player->offroad && K_ApplyOffroad(player)) // Stuck in offroad, use it NOW
|
|
|| K_GetWaypointIsShortcut(player->nextwaypoint) == true // Going toward a shortcut!
|
|
|| player->speed < K_GetKartSpeed(player, false, true) / 2 // Being slowed down too much
|
|
|| player->speedboost > (FRACUNIT/8) // Have another type of boost (tethering)
|
|
|| player->botvars.itemconfirm > 4*TICRATE) // Held onto it for too long
|
|
{
|
|
if (player->sneakertimer == 0 && K_ItemButtonWasDown(player) == false)
|
|
{
|
|
cmd->buttons |= BT_ATTACK;
|
|
//player->botvars.itemconfirm = 2*TICRATE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cmd->bot.itemconfirm++;
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static void K_BotItemRocketSneaker(player_t *player, ticcmd_t *cmd)
|
|
|
|
Item usage for rocket sneakers.
|
|
|
|
Input Arguments:-
|
|
player - Bot to do this for.
|
|
cmd - Bot's ticcmd to edit.
|
|
|
|
Return:-
|
|
None
|
|
--------------------------------------------------*/
|
|
static void K_BotItemRocketSneaker(const player_t *player, ticcmd_t *cmd)
|
|
{
|
|
if (P_IsObjectOnGround(player->mo) == false)
|
|
{
|
|
// Don't use while mid-air.
|
|
return;
|
|
}
|
|
|
|
if (player->botvars.itemconfirm > TICRATE)
|
|
{
|
|
if (player->sneakertimer == 0 && K_ItemButtonWasDown(player) == false)
|
|
{
|
|
cmd->buttons |= BT_ATTACK;
|
|
//player->botvars.itemconfirm = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cmd->bot.itemconfirm++;
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static void K_BotItemBanana(player_t *player, ticcmd_t *cmd, INT16 turnamt)
|
|
|
|
Item usage for trap item throwing.
|
|
|
|
Input Arguments:-
|
|
player - Bot to do this for.
|
|
cmd - Bot's ticcmd to edit.
|
|
turnamt - How hard they currently are turning.
|
|
|
|
Return:-
|
|
None
|
|
--------------------------------------------------*/
|
|
static void K_BotItemBanana(const player_t *player, ticcmd_t *cmd, INT16 turnamt)
|
|
{
|
|
const fixed_t coneDist = FixedMul(1280 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed));
|
|
SINT8 throwdir = -1;
|
|
boolean tryLookback = false;
|
|
player_t *target = NULL;
|
|
|
|
cmd->bot.itemconfirm++;
|
|
|
|
target = K_PlayerInCone(player, coneDist, 15, true);
|
|
if (target != NULL)
|
|
{
|
|
K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty);
|
|
throwdir = -1;
|
|
tryLookback = true;
|
|
}
|
|
|
|
if (abs(turnamt) >= KART_FULLTURN/2)
|
|
{
|
|
cmd->bot.itemconfirm += player->botvars.difficulty / 2;
|
|
throwdir = -1;
|
|
}
|
|
else
|
|
{
|
|
target = K_PlayerPredictThrow(player, 0);
|
|
|
|
if (target != NULL)
|
|
{
|
|
K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty * 2);
|
|
throwdir = 1;
|
|
}
|
|
}
|
|
|
|
if (tryLookback == true && throwdir == -1)
|
|
{
|
|
cmd->buttons |= BT_LOOKBACK;
|
|
}
|
|
|
|
if (player->botvars.itemconfirm > 10*TICRATE || player->bananadrag >= TICRATE)
|
|
{
|
|
K_BotGenericPressItem(player, cmd, throwdir);
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static void K_BotItemMine(player_t *player, ticcmd_t *cmd, INT16 turnamt)
|
|
|
|
Item usage for trap item throwing.
|
|
|
|
Input Arguments:-
|
|
player - Bot to do this for.
|
|
cmd - Bot's ticcmd to edit.
|
|
turnamt - How hard they currently are turning.
|
|
|
|
Return:-
|
|
None
|
|
--------------------------------------------------*/
|
|
static void K_BotItemMine(const player_t *player, ticcmd_t *cmd, INT16 turnamt)
|
|
{
|
|
const fixed_t coneDist = FixedMul(1280 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed));
|
|
SINT8 throwdir = 0;
|
|
boolean tryLookback = false;
|
|
player_t *target = NULL;
|
|
|
|
cmd->bot.itemconfirm++;
|
|
|
|
target = K_PlayerInCone(player, coneDist, 15, true);
|
|
if (target != NULL)
|
|
{
|
|
K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty);
|
|
throwdir = -1;
|
|
}
|
|
|
|
if (abs(turnamt) >= KART_FULLTURN/2)
|
|
{
|
|
cmd->bot.itemconfirm += player->botvars.difficulty / 2;
|
|
throwdir = -1;
|
|
tryLookback = true;
|
|
}
|
|
else
|
|
{
|
|
target = K_PlayerPredictThrow(player, 0);
|
|
if (target != NULL)
|
|
{
|
|
K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty * 2);
|
|
throwdir = 0;
|
|
}
|
|
|
|
target = K_PlayerPredictThrow(player, 1);
|
|
if (target != NULL)
|
|
{
|
|
K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty * 2);
|
|
throwdir = 1;
|
|
}
|
|
}
|
|
|
|
if (tryLookback == true && throwdir == -1)
|
|
{
|
|
cmd->buttons |= BT_LOOKBACK;
|
|
}
|
|
|
|
if (player->botvars.itemconfirm > 10*TICRATE || player->bananadrag >= TICRATE)
|
|
{
|
|
K_BotGenericPressItem(player, cmd, throwdir);
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static void K_BotItemLandmine(player_t *player, ticcmd_t *cmd, INT16 turnamt)
|
|
|
|
Item usage for landmine tossing.
|
|
|
|
Input Arguments:-
|
|
player - Bot to do this for.
|
|
cmd - Bot's ticcmd to edit.
|
|
turnamt - How hard they currently are turning.
|
|
|
|
Return:-
|
|
None
|
|
--------------------------------------------------*/
|
|
static void K_BotItemLandmine(const player_t *player, ticcmd_t *cmd, INT16 turnamt)
|
|
{
|
|
const fixed_t coneDist = FixedMul(1280 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed));
|
|
player_t *target = NULL;
|
|
|
|
cmd->bot.itemconfirm++;
|
|
|
|
if (abs(turnamt) >= KART_FULLTURN/2)
|
|
{
|
|
cmd->bot.itemconfirm += player->botvars.difficulty / 2;
|
|
}
|
|
|
|
target = K_PlayerInCone(player, coneDist, 15, true);
|
|
if (target != NULL)
|
|
{
|
|
K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty);
|
|
cmd->buttons |= BT_LOOKBACK;
|
|
}
|
|
|
|
if (player->botvars.itemconfirm > 10*TICRATE)
|
|
{
|
|
K_BotGenericPressItem(player, cmd, -1);
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static void K_BotItemEggman(player_t *player, ticcmd_t *cmd)
|
|
|
|
Item usage for Eggman item throwing.
|
|
|
|
Input Arguments:-
|
|
player - Bot to do this for.
|
|
cmd - Bot's ticcmd to edit.
|
|
|
|
Return:-
|
|
None
|
|
--------------------------------------------------*/
|
|
static void K_BotItemEggman(const player_t *player, ticcmd_t *cmd)
|
|
{
|
|
const fixed_t coneDist = FixedMul(1280 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed));
|
|
const UINT8 stealth = K_EggboxStealth(player->mo->x, player->mo->y);
|
|
SINT8 throwdir = -1;
|
|
boolean tryLookback = false;
|
|
player_t *target = NULL;
|
|
|
|
cmd->bot.itemconfirm++;
|
|
|
|
target = K_PlayerPredictThrow(player, 0);
|
|
if (target != NULL)
|
|
{
|
|
K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty / 2);
|
|
throwdir = 1;
|
|
}
|
|
|
|
target = K_PlayerInCone(player, coneDist, 15, true);
|
|
if (target != NULL)
|
|
{
|
|
K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty);
|
|
throwdir = -1;
|
|
tryLookback = true;
|
|
}
|
|
|
|
if (stealth > 1 || player->itemRoulette.active == true)
|
|
{
|
|
cmd->bot.itemconfirm += player->botvars.difficulty * 4;
|
|
throwdir = -1;
|
|
}
|
|
|
|
if (tryLookback == true && throwdir == -1)
|
|
{
|
|
cmd->buttons |= BT_LOOKBACK;
|
|
}
|
|
|
|
if (player->botvars.itemconfirm > 10*TICRATE || player->bananadrag >= TICRATE)
|
|
{
|
|
K_BotGenericPressItem(player, cmd, throwdir);
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static boolean K_BotRevealsEggbox(const player_t *player)
|
|
|
|
Decides if a bot is ready to place their Eggman item or not.
|
|
|
|
Input Arguments:-
|
|
player - Bot that has the eggbox.
|
|
|
|
Return:-
|
|
true if we want the bot to reveal their eggbox, otherwise false.
|
|
--------------------------------------------------*/
|
|
static boolean K_BotRevealsEggbox(const player_t *player)
|
|
{
|
|
const fixed_t coneDist = FixedMul(1280 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed));
|
|
const UINT8 stealth = K_EggboxStealth(player->mo->x, player->mo->y);
|
|
player_t *target = NULL;
|
|
|
|
// This is a stealthy spot for an eggbox, lets reveal it!
|
|
if (stealth > 1)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Check the predicted throws.
|
|
target = K_PlayerPredictThrow(player, 0);
|
|
if (target != NULL)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Check your behind.
|
|
target = K_PlayerInCone(player, coneDist, 15, true);
|
|
if (target != NULL)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static void K_BotItemEggmanShield(const player_t *player, ticcmd_t *cmd)
|
|
|
|
Item usage for Eggman shields.
|
|
|
|
Input Arguments:-
|
|
player - Bot to do this for.
|
|
cmd - Bot's ticcmd to edit.
|
|
|
|
Return:-
|
|
None
|
|
--------------------------------------------------*/
|
|
static void K_BotItemEggmanShield(const player_t *player, ticcmd_t *cmd)
|
|
{
|
|
if (player->itemflags & IF_EGGMANOUT)
|
|
{
|
|
return;
|
|
}
|
|
|
|
cmd->bot.itemconfirm++;
|
|
|
|
if (K_BotRevealsEggbox(player) == true || (player->botvars.itemconfirm > 20*TICRATE))
|
|
{
|
|
K_BotGenericPressItem(player, cmd, 0);
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static void K_BotItemEggmanExplosion(const player_t *player, ticcmd_t *cmd)
|
|
|
|
Item usage for Eggman explosions.
|
|
|
|
Input Arguments:-
|
|
player - Bot to do this for.
|
|
cmd - Bot's ticcmd to edit.
|
|
|
|
Return:-
|
|
None
|
|
--------------------------------------------------*/
|
|
static void K_BotItemEggmanExplosion(const player_t *player, ticcmd_t *cmd)
|
|
{
|
|
if (player->position == 1)
|
|
{
|
|
// Hey, we aren't gonna find anyone up here...
|
|
// why don't we slow down a bit? :)
|
|
cmd->forwardmove /= 2;
|
|
}
|
|
|
|
K_BotUseItemNearPlayer(player, cmd, 128*player->mo->scale);
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static void K_BotItemOrbinaut(const player_t *player, ticcmd_t *cmd)
|
|
|
|
Item usage for Orbinaut throwing.
|
|
|
|
Input Arguments:-
|
|
player - Bot to do this for.
|
|
cmd - Bot's ticcmd to edit.
|
|
|
|
Return:-
|
|
None
|
|
--------------------------------------------------*/
|
|
static void K_BotItemOrbinaut(const player_t *player, ticcmd_t *cmd)
|
|
{
|
|
const fixed_t topspeed = K_GetKartSpeed(player, false, true);
|
|
fixed_t radius = FixedMul(2560 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed));
|
|
SINT8 throwdir = -1;
|
|
boolean tryLookback = false;
|
|
UINT8 snipeMul = 2;
|
|
player_t *target = NULL;
|
|
|
|
if (player->speed > topspeed)
|
|
{
|
|
radius = FixedMul(radius, FixedDiv(player->speed, topspeed));
|
|
snipeMul = 3; // Confirm faster when you'll throw it with a bunch of extra speed!!
|
|
}
|
|
|
|
cmd->bot.itemconfirm++;
|
|
|
|
target = K_PlayerInCone(player, radius, 15, false);
|
|
if (target != NULL)
|
|
{
|
|
K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty * snipeMul);
|
|
throwdir = 1;
|
|
}
|
|
else
|
|
{
|
|
target = K_PlayerInCone(player, radius, 15, true);
|
|
|
|
if (target != NULL)
|
|
{
|
|
K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty);
|
|
throwdir = -1;
|
|
tryLookback = true;
|
|
}
|
|
}
|
|
|
|
if (tryLookback == true && throwdir == -1)
|
|
{
|
|
cmd->buttons |= BT_LOOKBACK;
|
|
}
|
|
|
|
if (player->botvars.itemconfirm > 25*TICRATE)
|
|
{
|
|
K_BotGenericPressItem(player, cmd, throwdir);
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static void K_BotItemBallhog(const player_t *player, ticcmd_t *cmd)
|
|
|
|
Item usage for Ballhog throwing.
|
|
|
|
Input Arguments:-
|
|
player - Bot to do this for.
|
|
cmd - Bot's ticcmd to edit.
|
|
|
|
Return:-
|
|
None
|
|
--------------------------------------------------*/
|
|
static void K_BotItemBallhog(const player_t *player, ticcmd_t *cmd)
|
|
{
|
|
const fixed_t topspeed = K_GetKartSpeed(player, false, true);
|
|
fixed_t radius = FixedMul(2560 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed));
|
|
SINT8 throwdir = -1;
|
|
boolean tryLookback = false;
|
|
UINT8 snipeMul = 2;
|
|
player_t *target = NULL;
|
|
boolean hold = false;
|
|
|
|
if (player->speed > topspeed)
|
|
{
|
|
radius = FixedMul(radius, FixedDiv(player->speed, topspeed));
|
|
snipeMul = 3; // Confirm faster when you'll throw it with a bunch of extra speed!!
|
|
}
|
|
|
|
target = K_PlayerInCone(player, radius, 15, false);
|
|
if (target != NULL)
|
|
{
|
|
K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty * snipeMul);
|
|
throwdir = 1;
|
|
}
|
|
else
|
|
{
|
|
target = K_PlayerInCone(player, radius, 15, true);
|
|
|
|
if (target != NULL)
|
|
{
|
|
K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty);
|
|
throwdir = -1;
|
|
tryLookback = true;
|
|
}
|
|
}
|
|
|
|
if (tryLookback == true && throwdir == -1)
|
|
{
|
|
cmd->buttons |= BT_LOOKBACK;
|
|
}
|
|
|
|
if (target != NULL)
|
|
{
|
|
// Charge up!
|
|
hold = true;
|
|
}
|
|
else
|
|
{
|
|
// If we lose sight of the target, then we'll just
|
|
// let go and it'll do a partial-blast.
|
|
|
|
// If we've been waiting for too long though, then
|
|
// we'll go for the full charge :)
|
|
cmd->bot.itemconfirm++;
|
|
hold = (player->botvars.itemconfirm > 10*TICRATE);
|
|
}
|
|
|
|
if (hold == true)
|
|
{
|
|
cmd->throwdir = KART_FULLTURN * throwdir;
|
|
cmd->buttons |= BT_ATTACK;
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static void K_BotItemDropTarget(const player_t *player, ticcmd_t *cmd, INT16 turnamt)
|
|
|
|
Item usage for Drop Target throwing.
|
|
|
|
Input Arguments:-
|
|
player - Bot to do this for.
|
|
cmd - Bot's ticcmd to edit.
|
|
turnamt - How hard they currently are turning.
|
|
|
|
Return:-
|
|
None
|
|
--------------------------------------------------*/
|
|
static void K_BotItemDropTarget(const player_t *player, ticcmd_t *cmd, INT16 turnamt)
|
|
{
|
|
const fixed_t topspeed = K_GetKartSpeed(player, false, true);
|
|
fixed_t radius = FixedMul(1280 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed));
|
|
SINT8 throwdir = -1;
|
|
boolean tryLookback = false;
|
|
UINT8 snipeMul = 2;
|
|
player_t *target = NULL;
|
|
|
|
if (player->speed > topspeed)
|
|
{
|
|
radius = FixedMul(radius, FixedDiv(player->speed, topspeed));
|
|
snipeMul = 3; // Confirm faster when you'll throw it with a bunch of extra speed!!
|
|
}
|
|
|
|
cmd->bot.itemconfirm++;
|
|
|
|
if (abs(turnamt) >= KART_FULLTURN/2)
|
|
{
|
|
cmd->bot.itemconfirm += player->botvars.difficulty / 2;
|
|
throwdir = -1;
|
|
}
|
|
|
|
target = K_PlayerInCone(player, radius, 15, false);
|
|
if (target != NULL)
|
|
{
|
|
K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty * snipeMul);
|
|
throwdir = 1;
|
|
}
|
|
else
|
|
{
|
|
target = K_PlayerInCone(player, radius, 15, true);
|
|
|
|
if (target != NULL)
|
|
{
|
|
K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty);
|
|
throwdir = -1;
|
|
tryLookback = true;
|
|
}
|
|
}
|
|
|
|
if (tryLookback == true && throwdir == -1)
|
|
{
|
|
cmd->buttons |= BT_LOOKBACK;
|
|
}
|
|
|
|
if (player->botvars.itemconfirm > 10*TICRATE || player->bananadrag >= TICRATE)
|
|
{
|
|
K_BotGenericPressItem(player, cmd, throwdir);
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static void K_BotItemJawz(const player_t *player, ticcmd_t *cmd)
|
|
|
|
Item usage for Jawz throwing.
|
|
|
|
Input Arguments:-
|
|
player - Bot to do this for.
|
|
cmd - Bot's ticcmd to edit.
|
|
|
|
Return:-
|
|
None
|
|
--------------------------------------------------*/
|
|
static void K_BotItemJawz(const player_t *player, ticcmd_t *cmd)
|
|
{
|
|
const fixed_t topspeed = K_GetKartSpeed(player, false, true);
|
|
fixed_t radius = FixedMul(2560 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed));
|
|
SINT8 throwdir = 1;
|
|
boolean tryLookback = false;
|
|
UINT8 snipeMul = 2;
|
|
INT32 lastTarg = player->lastjawztarget;
|
|
player_t *target = NULL;
|
|
|
|
if (player->speed > topspeed)
|
|
{
|
|
radius = FixedMul(radius, FixedDiv(player->speed, topspeed));
|
|
snipeMul = 3; // Confirm faster when you'll throw it with a bunch of extra speed!!
|
|
}
|
|
|
|
cmd->bot.itemconfirm++;
|
|
|
|
target = K_PlayerInCone(player, radius, 15, true);
|
|
if (target != NULL)
|
|
{
|
|
K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty);
|
|
throwdir = -1;
|
|
tryLookback = true;
|
|
}
|
|
|
|
if (lastTarg != -1
|
|
&& playeringame[lastTarg] == true
|
|
&& players[lastTarg].spectator == false
|
|
&& players[lastTarg].mo != NULL
|
|
&& P_MobjWasRemoved(players[lastTarg].mo) == false)
|
|
{
|
|
mobj_t *targMo = players[lastTarg].mo;
|
|
mobj_t *mobj = NULL, *next = NULL;
|
|
boolean targettedAlready = false;
|
|
|
|
target = &players[lastTarg];
|
|
|
|
// Make sure no other Jawz are targetting this player.
|
|
for (mobj = trackercap; mobj; mobj = next)
|
|
{
|
|
next = mobj->itnext;
|
|
|
|
if (mobj->type == MT_JAWZ && mobj->target == targMo)
|
|
{
|
|
targettedAlready = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (targettedAlready == false)
|
|
{
|
|
K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty * snipeMul);
|
|
throwdir = 1;
|
|
}
|
|
}
|
|
|
|
if (tryLookback == true && throwdir == -1)
|
|
{
|
|
cmd->buttons |= BT_LOOKBACK;
|
|
}
|
|
|
|
if (player->botvars.itemconfirm > 25*TICRATE)
|
|
{
|
|
K_BotGenericPressItem(player, cmd, throwdir);
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static void K_BotItemLightning(const player_t *player, ticcmd_t *cmd)
|
|
|
|
Item usage for Lightning Shield.
|
|
|
|
Input Arguments:-
|
|
player - Bot to do this for.
|
|
cmd - Bot's ticcmd to edit.
|
|
|
|
Return:-
|
|
None
|
|
--------------------------------------------------*/
|
|
static void K_BotItemLightning(const player_t *player, ticcmd_t *cmd)
|
|
{
|
|
if (K_BotUseItemNearPlayer(player, cmd, 192*player->mo->scale) == false)
|
|
{
|
|
if (player->botvars.itemconfirm > 10*TICRATE)
|
|
{
|
|
K_BotGenericPressItem(player, cmd, 0);
|
|
}
|
|
else
|
|
{
|
|
cmd->bot.itemconfirm++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static void K_BotItemBubble(const player_t *player, ticcmd_t *cmd)
|
|
|
|
Item usage for Bubble Shield.
|
|
|
|
Input Arguments:-
|
|
player - Bot to do this for.
|
|
cmd - Bot's ticcmd to edit.
|
|
|
|
Return:-
|
|
None
|
|
--------------------------------------------------*/
|
|
static void K_BotItemBubble(const player_t *player, ticcmd_t *cmd)
|
|
{
|
|
boolean hold = false;
|
|
|
|
if (player->bubbleblowup <= 0)
|
|
{
|
|
UINT8 i;
|
|
|
|
cmd->bot.itemconfirm++;
|
|
|
|
if (player->bubblecool <= 0)
|
|
{
|
|
const fixed_t radius = 192 * player->mo->scale;
|
|
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
{
|
|
player_t *target = NULL;
|
|
fixed_t dist = INT32_MAX;
|
|
|
|
if (!playeringame[i])
|
|
{
|
|
continue;
|
|
}
|
|
|
|
target = &players[i];
|
|
|
|
if (target->mo == NULL || P_MobjWasRemoved(target->mo)
|
|
|| player == target || target->spectator
|
|
|| target->flashing)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
dist = P_AproxDistance(P_AproxDistance(
|
|
player->mo->x - target->mo->x,
|
|
player->mo->y - target->mo->y),
|
|
(player->mo->z - target->mo->z) / 4
|
|
);
|
|
|
|
if (dist <= radius)
|
|
{
|
|
hold = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (player->bubbleblowup >= bubbletime)
|
|
{
|
|
if (player->botvars.itemconfirm > 10*TICRATE)
|
|
{
|
|
hold = true;
|
|
}
|
|
}
|
|
else if (player->bubbleblowup < bubbletime)
|
|
{
|
|
hold = true;
|
|
}
|
|
|
|
if (hold && (player->itemflags & IF_HOLDREADY))
|
|
{
|
|
cmd->buttons |= BT_ATTACK;
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static void K_BotItemFlame(const player_t *player, ticcmd_t *cmd)
|
|
|
|
Item usage for Flame Shield.
|
|
|
|
Input Arguments:-
|
|
player - Bot to do this for.
|
|
cmd - Bot's ticcmd to edit.
|
|
|
|
Return:-
|
|
None
|
|
--------------------------------------------------*/
|
|
static void K_BotItemFlame(const player_t *player, ticcmd_t *cmd)
|
|
{
|
|
if (player->botvars.itemconfirm > 0)
|
|
{
|
|
cmd->bot.itemconfirm--;
|
|
}
|
|
else if (player->itemflags & IF_HOLDREADY)
|
|
{
|
|
INT32 flamemax = player->flamelength;
|
|
|
|
if (player->flamemeter < flamemax || flamemax == 0)
|
|
{
|
|
cmd->buttons |= BT_ATTACK;
|
|
}
|
|
else
|
|
{
|
|
//player->botvars.itemconfirm = 3*flamemax/4;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static void K_BotItemGardenTopDeploy(const player_t *player, ticcmd_t *cmd)
|
|
|
|
Item usage for deploying the Garden Top.
|
|
|
|
Input Arguments:-
|
|
player - Bot to do this for.
|
|
cmd - Bot's ticcmd to edit.
|
|
|
|
Return:-
|
|
None
|
|
--------------------------------------------------*/
|
|
static void K_BotItemGardenTopDeploy(const player_t *player, ticcmd_t *cmd)
|
|
{
|
|
cmd->bot.itemconfirm++;
|
|
|
|
//if (player->curshield != KSHIELD_TOP)
|
|
if (player->botvars.itemconfirm > 2*TICRATE)
|
|
{
|
|
K_BotGenericPressItem(player, cmd, 0);
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static void K_BotItemGardenTop(const player_t *player, ticcmd_t *cmd, INT16 turnamt)
|
|
|
|
Item usage for Garden Top movement.
|
|
|
|
Input Arguments:-
|
|
player - Bot to do this for.
|
|
cmd - Bot's ticcmd to edit.
|
|
turnamt - How hard they currently are turning.
|
|
|
|
Return:-
|
|
None
|
|
--------------------------------------------------*/
|
|
static void K_BotItemGardenTop(const player_t *player, ticcmd_t *cmd, INT16 turnamt)
|
|
{
|
|
const fixed_t topspeed = K_GetKartSpeed(player, false, true);
|
|
fixed_t radius = FixedMul(2560 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed));
|
|
SINT8 throwdir = -1;
|
|
UINT8 snipeMul = 1;
|
|
player_t *target = NULL;
|
|
|
|
if (player->speed > topspeed)
|
|
{
|
|
radius = FixedMul(radius, FixedDiv(player->speed, topspeed));
|
|
snipeMul = 2; // Confirm faster when you'll throw it with a bunch of extra speed!!
|
|
}
|
|
|
|
cmd->bot.itemconfirm++;
|
|
|
|
target = K_PlayerInCone(player, radius, 15, false);
|
|
if (target != NULL)
|
|
{
|
|
K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty * snipeMul);
|
|
throwdir = 1;
|
|
}
|
|
|
|
if (player->topdriftheld > 0)
|
|
{
|
|
// Grinding in place.
|
|
// Wait until we're mostly done turning.
|
|
// Cancel early if we hit max thrust speed.
|
|
if ((abs(turnamt) >= KART_FULLTURN/8)
|
|
&& (player->topdriftheld <= GARDENTOP_MAXGRINDTIME))
|
|
{
|
|
cmd->buttons |= BT_DRIFT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const angle_t maxDelta = ANGLE_11hh;
|
|
angle_t delta = AngleDelta(player->mo->angle, K_MomentumAngle(player->mo));
|
|
|
|
if (delta > maxDelta)
|
|
{
|
|
// Do we need to turn? Start grinding!
|
|
cmd->buttons |= BT_DRIFT;
|
|
}
|
|
}
|
|
|
|
if (player->botvars.itemconfirm > 25*TICRATE)
|
|
{
|
|
K_BotGenericPressItem(player, cmd, throwdir);
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static void K_BotItemRings(const player_t *player, ticcmd_t *cmd)
|
|
|
|
Item usage for rings.
|
|
|
|
Input Arguments:-
|
|
player - Bot to do this for.
|
|
cmd - Bot's ticcmd to edit.
|
|
|
|
Return:-
|
|
None
|
|
--------------------------------------------------*/
|
|
static void K_BotItemRings(const player_t *player, ticcmd_t *cmd)
|
|
{
|
|
INT32 saferingsval = 16 - K_GetKartRingPower(player, false);
|
|
|
|
if (leveltime < starttime)
|
|
{
|
|
// Don't use rings during POSITION!!
|
|
return;
|
|
}
|
|
|
|
if ((cmd->buttons & BT_ACCELERATE) == 0)
|
|
{
|
|
// Don't use rings if you're not trying to accelerate.
|
|
return;
|
|
}
|
|
|
|
if (P_IsObjectOnGround(player->mo) == false)
|
|
{
|
|
// Don't use while mid-air.
|
|
return;
|
|
}
|
|
|
|
if (player->speed < K_GetKartSpeed(player, false, true) / 2 // Being slowed down too much
|
|
|| player->speedboost > (FRACUNIT/5)) // Have another type of boost (tethering)
|
|
{
|
|
saferingsval -= 5;
|
|
}
|
|
|
|
if (player->rings > saferingsval)
|
|
{
|
|
cmd->buttons |= BT_ATTACK;
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static void K_BotItemInstashield(const player_t *player, ticcmd_t *cmd)
|
|
|
|
Item usage for instashield.
|
|
|
|
Input Arguments:-
|
|
player - Bot to do this for.
|
|
cmd - Bot's ticcmd to edit.
|
|
|
|
Return:-
|
|
None
|
|
--------------------------------------------------*/
|
|
static void K_BotItemInstashield(const player_t *player, ticcmd_t *cmd)
|
|
{
|
|
const fixed_t radius = FixedMul(mobjinfo[MT_INSTAWHIP].radius, player->mo->scale);
|
|
size_t i = SIZE_MAX;
|
|
|
|
if (K_ItemButtonWasDown(player) == true)
|
|
{
|
|
// Release the button, dude.
|
|
return;
|
|
}
|
|
|
|
if (player->instaWhipCharge || leveltime < starttime || player->spindash)
|
|
{
|
|
// Instashield is on cooldown.
|
|
return;
|
|
}
|
|
|
|
// Find players within the instashield's range.
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
{
|
|
player_t *target = NULL;
|
|
fixed_t dist = INT32_MAX;
|
|
|
|
if (!playeringame[i])
|
|
{
|
|
continue;
|
|
}
|
|
|
|
target = &players[i];
|
|
if (P_MobjWasRemoved(target->mo) == true
|
|
|| player == target
|
|
|| target->spectator == true
|
|
|| target->flashing != 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
dist = P_AproxDistance(P_AproxDistance(
|
|
player->mo->x - target->mo->x,
|
|
player->mo->y - target->mo->y),
|
|
(player->mo->z - target->mo->z) / 4
|
|
);
|
|
|
|
if (dist <= radius)
|
|
{
|
|
K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty * 2);
|
|
}
|
|
}
|
|
|
|
if (player->botvars.itemconfirm > 10*TICRATE)
|
|
{
|
|
// Use it!!
|
|
cmd->buttons |= BT_ATTACK;
|
|
//player->botvars.itemconfirm = 0;
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static tic_t K_BotItemRouletteMashConfirm(const player_t *player)
|
|
|
|
How long this bot waits before selecting an item for
|
|
the item roulette.
|
|
|
|
Input Arguments:-
|
|
player - Bot to do this for.
|
|
|
|
Return:-
|
|
Time to wait, in tics.
|
|
--------------------------------------------------*/
|
|
static tic_t K_BotItemRouletteMashConfirm(const player_t *player)
|
|
{
|
|
// 24 tics late for Lv.1, frame-perfect for Lv.MAX
|
|
return (MAXBOTDIFFICULTY - player->botvars.difficulty) * 2;
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static void K_BotItemRouletteMash(const player_t *player, ticcmd_t *cmd)
|
|
|
|
Item usage for item roulette mashing.
|
|
|
|
Input Arguments:-
|
|
player - Bot to do this for.
|
|
cmd - Bot's ticcmd to edit.
|
|
|
|
Return:-
|
|
None
|
|
--------------------------------------------------*/
|
|
static void K_BotItemRouletteMash(const player_t *player, ticcmd_t *cmd)
|
|
{
|
|
const tic_t confirmTime = K_BotItemRouletteMashConfirm(player);
|
|
|
|
if (K_ItemButtonWasDown(player) == true)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (player->botvars.itemconfirm > confirmTime)
|
|
{
|
|
// We've waited out our reaction time -- press the button now!
|
|
cmd->buttons |= BT_ATTACK;
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
void K_BotItemUsage(const player_t *player, ticcmd_t *cmd, INT16 turnamt)
|
|
|
|
See header file for description.
|
|
--------------------------------------------------*/
|
|
void K_BotItemUsage(const player_t *player, ticcmd_t *cmd, INT16 turnamt)
|
|
{
|
|
if (player->itemflags & IF_USERINGS)
|
|
{
|
|
if (player->rings > 0)
|
|
{
|
|
// Use rings!
|
|
K_BotItemRings(player, cmd);
|
|
}
|
|
else
|
|
{
|
|
// Use the instashield!
|
|
K_BotItemInstashield(player, cmd);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (player->botvars.itemdelay)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (player->itemRoulette.active == true)
|
|
{
|
|
// Mashing behaviors
|
|
K_BotItemRouletteMash(player, cmd);
|
|
return;
|
|
}
|
|
|
|
if (player->stealingtimer == 0)
|
|
{
|
|
if (player->eggmanexplode)
|
|
{
|
|
K_BotItemEggmanExplosion(player, cmd);
|
|
}
|
|
else if (player->itemflags & IF_EGGMANOUT)
|
|
{
|
|
K_BotItemEggman(player, cmd);
|
|
}
|
|
else if (player->rocketsneakertimer > 0)
|
|
{
|
|
K_BotItemRocketSneaker(player, cmd);
|
|
}
|
|
else
|
|
{
|
|
switch (player->itemtype)
|
|
{
|
|
default:
|
|
if (player->itemtype != KITEM_NONE)
|
|
{
|
|
K_BotItemGenericTap(player, cmd);
|
|
}
|
|
|
|
//player->botvars.itemconfirm = 0;
|
|
break;
|
|
case KITEM_INVINCIBILITY:
|
|
case KITEM_SPB:
|
|
case KITEM_GROW:
|
|
case KITEM_SHRINK:
|
|
case KITEM_SUPERRING:
|
|
K_BotItemGenericTap(player, cmd);
|
|
break;
|
|
case KITEM_ROCKETSNEAKER:
|
|
if (player->rocketsneakertimer <= 0)
|
|
{
|
|
K_BotItemGenericTap(player, cmd);
|
|
}
|
|
break;
|
|
case KITEM_SNEAKER:
|
|
K_BotItemSneaker(player, cmd);
|
|
break;
|
|
case KITEM_BANANA:
|
|
if (!(player->itemflags & IF_ITEMOUT))
|
|
{
|
|
K_BotItemGenericTrapShield(player, cmd, turnamt, false);
|
|
}
|
|
else
|
|
{
|
|
K_BotItemBanana(player, cmd, turnamt);
|
|
}
|
|
break;
|
|
case KITEM_EGGMAN:
|
|
K_BotItemEggmanShield(player, cmd);
|
|
break;
|
|
case KITEM_ORBINAUT:
|
|
if (!(player->itemflags & IF_ITEMOUT))
|
|
{
|
|
K_BotItemGenericOrbitShield(player, cmd);
|
|
}
|
|
else if (player->position != 1) // Hold onto orbiting items when in 1st :)
|
|
{
|
|
K_BotItemOrbinaut(player, cmd);
|
|
}
|
|
break;
|
|
case KITEM_JAWZ:
|
|
if (!(player->itemflags & IF_ITEMOUT))
|
|
{
|
|
K_BotItemGenericOrbitShield(player, cmd);
|
|
}
|
|
else if (player->position != 1) // Hold onto orbiting items when in 1st :)
|
|
{
|
|
K_BotItemJawz(player, cmd);
|
|
}
|
|
break;
|
|
case KITEM_MINE:
|
|
if (!(player->itemflags & IF_ITEMOUT))
|
|
{
|
|
K_BotItemGenericTrapShield(player, cmd, turnamt, true);
|
|
}
|
|
else
|
|
{
|
|
K_BotItemMine(player, cmd, turnamt);
|
|
}
|
|
break;
|
|
case KITEM_LANDMINE:
|
|
case KITEM_HYUDORO: // Function re-use, as they have about the same usage.
|
|
K_BotItemLandmine(player, cmd, turnamt);
|
|
break;
|
|
case KITEM_BALLHOG:
|
|
K_BotItemBallhog(player, cmd);
|
|
break;
|
|
case KITEM_DROPTARGET:
|
|
if (!(player->itemflags & IF_ITEMOUT))
|
|
{
|
|
K_BotItemGenericTrapShield(player, cmd, turnamt, false);
|
|
}
|
|
else
|
|
{
|
|
K_BotItemDropTarget(player, cmd, turnamt);
|
|
}
|
|
break;
|
|
case KITEM_GARDENTOP:
|
|
if (player->curshield != KSHIELD_TOP)
|
|
{
|
|
K_BotItemGardenTopDeploy(player, cmd);
|
|
}
|
|
else
|
|
{
|
|
K_BotItemGardenTop(player, cmd, turnamt);
|
|
}
|
|
break;
|
|
case KITEM_LIGHTNINGSHIELD:
|
|
K_BotItemLightning(player, cmd);
|
|
break;
|
|
case KITEM_BUBBLESHIELD:
|
|
K_BotItemBubble(player, cmd);
|
|
break;
|
|
case KITEM_FLAMESHIELD:
|
|
K_BotItemFlame(player, cmd);
|
|
break;
|
|
/*
|
|
case KITEM_GACHABOM:
|
|
K_BotItemGachabom(player, cmd);
|
|
break;
|
|
*/
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static void K_UpdateBotGameplayVarsItemUsageMash(player_t *player)
|
|
|
|
Thinker function used by K_UpdateBotGameplayVarsItemUsage for
|
|
deterimining item rolls.
|
|
--------------------------------------------------*/
|
|
static void K_UpdateBotGameplayVarsItemUsageMash(player_t *player)
|
|
{
|
|
const tic_t confirmTime = K_BotItemRouletteMashConfirm(player);
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
void K_UpdateBotGameplayVarsItemUsage(player_t *player)
|
|
|
|
See header file for description.
|
|
--------------------------------------------------*/
|
|
void K_UpdateBotGameplayVarsItemUsage(player_t *player)
|
|
{
|
|
if (player->itemflags & IF_USERINGS)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (player->botvars.itemdelay)
|
|
{
|
|
player->botvars.itemdelay--;
|
|
player->botvars.itemconfirm = 0;
|
|
return;
|
|
}
|
|
|
|
player->botvars.itemconfirm += player->cmd.bot.itemconfirm;
|
|
|
|
if (player->itemRoulette.active == true)
|
|
{
|
|
// Mashing behaviors
|
|
K_UpdateBotGameplayVarsItemUsageMash(player);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
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;
|
|
}
|
|
|
|
if (priority == BOT_ITEM_PR_RINGDEBT)
|
|
{
|
|
if (player->rings > 0)
|
|
{
|
|
// Only consider this priority when in ring debt.
|
|
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;
|
|
}
|
|
}
|