Merge branch 'server-bots' into 'next'

Improve bots (resolves #710)

Closes #710

See merge request STJr/SRB2!1679
This commit is contained in:
sphere 2022-02-19 20:24:12 +00:00
commit c79c6531e6
3 changed files with 82 additions and 76 deletions

View file

@ -17,6 +17,7 @@
#include "p_local.h" #include "p_local.h"
#include "b_bot.h" #include "b_bot.h"
#include "lua_hook.h" #include "lua_hook.h"
#include "i_system.h" // I_BaseTiccmd
void B_UpdateBotleader(player_t *player) void B_UpdateBotleader(player_t *player)
{ {
@ -132,17 +133,17 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
// Update catchup_tics // Update catchup_tics
if (mem->thinkstate == AI_SPINFOLLOW) if (mem->thinkstate == AI_SPINFOLLOW)
{ {
mem-> catchup_tics = 0; mem->catchup_tics = 0;
} }
else if (dist > followmax || zdist > comfortheight || stalled) else if (dist > followmax || zdist > comfortheight || stalled)
{ {
mem-> catchup_tics = min(mem-> catchup_tics + 2, 70); mem->catchup_tics = min(mem->catchup_tics + 2, 70);
if (mem-> catchup_tics >= 70) if (mem->catchup_tics >= 70)
mem->thinkstate = AI_CATCHUP; mem->thinkstate = AI_CATCHUP;
} }
else else
{ {
mem-> catchup_tics = max(mem-> catchup_tics - 1, 0); mem->catchup_tics = max(mem->catchup_tics - 1, 0);
if (mem->thinkstate == AI_CATCHUP) if (mem->thinkstate == AI_CATCHUP)
mem->thinkstate = AI_FOLLOW; mem->thinkstate = AI_FOLLOW;
} }
@ -317,7 +318,6 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
{ {
// Copy inputs // Copy inputs
cmd->angleturn = (sonic->angle) >> 16; // NOT FRACBITS DAMNIT cmd->angleturn = (sonic->angle) >> 16; // NOT FRACBITS DAMNIT
bot->drawangle = ang;
cmd->forwardmove = 8 * pcmd->forwardmove / 10; cmd->forwardmove = 8 * pcmd->forwardmove / 10;
cmd->sidemove = 8 * pcmd->sidemove / 10; cmd->sidemove = 8 * pcmd->sidemove / 10;
} }
@ -344,7 +344,7 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
else if (!jump_last && !(bot->pflags & PF_JUMPED) //&& !(player->pflags & PF_SPINNING) else if (!jump_last && !(bot->pflags & PF_JUMPED) //&& !(player->pflags & PF_SPINNING)
&& ((zdist > 32*scale && player->pflags & PF_JUMPED) // Following && ((zdist > 32*scale && player->pflags & PF_JUMPED) // Following
|| (zdist > 64*scale && mem->thinkstate == AI_CATCHUP) // Vertical catch-up || (zdist > 64*scale && mem->thinkstate == AI_CATCHUP) // Vertical catch-up
|| (stalled && mem-> catchup_tics > 20 && bot->powers[pw_carry] == CR_NONE) || (stalled && mem->catchup_tics > 20 && bot->powers[pw_carry] == CR_NONE)
//|| (bmom < scale>>3 && dist > followthres && !(bot->powers[pw_carry])) // Stopped & not in carry state //|| (bmom < scale>>3 && dist > followthres && !(bot->powers[pw_carry])) // Stopped & not in carry state
|| (bot->pflags & PF_SPINNING && !(bot->pflags & PF_JUMPED)))) // Spinning || (bot->pflags & PF_SPINNING && !(bot->pflags & PF_JUMPED)))) // Spinning
jump = true; jump = true;
@ -371,6 +371,8 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
void B_BuildTiccmd(player_t *player, ticcmd_t *cmd) void B_BuildTiccmd(player_t *player, ticcmd_t *cmd)
{ {
G_CopyTiccmd(cmd, I_BaseTiccmd(), 1); // empty, or external driver
// Can't build a ticcmd if we aren't spawned... // Can't build a ticcmd if we aren't spawned...
if (!player->mo) if (!player->mo)
return; return;
@ -390,7 +392,7 @@ void B_BuildTiccmd(player_t *player, ticcmd_t *cmd)
return; return;
// Make sure we have a valid main character to follow // Make sure we have a valid main character to follow
B_UpdateBotleader(player); B_UpdateBotleader(player);
if (!player->botleader) if (!player->botleader)
return; return;
@ -403,7 +405,7 @@ void B_KeysToTiccmd(mobj_t *mo, ticcmd_t *cmd, boolean forward, boolean backward
{ {
player_t *player = mo->player; player_t *player = mo->player;
// don't try to do stuff if your sonic is in a minecart or something // don't try to do stuff if your sonic is in a minecart or something
if (&player->botleader && player->botleader->powers[pw_carry] && player->botleader->powers[pw_carry] != CR_PLAYER) if (player->botleader && player->botleader->powers[pw_carry] && player->botleader->powers[pw_carry] != CR_PLAYER)
return; return;
// Turn the virtual keypresses into ticcmd_t. // Turn the virtual keypresses into ticcmd_t.
if (twodlevel || mo->flags2 & MF2_TWOD) { if (twodlevel || mo->flags2 & MF2_TWOD) {
@ -593,25 +595,43 @@ void B_HandleFlightIndicator(player_t *player)
{ {
mobj_t *tails = player->mo; mobj_t *tails = player->mo;
botmem_t *mem = &player->botmem; botmem_t *mem = &player->botmem;
boolean shouldExist;
if (!tails) if (!tails)
return; return;
if (mem->thinkstate == AI_THINKFLY && player->bot == BOT_2PAI && tails->health) shouldExist = (mem->thinkstate == AI_THINKFLY) && player->botleader
&& player->bot == BOT_2PAI && player->playerstate == PST_LIVE;
// check whether the indicator doesn't exist
if (P_MobjWasRemoved(tails->hnext))
{ {
if (!tails->hnext) // if it shouldn't exist, everything is fine
{ if (!shouldExist)
P_SetTarget(&tails->hnext, P_SpawnMobjFromMobj(tails, 0, 0, 0, MT_OVERLAY)); return;
if (tails->hnext)
{ // otherwise, spawn it
P_SetTarget(&tails->hnext->target, tails); P_SetTarget(&tails->hnext, P_SpawnMobjFromMobj(tails, 0, 0, 0, MT_OVERLAY));
P_SetTarget(&tails->hnext->hprev, tails); P_SetTarget(&tails->hnext->target, tails);
P_SetMobjState(tails->hnext, S_FLIGHTINDICATOR); P_SetTarget(&tails->hnext->hprev, tails);
} P_SetMobjState(tails->hnext, S_FLIGHTINDICATOR);
}
} }
else if (tails->hnext && tails->hnext->type == MT_OVERLAY && tails->hnext->state == states+S_FLIGHTINDICATOR)
// if the mobj isn't a flight indicator, let's not mess with it
if (tails->hnext->type != MT_OVERLAY || (tails->hnext->state != states+S_FLIGHTINDICATOR))
return;
// if it shouldn't exist, remove it
if (!shouldExist)
{ {
P_RemoveMobj(tails->hnext); P_RemoveMobj(tails->hnext);
P_SetTarget(&tails->hnext, NULL); P_SetTarget(&tails->hnext, NULL);
return;
} }
// otherwise, update its visibility
if (P_IsLocalPlayer(player->botleader))
tails->hnext->flags2 &= ~MF2_DONTDRAW;
else
tails->hnext->flags2 |= MF2_DONTDRAW;
} }

View file

@ -1547,12 +1547,17 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
cmd->sidemove = (SINT8)(cmd->sidemove + side); cmd->sidemove = (SINT8)(cmd->sidemove + side);
// Note: Majority of botstuffs are handled in G_Ticker now. // Note: Majority of botstuffs are handled in G_Ticker now.
if (player->bot == BOT_2PHUMAN) //Player-controlled bot if (player->bot == BOT_2PAI
&& !player->powers[pw_tailsfly]
&& (cmd->forwardmove || cmd->sidemove || cmd->buttons))
{ {
// Fix offset angle for P2-controlled Tailsbot when P2's controls are set to non-Strafe player->bot = BOT_2PHUMAN; // A player-controlled bot. Returns to AI when it respawns.
cmd->angleturn = (INT16)((localangle - *myangle) >> 16); CV_SetValue(&cv_analog[1], true);
} }
if (player->bot == BOT_2PHUMAN)
cmd->angleturn = (INT16)((localangle - *myangle) >> 16);
*myangle += (cmd->angleturn<<16); *myangle += (cmd->angleturn<<16);
if (controlstyle == CS_LMAOGALOG) { if (controlstyle == CS_LMAOGALOG) {
@ -2307,66 +2312,45 @@ void G_Ticker(boolean run)
buf = gametic % BACKUPTICS; buf = gametic % BACKUPTICS;
// Generate ticcmds for bots FIRST, then copy received ticcmds for players.
// This emulates pre-2.2.10 behaviour where the bot referenced their leader's last copied ticcmd,
// which is desirable because P_PlayerThink can override inputs (e.g. while PF_STASIS is applied or in a waterslide),
// and the bot AI needs to respect that.
#define ISHUMAN (players[i].bot == BOT_NONE || players[i].bot == BOT_2PHUMAN)
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
{ {
if (playeringame[i]) if (playeringame[i] && !ISHUMAN) // Less work is required if we're building a bot ticcmd.
{ {
INT16 received; players[i].lastbuttons = players[i].cmd.buttons; // Save last frame's button readings
// Save last frame's button readings B_BuildTiccmd(&players[i], &players[i].cmd);
players[i].lastbuttons = players[i].cmd.buttons;
G_CopyTiccmd(&players[i].cmd, &netcmds[buf][i], 1); // Since bot TicCmd is pre-determined for both the client and server, the latency and packet checks are simplified.
// Bot ticcmd handling players[i].cmd.latency = 0;
// Yes, ordinarily this would be handled in G_BuildTiccmd... P_SetPlayerAngle(&players[i], players[i].cmd.angleturn << 16);
// ...however, bot players won't have a corresponding consoleplayer or splitscreen player 2 to send that information.
// Therefore, this has to be done after ticcmd sends are received.
if (players[i].bot == BOT_2PAI) { // Tailsbot for P2
if (!players[i].powers[pw_tailsfly] && (players[i].cmd.forwardmove || players[i].cmd.sidemove || players[i].cmd.buttons))
{
players[i].bot = BOT_2PHUMAN; // A player-controlled bot. Returns to AI when it respawns.
CV_SetValue(&cv_analog[1], true);
}
else
{
B_BuildTiccmd(&players[i], &players[i].cmd);
}
B_HandleFlightIndicator(&players[i]);
}
else if (players[i].bot == BOT_MPAI) {
B_BuildTiccmd(&players[i], &players[i].cmd);
}
// Do angle adjustments.
if (players[i].bot == BOT_NONE || players[i].bot == BOT_2PHUMAN)
{
received = (players[i].cmd.angleturn & TICCMD_RECEIVED);
players[i].angleturn += players[i].cmd.angleturn - players[i].oldrelangleturn;
players[i].oldrelangleturn = players[i].cmd.angleturn;
if (P_ControlStyle(&players[i]) == CS_LMAOGALOG)
P_ForceLocalAngle(&players[i], players[i].angleturn << 16);
else
players[i].cmd.angleturn = players[i].angleturn;
if (P_ControlStyle(&players[i]) == CS_LMAOGALOG)
P_ForceLocalAngle(&players[i], players[i].angleturn << 16);
else
players[i].cmd.angleturn = players[i].angleturn;
players[i].cmd.angleturn &= ~TICCMD_RECEIVED;
// Use the leveltime sent in the player's ticcmd to determine control lag
players[i].cmd.latency = min(((leveltime & 0xFF) - players[i].cmd.latency) & 0xFF, MAXPREDICTTICS-1);
}
else // Less work is required if we're building a bot ticcmd.
{
// Since bot TicCmd is pre-determined for both the client and server, the latency and packet checks are simplified.
received = 1;
players[i].cmd.latency = 0;
players[i].angleturn = players[i].cmd.angleturn;
players[i].oldrelangleturn = players[i].cmd.angleturn;
}
players[i].cmd.angleturn |= received;
} }
} }
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && ISHUMAN)
{
players[i].lastbuttons = players[i].cmd.buttons; // Save last frame's button readings
G_CopyTiccmd(&players[i].cmd, &netcmds[buf][i], 1);
// Use the leveltime sent in the player's ticcmd to determine control lag
players[i].cmd.latency = min(((leveltime & 0xFF) - players[i].cmd.latency) & 0xFF, MAXPREDICTTICS-1);
// Do angle adjustments.
players[i].angleturn += players[i].cmd.angleturn - players[i].oldrelangleturn;
players[i].oldrelangleturn = players[i].cmd.angleturn;
if (P_ControlStyle(&players[i]) == CS_LMAOGALOG)
P_ForceLocalAngle(&players[i], players[i].angleturn << 16);
else
players[i].cmd.angleturn = (players[i].angleturn & ~TICCMD_RECEIVED) | (players[i].cmd.angleturn & TICCMD_RECEIVED);
}
}
#undef ISHUMAN
// do main actions // do main actions
switch (gamestate) switch (gamestate)
{ {

View file

@ -11441,6 +11441,8 @@ void P_PlayerThink(player_t *player)
{ {
if (B_CheckRespawn(player)) if (B_CheckRespawn(player))
player->playerstate = PST_REBORN; player->playerstate = PST_REBORN;
else
B_HandleFlightIndicator(player);
} }
if (player->playerstate == PST_REBORN) if (player->playerstate == PST_REBORN)
{ {