Merge branch 'bot-controller' into 'master'

Revamped Bot Controllers

See merge request KartKrew/Kart!1574
This commit is contained in:
Oni 2023-10-24 01:03:01 +00:00
commit bb537b8de9
10 changed files with 157 additions and 103 deletions

View file

@ -371,7 +371,6 @@ struct botvars_t
// All entries above persist between rounds and must be recorded in demos // All entries above persist between rounds and must be recorded in demos
fixed_t rubberband; // Bot rubberband value fixed_t rubberband; // Bot rubberband value
UINT16 controller; // Special bot controller linedef ID
tic_t itemdelay; // Delay before using item at all tic_t itemdelay; // Delay before using item at all
tic_t itemconfirm; // When high enough, they will use their item tic_t itemconfirm; // When high enough, they will use their item

View file

@ -2326,7 +2326,6 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
p->ringvolume = 255; p->ringvolume = 255;
p->botvars.rubberband = FRACUNIT; p->botvars.rubberband = FRACUNIT;
p->botvars.controller = UINT16_MAX;
p->spectatorReentry = spectatorReentry; p->spectatorReentry = spectatorReentry;
p->griefValue = griefValue; p->griefValue = griefValue;

View file

@ -379,74 +379,50 @@ static fixed_t K_BotSpeedScaled(player_t *player, fixed_t speed)
} }
/*-------------------------------------------------- /*--------------------------------------------------
static line_t *K_FindBotController(mobj_t *mo) const botcontroller_t *K_GetBotController(mobj_t *mobj)
Finds if any bot controller linedefs are tagged to the bot's sector. See header file for description.
Input Arguments:-
mo - The bot player's mobj.
Return:-
Linedef of the bot controller. nullptr if it doesn't exist.
--------------------------------------------------*/ --------------------------------------------------*/
static line_t *K_FindBotController(mobj_t *mo) const botcontroller_t *K_GetBotController(mobj_t *mobj)
{ {
msecnode_t *node; botcontroller_t *ret = nullptr;
ffloor_t *rover;
INT16 lineNum = -1;
mtag_t tag;
I_Assert(mo != nullptr); if (P_MobjWasRemoved(mobj) == true)
I_Assert(!P_MobjWasRemoved(mo));
for (node = mo->touching_sectorlist; node; node = node->m_sectorlist_next)
{ {
if (!node->m_sector) return nullptr;
}
if (mobj->subsector == nullptr || mobj->subsector->sector == nullptr)
{
return nullptr;
}
ret = &mobj->subsector->sector->botController;
ffloor_t *rover = nullptr;
for (rover = mobj->subsector->sector->ffloors; rover; rover = rover->next)
{
if ((rover->fofflags & FOF_EXISTS) == 0)
{ {
continue; continue;
} }
tag = Tag_FGet(&node->m_sector->tags); fixed_t topheight = P_GetFOFTopZ(mobj, mobj->subsector->sector, rover, mobj->x, mobj->y, nullptr);
lineNum = P_FindSpecialLineFromTag(2004, tag, -1); // todo: needs to not use P_FindSpecialLineFromTag fixed_t bottomheight = P_GetFOFBottomZ(mobj, mobj->subsector->sector, rover, mobj->x, mobj->y, nullptr);
if (lineNum != -1) if (mobj->z > topheight || mobj->z + mobj->height < bottomheight)
{ {
break; continue;
} }
for (rover = node->m_sector->ffloors; rover; rover = rover->next) botcontroller_t *roverController = &rover->master->frontsector->botController;
if (roverController->trick != 0 || roverController->flags != 0)
{ {
sector_t *rs = nullptr; ret = roverController;
if (!(rover->fofflags & FOF_EXISTS))
{
continue;
}
if (mo->z > *rover->topheight || mo->z + mo->height < *rover->bottomheight)
{
continue;
}
rs = &sectors[rover->secnum];
tag = Tag_FGet(&rs->tags);
lineNum = P_FindSpecialLineFromTag(2004, tag, -1);
if (lineNum != -1)
{
break;
}
} }
} }
if (lineNum != -1) return ret;
{
return &lines[lineNum];
}
else
{
return nullptr;
}
} }
/*-------------------------------------------------- /*--------------------------------------------------
@ -555,18 +531,10 @@ fixed_t K_BotRubberband(player_t *player)
return FRACUNIT; return FRACUNIT;
} }
if (player->botvars.controller != UINT16_MAX) const botcontroller_t *botController = K_GetBotController(player->mo);
if (botController != nullptr && (botController->flags & TMBOT_NORUBBERBAND) == TMBOT_NORUBBERBAND) // Disable rubberbanding
{ {
const line_t *botController = &lines[player->botvars.controller]; return FRACUNIT;
if (botController != nullptr)
{
// Disable rubberbanding
if (botController->args[1] & TMBOT_NORUBBERBAND)
{
return FRACUNIT;
}
}
} }
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
@ -1121,19 +1089,19 @@ static void K_DrawPredictionDebug(botprediction_t *predict, player_t *player)
} }
/*-------------------------------------------------- /*--------------------------------------------------
static void K_BotTrick(player_t *player, ticcmd_t *cmd, line_t *botController) static void K_BotTrick(player_t *player, ticcmd_t *cmd, const botcontroller_t *botController)
Determines inputs for trick panels. Determines inputs for trick panels.
Input Arguments:- Input Arguments:-
player - Player to generate the ticcmd for. player - Player to generate the ticcmd for.
cmd - The player's ticcmd to modify. cmd - The player's ticcmd to modify.
botController - Linedef for the bot controller. botController - Bot controller struct.
Return:- Return:-
None None
--------------------------------------------------*/ --------------------------------------------------*/
static void K_BotTrick(player_t *player, ticcmd_t *cmd, const line_t *botController) static void K_BotTrick(player_t *player, ticcmd_t *cmd, const botcontroller_t *botController)
{ {
// Trick panel state -- do nothing until a controller line is found, in which case do a trick. // Trick panel state -- do nothing until a controller line is found, in which case do a trick.
if (botController == nullptr) if (botController == nullptr)
@ -1143,21 +1111,18 @@ static void K_BotTrick(player_t *player, ticcmd_t *cmd, const line_t *botControl
if (player->trickpanel == 1) if (player->trickpanel == 1)
{ {
INT32 type = botController->args[0]; switch (botController->trick)
// Y Offset: Trick type
switch (type)
{ {
case 1: case TMBOTTR_LEFT:
cmd->turning = KART_FULLTURN; cmd->turning = KART_FULLTURN;
break; break;
case 2: case TMBOTTR_RIGHT:
cmd->turning = -KART_FULLTURN; cmd->turning = -KART_FULLTURN;
break; break;
case 3: case TMBOTTR_UP:
cmd->throwdir = KART_FULLTURN; cmd->throwdir = KART_FULLTURN;
break; break;
case 4: case TMBOTTR_DOWN:
cmd->throwdir = -KART_FULLTURN; cmd->throwdir = -KART_FULLTURN;
break; break;
} }
@ -1531,7 +1496,6 @@ static void K_BuildBotTiccmdNormal(player_t *player, ticcmd_t *cmd)
angle_t destangle = 0; angle_t destangle = 0;
UINT8 spindash = 0; UINT8 spindash = 0;
INT32 turnamt = 0; INT32 turnamt = 0;
const line_t *botController = player->botvars.controller != UINT16_MAX ? &lines[player->botvars.controller] : nullptr;
if (!(gametyperules & GTR_BOTS) // No bot behaviors if (!(gametyperules & GTR_BOTS) // No bot behaviors
|| K_GetNumWaypoints() == 0 // No waypoints || K_GetNumWaypoints() == 0 // No waypoints
@ -1554,15 +1518,9 @@ static void K_BuildBotTiccmdNormal(player_t *player, ticcmd_t *cmd)
if (!cv_botcontrol.value) if (!cv_botcontrol.value)
return; return;
#endif #endif
// Actual gameplay behaviors below this block! // Actual gameplay behaviors below this block!
const botcontroller_t *botController = K_GetBotController(player->mo);
if (K_TryRingShooter(player) == true)
{
// We want to respawn. Simply hold Y and stop here!
cmd->buttons |= (BT_RESPAWN | BT_EBRAKEMASK);
return;
}
if (player->trickpanel != 0) if (player->trickpanel != 0)
{ {
K_BotTrick(player, cmd, botController); K_BotTrick(player, cmd, botController);
@ -1571,29 +1529,48 @@ static void K_BuildBotTiccmdNormal(player_t *player, ticcmd_t *cmd)
return; return;
} }
if (botController != nullptr && (botController->args[1] & TMBOT_NOCONTROL)) if (botController != nullptr && (botController->flags & TMBOT_NOCONTROL) == TMBOT_NOCONTROL)
{ {
// Disable bot controls entirely. // Disable bot controls entirely.
return; return;
} }
if (K_TryRingShooter(player) == true)
{
// We want to respawn. Simply hold Y and stop here!
cmd->buttons |= (BT_RESPAWN | BT_EBRAKEMASK);
return;
}
destangle = player->mo->angle; destangle = player->mo->angle;
if (botController != nullptr && (botController->args[1] & TMBOT_FORCEDIR)) if (botController != nullptr && (botController->flags & TMBOT_FORCEDIR) == TMBOT_FORCEDIR)
{ {
const fixed_t dist = DEFAULT_WAYPOINT_RADIUS * player->mo->scale; const fixed_t dist = DEFAULT_WAYPOINT_RADIUS * player->mo->scale;
// X Offset: Movement direction
destangle = FixedAngle(botController->args[2] * FRACUNIT);
// Overwritten prediction // Overwritten prediction
predict = static_cast<botprediction_t *>(Z_Calloc(sizeof(botprediction_t), PU_STATIC, nullptr)); predict = static_cast<botprediction_t *>(Z_Calloc(sizeof(botprediction_t), PU_STATIC, nullptr));
predict->x = player->mo->x + FixedMul(dist, FINECOSINE(destangle >> ANGLETOFINESHIFT)); predict->x = player->mo->x + FixedMul(dist, FINECOSINE(botController->forceAngle >> ANGLETOFINESHIFT));
predict->y = player->mo->y + FixedMul(dist, FINESINE(destangle >> ANGLETOFINESHIFT)); predict->y = player->mo->y + FixedMul(dist, FINESINE(botController->forceAngle >> ANGLETOFINESHIFT));
predict->radius = (DEFAULT_WAYPOINT_RADIUS / 4) * mapobjectscale; predict->radius = (DEFAULT_WAYPOINT_RADIUS / 4) * mapobjectscale;
} }
if (P_IsObjectOnGround(player->mo) == false)
{
if (player->fastfall == 0)
{
if (botController != nullptr && (botController->flags & TMBOT_FASTFALL) == TMBOT_FASTFALL)
{
// Fast fall!
cmd->buttons |= BT_EBRAKEMASK;
return;
}
}
//return; // Don't allow bots to turn in the air.
}
if (leveltime <= starttime && finishBeamLine != nullptr) if (leveltime <= starttime && finishBeamLine != nullptr)
{ {
// Handle POSITION!! // Handle POSITION!!
@ -1838,9 +1815,6 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd)
--------------------------------------------------*/ --------------------------------------------------*/
void K_UpdateBotGameplayVars(player_t *player) void K_UpdateBotGameplayVars(player_t *player)
{ {
const line_t *botController;
player->botvars.controller = UINT16_MAX;
player->botvars.rubberband = FRACUNIT; player->botvars.rubberband = FRACUNIT;
if (gamestate != GS_LEVEL || !player->mo) if (gamestate != GS_LEVEL || !player->mo)
@ -1849,8 +1823,5 @@ void K_UpdateBotGameplayVars(player_t *player)
return; return;
} }
botController = K_FindBotController(player->mo);
player->botvars.controller = botController ? (botController - lines) : UINT16_MAX;
player->botvars.rubberband = K_UpdateRubberband(player); player->botvars.rubberband = K_UpdateRubberband(player);
} }

View file

@ -13,6 +13,7 @@
#ifndef __K_BOT__ #ifndef __K_BOT__
#define __K_BOT__ #define __K_BOT__
#include "typedef.h"
#include "k_waypoint.h" #include "k_waypoint.h"
#include "d_player.h" #include "d_player.h"
#include "r_defs.h" #include "r_defs.h"
@ -85,6 +86,22 @@ boolean K_PlayerUsesBotMovement(player_t *player);
boolean K_BotCanTakeCut(player_t *player); boolean K_BotCanTakeCut(player_t *player);
/*--------------------------------------------------
const botcontroller_t *K_GetBotController(mobj_t *mobj);
Retrieves the current bot controller values from
the player's current sector.
Input Arguments:-
mobj - The player's object to get the bot controller for.
Return:-
Pointer to the sector's bot controller struct.
--------------------------------------------------*/
const botcontroller_t *K_GetBotController(mobj_t *mobj);
/*-------------------------------------------------- /*--------------------------------------------------
fixed_t K_BotMapModifier(void); fixed_t K_BotMapModifier(void);

View file

@ -611,7 +611,6 @@ static void P_NetArchivePlayers(savebuffer_t *save)
WRITEUINT8(save->p, players[i].botvars.diffincrease); WRITEUINT8(save->p, players[i].botvars.diffincrease);
WRITEUINT8(save->p, players[i].botvars.rival); WRITEUINT8(save->p, players[i].botvars.rival);
WRITEFIXED(save->p, players[i].botvars.rubberband); WRITEFIXED(save->p, players[i].botvars.rubberband);
WRITEUINT16(save->p, players[i].botvars.controller);
WRITEUINT32(save->p, players[i].botvars.itemdelay); WRITEUINT32(save->p, players[i].botvars.itemdelay);
WRITEUINT32(save->p, players[i].botvars.itemconfirm); WRITEUINT32(save->p, players[i].botvars.itemconfirm);
WRITESINT8(save->p, players[i].botvars.turnconfirm); WRITESINT8(save->p, players[i].botvars.turnconfirm);
@ -1126,7 +1125,6 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
players[i].botvars.diffincrease = READUINT8(save->p); players[i].botvars.diffincrease = READUINT8(save->p);
players[i].botvars.rival = (boolean)READUINT8(save->p); players[i].botvars.rival = (boolean)READUINT8(save->p);
players[i].botvars.rubberband = READFIXED(save->p); players[i].botvars.rubberband = READFIXED(save->p);
players[i].botvars.controller = READUINT16(save->p);
players[i].botvars.itemdelay = READUINT32(save->p); players[i].botvars.itemdelay = READUINT32(save->p);
players[i].botvars.itemconfirm = READUINT32(save->p); players[i].botvars.itemconfirm = READUINT32(save->p);
players[i].botvars.turnconfirm = READSINT8(save->p); players[i].botvars.turnconfirm = READSINT8(save->p);
@ -1729,6 +1727,7 @@ static void P_NetUnArchiveColormaps(savebuffer_t *save)
//diff5 flags //diff5 flags
#define SD_ACTIVATION 0x01 #define SD_ACTIVATION 0x01
#define SD_BOTCONTROLLER 0x02
static boolean P_SectorArgsEqual(const sector_t *sc, const sector_t *spawnsc) static boolean P_SectorArgsEqual(const sector_t *sc, const sector_t *spawnsc)
{ {
@ -1964,6 +1963,12 @@ static void ArchiveSectors(savebuffer_t *save)
diff4 |= SD_STRINGARGS; diff4 |= SD_STRINGARGS;
if (ss->activation != spawnss->activation) if (ss->activation != spawnss->activation)
diff5 |= SD_ACTIVATION; diff5 |= SD_ACTIVATION;
if (ss->botController.trick != spawnss->botController.trick
|| ss->botController.flags != spawnss->botController.flags
|| ss->botController.forceAngle != spawnss->botController.forceAngle)
{
diff5 |= SD_BOTCONTROLLER;
}
if (ss->ffloors && CheckFFloorDiff(ss)) if (ss->ffloors && CheckFFloorDiff(ss))
diff |= SD_FFLOORS; diff |= SD_FFLOORS;
@ -2076,6 +2081,12 @@ static void ArchiveSectors(savebuffer_t *save)
} }
if (diff5 & SD_ACTIVATION) if (diff5 & SD_ACTIVATION)
WRITEUINT32(save->p, ss->activation); WRITEUINT32(save->p, ss->activation);
if (diff5 & SD_BOTCONTROLLER)
{
WRITEUINT8(save->p, ss->botController.trick);
WRITEUINT32(save->p, ss->botController.flags);
WRITEANGLE(save->p, ss->botController.forceAngle);
}
if (diff & SD_FFLOORS) if (diff & SD_FFLOORS)
ArchiveFFloors(save, ss); ArchiveFFloors(save, ss);
@ -2232,6 +2243,12 @@ static void UnArchiveSectors(savebuffer_t *save)
} }
if (diff5 & SD_ACTIVATION) if (diff5 & SD_ACTIVATION)
sectors[i].activation = READUINT32(save->p); sectors[i].activation = READUINT32(save->p);
if (diff5 & SD_BOTCONTROLLER)
{
sectors[i].botController.trick = READUINT8(save->p);
sectors[i].botController.flags = READUINT32(save->p);
sectors[i].botController.forceAngle = READANGLE(save->p);
}
if (diff & SD_FFLOORS) if (diff & SD_FFLOORS)
UnArchiveFFloors(save, &sectors[i]); UnArchiveFFloors(save, &sectors[i]);

View file

@ -951,6 +951,8 @@ static void P_InitializeSector(sector_t *ss)
ss->spawn_lightlevel = ss->lightlevel; ss->spawn_lightlevel = ss->lightlevel;
ss->spawn_extra_colormap = NULL; ss->spawn_extra_colormap = NULL;
memset(&ss->botController, 0, sizeof(ss->botController));
} }
static void P_LoadSectors(UINT8 *data) static void P_LoadSectors(UINT8 *data)

View file

@ -1380,7 +1380,8 @@ boolean P_CanActivateSpecial(INT16 special)
{ {
case 2001: // Finish line case 2001: // Finish line
case 2003: // Respawn line case 2003: // Respawn line
case 2005: // Dismount Flying Object (always true here so that conditions are only kept on execution) case 2004: // Bot controller
case 2005: // Dismount Flying Object
{ {
return true; return true;
} }
@ -2474,6 +2475,20 @@ mobj_t* P_FindObjectTypeFromTag(mobjtype_t type, mtag_t tag)
} }
} }
static void K_UpdateBotControllers(INT32 *args)
{
INT32 secnum;
TAG_ITER_SECTORS(args[0], secnum)
{
sector_t *const sec = sectors + secnum;
sec->botController.trick = args[1];
sec->botController.flags = args[2];
sec->botController.forceAngle = FixedAngle(args[3] * FRACUNIT);
}
}
/** Processes the line special triggered by an object. /** Processes the line special triggered by an object.
* *
* \param line Line with the special command on it. * \param line Line with the special command on it.
@ -4418,8 +4433,8 @@ boolean P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, cha
if ((gametyperules & GTR_CIRCUIT) && (mo->player->exiting == 0) && !(mo->player->pflags & PF_HITFINISHLINE)) if ((gametyperules & GTR_CIRCUIT) && (mo->player->exiting == 0) && !(mo->player->pflags & PF_HITFINISHLINE))
{ {
if (((line->args[0] & TMCFF_FLIP) && (side == 0)) if (((args[0] & TMCFF_FLIP) && (side == 0))
|| (!(line->args[0] & TMCFF_FLIP) && (side == 1))) // crossed from behind to infront || (!(args[0] & TMCFF_FLIP) && (side == 1))) // crossed from behind to infront
{ {
K_HandleLapIncrement(mo->player); K_HandleLapIncrement(mo->player);
@ -4446,7 +4461,7 @@ boolean P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, cha
if if
( (
mo->player->respawn.state == RESPAWNST_NONE && mo->player->respawn.state == RESPAWNST_NONE &&
(!(line->args[0] & TMCRF_FRONTONLY) || side == 0) (!(args[0] & TMCRF_FRONTONLY) || side == 0)
) )
{ {
P_DamageMobj(mo, NULL, NULL, 1, DMG_DEATHPIT); P_DamageMobj(mo, NULL, NULL, 1, DMG_DEATHPIT);
@ -4454,6 +4469,12 @@ boolean P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, cha
} }
break; break;
case 2004: // Bot Controller.
{
K_UpdateBotControllers(args);
break;
}
case 2005: // Dismount Flying object case 2005: // Dismount Flying object
// the rideroid is a bit complex so it's the one controlling the player rather than the player controlling it. // the rideroid is a bit complex so it's the one controlling the player rather than the player controlling it.
// so it is the object needing to be checked for rather than the player // so it is the object needing to be checked for rather than the player
@ -7641,6 +7662,10 @@ void P_SpawnSpecials(boolean fromnetsave)
} }
break; break;
case 2004: // Bot Controller.
K_UpdateBotControllers(lines[i].args);
break;
default: default:
break; break;
} }

View file

@ -525,8 +525,18 @@ typedef enum
TMBOT_NORUBBERBAND = 1, TMBOT_NORUBBERBAND = 1,
TMBOT_NOCONTROL = 1<<1, TMBOT_NOCONTROL = 1<<1,
TMBOT_FORCEDIR = 1<<2, TMBOT_FORCEDIR = 1<<2,
TMBOT_FASTFALL = 1<<3,
} textmapbotcontroller_t; } textmapbotcontroller_t;
typedef enum
{
TMBOTTR_NONE = 0,
TMBOTTR_LEFT = 1,
TMBOTTR_RIGHT = 2,
TMBOTTR_UP = 3,
TMBOTTR_DOWN = 4,
} textmapbottrick_t;
typedef enum typedef enum
{ {
TMLOOP_ALPHA = 0, TMLOOP_ALPHA = 0,

View file

@ -32,6 +32,8 @@
#include "k_mapuser.h" #include "k_mapuser.h"
#include "k_bot.h" // botcontroller_t
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
@ -331,6 +333,14 @@ struct pslope_t
#endif #endif
}; };
// Per-sector bot controller override
struct botcontroller_t
{
UINT8 trick;
UINT32 flags;
angle_t forceAngle;
};
typedef enum typedef enum
{ {
// flipspecial - planes with effect // flipspecial - planes with effect
@ -552,6 +562,9 @@ struct sector_t
// colormap structure // colormap structure
extracolormap_t *spawn_extra_colormap; extracolormap_t *spawn_extra_colormap;
// Ring Racers bots
botcontroller_t botController;
// Action specials // Action specials
INT16 action; INT16 action;
INT32 args[NUM_SCRIPT_ARGS]; INT32 args[NUM_SCRIPT_ARGS];

View file

@ -182,6 +182,7 @@ TYPEDEF (weakspot_t);
// k_bot.h // k_bot.h
TYPEDEF (botprediction_t); TYPEDEF (botprediction_t);
TYPEDEF (botcontroller_t);
// k_brightmap.h // k_brightmap.h
TYPEDEF (brightmapStorage_t); TYPEDEF (brightmapStorage_t);