Bot controller is stored on the sector now

- Moved destination tag off of linedef tag and into args.
- Bot controller values can be changed mid-level with ACS. (Linedefs using type 2004 will still be activated on level load.)
- Add flag to make bots fastfall
This commit is contained in:
Sally Coolatta 2023-10-21 03:36:26 -04:00
parent de5b151985
commit de9780ab3f
9 changed files with 136 additions and 114 deletions

View file

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

View file

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

View file

@ -379,74 +379,23 @@ static fixed_t K_BotSpeedScaled(player_t *player, fixed_t speed)
}
/*--------------------------------------------------
static line_t *K_FindBotController(mobj_t *mo)
botcontroller_t *K_GetBotController(mobj_t *mobj)
Finds if any bot controller linedefs are tagged to the bot's sector.
Input Arguments:-
mo - The bot player's mobj.
Return:-
Linedef of the bot controller. nullptr if it doesn't exist.
See header file for description.
--------------------------------------------------*/
static line_t *K_FindBotController(mobj_t *mo)
botcontroller_t *K_GetBotController(mobj_t *mobj)
{
msecnode_t *node;
ffloor_t *rover;
INT16 lineNum = -1;
mtag_t tag;
I_Assert(mo != nullptr);
I_Assert(!P_MobjWasRemoved(mo));
for (node = mo->touching_sectorlist; node; node = node->m_sectorlist_next)
{
if (!node->m_sector)
{
continue;
}
tag = Tag_FGet(&node->m_sector->tags);
lineNum = P_FindSpecialLineFromTag(2004, tag, -1); // todo: needs to not use P_FindSpecialLineFromTag
if (lineNum != -1)
{
break;
}
for (rover = node->m_sector->ffloors; rover; rover = rover->next)
{
sector_t *rs = nullptr;
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 &lines[lineNum];
}
else
if (P_MobjWasRemoved(mobj) == true)
{
return nullptr;
}
if (mobj->subsector == nullptr || mobj->subsector->sector == nullptr)
{
return nullptr;
}
return &mobj->subsector->sector->botController;
}
/*--------------------------------------------------
@ -555,18 +504,10 @@ fixed_t K_BotRubberband(player_t *player)
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];
if (botController != nullptr)
{
// Disable rubberbanding
if (botController->args[1] & TMBOT_NORUBBERBAND)
{
return FRACUNIT;
}
}
return FRACUNIT;
}
for (i = 0; i < MAXPLAYERS; i++)
@ -1121,19 +1062,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.
Input Arguments:-
player - Player to generate the ticcmd for.
cmd - The player's ticcmd to modify.
botController - Linedef for the bot controller.
botController - Bot controller struct.
Return:-
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.
if (botController == nullptr)
@ -1143,21 +1084,18 @@ static void K_BotTrick(player_t *player, ticcmd_t *cmd, const line_t *botControl
if (player->trickpanel == 1)
{
INT32 type = botController->args[0];
// Y Offset: Trick type
switch (type)
switch (botController->trick)
{
case 1:
case TMBOTTR_LEFT:
cmd->turning = KART_FULLTURN;
break;
case 2:
case TMBOTTR_RIGHT:
cmd->turning = -KART_FULLTURN;
break;
case 3:
case TMBOTTR_UP:
cmd->throwdir = KART_FULLTURN;
break;
case 4:
case TMBOTTR_DOWN:
cmd->throwdir = -KART_FULLTURN;
break;
}
@ -1531,7 +1469,6 @@ static void K_BuildBotTiccmdNormal(player_t *player, ticcmd_t *cmd)
angle_t destangle = 0;
UINT8 spindash = 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
|| K_GetNumWaypoints() == 0 // No waypoints
@ -1554,15 +1491,9 @@ static void K_BuildBotTiccmdNormal(player_t *player, ticcmd_t *cmd)
if (!cv_botcontrol.value)
return;
#endif
// Actual gameplay behaviors below this block!
if (K_TryRingShooter(player) == true)
{
// We want to respawn. Simply hold Y and stop here!
cmd->buttons |= (BT_RESPAWN | BT_EBRAKEMASK);
return;
}
const botcontroller_t *botController = K_GetBotController(player->mo);
if (player->trickpanel != 0)
{
K_BotTrick(player, cmd, botController);
@ -1571,29 +1502,45 @@ static void K_BuildBotTiccmdNormal(player_t *player, ticcmd_t *cmd)
return;
}
if (botController != nullptr && (botController->args[1] & TMBOT_NOCONTROL))
if (botController != nullptr && (botController->flags & TMBOT_NOCONTROL) == TMBOT_NOCONTROL)
{
// Disable bot controls entirely.
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;
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;
// X Offset: Movement direction
destangle = FixedAngle(botController->args[2] * FRACUNIT);
// Overwritten prediction
predict = static_cast<botprediction_t *>(Z_Calloc(sizeof(botprediction_t), PU_STATIC, nullptr));
predict->x = player->mo->x + FixedMul(dist, FINECOSINE(destangle >> ANGLETOFINESHIFT));
predict->y = player->mo->y + FixedMul(dist, FINESINE(destangle >> ANGLETOFINESHIFT));
predict->x = player->mo->x + FixedMul(dist, FINECOSINE(botController->forceAngle >> ANGLETOFINESHIFT));
predict->y = player->mo->y + FixedMul(dist, FINESINE(botController->forceAngle >> ANGLETOFINESHIFT));
predict->radius = (DEFAULT_WAYPOINT_RADIUS / 4) * mapobjectscale;
}
if (P_IsObjectOnGround(player->mo) == false)
{
if (botController != nullptr && (botController->flags & TMBOT_FASTFALL) == TMBOT_FASTFALL)
{
// Fast fall!
cmd->buttons |= BT_EBRAKEMASK;
return;
}
return;
}
if (leveltime <= starttime && finishBeamLine != nullptr)
{
// Handle POSITION!!
@ -1838,9 +1785,6 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd)
--------------------------------------------------*/
void K_UpdateBotGameplayVars(player_t *player)
{
const line_t *botController;
player->botvars.controller = UINT16_MAX;
player->botvars.rubberband = FRACUNIT;
if (gamestate != GS_LEVEL || !player->mo)
@ -1849,8 +1793,5 @@ void K_UpdateBotGameplayVars(player_t *player)
return;
}
botController = K_FindBotController(player->mo);
player->botvars.controller = botController ? (botController - lines) : UINT16_MAX;
player->botvars.rubberband = K_UpdateRubberband(player);
}

