diff --git a/src/b_bot.c b/src/b_bot.c index cdd74fc07..ffdcace6d 100644 --- a/src/b_bot.c +++ b/src/b_bot.c @@ -127,17 +127,17 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd) // Update catchup_tics if (mem->thinkstate == AI_SPINFOLLOW) { - mem-> catchup_tics = 0; + mem->catchup_tics = 0; } else if (dist > followmax || zdist > comfortheight || stalled) { - mem-> catchup_tics = min(mem-> catchup_tics + 2, 70); - if (mem-> catchup_tics >= 70) + mem->catchup_tics = min(mem->catchup_tics + 2, 70); + if (mem->catchup_tics >= 70) mem->thinkstate = AI_CATCHUP; } else { - mem-> catchup_tics = max(mem-> catchup_tics - 1, 0); + mem->catchup_tics = max(mem->catchup_tics - 1, 0); if (mem->thinkstate == AI_CATCHUP) mem->thinkstate = AI_FOLLOW; } @@ -171,7 +171,6 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd) { jump = true; mem->thinkstate = AI_FLYSTANDBY; - bot->pflags |= PF_CANCARRY; } } @@ -183,7 +182,10 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd) && P_IsObjectOnGround(sonic) && P_IsObjectOnGround(tails) && !(player->pflags & PF_STASIS) && bot->charability == CA_FLY) - mem->thinkstate = AI_THINKFLY; + { + mem->thinkstate = AI_THINKFLY; + cmd->flags |= TCF_FLIGHTINDICATOR; + } else if (mem->thinkstate == AI_THINKFLY) mem->thinkstate = AI_FOLLOW; @@ -204,6 +206,8 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd) // Abort if the player moves away or spins if (dist > followthres || player->dashspeed) mem->thinkstate = AI_FOLLOW; + else + cmd->flags |= TCF_SETCARRY; } // Read player inputs while carrying else if (mem->thinkstate == AI_FLYCARRY) @@ -312,7 +316,6 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd) { // Copy inputs cmd->angleturn = (sonic->angle) >> 16; // NOT FRACBITS DAMNIT - bot->drawangle = ang; cmd->forwardmove = 8 * pcmd->forwardmove / 10; cmd->sidemove = 8 * pcmd->sidemove / 10; } @@ -339,7 +342,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) && ((zdist > 32*scale && player->pflags & PF_JUMPED) // Following || (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 || (bot->pflags & PF_SPINNING && !(bot->pflags & PF_JUMPED)))) // Spinning jump = true; @@ -385,7 +388,6 @@ void B_BuildTiccmd(player_t *player, ticcmd_t *cmd) return; // Make sure we have a valid main character to follow - B_UpdateBotleader(player); if (!player->botleader) return; @@ -398,7 +400,7 @@ void B_KeysToTiccmd(mobj_t *mo, ticcmd_t *cmd, boolean forward, boolean backward { player_t *player = mo->player; // 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; // Turn the virtual keypresses into ticcmd_t. if (twodlevel || mo->flags2 & MF2_TWOD) { @@ -587,26 +589,43 @@ void B_RespawnBot(INT32 playernum) void B_HandleFlightIndicator(player_t *player) { mobj_t *tails = player->mo; - botmem_t *mem = &player->botmem; + boolean shouldExist; + if (!tails) return; - if (mem->thinkstate == AI_THINKFLY && player->bot == BOT_2PAI && tails->health) + shouldExist = (player->cmd.flags & TCF_FLIGHTINDICATOR) && 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) - { - P_SetTarget(&tails->hnext, P_SpawnMobjFromMobj(tails, 0, 0, 0, MT_OVERLAY)); - if (tails->hnext) - { - P_SetTarget(&tails->hnext->target, tails); - P_SetTarget(&tails->hnext->hprev, tails); - P_SetMobjState(tails->hnext, S_FLIGHTINDICATOR); - } - } + // if it shouldn't exist, everything is fine + if (!shouldExist) + return; + + // otherwise, spawn it + P_SetTarget(&tails->hnext, P_SpawnMobjFromMobj(tails, 0, 0, 0, MT_OVERLAY)); + P_SetTarget(&tails->hnext->target, tails); + 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_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; } diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 78a3ebe6c..c70532494 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -46,6 +46,7 @@ #include "lua_libs.h" #include "md5.h" #include "m_perfstats.h" +#include "b_bot.h" // B_BuildTiccmd #ifndef NONET // cl loading screen @@ -5166,6 +5167,25 @@ static void Local_Maketic(INT32 realtics) localcmds2.angleturn |= TICCMD_RECEIVED; } +static void SV_MakeBotTics(void) +{ + UINT8 i; + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + + if (players[i].bot == BOT_2PAI || players[i].bot == BOT_MPAI) + { + ticcmd_t *cmd = &netcmds[maketic % BACKUPTICS][i]; + + G_CopyTiccmd(cmd, I_BaseTiccmd(), 1); // empty, or external driver + B_BuildTiccmd(&players[i], cmd); + cmd->angleturn |= TICCMD_RECEIVED; + } + } +} + // create missed tic static void SV_Maketic(void) { @@ -5409,6 +5429,15 @@ void NetUpdate(void) if (client) maketic = neededtic; + // update players' lastbuttons so they can be used in ticcmd generation + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + + players[i].lastbuttons = players[i].cmd.buttons; + } + Local_Maketic(realtics); // make local tic, and call menu? if (server) @@ -5452,6 +5481,8 @@ void NetUpdate(void) Net_ConnectionTimeout(i); } + SV_MakeBotTics(); + // Don't erase tics not acknowledged counts = realtics; diff --git a/src/d_ticcmd.h b/src/d_ticcmd.h index 182b30e6a..480fbe2d4 100644 --- a/src/d_ticcmd.h +++ b/src/d_ticcmd.h @@ -45,6 +45,14 @@ typedef enum BT_CUSTOM3 = 1<<15, } buttoncode_t; +// ticcmd flags +typedef enum +{ + TCF_FLIGHTINDICATOR = 1 << 0, // show flight indicator + TCF_SETCARRY = 1 << 1, // set PF_CARRY upon activating flight + // free up to and including 1 << 7 +} ticcmdflag_t; + // The data sampled per tick (single player) // and transmitted to other peers (multiplayer). // Mainly movements/button commands per game tick, @@ -66,6 +74,7 @@ typedef struct INT16 aiming; // vertical aiming, see G_BuildTicCmd UINT16 buttons; UINT8 latency; // Netgames: how many tics ago was this ticcmd generated from this player's end? + UINT8 flags; // miscellaneous info } ATTRPACK ticcmd_t; #if defined(_MSC_VER) diff --git a/src/deh_tables.c b/src/deh_tables.c index f30f7c14d..3aece916c 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -5333,6 +5333,11 @@ struct int_const_s const INT_CONST[] = { {"BT_CUSTOM2",BT_CUSTOM2}, // Lua customizable {"BT_CUSTOM3",BT_CUSTOM3}, // Lua customizable + // Ticcmd flags (ticcmdflag_t) + // (maybe move these into their own table in the future but I cba when there's only 2 LOL) + {"TCF_FLIGHTINDICATOR", TCF_FLIGHTINDICATOR}, + {"TCF_SETCARRY", TCF_SETCARRY}, + // Lua command registration flags {"COM_ADMIN",COM_ADMIN}, {"COM_SPLITSCREEN",COM_SPLITSCREEN}, diff --git a/src/g_demo.c b/src/g_demo.c index c97dbcf9e..2e2f6d56a 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -110,6 +110,7 @@ demoghost *ghosts = NULL; #define ZT_BUTTONS 0x08 #define ZT_AIMING 0x10 #define ZT_LATENCY 0x20 +#define ZT_FLAGS 0x40 #define DEMOMARKER 0x80 // demoend #define METALDEATH 0x44 #define METALSNICE 0x69 @@ -184,6 +185,8 @@ void G_ReadDemoTiccmd(ticcmd_t *cmd, INT32 playernum) oldcmd.aiming = READINT16(demo_p); if (ziptic & ZT_LATENCY) oldcmd.latency = READUINT8(demo_p); + if (ziptic & ZT_FLAGS) + oldcmd.flags = READUINT8(demo_p); G_CopyTiccmd(cmd, &oldcmd, 1); players[playernum].angleturn = cmd->angleturn; @@ -248,6 +251,13 @@ void G_WriteDemoTiccmd(ticcmd_t *cmd, INT32 playernum) ziptic |= ZT_LATENCY; } + if (cmd->flags != oldcmd.flags) + { + WRITEUINT8(demo_p, cmd->flags); + oldcmd.flags = cmd->flags; + ziptic |= ZT_FLAGS; + } + *ziptic_p = ziptic; // attention here for the ticcmd size! @@ -691,6 +701,8 @@ void G_GhostTicker(void) g->p += 2; if (ziptic & ZT_LATENCY) g->p++; + if (ziptic & ZT_FLAGS) + g->p++; // Grab ghost data. ziptic = READUINT8(g->p); diff --git a/src/g_game.c b/src/g_game.c index 3955834b2..050f2d714 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1546,12 +1546,17 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) cmd->forwardmove = (SINT8)(cmd->forwardmove + forward); cmd->sidemove = (SINT8)(cmd->sidemove + side); - // Note: Majority of botstuffs are handled in G_Ticker now. - if (player->bot == BOT_2PHUMAN) //Player-controlled bot + // Note: Majority of botstuffs are handled in G_Ticker and NetUpdate now. + 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 - cmd->angleturn = (INT16)((localangle - *myangle) >> 16); - } + player->bot = BOT_2PHUMAN; // A player-controlled bot. Returns to AI when it respawns. + CV_SetValue(&cv_analog[1], true); + } + + if (player->bot == BOT_2PHUMAN) + cmd->angleturn = (localangle - *myangle) >> 16; *myangle += (cmd->angleturn<<16); @@ -1705,6 +1710,7 @@ ticcmd_t *G_MoveTiccmd(ticcmd_t* dest, const ticcmd_t* src, const size_t n) dest[i].aiming = (INT16)SHORT(src[i].aiming); dest[i].buttons = (UINT16)SHORT(src[i].buttons); dest[i].latency = src[i].latency; + dest[i].flags = src[i].flags; } return dest; } @@ -2312,57 +2318,32 @@ void G_Ticker(boolean run) if (playeringame[i]) { INT16 received; - // Save last frame's button readings - players[i].lastbuttons = players[i].cmd.buttons; G_CopyTiccmd(&players[i].cmd, &netcmds[buf][i], 1); - // Bot ticcmd handling - // Yes, ordinarily this would be handled in G_BuildTiccmd... - // ...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) { + // 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. 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; + // Since bot TicCmd is pre-determined for both the client and server, the latency and packet checks are simplified. + players[i].cmd.latency = 0; + P_SetPlayerAngle(&players[i], players[i].cmd.angleturn << 16); } + + players[i].cmd.angleturn &= ~TICCMD_RECEIVED; players[i].cmd.angleturn |= received; } } diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 12ad4fee0..e0d09dee9 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -32,6 +32,7 @@ #include "b_bot.h" // B_UpdateBotleader #include "d_clisrv.h" // CL_RemovePlayer #include "i_system.h" // I_GetPreciseTime, I_PreciseToMicros +#include "i_net.h" // doomcom #include "lua_script.h" #include "lua_libs.h" @@ -3463,6 +3464,8 @@ static int lib_gAddPlayer(lua_State *L) playeringame[newplayernum] = true; G_AddPlayer(newplayernum); + if (newplayernum+1 > doomcom->numslots) + doomcom->numslots = (INT16)(newplayernum+1); newplayer = &players[newplayernum]; newplayer->jointime = 0; diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 1c634da45..097fd94e4 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -833,6 +833,8 @@ static int ticcmd_get(lua_State *L) lua_pushinteger(L, cmd->buttons); else if (fastcmp(field,"latency")) lua_pushinteger(L, cmd->latency); + else if (fastcmp(field,"flags")) + lua_pushinteger(L, cmd->flags); else return NOFIELD; @@ -861,6 +863,8 @@ static int ticcmd_set(lua_State *L) cmd->buttons = (UINT16)luaL_checkinteger(L, 3); else if (fastcmp(field,"latency")) return NOSET; + else if (fastcmp(field,"flags")) + cmd->buttons = (UINT8)luaL_checkinteger(L, 3); else return NOFIELD; diff --git a/src/p_saveg.c b/src/p_saveg.c index 1270064c0..8a75013a3 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -51,14 +51,15 @@ UINT8 *save_p; // than an UINT16 typedef enum { -// RFLAGPOINT = 0x01, -// BFLAGPOINT = 0x02, - CAPSULE = 0x04, - AWAYVIEW = 0x08, - FIRSTAXIS = 0x10, - SECONDAXIS = 0x20, - FOLLOW = 0x40, - DRONE = 0x80, +// RFLAGPOINT = 0x001, +// BFLAGPOINT = 0x002, + CAPSULE = 0x004, + AWAYVIEW = 0x008, + FIRSTAXIS = 0x010, + SECONDAXIS = 0x020, + FOLLOW = 0x040, + DRONE = 0x080, + BOTLEADER = 0x100, } player_saveflags; static inline void P_ArchivePlayer(void) @@ -197,10 +198,11 @@ static void P_NetArchivePlayers(void) // Bots // ////////// WRITEUINT8(save_p, players[i].bot); - WRITEUINT8(save_p, players[i].botmem.lastForward); - WRITEUINT8(save_p, players[i].botmem.lastBlocked); - WRITEUINT8(save_p, players[i].botmem.catchup_tics); - WRITEUINT8(save_p, players[i].botmem.thinkstate); + // We no longer need to sync these since ticcmds are generated only by the server + //WRITEUINT8(save_p, players[i].botmem.lastForward); + //WRITEUINT8(save_p, players[i].botmem.lastBlocked); + //WRITEUINT8(save_p, players[i].botmem.catchup_tics); + //WRITEUINT8(save_p, players[i].botmem.thinkstate); WRITEUINT8(save_p, players[i].removing); WRITEUINT8(save_p, players[i].blocked); @@ -293,6 +295,9 @@ static void P_NetArchivePlayers(void) if (players[i].drone) flags |= DRONE; + if (players[i].botleader) + flags |= BOTLEADER; + WRITEINT16(save_p, players[i].lastsidehit); WRITEINT16(save_p, players[i].lastlinehit); @@ -325,6 +330,9 @@ static void P_NetArchivePlayers(void) if (flags & DRONE) WRITEUINT32(save_p, players[i].drone->mobjnum); + if (flags & BOTLEADER) + WRITEUINT8(save_p, (UINT8)(players[i].botleader - players)); + WRITEFIXED(save_p, players[i].camerascale); WRITEFIXED(save_p, players[i].shieldscale); @@ -425,10 +433,10 @@ static void P_NetUnArchivePlayers(void) ////////// players[i].bot = READUINT8(save_p); - players[i].botmem.lastForward = READUINT8(save_p); - players[i].botmem.lastBlocked = READUINT8(save_p); - players[i].botmem.catchup_tics = READUINT8(save_p); - players[i].botmem.thinkstate = READUINT8(save_p); + //players[i].botmem.lastForward = READUINT8(save_p); + //players[i].botmem.lastBlocked = READUINT8(save_p); + //players[i].botmem.catchup_tics = READUINT8(save_p); + //players[i].botmem.thinkstate = READUINT8(save_p); players[i].removing = READUINT8(save_p); players[i].blocked = READUINT8(save_p); @@ -535,6 +543,9 @@ static void P_NetUnArchivePlayers(void) if (flags & DRONE) players[i].drone = (mobj_t *)(size_t)READUINT32(save_p); + if (flags & BOTLEADER) + players[i].botleader = &players[READUINT8(save_p)]; + players[i].camerascale = READFIXED(save_p); players[i].shieldscale = READFIXED(save_p); diff --git a/src/p_user.c b/src/p_user.c index f21118a81..cc232d08d 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -5349,10 +5349,10 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) player->powers[pw_tailsfly] = tailsflytics + 1; // Set the fly timer player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE|PF_SPINNING|PF_STARTDASH); - if (player->bot == BOT_2PAI) - player->pflags |= PF_THOKKED; - else + if ((player->bot != BOT_2PAI) || (cmd->flags & TCF_SETCARRY)) player->pflags |= (PF_THOKKED|PF_CANCARRY); + else + player->pflags |= PF_THOKKED; } break; case CA_GLIDEANDCLIMB: @@ -11450,6 +11450,12 @@ void P_PlayerThink(player_t *player) { if (B_CheckRespawn(player)) player->playerstate = PST_REBORN; + else + { + if (player->bot == BOT_2PAI) + B_UpdateBotleader(player); + B_HandleFlightIndicator(player); + } } if (player->playerstate == PST_REBORN) {