View file

@ -13,6 +13,7 @@
#ifndef __K_BOT__
#define __K_BOT__
#include "typedef.h"
#include "k_waypoint.h"
#include "d_player.h"
#include "r_defs.h"
@ -85,6 +86,22 @@ boolean K_PlayerUsesBotMovement(player_t *player);
boolean K_BotCanTakeCut(player_t *player);
/*--------------------------------------------------
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.
--------------------------------------------------*/
botcontroller_t *K_GetBotController(mobj_t *mobj);
/*--------------------------------------------------
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.rival);
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.itemconfirm);
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.rival = (boolean)READUINT8(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.itemconfirm = READUINT32(save->p);
players[i].botvars.turnconfirm = READSINT8(save->p);
@ -1729,6 +1727,7 @@ static void P_NetUnArchiveColormaps(savebuffer_t *save)
//diff5 flags
#define SD_ACTIVATION 0x01
#define SD_BOTCONTROLLER 0x02
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;
if (ss->activation != spawnss->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))
diff |= SD_FFLOORS;
@ -2076,6 +2081,12 @@ static void ArchiveSectors(savebuffer_t *save)
}
if (diff5 & SD_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)
ArchiveFFloors(save, ss);
@ -2232,6 +2243,12 @@ static void UnArchiveSectors(savebuffer_t *save)
}
if (diff5 & SD_ACTIVATION)
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)
UnArchiveFFloors(save, &sectors[i]);

View file

@ -1380,7 +1380,8 @@ boolean P_CanActivateSpecial(INT16 special)
{
case 2001: // Finish 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;
}
@ -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.
*
* \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 (((line->args[0] & TMCFF_FLIP) && (side == 0))
|| (!(line->args[0] & TMCFF_FLIP) && (side == 1))) // crossed from behind to infront
if (((args[0] & TMCFF_FLIP) && (side == 0))
|| (!(args[0] & TMCFF_FLIP) && (side == 1))) // crossed from behind to infront
{
K_HandleLapIncrement(mo->player);
@ -4446,7 +4461,7 @@ boolean P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, cha
if
(
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);
@ -4454,6 +4469,12 @@ boolean P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, cha
}
break;
case 2004: // Bot Controller.
{
K_UpdateBotControllers(args);
break;
}
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.
// so it is the object needing to be checked for rather than the player
@ -7641,6 +7662,10 @@ void P_SpawnSpecials(boolean fromnetsave)
}
break;
case 2004: // Bot Controller.
K_UpdateBotControllers(lines[i].args);
break;
default:
break;
}

View file

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

View file

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

View file

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