From 751fd9ad1b0e075fc9bedc26ce349bd5042e65d7 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Fri, 27 Mar 2020 23:51:49 -0400 Subject: [PATCH 01/85] Start on bots, doesn't work --- src/Makefile | 2 +- src/d_clisrv.c | 62 ++++++++++++++++++--- src/d_main.c | 3 +- src/d_netcmd.c | 56 ++++--------------- src/d_netcmd.h | 5 +- src/g_game.c | 116 ++------------------------------------- src/g_state.h | 3 - src/{b_bot.c => k_bot.c} | 51 ++++++++++++++++- src/{b_bot.h => k_bot.h} | 1 + src/lua_hooklib.c | 2 +- src/lua_playerlib.c | 2 +- src/p_inter.c | 52 ++++-------------- src/p_map.c | 85 ---------------------------- src/p_mobj.c | 6 +- src/p_saveg.c | 18 +----- src/p_setup.c | 7 +-- src/p_spec.c | 49 ++--------------- src/p_user.c | 14 +---- src/r_main.c | 4 +- 19 files changed, 151 insertions(+), 387 deletions(-) rename src/{b_bot.c => k_bot.c} (89%) rename src/{b_bot.h => k_bot.h} (96%) diff --git a/src/Makefile b/src/Makefile index a6f18a72a..2b1e7c7f8 100644 --- a/src/Makefile +++ b/src/Makefile @@ -550,7 +550,7 @@ OBJS:=$(i_main_o) \ $(OBJDIR)/i_tcp.o \ $(OBJDIR)/lzf.o \ $(OBJDIR)/vid_copy.o \ - $(OBJDIR)/b_bot.o \ + $(OBJDIR)/k_bot.o \ $(i_cdmus_o) \ $(i_net_o) \ $(i_system_o) \ diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 084a6fd81..c5f029842 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1326,8 +1326,6 @@ static boolean CL_SendJoin(void) if (splitscreen) localplayers += splitscreen; - else if (botingame) - localplayers++; netbuffer->u.clientcfg.localplayers = localplayers; netbuffer->u.clientcfg.version = VERSION; @@ -2623,8 +2621,7 @@ static void Command_connect(void) splitscreen = cv_splitplayers.value-1; SplitScreen_OnChange(); } - botingame = false; - botskin = 0; + CL_ConnectToServer(viams); } #endif @@ -3292,6 +3289,7 @@ consvar_t cv_downloadspeed = {"downloadspeed", "16", CV_SAVE, downloadspeed_cons static void Got_AddPlayer(UINT8 **p, INT32 playernum); static void Got_RemovePlayer(UINT8 **p, INT32 playernum); +static void Got_AddBot(UINT8 **p, INT32 playernum); // called one time at init void D_ClientServerInit(void) @@ -3321,6 +3319,7 @@ void D_ClientServerInit(void) RegisterNetXCmd(XD_KICK, Got_KickCmd); RegisterNetXCmd(XD_ADDPLAYER, Got_AddPlayer); RegisterNetXCmd(XD_REMOVEPLAYER, Got_RemovePlayer); + RegisterNetXCmd(XD_ADDBOT, Got_AddBot); #ifndef NONET #ifdef DUMPCONSISTENCY CV_RegisterVar(&cv_dumpconsistency); @@ -3536,8 +3535,6 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum) displayplayers[splitscreenplayer] = newplayernum; g_localplayers[splitscreenplayer] = newplayernum; DEBFILE(va("spawning sister # %d\n", splitscreenplayer)); - if (splitscreenplayer == 1 && botingame) - players[newplayernum].bot = 1; } else { @@ -3556,6 +3553,7 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum) } players[newplayernum].splitscreenindex = splitscreenplayer; + players[newplayernum].bot = false; playerconsole[newplayernum] = console; splitscreen_original_party_size[console] = @@ -3609,6 +3607,54 @@ static void Got_RemovePlayer(UINT8 **p, INT32 playernum) CL_RemovePlayer(pnum, reason); } +// Xcmd XD_ADDBOT +// Compacted version of XD_ADDPLAYER for simplicity +static void Got_AddBot(UINT8 **p, INT32 playernum) +{ + INT16 newplayernum; + + if (playernum != serverplayer && !IsPlayerAdmin(playernum)) + { + // protect against hacked/buggy client + CONS_Alert(CONS_WARNING, M_GetText("Illegal add player command received from %s\n"), player_names[playernum]); + if (server) + { + XBOXSTATIC UINT8 buf[2]; + + buf[0] = (UINT8)playernum; + buf[1] = KICK_MSG_CON_FAIL; + SendNetXCmd(XD_KICK, &buf, 2); + } + return; + } + + newplayernum = (UINT8)READUINT8(*p); + + CONS_Debug(DBG_NETPLAY, "addbot: %d\n", newplayernum); + + // Clear player before joining, lest some things get set incorrectly + CL_ClearPlayer(newplayernum); + + playeringame[newplayernum] = true; + G_AddPlayer(newplayernum); + if (newplayernum+1 > doomcom->numslots) + doomcom->numslots = (INT16)(newplayernum+1); + + players[newplayernum].splitscreenindex = 0; + players[newplayernum].bot = true; + + playerconsole[newplayernum] = 0; + + if (netgame) + { + HU_AddChatText(va("\x82*Bot %d has been added to the game", newplayernum+1), false); + } + +#ifdef HAVE_BLUA + LUAh_PlayerJoin(newplayernum); +#endif +} + static boolean SV_AddWaitingPlayers(void) { INT32 node, n, newplayer = false; @@ -5057,7 +5103,7 @@ static void CL_SendClientCmd(void) G_MoveTiccmd(&netbuffer->u.clientpak.cmd, &localcmds, 1); netbuffer->u.clientpak.consistancy = SHORT(consistancy[gametic%BACKUPTICS]); - if (splitscreen || botingame) // Send a special packet with 2 cmd for splitscreen + if (splitscreen) // Send a special packet with 2 cmd for splitscreen { netbuffer->packettype = (mis ? PT_CLIENT2MIS : PT_CLIENT2CMD); packetsize = sizeof (client2cmd_pak); @@ -5253,7 +5299,7 @@ static void Local_Maketic(INT32 realtics) if (!dedicated) rendergametic = gametic; // translate inputs (keyboard/mouse/joystick) into game controls G_BuildTiccmd(&localcmds, realtics, 1); - if (splitscreen || botingame) + if (splitscreen) { G_BuildTiccmd(&localcmds2, realtics, 2); if (splitscreen > 1) diff --git a/src/d_main.c b/src/d_main.c index 80bc1c72c..f4a3cf2dc 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -766,8 +766,7 @@ void D_StartTitle(void) splitscreen = 0; SplitScreen_OnChange(); - botingame = false; - botskin = 0; + cv_debug = 0; emeralds = 0; diff --git a/src/d_netcmd.c b/src/d_netcmd.c index e3435b39f..999397aac 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -49,6 +49,7 @@ #include "k_kart.h" // SRB2kart #include "k_battle.h" #include "k_pwrlv.h" +#include "k_bot.h" #include "y_inter.h" #ifdef NETGAME_DEVMODE @@ -525,6 +526,11 @@ const char *netxcmdnames[MAXNETXCMD - 1] = "PICKVOTE", "REMOVEPLAYER", "POWERLEVEL", + "PARTYINVITE", + "ACCEPTPARTYINVITE", + "LEAVEPARTY", + "CANCELPARTYINVITE", + "ADDBOT", #ifdef HAVE_BLUA "LUACMD", "LUAVAR" @@ -1493,7 +1499,7 @@ static void SendNameAndColor2(void) XBOXSTATIC char buf[MAXPLAYERNAME+2]; char *p; - if (splitscreen < 1 && !botingame) + if (splitscreen < 1) return; // can happen if skin2/color2/name2 changed if (g_localplayers[1] != consoleplayer) @@ -1531,15 +1537,7 @@ static void SendNameAndColor2(void) return; // If you're not in a netgame, merely update the skin, color, and name. - if (botingame) - { - players[secondplaya].skincolor = botcolor; - if (players[secondplaya].mo) - players[secondplaya].mo->color = players[secondplaya].skincolor; - SetPlayerSkinByNum(secondplaya, botskin-1); - return; - } - else if (!netgame) + if (!netgame) { INT32 foundskin; @@ -1769,15 +1767,7 @@ static void SendNameAndColor4(void) return; // If you're not in a netgame, merely update the skin, color, and name. - if (botingame) - { - players[fourthplaya].skincolor = botcolor; - if (players[fourthplaya].mo) - players[fourthplaya].mo->color = players[fourthplaya].skincolor; - SetPlayerSkinByNum(fourthplaya, botskin-1); - return; - } - else if (!netgame) + if (!netgame) { INT32 foundskin; @@ -2187,7 +2177,7 @@ static void Got_LeaveParty(UINT8 **cp,INT32 playernum) void D_SendPlayerConfig(void) { SendNameAndColor(); - if (splitscreen || botingame) + if (splitscreen) SendNameAndColor2(); if (splitscreen > 1) SendNameAndColor3(); @@ -2745,28 +2735,7 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pencoremode, boolean r return; } - // Kick bot from special stages - if (botskin) - { - if (G_IsSpecialStage(mapnum)) - { - if (botingame) - { - //CL_RemoveSplitscreenPlayer(); - botingame = false; - playeringame[1] = false; - } - } - else if (!botingame) - { - //CL_AddSplitscreenPlayer(); - botingame = true; - displayplayers[1] = 1; - playeringame[1] = true; - players[1].bot = 1; - SendNameAndColor2(); - } - } + K_AddBots(13); chmappending++; if (netgame) @@ -5738,8 +5707,7 @@ void Command_ExitGame_f(void) splitscreen = 0; SplitScreen_OnChange(); - botingame = false; - botskin = 0; + cv_debug = 0; emeralds = 0; diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 8238dea2b..d8e7dd70d 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -186,9 +186,10 @@ typedef enum XD_ACCEPTPARTYINVITE, // 28 XD_LEAVEPARTY, // 29 XD_CANCELPARTYINVITE, // 30 + XD_ADDBOT, // 31 #ifdef HAVE_BLUA - XD_LUACMD, // 31 - XD_LUAVAR, // 32 + XD_LUACMD, // 32 + XD_LUAVAR, // 33 #endif MAXNETXCMD } netxcmd_t; diff --git a/src/g_game.c b/src/g_game.c index f9059ab60..3e6a13868 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -44,7 +44,7 @@ #include "v_video.h" #include "dehacked.h" // get_number (for ghost thok) #include "lua_hook.h" -#include "b_bot.h" +#include "k_bot.h" #include "m_cond.h" // condition sets #include "md5.h" // demo checksums #include "k_kart.h" // SRB2kart @@ -55,10 +55,6 @@ gameaction_t gameaction; gamestate_t gamestate = GS_NULL; UINT8 ultimatemode = false; -boolean botingame; -UINT8 botskin; -UINT8 botcolor; - JoyType_t Joystick; JoyType_t Joystick2; JoyType_t Joystick3; @@ -1265,10 +1261,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) else player = &players[g_localplayers[ssplayer-1]]; - if (ssplayer == 2) - thiscam = (player->bot == 2 ? &camera[0] : &camera[ssplayer-1]); - else - thiscam = &camera[ssplayer-1]; + thiscam = &camera[ssplayer-1]; lang = localangle[ssplayer-1]; laim = localaiming[ssplayer-1]; th = turnheld[ssplayer-1]; @@ -1624,8 +1617,6 @@ static void UserAnalog_OnChange(void) static void UserAnalog2_OnChange(void) { - if (botingame) - return; /*if (cv_useranalog2.value) CV_SetValue(&cv_analog2, 1); else @@ -1634,8 +1625,6 @@ static void UserAnalog2_OnChange(void) static void UserAnalog3_OnChange(void) { - if (botingame) - return; /*if (cv_useranalog3.value) CV_SetValue(&cv_analog3, 1); else @@ -1644,8 +1633,6 @@ static void UserAnalog3_OnChange(void) static void UserAnalog4_OnChange(void) { - if (botingame) - return; /*if (cv_useranalog4.value) CV_SetValue(&cv_analog4, 1); else @@ -1671,7 +1658,7 @@ static void Analog_OnChange(void) static void Analog2_OnChange(void) { - if (!(splitscreen || botingame) || !cv_cam2_dist.string) + if (!splitscreen || !cv_cam2_dist.string) return; // cameras are not initialized at this point @@ -1763,7 +1750,7 @@ void G_DoLoadLevel(boolean resetplayer) for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) { - if (i > 0 && !(i == 1 && botingame) && r_splitscreen < i) + if (i > 0 && r_splitscreen < i) g_localplayers[i] = consoleplayer; } @@ -2570,7 +2557,7 @@ void G_PlayerReborn(INT32 player) tic_t jointime; UINT8 splitscreenindex; boolean spectator; - INT16 bot; + boolean bot; SINT8 pity; // SRB2kart @@ -2701,8 +2688,7 @@ void G_PlayerReborn(INT32 player) p->totalring = totalring; p->mare = mare; - if (bot) - p->bot = 1; // reset to AI-controlled + p->bot = bot; p->pity = pity; // SRB2kart @@ -3151,96 +3137,9 @@ void G_DoReborn(INT32 playernum) player_t *player = &players[playernum]; boolean starpost = false; - /*if (modeattacking) // Not needed for SRB2Kart. - { - M_EndModeAttackRun(); - return; - }*/ - // Make sure objectplace is OFF when you first start the level! OP_ResetObjectplace(); - if (player->bot && playernum != consoleplayer) - { // Bots respawn next to their master. - mobj_t *oldmo = NULL; - - // first dissasociate the corpse - if (player->mo) - { - oldmo = player->mo; - // Don't leave your carcass stuck 10-billion feet in the ground! - P_RemoveMobj(player->mo); - } - - B_RespawnBot(playernum); - if (oldmo) - G_ChangePlayerReferences(oldmo, players[playernum].mo); - } - /*else if (countdowntimeup || (!multiplayer && !modeattacking)) - { - // reload the level from scratch - if (countdowntimeup) - { - player->starpostangle = 0; - player->starposttime = 0; - player->starpostx = 0; - player->starposty = 0; - player->starpostz = 0; - player->starpostnum = 0; - } - if (!countdowntimeup && (mapheaderinfo[gamemap-1]->levelflags & LF_NORELOAD)) - { - INT32 i; - - player->playerstate = PST_REBORN; - - P_LoadThingsOnly(); - - // Do a wipe - wipegamestate = -1; - - if (player->starpostnum) // SRB2kart - starpost = true; - - for (i = 0; i <= splitscreen; i++) - { - if (camera[i].chase) - P_ResetCamera(&players[displayplayers[i]], &camera[i]); - } - - // clear cmd building stuff - memset(gamekeydown, 0, sizeof (gamekeydown)); - for (i = 0;i < JOYAXISSET; i++) - { - joyxmove[i] = joyymove[i] = 0; - joy2xmove[i] = joy2ymove[i] = 0; - joy3xmove[i] = joy3ymove[i] = 0; - joy4xmove[i] = joy4ymove[i] = 0; - } - mousex = mousey = 0; - mouse2x = mouse2y = 0; - - // clear hud messages remains (usually from game startup) - CON_ClearHUD(); - - // Starpost support - G_SpawnPlayer(playernum, starpost); - - if (botingame) - { // Bots respawn next to their master. - players[displayplayers[1]].playerstate = PST_REBORN; - G_SpawnPlayer(displayplayers[1], false); - } - } - else - { -#ifdef HAVE_BLUA - LUAh_MapChange(gamemap); -#endif - G_DoLoadLevel(true); - } - }*/ - else { // respawn at the start mobj_t *oldmo = NULL; @@ -4602,9 +4501,6 @@ void G_DeferedInitNew(boolean pencoremode, const char *mapname, INT32 pickedchar if (savedata.lives > 0) { color = savedata.skincolor; - botskin = savedata.botskin; - botcolor = savedata.botcolor; - botingame = (botskin != 0); } else if (splitscreen != ssplayers) { diff --git a/src/g_state.h b/src/g_state.h index f9f1babd3..24904e67c 100644 --- a/src/g_state.h +++ b/src/g_state.h @@ -55,7 +55,4 @@ extern gamestate_t gamestate; extern UINT8 ultimatemode; // was sk_insane extern gameaction_t gameaction; -extern boolean botingame; -extern UINT8 botskin, botcolor; - #endif //__G_STATE__ diff --git a/src/b_bot.c b/src/k_bot.c similarity index 89% rename from src/b_bot.c rename to src/k_bot.c index c16976b07..4006fb8b5 100644 --- a/src/b_bot.c +++ b/src/k_bot.c @@ -7,7 +7,7 @@ // terms of the GNU General Public License, version 2. // See the 'LICENSE' file for more details. //----------------------------------------------------------------------------- -/// \file b_bot.c +/// \file k_bot.c /// \brief Basic bot handling #include "doomdef.h" @@ -15,8 +15,10 @@ #include "g_game.h" #include "r_main.h" #include "p_local.h" -#include "b_bot.h" +#include "k_bot.h" #include "lua_hook.h" +#include "byteptr.h" +#include "d_net.h" // nodetoplayer // If you want multiple bots, variables like this will // have to be stuffed in something accessible through player_t. @@ -24,6 +26,50 @@ static boolean lastForward = false; static boolean lastBlocked = false; static boolean blocked = false; +void K_AddBots(UINT8 numbots) +{ + UINT8 newplayernum = 0; + UINT8 buf[4]; + UINT8 *buf_p = buf; + + if (dedicated) + newplayernum = 1; + + while (numbots > 0) + { + numbots--; + + // search for a free playernum + // we can't use playeringame since it is not updated here + for (; newplayernum < MAXPLAYERS; newplayernum++) + { + UINT8 n; + + for (n = 0; n < MAXNETNODES; n++) + if (nodetoplayer[n] == newplayernum + || nodetoplayer2[n] == newplayernum + || nodetoplayer3[n] == newplayernum + || nodetoplayer4[n] == newplayernum) + break; + + if (n == MAXNETNODES) + break; + } + + // should never happen since we check the playernum + // before accepting the join + I_Assert(newplayernum < MAXPLAYERS); + + WRITEUINT8(buf_p, newplayernum); + + SendNetXCmd(XD_ADDPLAYER, buf, buf_p - buf); + + DEBFILE(va("Server added bot %d\n", newplayernum)); + // use the next free slot (we can't put playeringame[newplayernum] = true here) + newplayernum++; + } +} + static inline void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd) { boolean forward=false, backward=false, left=false, right=false, jump=false, spin=false; @@ -242,7 +288,6 @@ void B_RespawnBot(INT32 playernum) if (!sonic || sonic->health <= 0) return; - player->bot = 1; P_SpawnPlayer(playernum); tails = player->mo; diff --git a/src/b_bot.h b/src/k_bot.h similarity index 96% rename from src/b_bot.h rename to src/k_bot.h index 20b2803b6..9b34194de 100644 --- a/src/b_bot.h +++ b/src/k_bot.h @@ -10,6 +10,7 @@ /// \file b_bot.h /// \brief Basic bot handling +void K_AddBots(UINT8 numbots); void B_BuildTiccmd(player_t *player, ticcmd_t *cmd); void B_KeysToTiccmd(mobj_t *mo, ticcmd_t *cmd, boolean forward, boolean backward, boolean left, boolean right, boolean strafeleft, boolean straferight, boolean jump, boolean spin); boolean B_CheckRespawn(player_t *player); diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c index c15d13a0c..93ce13103 100644 --- a/src/lua_hooklib.c +++ b/src/lua_hooklib.c @@ -16,7 +16,7 @@ #include "p_mobj.h" #include "g_game.h" #include "r_things.h" -#include "b_bot.h" +#include "k_bot.h" #include "z_zone.h" #include "lua_script.h" diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 3cca1f91f..5dc2a6350 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -294,7 +294,7 @@ static int player_get(lua_State *L) else if (fastcmp(field,"spectator")) lua_pushboolean(L, plr->spectator); else if (fastcmp(field,"bot")) - lua_pushinteger(L, plr->bot); + lua_pushboolean(L, plr->bot); else if (fastcmp(field,"jointime")) lua_pushinteger(L, plr->jointime); else if (fastcmp(field,"splitscreenindex")) diff --git a/src/p_inter.c b/src/p_inter.c index a1f507445..bbb63de98 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -166,8 +166,6 @@ void P_DoNightsScore(player_t *player) return; // Don't do any fancy shit for failures. dummymo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z+player->mo->height/2, MT_NIGHTSCORE); - if (player->bot) - player = &players[consoleplayer]; if (G_IsSpecialStage(gamemap)) // Global link count? Maybe not a good idea... { @@ -793,8 +791,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) // ***************************** // // Special Stage Token case MT_EMMY: - if (player->bot) - return; tokenlist += special->health; if (ALL7EMERALDS(emeralds)) // Got all 7 @@ -811,9 +807,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) // Emerald Hunt case MT_EMERHUNT: - if (player->bot) - return; - if (hunt1 == special) hunt1 = NULL; else if (hunt2 == special) @@ -842,9 +835,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) case MT_EMERALD5: case MT_EMERALD6: case MT_EMERALD7: - if (player->bot) - return; - if (special->threshold) player->powers[pw_emeralds] |= special->info->speed; else @@ -872,12 +862,11 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) // Secret emblem thingy case MT_EMBLEM: { - if (demo.playback || player->bot) + if (demo.playback) return; + emblemlocations[special->health-1].collected = true; - M_UpdateUnlockablesAndExtraEmblems(false); - G_SaveGameData(false); break; } @@ -885,8 +874,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) // CTF Flags case MT_REDFLAG: case MT_BLUEFLAG: - if (player->bot) - return; if (player->powers[pw_flashing] || player->tossdelay) return; if (!special->spawnpoint) @@ -957,8 +944,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) // NiGHTS gameplay items and powerups // // ********************************** // /*case MT_NIGHTSDRONE: - if (player->bot) - return; if (player->exiting) return; if (player->bonustime) @@ -1112,9 +1097,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) } return; case MT_EGGCAPSULE: - if (player->bot) - return; - // make sure everything is as it should be, THEN take rings from players in special stages if (player->pflags & PF_NIGHTSMODE && !toucher->target) return; @@ -1216,7 +1198,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) } return; /*case MT_NIGHTSSUPERLOOP: - if (player->bot || !(player->pflags & PF_NIGHTSMODE)) + if (!(player->pflags & PF_NIGHTSMODE)) return; if (!G_IsSpecialStage(gamemap)) player->powers[pw_nights_superloop] = (UINT16)special->info->speed; @@ -1238,7 +1220,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) } break; case MT_NIGHTSDRILLREFILL: - if (player->bot || !(player->pflags & PF_NIGHTSMODE)) + if (!(player->pflags & PF_NIGHTSMODE)) return; if (!G_IsSpecialStage(gamemap)) player->drillmeter = special->info->speed; @@ -1260,7 +1242,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) } break; case MT_NIGHTSHELPER: - if (player->bot || !(player->pflags & PF_NIGHTSMODE)) + if (!(player->pflags & PF_NIGHTSMODE)) return; if (!G_IsSpecialStage(gamemap)) { @@ -1292,7 +1274,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) } break; case MT_NIGHTSEXTRATIME: - if (player->bot || !(player->pflags & PF_NIGHTSMODE)) + if (!(player->pflags & PF_NIGHTSMODE)) return; if (!G_IsSpecialStage(gamemap)) { @@ -1322,7 +1304,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) } break; case MT_NIGHTSLINKFREEZE: - if (player->bot || !(player->pflags & PF_NIGHTSMODE)) + if (!(player->pflags & PF_NIGHTSMODE)) return; if (!G_IsSpecialStage(gamemap)) { @@ -1393,8 +1375,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (playeringame[i] && players[i].pflags & PF_NIGHTSMODE) players[i].drillmeter += TICRATE/2; } - else if (player->bot) - players[consoleplayer].drillmeter += TICRATE/2; else player->drillmeter += TICRATE/2; @@ -1428,9 +1408,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) thinker_t *th; mobj_t *mo2; - if (player->bot) - return; - junk.tag = 649; EV_DoElevator(&junk, bridgeFall, false); @@ -1450,8 +1427,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) } break; case MT_FIREFLOWER: - if (player->bot) - return; player->powers[pw_shield] |= SH_FIREFLOWER; toucher->color = SKINCOLOR_WHITE; G_GhostAddColor(player - players, GHC_FIREFLOWER); @@ -1461,9 +1436,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) // Misc touchables // // *************** // case MT_STARPOST: - if (player->bot) - return; - // // SRB2kart: make sure the player will have enough checkpoints to touch if (circuitmap && special->health - player->starpostnum > 1) { @@ -1666,7 +1638,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (player->powers[pw_invulnerability] || player->powers[pw_flashing] || (player->powers[pw_super] && !(ALL7EMERALDS(player->powers[pw_emeralds])))) return; - if (player->powers[pw_shield] || player->bot) //If One-Hit Shield + if (player->powers[pw_shield]) //If One-Hit Shield { P_RemoveShield(player); S_StartSound(toucher, sfx_shldls); // Ba-Dum! Shield loss. @@ -1751,8 +1723,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) return; default: // SOC or script pickup - if (player->bot) - return; P_SetTarget(&special->target, toucher); break; } @@ -2315,8 +2285,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source) target->flags |= MF_NOBLOCKMAP|MF_NOCLIPHEIGHT; P_SetThingPosition(target); - if (!target->player->bot && !G_IsSpecialStage(gamemap) - && G_GametypeUsesLives()) + if (!target->player->bot && !G_IsSpecialStage(gamemap) && G_GametypeUsesLives()) { target->player->lives -= 1; // Lose a life Tails 03-11-2000 @@ -2329,6 +2298,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source) } } } + target->player->playerstate = PST_DEAD; if (target->player == &players[consoleplayer]) @@ -3073,7 +3043,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da return false; // Make sure that boxes cannot be popped by enemies, red rings, etc. - if (target->flags & MF_MONITOR && ((!source || !source->player || source->player->bot) || (inflictor && !inflictor->player))) + if (target->flags & MF_MONITOR && ((!source || !source->player) || (inflictor && !inflictor->player))) return false; } diff --git a/src/p_map.c b/src/p_map.c index 559b9a461..5b79dcf05 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -491,89 +491,6 @@ static void P_DoFanAndGasJet(mobj_t *spring, mobj_t *object) } } -#if 0 -static void P_DoTailsCarry(player_t *sonic, player_t *tails) -{ - INT32 p; - fixed_t zdist; // z distance between the two players' bottoms - - if ((tails->pflags & PF_CARRIED) && tails->mo->tracer == sonic->mo) - return; - if ((sonic->pflags & PF_CARRIED) && sonic->mo->tracer == tails->mo) - return; - - //if (!tails->powers[pw_tailsfly] && !(tails->charability == CA_FLY && (tails->mo->state >= &states[S_PLAY_SPC1] && tails->mo->state <= &states[S_PLAY_SPC4]))) - // return; // SRB2kart - no changey statey - - if (tails->bot == 1) - return; - - if (sonic->pflags & PF_NIGHTSMODE) - return; - - if (sonic->mo->tracer && sonic->mo->tracer->type == MT_TUBEWAYPOINT - && !(sonic->pflags & PF_ROPEHANG)) - return; // don't steal players from zoomtubes! - - if ((sonic->mo->eflags & MFE_VERTICALFLIP) != (tails->mo->eflags & MFE_VERTICALFLIP)) - return; // Both should be in same gravity - - if (tails->mo->eflags & MFE_VERTICALFLIP) - { - if (tails->mo->ceilingz - (tails->mo->z + tails->mo->height) < sonic->mo->height-FixedMul(2*FRACUNIT, sonic->mo->scale)) - return; - } - else if (tails->mo->z - tails->mo->floorz < sonic->mo->height-FixedMul(2*FRACUNIT, sonic->mo->scale)) - return; // No room to pick up this guy! - - // Search in case another player is already being carried by this fox. - for (p = 0; p < MAXPLAYERS; p++) - if (playeringame[p] && players[p].mo - && players[p].pflags & PF_CARRIED && players[p].mo->tracer == tails->mo) - return; - - if (tails->mo->eflags & MFE_VERTICALFLIP) - zdist = (sonic->mo->z + sonic->mo->height) - (tails->mo->z + tails->mo->height); - else - zdist = tails->mo->z - sonic->mo->z; - - if (zdist <= sonic->mo->height + FixedMul(FRACUNIT, sonic->mo->scale) - && zdist > sonic->mo->height*2/3 - && P_MobjFlip(tails->mo)*sonic->mo->momz <= 0) - { - // Why block opposing teams from tailsflying each other? - // Sneaking into the hands of a flying tails player in Race might be a viable strategy, who knows. - /* - if (gametype == GT_RACE || gametype == GT_COMPETITION - || (netgame && (tails->spectator || sonic->spectator)) - || (G_TagGametype() && (!(tails->pflags & PF_TAGIT) != !(sonic->pflags & PF_TAGIT))) - || (gametype == GT_MATCH) - || (G_GametypeHasTeams() && tails->ctfteam != sonic->ctfteam)) - sonic->pflags &= ~PF_CARRIED; */ - if (tails->spectator || sonic->spectator || G_RaceGametype()) // SRB2kart - sonic->pflags &= ~PF_CARRIED; - else - { - if (sonic-players == consoleplayer && botingame) - //CV_SetValue(&cv_analog2, false); - P_ResetPlayer(sonic); - P_SetTarget(&sonic->mo->tracer, tails->mo); - sonic->pflags |= PF_CARRIED; - S_StartSound(sonic->mo, sfx_s3k4a); - P_UnsetThingPosition(sonic->mo); - sonic->mo->x = tails->mo->x; - sonic->mo->y = tails->mo->y; - P_SetThingPosition(sonic->mo); - } - } - else { - if (sonic-players == consoleplayer && botingame) - //CV_SetValue(&cv_analog2, true); - sonic->pflags &= ~PF_CARRIED; - } -} -#endif - // // PIT_CheckThing // @@ -1338,8 +1255,6 @@ static boolean PIT_CheckThing(mobj_t *thing) } } else if (thing->player) { - if (thing->player-players == consoleplayer && botingame) - //CV_SetValue(&cv_analog2, true); thing->player->pflags &= ~PF_CARRIED; }*/ diff --git a/src/p_mobj.c b/src/p_mobj.c index a1ac18dc9..426ad49c2 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -30,7 +30,7 @@ #include "info.h" #include "i_video.h" #include "lua_hook.h" -#include "b_bot.h" +#include "k_bot.h" #ifdef ESLOPE #include "p_slopes.h" #endif @@ -1667,10 +1667,6 @@ void P_XYMovement(mobj_t *mo) // blocked move moved = false; - if (player) { - if (player->bot) - B_MoveBlocked(player); - } //{ SRB2kart - Jawz if (mo->type == MT_JAWZ || mo->type == MT_JAWZ_DUD) { diff --git a/src/p_saveg.c b/src/p_saveg.c index ccae035ee..8546d3029 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -78,12 +78,6 @@ static inline void P_ArchivePlayer(void) WRITEUINT32(save_p, player->score); WRITEINT32(save_p, pllives); WRITEINT32(save_p, player->continues); - - if (botskin) - { - WRITEUINT8(save_p, botskin); - WRITEUINT8(save_p, botcolor); - } } // @@ -97,16 +91,6 @@ static inline void P_UnArchivePlayer(void) savedata.score = READINT32(save_p); savedata.lives = READINT32(save_p); savedata.continues = READINT32(save_p); - - if (savedata.botcolor) - { - savedata.botskin = READUINT8(save_p); - if (savedata.botskin-1 >= numskins) - savedata.botskin = 0; - savedata.botcolor = READUINT8(save_p); - } - else - savedata.botskin = 0; } // @@ -3203,7 +3187,7 @@ static inline void P_ArchiveMisc(void) lastmapsaved = gamemap; - WRITEUINT16(save_p, (botskin ? (emeralds|(1<<10)) : emeralds)+357); + WRITEUINT16(save_p, emeralds+357); WRITESTRINGN(save_p, timeattackfolder, sizeof(timeattackfolder)); } diff --git a/src/p_setup.c b/src/p_setup.c index 40ea643b7..86da8b02e 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -2824,7 +2824,7 @@ boolean P_SetupLevel(boolean skipprecip) P_Initsecnode(); if (netgame || multiplayer) - cv_debug = botskin = 0; + cv_debug = 0; if (metalplayback) G_StopMetalDemo(); @@ -3264,7 +3264,7 @@ boolean P_SetupLevel(boolean skipprecip) /*if (cv_useranalog.value) CV_SetValue(&cv_analog, true); - if ((splitscreen && cv_useranalog2.value) || botingame) + if (splitscreen && cv_useranalog2.value) CV_SetValue(&cv_analog2, true); if (splitscreen > 1 && cv_useranalog3.value) @@ -3335,9 +3335,6 @@ boolean P_SetupLevel(boolean skipprecip) players[consoleplayer].continues = savedata.continues; players[consoleplayer].lives = savedata.lives; players[consoleplayer].score = savedata.score; - botskin = savedata.botskin; - botcolor = savedata.botcolor; - botingame = (savedata.botskin != 0); emeralds = savedata.emeralds; savedata.lives = 0; } diff --git a/src/p_spec.c b/src/p_spec.c index 425d1c779..6debb5df2 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -2278,13 +2278,9 @@ static mobj_t *P_GetObjectTypeInSectorNum(mobjtype_t type, size_t s) static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) { INT32 secnum = -1; - mobj_t *bot = NULL; I_Assert(!mo || !P_MobjWasRemoved(mo)); // If mo is there, mo must be valid! - if (mo && mo->player && botingame) - bot = players[displayplayers[1]].mo; - // note: only commands with linedef types >= 400 && < 500 can be used switch (line->special) { @@ -2423,9 +2419,6 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) { UINT8 i; - if (bot) // This might put poor Tails in a wall if he's too far behind! D: But okay, whatever! >:3 - P_TeleportMove(bot, bot->x + x, bot->y + y, bot->z + z); - for (i = 0; i <= r_splitscreen; i++) { if (mo->player == &players[displayplayers[i]] && camera[i].chase) @@ -2448,8 +2441,6 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) if (!dest) return; - if (bot) - P_Teleport(bot, dest->x, dest->y, dest->z, (line->flags & ML_NOCLIMB) ? mo->angle : dest->angle, (line->flags & ML_BLOCKPLAYERS) == 0, (line->flags & ML_EFFECT4) == ML_EFFECT4); if (line->flags & ML_BLOCKPLAYERS) P_Teleport(mo, dest->x, dest->y, dest->z, (line->flags & ML_NOCLIMB) ? mo->angle : dest->angle, false, (line->flags & ML_EFFECT4) == ML_EFFECT4); else @@ -2882,18 +2873,6 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) mo->player->cmomx = mo->player->cmomy = 0; P_ResetPlayer(mo->player); P_SetPlayerMobjState(mo, S_KART_STND1); // SRB2kart - was S_PLAY_STND - - // Reset bot too. - if (bot) { - if (line->flags & ML_NOCLIMB) - P_TeleportMove(bot, mo->x, mo->y, mo->z); - bot->momx = bot->momy = bot->momz = 1; - bot->pmomz = 0; - bot->player->rmomx = bot->player->rmomy = 1; - bot->player->cmomx = bot->player->cmomy = 0; - P_ResetPlayer(bot->player); - P_SetPlayerMobjState(bot, S_KART_STND1); // SRB2kart - was S_PLAY_STND - } } break; @@ -2925,13 +2904,6 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) mo->flags2 &= ~MF2_TWOD; else mo->flags2 |= MF2_TWOD; - - // Copy effect to bot if necessary - // (Teleport them to you so they don't break it.) - if (bot && (bot->flags2 & MF2_TWOD) != (mo->flags2 & MF2_TWOD)) { - bot->flags2 = (bot->flags2 & ~MF2_TWOD) | (mo->flags2 & MF2_TWOD); - P_TeleportMove(bot, mo->x, mo->y, mo->z); - } } break; @@ -2940,8 +2912,6 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) mo->flags2 &= ~MF2_OBJECTFLIP; else mo->flags2 |= MF2_OBJECTFLIP; - if (bot) - bot->flags2 = (bot->flags2 & ~MF2_OBJECTFLIP) | (mo->flags2 & MF2_OBJECTFLIP); break; case 434: // Custom Power @@ -2961,10 +2931,6 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) P_SetTarget(&dummy->target, mo); A_CustomPower(dummy); - if (bot) { - P_SetTarget(&dummy->target, bot); - A_CustomPower(dummy); - } P_RemoveMobj(dummy); } break; @@ -3033,8 +2999,6 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) if (line->flags & ML_NOCLIMB) fractime |= 1<<15; //more crazy &ing, as if music stuff wasn't enough mo->player->powers[pw_nocontrol] = fractime; - if (bot) - bot->player->powers[pw_nocontrol] = fractime; } break; @@ -3044,8 +3008,6 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) mo->destscale = FixedDiv(P_AproxDistance(line->dx, line->dy), 100<destscale < FRACUNIT/100) mo->destscale = FRACUNIT/100; - if (mo->player && bot) - bot->destscale = mo->destscale; } break; @@ -3876,7 +3838,7 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers } break; case 11: // Special Stage Damage - Kind of like a mini-P_DamageMobj() - if (player->powers[pw_invulnerability] || player->powers[pw_flashing] || player->powers[pw_super] || player->exiting || player->bot) + if (player->powers[pw_invulnerability] || player->powers[pw_flashing] || player->powers[pw_super] || player->exiting) break; if (!(player->powers[pw_shield] || player->mo->health > 1)) // Don't do anything if no shield or rings anyway @@ -3920,7 +3882,7 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers case 3: // Linedef executor requires all players present /// \todo check continues for proper splitscreen support? for (i = 0; i < MAXPLAYERS; i++) - if (playeringame[i] && !players[i].bot && players[i].mo && (gametype != GT_COOP || players[i].lives > 0)) + if (playeringame[i] && players[i].mo && (gametype != GT_COOP || players[i].lives > 0)) { if (roversector) { @@ -3978,8 +3940,7 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers case 5: // Linedef executor case 6: // Linedef executor (7 Emeralds) case 7: // Linedef executor (NiGHTS Mare) - if (!player->bot) - P_LinedefExecute(sector->tag, player->mo, sector); + P_LinedefExecute(sector->tag, player->mo, sector); break; case 8: // Tells pushable things to check FOFs break; @@ -3989,7 +3950,7 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers mobj_t *mo2; line_t junk; - if (player->bot || sector->ceilingdata || sector->floordata) + if (sector->ceilingdata || sector->floordata) return; // Find the center of the Eggtrap and release all the pretty animals! @@ -4177,8 +4138,6 @@ DoneSection2: } case 2: // Special stage GOAL sector / Exit Sector / CTF Flag Return - if (player->bot) - break; if (!useNightsSS && G_IsSpecialStage(gamemap) && sstimer > 6) sstimer = 6; // Just let P_Ticker take care of the rest. diff --git a/src/p_user.c b/src/p_user.c index 22149f408..107615cfd 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -39,7 +39,7 @@ #include "st_stuff.h" #include "lua_script.h" #include "lua_hook.h" -#include "b_bot.h" +#include "k_bot.h" // Objectplace #include "m_cheat.h" // SRB2kart @@ -705,10 +705,6 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) { INT32 oldmare; - // Bots can't be super, silly!1 :P - if (player->bot) - return; - if (!(player->pflags & PF_NIGHTSMODE)) { P_SetTarget(&player->mo->tracer, P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_NIGHTSCHAR)); @@ -934,8 +930,6 @@ void P_ResetPlayer(player_t *player) player->powers[pw_tailsfly] = 0; player->onconveyor = 0; player->skidtime = 0; - /*if (player-players == consoleplayer && botingame) - CV_SetValue(&cv_analog2, true);*/ } // @@ -1029,9 +1023,6 @@ void P_AddPlayerScore(player_t *player, UINT32 amount) if (!(G_BattleGametype())) return; - if (player->bot) - player = &players[consoleplayer]; - if (player->exiting) // srb2kart return; @@ -2264,7 +2255,7 @@ static void P_CheckUnderwaterAndSpaceTimer(player_t *player) } // Underwater audio cues - if (P_IsLocalPlayer(player) && !player->bot) + if (P_IsLocalPlayer(player)) { if (player->powers[pw_underwater] == 11*TICRATE + 1 && player == &players[consoleplayer]) @@ -8038,6 +8029,7 @@ void P_PlayerThink(player_t *player) if (B_CheckRespawn(player)) player->playerstate = PST_REBORN; } + if (player->playerstate == PST_REBORN) return; } diff --git a/src/r_main.c b/src/r_main.c index 5afda93f7..b4e8b3ec7 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -202,7 +202,7 @@ void SplitScreen_OnChange(void) // recompute screen size R_ExecuteSetViewSize(); - if (!demo.playback && !botingame) + if (!demo.playback) { for (i = 1; i < MAXSPLITSCREENPLAYERS; i++) { @@ -259,8 +259,6 @@ static void ChaseCam_OnChange(void) static void ChaseCam2_OnChange(void) { - if (botingame) - return; /*if (!cv_chasecam2.value || !cv_useranalog2.value) CV_SetValue(&cv_analog2, 0); else From ff621d3e468cf5ad9edba547957cd6d982f7164a Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sat, 28 Mar 2020 12:39:27 -0400 Subject: [PATCH 02/85] Skins, rudimentary ticcmd building for bots --- src/d_clisrv.c | 9 +- src/d_netcmd.c | 3 - src/g_game.c | 22 ++- src/k_bot.c | 365 ++++++++++++++++------------------------------ src/k_bot.h | 8 +- src/k_kart.c | 99 +++++++------ src/lua_hooklib.c | 7 + src/p_inter.c | 2 +- src/p_user.c | 12 -- 9 files changed, 214 insertions(+), 313 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index c5f029842..5b43c2213 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -49,6 +49,7 @@ #include "k_kart.h" #include "k_battle.h" #include "k_pwrlv.h" +#include "k_bot.h" #ifdef CLIENT_LOADINGSCREEN // cl loading screen @@ -3612,6 +3613,7 @@ static void Got_RemovePlayer(UINT8 **p, INT32 playernum) static void Got_AddBot(UINT8 **p, INT32 playernum) { INT16 newplayernum; + UINT8 skinnum = 0; if (playernum != serverplayer && !IsPlayerAdmin(playernum)) { @@ -3629,6 +3631,7 @@ static void Got_AddBot(UINT8 **p, INT32 playernum) } newplayernum = (UINT8)READUINT8(*p); + skinnum = (UINT8)READUINT8(*p); CONS_Debug(DBG_NETPLAY, "addbot: %d\n", newplayernum); @@ -3643,7 +3646,9 @@ static void Got_AddBot(UINT8 **p, INT32 playernum) players[newplayernum].splitscreenindex = 0; players[newplayernum].bot = true; - playerconsole[newplayernum] = 0; + players[newplayernum].skincolor = skins[skinnum].prefcolor; + sprintf(player_names[newplayernum], "%s", skins[skinnum].realname); + SetPlayerSkinByNum(newplayernum, skinnum); if (netgame) { @@ -3777,6 +3782,8 @@ boolean SV_SpawnServer(void) if (!dedicated) CL_ConnectToServer(false); else doomcom->numslots = 1; + + K_AddBots(7); // test } return SV_AddWaitingPlayers(); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index bb189513e..62ac1ccac 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -49,7 +49,6 @@ #include "k_kart.h" // SRB2kart #include "k_battle.h" #include "k_pwrlv.h" -#include "k_bot.h" #include "y_inter.h" #ifdef NETGAME_DEVMODE @@ -2737,8 +2736,6 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pencoremode, boolean r return; } - K_AddBots(13); - chmappending++; if (netgame) WRITEUINT32(buf_p, M_RandomizedSeed()); // random seed diff --git a/src/g_game.c b/src/g_game.c index 7ef2928c8..fa4ae8b00 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1297,6 +1297,11 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) return; } + if (K_PlayerUsesBotMovement(player)) + { + return; + } + switch (ssplayer) { case 2: @@ -1601,9 +1606,8 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) //Reset away view if a command is given. if ((cmd->forwardmove || cmd->sidemove || cmd->buttons) - && ! r_splitscreen && displayplayers[0] != consoleplayer && ssplayer == 1) + && !r_splitscreen && displayplayers[0] != consoleplayer && ssplayer == 1) displayplayers[0] = consoleplayer; - } // User has designated that they want @@ -2374,9 +2378,17 @@ void G_Ticker(boolean run) if (playeringame[i]) { - G_CopyTiccmd(cmd, &netcmds[buf][i], 1); - // Use the leveltime sent in the player's ticcmd to determine control lag - cmd->latency = modeattacking ? 0 : min(((leveltime & 0xFF) - cmd->latency) & 0xFF, MAXPREDICTTICS-1); //@TODO add a cvar to allow setting this max + if (K_PlayerUsesBotMovement(&players[i])) + { + K_BuildBotTiccmd(&players[i], cmd); + cmd->latency = 0; + } + else + { + G_CopyTiccmd(cmd, &netcmds[buf][i], 1); + // Use the leveltime sent in the player's ticcmd to determine control lag + cmd->latency = modeattacking ? 0 : min(((leveltime & 0xFF) - cmd->latency) & 0xFF, MAXPREDICTTICS-1); //@TODO add a cvar to allow setting this max + } } } diff --git a/src/k_bot.c b/src/k_bot.c index 4006fb8b5..577c9d260 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -19,24 +19,20 @@ #include "lua_hook.h" #include "byteptr.h" #include "d_net.h" // nodetoplayer - -// If you want multiple bots, variables like this will -// have to be stuffed in something accessible through player_t. -static boolean lastForward = false; -static boolean lastBlocked = false; -static boolean blocked = false; +#include "k_kart.h" void K_AddBots(UINT8 numbots) { UINT8 newplayernum = 0; - UINT8 buf[4]; - UINT8 *buf_p = buf; if (dedicated) newplayernum = 1; while (numbots > 0) { + UINT8 buf[2]; + UINT8 *buf_p = buf; + numbots--; // search for a free playernum @@ -56,13 +52,19 @@ void K_AddBots(UINT8 numbots) break; } - // should never happen since we check the playernum - // before accepting the join - I_Assert(newplayernum < MAXPLAYERS); - WRITEUINT8(buf_p, newplayernum); - SendNetXCmd(XD_ADDPLAYER, buf, buf_p - buf); + // test skins + if (numbots == 6) + WRITEUINT8(buf_p, 0); + else if (numbots == 5) + WRITEUINT8(buf_p, 1); + else if (numbots == 4) + WRITEUINT8(buf_p, 9); + else + WRITEUINT8(buf_p, 10); + + SendNetXCmd(XD_ADDBOT, buf, buf_p - buf); DEBFILE(va("Server added bot %d\n", newplayernum)); // use the next free slot (we can't put playeringame[newplayernum] = true here) @@ -70,90 +72,30 @@ void K_AddBots(UINT8 numbots) } } -static inline void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd) +boolean K_PlayerUsesBotMovement(player_t *player) { - boolean forward=false, backward=false, left=false, right=false, jump=false, spin=false; - angle_t angle; - INT16 rangle; - fixed_t dist; + if (player->bot || player->exiting) + return true; - // We can't follow Sonic if he's not around! - if (!sonic || sonic->health <= 0) - return; - -#ifdef HAVE_BLUA - // Lua can handle it! - if (LUAh_BotAI(sonic, tails, cmd)) - return; -#endif - - if (tails->player->pflags & (PF_MACESPIN|PF_ITEMHANG)) - { - dist = P_AproxDistance(tails->x-sonic->x, tails->y-sonic->y); - if (sonic->player->cmd.buttons & BT_DRIFT && sonic->player->pflags & (PF_JUMPED|PF_MACESPIN|PF_ITEMHANG)) - cmd->buttons |= BT_DRIFT; - if (sonic->player->pflags & (PF_MACESPIN|PF_ITEMHANG)) - { - cmd->forwardmove = sonic->player->cmd.forwardmove; - cmd->angleturn = abs((signed)(tails->angle - sonic->angle))>>16; - if (sonic->angle < tails->angle) - cmd->angleturn = -cmd->angleturn; - } else if (dist > FixedMul(512*FRACUNIT, tails->scale)) - cmd->buttons |= BT_DRIFT; - return; - } - - // Gather data about the environment - dist = P_AproxDistance(tails->x-sonic->x, tails->y-sonic->y); - if (tails->player->pflags & PF_STARTDASH) - angle = sonic->angle; - else - angle = R_PointToAngle2(tails->x, tails->y, sonic->x, sonic->y); - - // Decide which direction to turn - angle = (tails->angle - angle); - if (angle < ANGLE_180) { - right = true; // We need to turn right - rangle = AngleFixed(angle)>>FRACBITS; - } else { - left = true; // We need to turn left - rangle = 360-(AngleFixed(angle)>>FRACBITS); - } - - // Decide to move forward if you're finished turning - if (abs(rangle) < 10) { // We're facing the right way? - left = right = false; // Stop turning - forward = true; // and walk forward instead. - } - if (dist < (sonic->radius+tails->radius)*3) // We're close enough? - forward = false; // Stop walking. - - // Decide when to jump - if (!(tails->player->pflags & (PF_JUMPED|PF_JUMPDOWN))) { // We're not jumping yet... - if (forward && lastForward && blocked && lastBlocked) // We've been stopped by a wall or something - jump = true; // Try to jump up - } else if ((tails->player->pflags & (PF_JUMPDOWN|PF_JUMPED)) == (PF_JUMPDOWN|PF_JUMPED)) { // When we're already jumping... - if (lastForward && blocked) // We're still stuck on something? - jump = true; - if (sonic->floorz > tails->floorz) // He's still above us? Jump HIGHER, then! - jump = true; - } - - // Decide when to spin - if (sonic->player->pflags & PF_STARTDASH - && (tails->player->pflags & PF_STARTDASH || (P_AproxDistance(tails->momx, tails->momy) < 2*FRACUNIT && !forward))) - spin = true; - - // Turn the virtual keypresses into ticcmd_t. - B_KeysToTiccmd(tails, cmd, forward, backward, left, right, false, false, jump, spin); - - // Update our status - lastForward = forward; - lastBlocked = blocked; - blocked = false; + return false; } -void B_BuildTiccmd(player_t *player, ticcmd_t *cmd) +fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, fixed_t v2y, fixed_t cx, fixed_t cy) +{ + fixed_t v1toc[2] = {cx - v1x, cy - v1y}; + fixed_t v1tov2[2] = {v2x - v1x, v2y - v1y}; + + fixed_t mag = FixedMul(v1tov2[0], v1tov2[0]) + FixedMul(v1tov2[1], v1tov2[1]); + fixed_t dot = FixedMul(v1toc[0], v1tov2[0]) + FixedMul(v1toc[1], v1tov2[1]); + fixed_t t = FixedDiv(dot, mag); + + fixed_t px = v1x + FixedMul(v1tov2[0], t); + fixed_t py = v1y + FixedMul(v1tov2[1], t); + + return P_AproxDistance(cx - px, cy - py); +} + +void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) { // Can't build a ticcmd if we aren't spawned... if (!player->mo) @@ -161,162 +103,113 @@ void B_BuildTiccmd(player_t *player, ticcmd_t *cmd) if (player->playerstate == PST_DEAD) { - if (B_CheckRespawn(player)) - cmd->buttons |= BT_DRIFT; + cmd->buttons |= BT_ACCELERATE; return; } - // Bot AI isn't programmed in analog. - //CV_SetValue(&cv_analog2, false); - #ifdef HAVE_BLUA // Let Lua scripts build ticcmds if (LUAh_BotTiccmd(player, cmd)) return; #endif - // We don't have any main character AI, sorry. D: - if (player-players == consoleplayer) - return; + if (player->nextwaypoint != NULL && player->nextwaypoint->mobj != NULL && !P_MobjWasRemoved(player->nextwaypoint->mobj)) + { + INT16 turnamt = KART_FULLTURN; + SINT8 turnsign = 0; + angle_t wpangle, moveangle, angle; + INT16 anglediff; - // Basic Tails AI - B_BuildTailsTiccmd(players[consoleplayer].mo, player->mo, cmd); -} + wpangle = R_PointToAngle2(player->mo->x, player->mo->y, player->nextwaypoint->mobj->x, player->nextwaypoint->mobj->y); -void B_KeysToTiccmd(mobj_t *mo, ticcmd_t *cmd, boolean forward, boolean backward, boolean left, boolean right, boolean strafeleft, boolean straferight, boolean jump, boolean spin) -{ - // Turn the virtual keypresses into ticcmd_t. - if (twodlevel || mo->flags2 & MF2_TWOD) { - if (players[consoleplayer].climbing - || mo->player->pflags & PF_GLIDING) { - // Don't mess with bot inputs during these unhandled movement conditions. - // The normal AI doesn't use abilities, so custom AI should be sending us exactly what it wants anyway. - if (forward) - cmd->forwardmove += MAXPLMOVE<>16; - if (backward) - cmd->forwardmove -= MAXPLMOVE<>16; - if (left || strafeleft) - cmd->sidemove -= MAXPLMOVE<>16; - if (right || straferight) - cmd->sidemove += MAXPLMOVE<>16; - } else { - // In standard 2D mode, interpret "forward" as "the way you're facing" and everything else as "the way you're not facing" - if (left || right) - backward = true; - left = right = false; - if (forward) { - if (mo->angle < ANGLE_90 || mo->angle > ANGLE_270) - right = true; - else - left = true; - } else if (backward) { - if (mo->angle < ANGLE_90 || mo->angle > ANGLE_270) - left = true; - else - right = true; + if (player->mo->momx || player->mo->momy) + { + angle_t movevswp; + + moveangle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); + + movevswp = (moveangle - wpangle); + if (movevswp > ANGLE_180) + { + movevswp = InvAngle(movevswp); + } + + if (movevswp > ANGLE_45) + { + // Use facing direction when going the wrong way + moveangle = player->mo->angle; } - if (left || strafeleft) - cmd->sidemove -= MAXPLMOVE<>16; - if (right || straferight) - cmd->sidemove += MAXPLMOVE<>16; } - } else { - if (forward) - cmd->forwardmove += MAXPLMOVE<>16; - if (backward) - cmd->forwardmove -= MAXPLMOVE<>16; - if (left) - cmd->angleturn += 1280; - if (right) - cmd->angleturn -= 1280; - if (strafeleft) - cmd->sidemove -= MAXPLMOVE<>16; - if (straferight) - cmd->sidemove += MAXPLMOVE<>16; + else + { + // Default to facing direction + moveangle = player->mo->angle; + } + + angle = (moveangle - wpangle); + + if (angle < ANGLE_180) + { + turnsign = -1; // Turn right + anglediff = AngleFixed(angle)>>FRACBITS; + } + else + { + turnsign = 1; // Turn left + anglediff = 360-(AngleFixed(angle)>>FRACBITS); + } + + anglediff = abs(anglediff); + + if (anglediff > 90) + { + // Wrong way! + cmd->forwardmove = -25; + cmd->buttons |= BT_BRAKE; + } + else + { + fixed_t rad = player->nextwaypoint->mobj->radius - (player->mo->radius*2); + fixed_t dirdist = K_DistanceOfLineFromPoint( + player->mo->x, player->mo->y, + player->mo->x + FINECOSINE(moveangle >> ANGLETOFINESHIFT), player->mo->y + FINESINE(moveangle >> ANGLETOFINESHIFT), + player->nextwaypoint->mobj->x, player->nextwaypoint->mobj->y + ); + + if (player == &players[displayplayers[0]]) + CONS_Printf("perpendicular dist: %d\n", dirdist / FRACUNIT); + + cmd->buttons |= BT_ACCELERATE; + + // Full speed ahead! + cmd->forwardmove = 50; + + if (dirdist <= 3*rad/4) + { + if (dirdist < rad/4) + { + // Don't need to turn! + turnamt = 0; + } + else + { + // Make minor adjustments + turnamt /= 4; + } + } + else + { + // Actually, don't go too fast... + cmd->forwardmove /= 2; + cmd->buttons |= BT_BRAKE; + } + } + + if (turnamt != 0) + { + cmd->driftturn = KART_FULLTURN * turnsign; + cmd->angleturn += KART_FULLTURN * turnsign; + } } - if (jump) - cmd->buttons |= BT_DRIFT; - if (spin) - cmd->buttons |= BT_BRAKE; } -void B_MoveBlocked(player_t *player) -{ - (void)player; - blocked = true; -} - -boolean B_CheckRespawn(player_t *player) -{ - mobj_t *sonic = players[consoleplayer].mo; - mobj_t *tails = player->mo; - - // We can't follow Sonic if he's not around! - if (!sonic || sonic->health <= 0) - return false; - - // Check if Sonic is busy first. - // If he's doing any of these things, he probably doesn't want to see us. - if (sonic->player->pflags & (PF_ROPEHANG|PF_GLIDING|PF_CARRIED|PF_SLIDING|PF_ITEMHANG|PF_MACESPIN|PF_NIGHTSMODE) - || (sonic->player->panim != PA_IDLE && sonic->player->panim != PA_WALK)) - return false; - - // Low ceiling, do not want! - if (sonic->ceilingz - sonic->z < 2*sonic->height) - return false; - - // If you're dead, wait a few seconds to respawn. - if (player->playerstate == PST_DEAD) { - if (player->deadtimer > 4*TICRATE) - return true; - return false; - } - - // If you can't see Sonic, I guess we should? - if (!P_CheckSight(sonic, tails) && P_AproxDistance(P_AproxDistance(tails->x-sonic->x, tails->y-sonic->y), tails->z-sonic->z) > FixedMul(1024*FRACUNIT, tails->scale)) - return true; - return false; -} - -void B_RespawnBot(INT32 playernum) -{ - player_t *player = &players[playernum]; - fixed_t x,y,z; - mobj_t *sonic = players[consoleplayer].mo; - mobj_t *tails; - - if (!sonic || sonic->health <= 0) - return; - - P_SpawnPlayer(playernum); - tails = player->mo; - - x = sonic->x; - y = sonic->y; - if (sonic->eflags & MFE_VERTICALFLIP) { - tails->eflags |= MFE_VERTICALFLIP; - z = sonic->z - FixedMul(512*FRACUNIT,sonic->scale); - if (z < sonic->floorz) - z = sonic->floorz; - } else { - z = sonic->z + sonic->height + FixedMul(512*FRACUNIT,sonic->scale); - if (z > sonic->ceilingz - sonic->height) - z = sonic->ceilingz - sonic->height; - } - - if (sonic->flags2 & MF2_OBJECTFLIP) - tails->flags2 |= MF2_OBJECTFLIP; - if (sonic->flags2 & MF2_TWOD) - tails->flags2 |= MF2_TWOD; - if (sonic->eflags & MFE_UNDERWATER) - tails->eflags |= MFE_UNDERWATER; - player->powers[pw_underwater] = sonic->player->powers[pw_underwater]; - player->powers[pw_spacetime] = sonic->player->powers[pw_spacetime]; - player->powers[pw_gravityboots] = sonic->player->powers[pw_gravityboots]; - player->powers[pw_nocontrol] = sonic->player->powers[pw_nocontrol]; - - P_TeleportMove(tails, x, y, z); - P_SetPlayerMobjState(tails, S_KART_STND1); // SRB2kart - was S_PLAY_FALL1 - P_SetScale(tails, sonic->scale); - tails->destscale = sonic->destscale; -} diff --git a/src/k_bot.h b/src/k_bot.h index 9b34194de..b4c703aa7 100644 --- a/src/k_bot.h +++ b/src/k_bot.h @@ -11,8 +11,6 @@ /// \brief Basic bot handling void K_AddBots(UINT8 numbots); -void B_BuildTiccmd(player_t *player, ticcmd_t *cmd); -void B_KeysToTiccmd(mobj_t *mo, ticcmd_t *cmd, boolean forward, boolean backward, boolean left, boolean right, boolean strafeleft, boolean straferight, boolean jump, boolean spin); -boolean B_CheckRespawn(player_t *player); -void B_MoveBlocked(player_t *player); -void B_RespawnBot(INT32 playernum); +boolean K_PlayerUsesBotMovement(player_t *player); +fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, fixed_t v2y, fixed_t cx, fixed_t cy); +void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd); diff --git a/src/k_kart.c b/src/k_kart.c index 1cf0eb365..d60a2c61e 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -27,6 +27,7 @@ #include "lua_hook.h" // For MobjDamage and ShouldDamage #include "k_waypoint.h" +#include "k_bot.h" // SOME IMPORTANT VARIABLES DEFINED IN DOOMDEF.H: // gamespeed is cc (0 for easy, 1 for normal, 2 for hard) @@ -6402,7 +6403,8 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) } } - if ((waypoint->prevwaypoints != NULL) && (waypoint->numprevwaypoints > 0U)) + if ((waypoint->prevwaypoints != NULL) && (waypoint->numprevwaypoints > 0U) + && !K_PlayerUsesBotMovement(player)) { for (i = 0U; i < waypoint->numprevwaypoints; i++) { @@ -6557,70 +6559,67 @@ static void K_UpdateDistanceFromFinishLine(player_t *const player) { if ((player != NULL) && (player->mo != NULL)) { + waypoint_t *finishline = K_GetFinishLineWaypoint(); + waypoint_t *nextwaypoint = K_GetPlayerNextWaypoint(player); + + if (nextwaypoint != NULL) + { + // If nextwaypoint is NULL, it means we don't want to update the waypoint until we touch another one. + // player->nextwaypoint will keep its previous value in this case. + player->nextwaypoint = nextwaypoint; + } + + // nextwaypoint is now the waypoint that is in front of us if (player->exiting) { - player->nextwaypoint = K_GetFinishLineWaypoint(); + // Player has finished, we don't need to calculate distance player->distancetofinish = 0U; } - else + else if ((player->nextwaypoint != NULL) && (finishline != NULL)) { - waypoint_t *finishline = K_GetFinishLineWaypoint(); - waypoint_t *nextwaypoint = K_GetPlayerNextWaypoint(player); + const boolean useshortcuts = false; + const boolean huntbackwards = false; + boolean pathfindsuccess = false; + path_t pathtofinish = {}; - if (nextwaypoint != NULL) + pathfindsuccess = + K_PathfindToWaypoint(player->nextwaypoint, finishline, &pathtofinish, useshortcuts, huntbackwards); + + // Update the player's distance to the finish line if a path was found. + // Using shortcuts won't find a path, so distance won't be updated until the player gets back on track + if (pathfindsuccess == true) { - // If nextwaypoint is NULL, it means we don't want to update the waypoint until we touch another one. - // player->nextwaypoint will keep its previous value in this case. - player->nextwaypoint = nextwaypoint; - } + // Add euclidean distance to the next waypoint to the distancetofinish + UINT32 adddist; + fixed_t disttowaypoint = + P_AproxDistance( + (player->mo->x >> FRACBITS) - (player->nextwaypoint->mobj->x >> FRACBITS), + (player->mo->y >> FRACBITS) - (player->nextwaypoint->mobj->y >> FRACBITS)); + disttowaypoint = P_AproxDistance(disttowaypoint, (player->mo->z >> FRACBITS) - (player->nextwaypoint->mobj->z >> FRACBITS)); - // nextwaypoint is now the waypoint that is in front of us - if ((player->nextwaypoint != NULL) && (finishline != NULL)) - { - const boolean useshortcuts = false; - const boolean huntbackwards = false; - boolean pathfindsuccess = false; - path_t pathtofinish = {}; + adddist = (UINT32)disttowaypoint; - pathfindsuccess = - K_PathfindToWaypoint(player->nextwaypoint, finishline, &pathtofinish, useshortcuts, huntbackwards); + player->distancetofinish = pathtofinish.totaldist + adddist; + Z_Free(pathtofinish.array); - // Update the player's distance to the finish line if a path was found. - // Using shortcuts won't find a path, so distance won't be updated until the player gets back on track - if (pathfindsuccess == true) + // distancetofinish is currently a flat distance to the finish line, but in order to be fully + // correct we need to add to it the length of the entire circuit multiplied by the number of laps + // left after this one. This will give us the total distance to the finish line, and allow item + // distance calculation to work easily + if ((mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE) == 0U) { - // Add euclidean distance to the next waypoint to the distancetofinish - UINT32 adddist; - fixed_t disttowaypoint = - P_AproxDistance( - (player->mo->x >> FRACBITS) - (player->nextwaypoint->mobj->x >> FRACBITS), - (player->mo->y >> FRACBITS) - (player->nextwaypoint->mobj->y >> FRACBITS)); - disttowaypoint = P_AproxDistance(disttowaypoint, (player->mo->z >> FRACBITS) - (player->nextwaypoint->mobj->z >> FRACBITS)); + const UINT8 numfulllapsleft = ((UINT8)cv_numlaps.value - player->laps); - adddist = (UINT32)disttowaypoint; + player->distancetofinish += numfulllapsleft * K_GetCircuitLength(); - player->distancetofinish = pathtofinish.totaldist + adddist; - Z_Free(pathtofinish.array); - - // distancetofinish is currently a flat distance to the finish line, but in order to be fully - // correct we need to add to it the length of the entire circuit multiplied by the number of laps - // left after this one. This will give us the total distance to the finish line, and allow item - // distance calculation to work easily - if ((mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE) == 0U) + // An additional HACK, to fix looking backwards towards the finish line + // If the player's next waypoint is the finishline and the angle distance from player to + // connectin waypoints implies they're closer to a next waypoint, add a full track distance + if (player->nextwaypoint == finishline) { - const UINT8 numfulllapsleft = ((UINT8)cv_numlaps.value - player->laps); - - player->distancetofinish += numfulllapsleft * K_GetCircuitLength(); - - // An additional HACK, to fix looking backwards towards the finish line - // If the player's next waypoint is the finishline and the angle distance from player to - // connectin waypoints implies they're closer to a next waypoint, add a full track distance - if (player->nextwaypoint == finishline) + if (K_PlayerCloserToNextWaypoints(player->nextwaypoint, player) == true) { - if (K_PlayerCloserToNextWaypoints(player->nextwaypoint, player) == true) - { - player->distancetofinish += K_GetCircuitLength(); - } + player->distancetofinish += K_GetCircuitLength(); } } } diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c index 93ce13103..e5c9c06aa 100644 --- a/src/lua_hooklib.c +++ b/src/lua_hooklib.c @@ -945,6 +945,12 @@ boolean LUAh_PlayerCmd(player_t *player, ticcmd_t *cmd) // Hook for B_BuildTailsTiccmd by skin name boolean LUAh_BotAI(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd) { +#if 1 + (void)sonic; + (void)tails; + (void)cmd; + return false; +#else hook_p hookp; boolean hooked = false; if (!gL || !(hooksAvailable[hook_BotAI/8] & (1<<(hook_BotAI%8)))) @@ -1001,6 +1007,7 @@ boolean LUAh_BotAI(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd) lua_settop(gL, 0); return hooked; +#endif } // Hook for linedef executors diff --git a/src/p_inter.c b/src/p_inter.c index 6ce32f922..0046ce3fe 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1978,7 +1978,7 @@ boolean P_CheckRacers(void) // Check if all the players in the race have finished. If so, end the level. for (i = 0; i < MAXPLAYERS; i++) { - if (!playeringame[i] || players[i].spectator || players[i].exiting || !players[i].lives) + if (!playeringame[i] || players[i].spectator || players[i].exiting || players[i].bot || !players[i].lives) continue; break; diff --git a/src/p_user.c b/src/p_user.c index 77860e98b..6e1b826b0 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8023,18 +8023,6 @@ void P_PlayerThink(player_t *player) player->playerstate = PST_DEAD; } - if (player->bot) - { - if (player->playerstate == PST_LIVE || player->playerstate == PST_DEAD) - { - if (B_CheckRespawn(player)) - player->playerstate = PST_REBORN; - } - - if (player->playerstate == PST_REBORN) - return; - } - #ifdef SEENAMES if (netgame && player == &players[displayplayers[0]] && !(leveltime % (TICRATE/5)) && !r_splitscreen) { From 15bda7da2dafdcb06c17d334284110927ef8faf3 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sat, 28 Mar 2020 14:57:49 -0400 Subject: [PATCH 03/85] Use K_MomentumToFacing to give bots a bit better traction --- src/k_bot.c | 35 ++++++----------------------------- src/k_bot.h | 1 - src/p_user.c | 18 ++++++------------ 3 files changed, 12 insertions(+), 42 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index 577c9d260..ee910bd59 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -80,7 +80,7 @@ boolean K_PlayerUsesBotMovement(player_t *player) return false; } -fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, fixed_t v2y, fixed_t cx, fixed_t cy) +static fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, fixed_t v2y, fixed_t cx, fixed_t cy) { fixed_t v1toc[2] = {cx - v1x, cy - v1y}; fixed_t v1tov2[2] = {v2x - v1x, v2y - v1y}; @@ -121,30 +121,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) INT16 anglediff; wpangle = R_PointToAngle2(player->mo->x, player->mo->y, player->nextwaypoint->mobj->x, player->nextwaypoint->mobj->y); - - if (player->mo->momx || player->mo->momy) - { - angle_t movevswp; - - moveangle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); - - movevswp = (moveangle - wpangle); - if (movevswp > ANGLE_180) - { - movevswp = InvAngle(movevswp); - } - - if (movevswp > ANGLE_45) - { - // Use facing direction when going the wrong way - moveangle = player->mo->angle; - } - } - else - { - // Default to facing direction - moveangle = player->mo->angle; - } + moveangle = player->mo->angle; angle = (moveangle - wpangle); @@ -184,9 +161,9 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) // Full speed ahead! cmd->forwardmove = 50; - if (dirdist <= 3*rad/4) + if (dirdist <= rad) { - if (dirdist < rad/4) + if (dirdist < rad/2) { // Don't need to turn! turnamt = 0; @@ -197,12 +174,12 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) turnamt /= 4; } } - else + /*else { // Actually, don't go too fast... cmd->forwardmove /= 2; cmd->buttons |= BT_BRAKE; - } + }*/ } if (turnamt != 0) diff --git a/src/k_bot.h b/src/k_bot.h index b4c703aa7..a9353f6e2 100644 --- a/src/k_bot.h +++ b/src/k_bot.h @@ -12,5 +12,4 @@ void K_AddBots(UINT8 numbots); boolean K_PlayerUsesBotMovement(player_t *player); -fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, fixed_t v2y, fixed_t cx, fixed_t cy); void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd); diff --git a/src/p_user.c b/src/p_user.c index 6e1b826b0..e9459fb0a 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -4075,9 +4075,8 @@ static void P_3dMovement(player_t *player) player->aiming = cmd->aiming<exiting || mapreset) || (P_PlayerInPain(player) && !onground))) + if (!(P_PlayerInPain(player) && !onground)) { - //movepushforward = cmd->forwardmove * (thrustfactor * acceleration); movepushforward = K_3dKartMovement(player, onground, cmd->forwardmove); if (player->mo->movefactor != FRACUNIT) // Friction-scaled acceleration... @@ -4095,12 +4094,8 @@ static void P_3dMovement(player_t *player) movepushforward = 0; } -#ifdef ESLOPE totalthrust.x += P_ReturnThrustX(player->mo, movepushangle, movepushforward); totalthrust.y += P_ReturnThrustY(player->mo, movepushangle, movepushforward); -#else - P_Thrust(player->mo, movepushangle, movepushforward); -#endif } else if (!(player->kartstuff[k_spinouttimer])) { @@ -4115,15 +4110,10 @@ static void P_3dMovement(player_t *player) else movepushside = (cmd->sidemove * FRACUNIT/128) - FixedDiv(player->speed, K_GetKartSpeed(player, true)); -#ifdef ESLOPE totalthrust.x += P_ReturnThrustX(player->mo, movepushsideangle, movepushside); totalthrust.y += P_ReturnThrustY(player->mo, movepushsideangle, movepushside); -#else - P_Thrust(player->mo, movepushsideangle, movepushside); -#endif } -#ifdef ESLOPE if ((totalthrust.x || totalthrust.y) && player->mo->standingslope && (!(player->mo->standingslope->flags & SL_NOPHYSICS)) && abs(player->mo->standingslope->zdelta) > FRACUNIT/2) { // Factor thrust to slope, but only for the part pushing up it! @@ -4141,6 +4131,11 @@ static void P_3dMovement(player_t *player) } } + if (K_PlayerUsesBotMovement(player)) + { + K_MomentumToFacing(player); + } + player->mo->momx += totalthrust.x; player->mo->momy += totalthrust.y; @@ -4155,7 +4150,6 @@ static void P_3dMovement(player_t *player) player->mo->momy = FixedMul(FixedDiv(player->mo->momy, speed), newspeed); } } -#endif // Time to ask three questions: // 1) Are we over topspeed? From 2e4b1c6ecbb76b9509de0b0cdcf955a8e80a6804 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sun, 29 Mar 2020 17:58:17 -0400 Subject: [PATCH 04/85] Help reduce waypoint flickering by using closest waypoint to finish line --- src/k_waypoint.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/k_waypoint.c b/src/k_waypoint.c index f77e6d62f..91cb9a1b1 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -270,9 +270,12 @@ waypoint_t *K_GetBestWaypointForMobj(mobj_t *const mobj) waypoint_t *checkwaypoint = NULL; fixed_t closestdist = INT32_MAX; fixed_t checkdist = INT32_MAX; + fixed_t bestfindist = INT32_MAX; for (i = 0; i < numwaypoints; i++) { + fixed_t rad; + checkwaypoint = &waypointheap[i]; checkdist = P_AproxDistance( @@ -280,7 +283,34 @@ waypoint_t *K_GetBestWaypointForMobj(mobj_t *const mobj) (mobj->y / FRACUNIT) - (checkwaypoint->mobj->y / FRACUNIT)); checkdist = P_AproxDistance(checkdist, ((mobj->z / FRACUNIT) - (checkwaypoint->mobj->z / FRACUNIT)) * 4); - if (checkdist < closestdist) + rad = (checkwaypoint->mobj->radius / FRACUNIT); + + if (closestdist < rad && checkdist < rad && finishline != NULL) + { + const boolean useshortcuts = false; + const boolean huntbackwards = false; + boolean pathfindsuccess = false; + path_t pathtofinish = {}; + + // If the mobj is touching multiple waypoints at once, + // then solve ties by taking the one closest to the finish line. + // Prevents position from flickering wildly when taking turns. + + pathfindsuccess = + K_PathfindToWaypoint(checkwaypoint, finishline, &pathtofinish, useshortcuts, huntbackwards); + + if (pathfindsuccess == true) + { + if ((INT32)(pathtofinish.totaldist) < bestfindist) + { + bestwaypoint = checkwaypoint; + bestfindist = pathtofinish.totaldist; + } + + Z_Free(pathtofinish.array); + } + } + else if (checkdist < closestdist && bestfindist == INT32_MAX) { if (!P_CheckSight(mobj, checkwaypoint->mobj)) { From 0ef12d3ccec6433d0da06d6826cee3fd7d390f4d Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sun, 29 Mar 2020 17:59:26 -0400 Subject: [PATCH 05/85] Properly use bot movement when exiting --- src/k_kart.c | 14 ++++++++------ src/p_user.c | 4 +--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index d60a2c61e..76c532db1 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2674,6 +2674,9 @@ fixed_t K_3dKartMovement(player_t *player, boolean onground, fixed_t forwardmove // 0 with no gas, and // -25 when only braking. + if (EITHERSNEAKER(player)) + forwardmove = 50; + finalspeed *= forwardmove/25; finalspeed /= 2; @@ -5459,7 +5462,7 @@ static void K_UpdateEngineSounds(player_t *player, ticcmd_t *cmd) class = s+(3*w); // Silence the engines - if (leveltime < 8 || player->spectator || player->exiting) + if (leveltime < 8 || player->spectator) { player->karthud[khud_enginesnd] = 0; // Reset sound number return; @@ -6184,7 +6187,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) player->kartstuff[k_bubblecool] = 0; } - if (player->kartstuff[k_itemtype] != KITEM_FLAMESHIELD || player->exiting) + if (player->kartstuff[k_itemtype] != KITEM_FLAMESHIELD) { if (player->kartstuff[k_flamedash]) K_FlameDashLeftoverSmoke(player->mo); @@ -6403,8 +6406,7 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) } } - if ((waypoint->prevwaypoints != NULL) && (waypoint->numprevwaypoints > 0U) - && !K_PlayerUsesBotMovement(player)) + if ((waypoint->prevwaypoints != NULL) && (waypoint->numprevwaypoints > 0U)) { for (i = 0U; i < waypoint->numprevwaypoints; i++) { @@ -6572,7 +6574,7 @@ static void K_UpdateDistanceFromFinishLine(player_t *const player) // nextwaypoint is now the waypoint that is in front of us if (player->exiting) { - // Player has finished, we don't need to calculate distance + // Player has finished, we don't need to calculate this player->distancetofinish = 0U; } else if ((player->nextwaypoint != NULL) && (finishline != NULL)) @@ -7141,7 +7143,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) else if (cmd->buttons & BT_ATTACK) player->pflags |= PF_ATTACKDOWN; - if (player && player->mo && player->mo->health > 0 && !player->spectator && !(player->exiting || mapreset) && leveltime > starttime + if (player && player->mo && player->mo->health > 0 && !player->spectator && !mapreset && leveltime > starttime && player->kartstuff[k_spinouttimer] == 0 && player->kartstuff[k_squishedtimer] == 0 && player->kartstuff[k_respawn] == 0) { // First, the really specific, finicky items that function without the item being directly in your item slot. diff --git a/src/p_user.c b/src/p_user.c index e9459fb0a..b21b7a322 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -3993,11 +3993,9 @@ static void P_3dMovement(player_t *player) cmd = &player->cmd; - if ((player->exiting || mapreset) || player->pflags & PF_STASIS || player->kartstuff[k_spinouttimer]) // pw_introcam? + if (player->pflags & PF_STASIS || player->kartstuff[k_spinouttimer]) // pw_introcam? { cmd->forwardmove = cmd->sidemove = 0; - if (EITHERSNEAKER(player)) - cmd->forwardmove = 50; } if (!(player->pflags & PF_FORCESTRAFE) && !player->kartstuff[k_pogospring]) From 102aca3ab4a9288829863af4092bceff297c3d61 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sun, 29 Mar 2020 18:00:35 -0400 Subject: [PATCH 06/85] Bots have a dumb path prediction --- src/k_bot.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++------ src/k_bot.h | 10 +++++- 2 files changed, 99 insertions(+), 11 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index ee910bd59..c36ff832b 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -20,8 +20,9 @@ #include "byteptr.h" #include "d_net.h" // nodetoplayer #include "k_kart.h" +#include "z_zone.h" -void K_AddBots(UINT8 numbots) +void K_AddBots(SINT8 numbots) { UINT8 newplayernum = 0; @@ -60,7 +61,13 @@ void K_AddBots(UINT8 numbots) else if (numbots == 5) WRITEUINT8(buf_p, 1); else if (numbots == 4) - WRITEUINT8(buf_p, 9); + WRITEUINT8(buf_p, 2); + else if (numbots == 3) + WRITEUINT8(buf_p, 3); + else if (numbots == 2) + WRITEUINT8(buf_p, 5); + else if (numbots == 1) + WRITEUINT8(buf_p, 7); else WRITEUINT8(buf_p, 10); @@ -95,6 +102,72 @@ static fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, return P_AproxDistance(cx - px, cy - py); } +static botprediction_t *K_CreateBotPrediction(player_t *player) +{ + const INT32 distance = ((256 * player->mo->scale) + (player->speed * 16)) / FRACUNIT; + INT32 distanceleft = distance; + botprediction_t *predictcoords = Z_Calloc(sizeof(botprediction_t), PU_LEVEL, NULL); + waypoint_t *wp = player->nextwaypoint; + size_t nwp; + size_t i; + INT32 lp = 0; + + while (distanceleft > 0) + { + lp++; + + nwp = 0; + + if (wp->numnextwaypoints == 0) + { + distanceleft = 0; + break; + } + + if (wp->numnextwaypoints > 1) + { + fixed_t closest = INT32_MAX; + fixed_t dist = INT32_MAX; + + for (i = 0; i < wp->numnextwaypoints; i++) + { + dist = P_AproxDistance( + player->mo->x - wp->nextwaypoints[i]->mobj->x, + player->mo->y - wp->nextwaypoints[i]->mobj->y + ); + + if (dist < closest) + { + nwp = i; + closest = dist; + } + } + } + + if ((INT32)(wp->nextwaypointdistances[nwp]) > distanceleft) + { + break; + } + + wp = wp->nextwaypoints[nwp]; + distanceleft -= wp->nextwaypointdistances[nwp]; + } + + predictcoords->x = wp->mobj->x; + predictcoords->y = wp->mobj->y; + predictcoords->radius = wp->mobj->radius; + + if (distanceleft > 0) + { + angle_t a = R_PointToAngle2(wp->mobj->x, wp->mobj->y, wp->nextwaypoints[nwp]->mobj->x, wp->nextwaypoints[nwp]->mobj->y); + + predictcoords->x += P_ReturnThrustX(NULL, a, distanceleft * FRACUNIT); + predictcoords->y += P_ReturnThrustY(NULL, a, distanceleft * FRACUNIT); + } + + return predictcoords; +} + void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) { // Can't build a ticcmd if we aren't spawned... @@ -113,17 +186,25 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) return; #endif + if (leveltime <= starttime) + { + if (leveltime >= starttime-50) + cmd->buttons |= BT_ACCELERATE; + return; + } + if (player->nextwaypoint != NULL && player->nextwaypoint->mobj != NULL && !P_MobjWasRemoved(player->nextwaypoint->mobj)) { + botprediction_t *predict = K_CreateBotPrediction(player); INT16 turnamt = KART_FULLTURN; SINT8 turnsign = 0; - angle_t wpangle, moveangle, angle; + angle_t destangle, moveangle, angle; INT16 anglediff; - wpangle = R_PointToAngle2(player->mo->x, player->mo->y, player->nextwaypoint->mobj->x, player->nextwaypoint->mobj->y); + destangle = R_PointToAngle2(player->mo->x, player->mo->y, predict->x, predict->y); moveangle = player->mo->angle; - angle = (moveangle - wpangle); + angle = (moveangle - destangle); if (angle < ANGLE_180) { @@ -146,16 +227,13 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } else { - fixed_t rad = player->nextwaypoint->mobj->radius - (player->mo->radius*2); + fixed_t rad = predict->radius - (player->mo->radius*2); fixed_t dirdist = K_DistanceOfLineFromPoint( player->mo->x, player->mo->y, player->mo->x + FINECOSINE(moveangle >> ANGLETOFINESHIFT), player->mo->y + FINESINE(moveangle >> ANGLETOFINESHIFT), - player->nextwaypoint->mobj->x, player->nextwaypoint->mobj->y + predict->x, predict->y ); - if (player == &players[displayplayers[0]]) - CONS_Printf("perpendicular dist: %d\n", dirdist / FRACUNIT); - cmd->buttons |= BT_ACCELERATE; // Full speed ahead! @@ -187,6 +265,8 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) cmd->driftturn = KART_FULLTURN * turnsign; cmd->angleturn += KART_FULLTURN * turnsign; } + + Z_Free(predict); } } diff --git a/src/k_bot.h b/src/k_bot.h index a9353f6e2..c8098b86e 100644 --- a/src/k_bot.h +++ b/src/k_bot.h @@ -10,6 +10,14 @@ /// \file b_bot.h /// \brief Basic bot handling -void K_AddBots(UINT8 numbots); +#include "k_waypoint.h" + +// Path that bot will attempt to take +typedef struct botprediction_s { + fixed_t x, y; + fixed_t radius; +} botprediction_t; + +void K_AddBots(SINT8 numbots); boolean K_PlayerUsesBotMovement(player_t *player); void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd); From 774c85d660c13cae60a3106e758bb407be50edbd Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sun, 29 Mar 2020 19:56:05 -0400 Subject: [PATCH 07/85] Rudimentary item usage code --- src/d_player.h | 2 ++ src/dehacked.c | 4 +++- src/k_bot.c | 36 +++++++++++++++++++++++++++++++++++- src/k_kart.c | 49 +++++++++++++++++++++++++++++++++++++++---------- 4 files changed, 79 insertions(+), 12 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index 2465b90ef..57cbca3cc 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -347,6 +347,8 @@ typedef enum k_killfield, // How long have you been in the kill field, stay in too long and lose a bumper k_wrongway, // Display WRONG WAY on screen + k_botitemdelay, + NUMKARTSTUFF } kartstufftype_t; diff --git a/src/dehacked.c b/src/dehacked.c index 247101a80..b822ca4d1 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -8594,7 +8594,9 @@ static const char *const KARTSTUFF_LIST[] = { "SPRINGSTARS", "SPRINGCOLOR", "KILLFIELD", - "WRONGWAY" + "WRONGWAY", + + "BOTITEMDELAY" }; #endif diff --git a/src/k_bot.c b/src/k_bot.c index c36ff832b..2c9956d46 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -21,6 +21,7 @@ #include "d_net.h" // nodetoplayer #include "k_kart.h" #include "z_zone.h" +#include "i_system.h" void K_AddBots(SINT8 numbots) { @@ -174,6 +175,10 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if (!player->mo) return; + cmd->forwardmove = 0; + cmd->driftturn = 0; + cmd->buttons = 0; + if (player->playerstate == PST_DEAD) { cmd->buttons |= BT_ACCELERATE; @@ -188,7 +193,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if (leveltime <= starttime) { - if (leveltime >= starttime-50) + if (leveltime >= starttime-35) cmd->buttons |= BT_ACCELERATE; return; } @@ -268,5 +273,34 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) Z_Free(predict); } + + if (player->kartstuff[k_userings] == 1 && !player->exiting) + { + if (player->kartstuff[k_rings] > 10) + cmd->buttons |= BT_ATTACK; + } + else + { + if (player->kartstuff[k_botitemdelay]) + return; + + switch (player->kartstuff[k_itemtype]) + { + case KITEM_SNEAKER: + if (player->kartstuff[k_offroad] || K_GetWaypointIsShortcut(player->nextwaypoint) == true) + cmd->buttons |= BT_ATTACK; + break; + case KITEM_INVINCIBILITY: + case KITEM_SPB: + case KITEM_GROW: + case KITEM_SHRINK: + case KITEM_HYUDORO: + case KITEM_SUPERRING: + cmd->buttons |= BT_ATTACK; + break; + default: + break; + } + } } diff --git a/src/k_kart.c b/src/k_kart.c index 76c532db1..1ce211447 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -762,6 +762,8 @@ static void K_KartGetItemResult(player_t *player, SINT8 getitem) if (getitem == KITEM_HYUDORO) // Hyudoro cooldown hyubgone = 5*TICRATE; + player->kartstuff[k_botitemdelay] = TICRATE; + switch (getitem) { // Special roulettes first, then the generic ones are handled by default @@ -810,7 +812,7 @@ static void K_KartGetItemResult(player_t *player, SINT8 getitem) \return void */ -static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean spbrush) +static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean spbrush, boolean bot) { INT32 newodds; INT32 i; @@ -896,6 +898,27 @@ static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean sp #define COOLDOWNONSTART (leveltime < (30*TICRATE)+starttime) + if (bot) + { + // TODO: Item use on bots should all be passed-in functions. + // Instead of manually inserting these, it should return 0 + // for any items without an item use function supplied + + switch (item) + { + case KITEM_SNEAKER: + case KITEM_INVINCIBILITY: + case KITEM_SPB: + case KITEM_GROW: + case KITEM_SHRINK: + case KITEM_HYUDORO: + case KITEM_SUPERRING: + break; + default: + return 0; + } + } + switch (item) { case KITEM_ROCKETSNEAKER: @@ -987,7 +1010,7 @@ static UINT8 K_FindUseodds(player_t *player, fixed_t mashed, UINT32 pdis, UINT8 for (j = 1; j < NUMKARTRESULTS; j++) { - if (K_KartGetItemOdds(i, j, mashed, spbrush) > 0) + if (K_KartGetItemOdds(i, j, mashed, spbrush, player->bot) > 0) { available = true; break; @@ -1275,7 +1298,7 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) useodds = K_FindUseodds(player, mashed, pdis, bestbumper, spbrush); for (i = 1; i < NUMKARTRESULTS; i++) - spawnchance[i] = (totalspawnchance += K_KartGetItemOdds(useodds, i, mashed, spbrush)); + spawnchance[i] = (totalspawnchance += K_KartGetItemOdds(useodds, i, mashed, spbrush, player->bot)); // Award the player whatever power is rolled if (totalspawnchance > 0) @@ -2513,6 +2536,13 @@ void K_MomentumToFacing(player_t *player) player->mo->momy = FixedMul(player->mo->momy - player->cmomy, player->mo->friction) + player->cmomy; } +static boolean K_ApplyOffroad(player_t *player) +{ + if (player->kartstuff[k_invincibilitytimer] || player->kartstuff[k_hyudorotimer] || EITHERSNEAKER(player)) + return false; + return true; +} + // sets k_boostpower, k_speedboost, and k_accelboost to whatever we need it to be static void K_GetKartBoostPower(player_t *player) { @@ -2527,8 +2557,7 @@ static void K_GetKartBoostPower(player_t *player) } // Offroad is separate, it's difficult to factor it in with a variable value anyway. - if (!(player->kartstuff[k_invincibilitytimer] || player->kartstuff[k_hyudorotimer] || EITHERSNEAKER(player)) - && player->kartstuff[k_offroad] >= 0) + if (K_ApplyOffroad(player) && player->kartstuff[k_offroad] >= 0) boostpower = FixedDiv(boostpower, FixedMul(player->kartstuff[k_offroad], K_GetKartGameSpeedScalar(gamespeed)) + FRACUNIT); if (player->kartstuff[k_bananadrag] > TICRATE) @@ -5985,6 +6014,9 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) player->powers[pw_flashing]--; } + if (player->kartstuff[k_botitemdelay]) + player->kartstuff[k_botitemdelay]--; + if (player->kartstuff[k_spinouttimer]) { if ((P_IsObjectOnGround(player->mo) @@ -6866,10 +6898,7 @@ static void K_KartDrift(player_t *player, boolean onground) // Disable drift-sparks until you're going fast enough if (player->kartstuff[k_getsparks] == 0 - || (player->kartstuff[k_offroad] - && !player->kartstuff[k_invincibilitytimer] - && !player->kartstuff[k_hyudorotimer] - && !EITHERSNEAKER(player))) + || (player->kartstuff[k_offroad] && K_ApplyOffroad(player))) driftadditive = 0; // Inbetween minspeed and minspeed*2, it'll keep your previous drift-spark state. @@ -11056,7 +11085,7 @@ static void K_drawDistributionDebugger(void) for (i = 1; i < NUMKARTRESULTS; i++) { - const INT32 itemodds = K_KartGetItemOdds(useodds, i, 0, spbrush); + const INT32 itemodds = K_KartGetItemOdds(useodds, i, 0, spbrush, stplyr->bot); if (itemodds <= 0) continue; From fb550f2868a8be280e1b31e197a6598e2e4ddf0f Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Mon, 30 Mar 2020 17:17:20 -0400 Subject: [PATCH 08/85] Slightly smarter sneaker usage - Use sneaker if held for too long without a proper use - Use sneaker if losing too much speed - Use sneaker if you have another boost (tether, boosters) - Add triple sneaker & rocket sneaker support - Don't go towards shortcut waypoints without a shortcut item - Bots get naturally better handling --- src/d_player.h | 1 + src/dehacked.c | 3 +- src/k_bot.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++---- src/k_bot.h | 1 + src/k_kart.c | 25 ++++++++++++++--- src/k_kart.h | 1 + 6 files changed, 96 insertions(+), 11 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index 57cbca3cc..24c692862 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -348,6 +348,7 @@ typedef enum k_wrongway, // Display WRONG WAY on screen k_botitemdelay, + k_botitemconfirm, NUMKARTSTUFF } kartstufftype_t; diff --git a/src/dehacked.c b/src/dehacked.c index 2bbe6797a..8b957c0bb 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -8607,7 +8607,8 @@ static const char *const KARTSTUFF_LIST[] = { "KILLFIELD", "WRONGWAY", - "BOTITEMDELAY" + "BOTITEMDELAY", + "BOTITEMCONFIRM" }; #endif diff --git a/src/k_bot.c b/src/k_bot.c index 2c9956d46..968ec09b2 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -68,7 +68,7 @@ void K_AddBots(SINT8 numbots) else if (numbots == 2) WRITEUINT8(buf_p, 5); else if (numbots == 1) - WRITEUINT8(buf_p, 7); + WRITEUINT8(buf_p, 9); else WRITEUINT8(buf_p, 10); @@ -88,6 +88,17 @@ boolean K_PlayerUsesBotMovement(player_t *player) return false; } +boolean K_BotCanTakeCut(player_t *player) +{ + if (!K_ApplyOffroad(player) + || player->kartstuff[k_itemtype] == KITEM_SNEAKER + || player->kartstuff[k_itemtype] == KITEM_INVINCIBILITY + || player->kartstuff[k_itemtype] == KITEM_HYUDORO) + return true; + + return false; +} + static fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, fixed_t v2y, fixed_t cx, fixed_t cy) { fixed_t v1toc[2] = {cx - v1x, cy - v1y}; @@ -105,7 +116,7 @@ static fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, static botprediction_t *K_CreateBotPrediction(player_t *player) { - const INT32 distance = ((256 * player->mo->scale) + (player->speed * 16)) / FRACUNIT; + const INT32 distance = (player->speed / FRACUNIT) * TICRATE; INT32 distanceleft = distance; botprediction_t *predictcoords = Z_Calloc(sizeof(botprediction_t), PU_LEVEL, NULL); waypoint_t *wp = player->nextwaypoint; @@ -113,6 +124,14 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) size_t i; INT32 lp = 0; + if (distance <= 0) + { + predictcoords->x = wp->mobj->x; + predictcoords->y = wp->mobj->y; + predictcoords->radius = wp->mobj->radius; + return predictcoords; + } + while (distanceleft > 0) { lp++; @@ -132,6 +151,11 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) for (i = 0; i < wp->numnextwaypoints; i++) { + if (K_GetWaypointIsShortcut(wp->nextwaypoints[i]) && !K_BotCanTakeCut(player)) + { + continue; + } + dist = P_AproxDistance( player->mo->x - wp->nextwaypoints[i]->mobj->x, player->mo->y - wp->nextwaypoints[i]->mobj->y @@ -246,7 +270,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if (dirdist <= rad) { - if (dirdist < rad/2) + if (dirdist < 2*rad/3) { // Don't need to turn! turnamt = 0; @@ -257,12 +281,12 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) turnamt /= 4; } } - /*else + else if (anglediff > 45) { // Actually, don't go too fast... cmd->forwardmove /= 2; cmd->buttons |= BT_BRAKE; - }*/ + } } if (turnamt != 0) @@ -282,13 +306,51 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) else { if (player->kartstuff[k_botitemdelay]) + { + player->kartstuff[k_botitemdelay]--; + player->kartstuff[k_botitemconfirm] = 0; return; + } switch (player->kartstuff[k_itemtype]) { case KITEM_SNEAKER: - if (player->kartstuff[k_offroad] || K_GetWaypointIsShortcut(player->nextwaypoint) == true) + if ((player->kartstuff[k_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)/2 // Being slowed down too much + || player->kartstuff[k_speedboost] > 0 // Have another type of boost (tethering) + || player->kartstuff[k_botitemconfirm] > 4*TICRATE) // Held onto it for too long + { cmd->buttons |= BT_ATTACK; + player->kartstuff[k_botitemconfirm] -= TICRATE; + } + else + { + player->kartstuff[k_botitemconfirm]++; + } + break; + case KITEM_ROCKETSNEAKER: + if (player->kartstuff[k_rocketsneakertimer] <= 0) + { + cmd->buttons |= BT_ATTACK; + player->kartstuff[k_botitemconfirm] = 0; + } + else + { + if ((player->kartstuff[k_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)/2 // Being slowed down too much + || player->kartstuff[k_speedboost] > 0 // Have another type of boost (tethering) + || player->kartstuff[k_botitemconfirm] > TICRATE) // Held onto it for too long + { + cmd->buttons |= BT_ATTACK; + player->kartstuff[k_botitemconfirm] -= TICRATE/2; + } + else + { + player->kartstuff[k_botitemconfirm]++; + } + } break; case KITEM_INVINCIBILITY: case KITEM_SPB: @@ -297,8 +359,10 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) case KITEM_HYUDORO: case KITEM_SUPERRING: cmd->buttons |= BT_ATTACK; + player->kartstuff[k_botitemconfirm] = 0; break; default: + player->kartstuff[k_botitemconfirm] = 0; break; } } diff --git a/src/k_bot.h b/src/k_bot.h index c8098b86e..071a4fb3e 100644 --- a/src/k_bot.h +++ b/src/k_bot.h @@ -20,4 +20,5 @@ typedef struct botprediction_s { void K_AddBots(SINT8 numbots); boolean K_PlayerUsesBotMovement(player_t *player); +boolean K_BotCanTakeCut(player_t *player); void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd); diff --git a/src/k_kart.c b/src/k_kart.c index f87afe65e..4ea7dab2f 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -763,6 +763,7 @@ static void K_KartGetItemResult(player_t *player, SINT8 getitem) hyubgone = 5*TICRATE; player->kartstuff[k_botitemdelay] = TICRATE; + player->kartstuff[k_botitemconfirm] = 0; switch (getitem) { @@ -907,12 +908,14 @@ static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean sp switch (item) { case KITEM_SNEAKER: + case KITEM_ROCKETSNEAKER: case KITEM_INVINCIBILITY: case KITEM_SPB: case KITEM_GROW: case KITEM_SHRINK: case KITEM_HYUDORO: case KITEM_SUPERRING: + case KRITEM_TRIPLESNEAKER: break; default: return 0; @@ -2536,7 +2539,7 @@ void K_MomentumToFacing(player_t *player) player->mo->momy = FixedMul(player->mo->momy - player->cmomy, player->mo->friction) + player->cmomy; } -static boolean K_ApplyOffroad(player_t *player) +boolean K_ApplyOffroad(player_t *player) { if (player->kartstuff[k_invincibilitytimer] || player->kartstuff[k_hyudorotimer] || EITHERSNEAKER(player)) return false; @@ -6023,9 +6026,6 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) player->powers[pw_flashing]--; } - if (player->kartstuff[k_botitemdelay]) - player->kartstuff[k_botitemdelay]--; - if (player->kartstuff[k_spinouttimer]) { if ((P_IsObjectOnGround(player->mo) @@ -6411,6 +6411,13 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) { for (i = 0U; i < waypoint->numnextwaypoints; i++) { + if (K_GetWaypointIsShortcut(waypoint->nextwaypoints[i]) + && K_PlayerUsesBotMovement(player) + && !K_BotCanTakeCut(player)) + { + continue; + } + angletowaypoint = R_PointToAngle2( player->mo->x, player->mo->y, waypoint->nextwaypoints[i]->mobj->x, waypoint->nextwaypoints[i]->mobj->y); @@ -6451,6 +6458,13 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) { for (i = 0U; i < waypoint->numprevwaypoints; i++) { + if (K_GetWaypointIsShortcut(waypoint->prevwaypoints[i]) + && K_PlayerUsesBotMovement(player) + && !K_BotCanTakeCut(player)) + { + continue; + } + angletowaypoint = R_PointToAngle2( player->mo->x, player->mo->y, waypoint->prevwaypoints[i]->mobj->x, waypoint->prevwaypoints[i]->mobj->y); @@ -6746,6 +6760,9 @@ INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue) if (EITHERSNEAKER(player) || player->kartstuff[k_invincibilitytimer] || player->kartstuff[k_growshrinktimer] > 0) turnvalue = FixedMul(turnvalue, (5*FRACUNIT)/4); + if (K_PlayerUsesBotMovement(player)) + turnvalue = FixedMul(turnvalue, (5*FRACUNIT)/4); + return turnvalue; } diff --git a/src/k_kart.h b/src/k_kart.h index edec02685..1c945e8c2 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -66,6 +66,7 @@ void K_DropItems(player_t *player); void K_StripItems(player_t *player); void K_StripOther(player_t *player); void K_MomentumToFacing(player_t *player); +boolean K_ApplyOffroad(player_t *player); fixed_t K_GetKartSpeedFromStat(UINT8 kartspeed); fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower); fixed_t K_GetKartAccel(player_t *player); From 4380caf7ddd9e2b595d80effeaec2dba3f6a08c0 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Wed, 1 Apr 2020 21:16:26 -0400 Subject: [PATCH 09/85] All around polishing - Make ring usage smarter - Ring usage threshold varies between characters (Tails will be more willing to dump rings, Metal will hold onto them) - Decrease ring use threshold when they have a speed boost (tethering) - Decrease ring use threshold when slowed down more than usual - Properly implement Triple Sneaker & Rocket Sneaker usage - Use smallest radius of all waypoints being predicted, to improve precision - Bots never use prev waypoints --- src/k_bot.c | 154 ++++++++++++++++++++++++++++++++++---------------- src/k_bot.h | 1 + src/k_kart.c | 21 ++++--- src/k_kart.h | 1 + src/p_enemy.c | 6 +- 5 files changed, 118 insertions(+), 65 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index 968ec09b2..2e280ffba 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -116,10 +116,12 @@ static fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, static botprediction_t *K_CreateBotPrediction(player_t *player) { - const INT32 distance = (player->speed / FRACUNIT) * TICRATE; + const INT32 futuresight = (3*TICRATE/4); // How far ahead into the future to try and predict + const INT32 distance = (player->speed / FRACUNIT) * futuresight; INT32 distanceleft = distance; botprediction_t *predictcoords = Z_Calloc(sizeof(botprediction_t), PU_LEVEL, NULL); waypoint_t *wp = player->nextwaypoint; + fixed_t smallestradius = wp->mobj->radius; size_t nwp; size_t i; INT32 lp = 0; @@ -128,7 +130,7 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) { predictcoords->x = wp->mobj->x; predictcoords->y = wp->mobj->y; - predictcoords->radius = wp->mobj->radius; + predictcoords->radius = smallestradius; return predictcoords; } @@ -174,13 +176,19 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) break; } - wp = wp->nextwaypoints[nwp]; distanceleft -= wp->nextwaypointdistances[nwp]; + + if (wp->nextwaypoints[nwp]->mobj->radius < smallestradius) + { + smallestradius = wp->nextwaypoints[nwp]->mobj->radius; + } + + wp = wp->nextwaypoints[nwp]; } predictcoords->x = wp->mobj->x; predictcoords->y = wp->mobj->y; - predictcoords->radius = wp->mobj->radius; + predictcoords->radius = smallestradius; if (distanceleft > 0) { @@ -195,6 +203,9 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) { + boolean ontrack = false; + INT16 turnamt = KART_FULLTURN; + // Can't build a ticcmd if we aren't spawned... if (!player->mo) return; @@ -225,7 +236,6 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if (player->nextwaypoint != NULL && player->nextwaypoint->mobj != NULL && !P_MobjWasRemoved(player->nextwaypoint->mobj)) { botprediction_t *predict = K_CreateBotPrediction(player); - INT16 turnamt = KART_FULLTURN; SINT8 turnsign = 0; angle_t destangle, moveangle, angle; INT16 anglediff; @@ -256,13 +266,18 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } else { - fixed_t rad = predict->radius - (player->mo->radius*2); + fixed_t rad = predict->radius - (player->mo->radius*4); fixed_t dirdist = K_DistanceOfLineFromPoint( player->mo->x, player->mo->y, player->mo->x + FINECOSINE(moveangle >> ANGLETOFINESHIFT), player->mo->y + FINESINE(moveangle >> ANGLETOFINESHIFT), predict->x, predict->y ); + if (rad < 0) + { + rad = 0; + } + cmd->buttons |= BT_ACCELERATE; // Full speed ahead! @@ -270,7 +285,22 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if (dirdist <= rad) { - if (dirdist < 2*rad/3) + fixed_t speedmul = FixedMul(player->speed, K_GetKartSpeed(player, false)); + fixed_t speedrad = rad/4; + + ontrack = true; + + if (speedmul > FRACUNIT) + { + speedmul = FRACUNIT; + } + + // Increase radius with speed + // At low speed, the CPU will try to be more accurate + // At high speed, they're more likely to lawnmower + speedrad += FixedMul(speedmul, rad/2); + + if (dirdist < speedrad) { // Don't need to turn! turnamt = 0; @@ -291,17 +321,36 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if (turnamt != 0) { - cmd->driftturn = KART_FULLTURN * turnsign; - cmd->angleturn += KART_FULLTURN * turnsign; + cmd->driftturn = turnamt * turnsign; + cmd->angleturn += turnamt * turnsign; } Z_Free(predict); } - - if (player->kartstuff[k_userings] == 1 && !player->exiting) + else { - if (player->kartstuff[k_rings] > 10) - cmd->buttons |= BT_ATTACK; + turnamt = 0; + } + + (void)ontrack; + + if (player->kartstuff[k_userings] == 1) + { + if (!player->exiting) + { + INT32 saferingsval = 8 + K_GetKartRingPower(player); + + if (player->speed < K_GetKartSpeed(player, false)/2 // Being slowed down too much + || player->kartstuff[k_speedboost] > 0) // Have another type of boost (tethering) + { + saferingsval -= 5; + } + + if (player->kartstuff[k_rings] > saferingsval) + { + cmd->buttons |= BT_ATTACK; + } + } } else { @@ -312,58 +361,63 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) return; } - switch (player->kartstuff[k_itemtype]) + if (player->kartstuff[k_rocketsneakertimer] > 0) { - case KITEM_SNEAKER: - if ((player->kartstuff[k_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)/2 // Being slowed down too much - || player->kartstuff[k_speedboost] > 0 // Have another type of boost (tethering) - || player->kartstuff[k_botitemconfirm] > 4*TICRATE) // Held onto it for too long - { - cmd->buttons |= BT_ATTACK; - player->kartstuff[k_botitemconfirm] -= TICRATE; - } - else - { - player->kartstuff[k_botitemconfirm]++; - } - break; - case KITEM_ROCKETSNEAKER: - if (player->kartstuff[k_rocketsneakertimer] <= 0) + if (player->kartstuff[k_botitemconfirm] > TICRATE) + { + if (player->kartstuff[k_sneakertimer] <= (TICRATE/3) && !(player->pflags & PF_ATTACKDOWN)) { cmd->buttons |= BT_ATTACK; player->kartstuff[k_botitemconfirm] = 0; } - else - { + } + else + { + player->kartstuff[k_botitemconfirm]++; + } + } + else + { + switch (player->kartstuff[k_itemtype]) + { + case KITEM_SNEAKER: if ((player->kartstuff[k_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)/2 // Being slowed down too much || player->kartstuff[k_speedboost] > 0 // Have another type of boost (tethering) - || player->kartstuff[k_botitemconfirm] > TICRATE) // Held onto it for too long + || player->kartstuff[k_botitemconfirm] > 4*TICRATE) // Held onto it for too long { - cmd->buttons |= BT_ATTACK; - player->kartstuff[k_botitemconfirm] -= TICRATE/2; + if (player->kartstuff[k_sneakertimer] <= (TICRATE/3) && !(player->pflags & PF_ATTACKDOWN)) + { + cmd->buttons |= BT_ATTACK; + player->kartstuff[k_botitemconfirm] -= 2*TICRATE; + } } else { player->kartstuff[k_botitemconfirm]++; } - } - break; - case KITEM_INVINCIBILITY: - case KITEM_SPB: - case KITEM_GROW: - case KITEM_SHRINK: - case KITEM_HYUDORO: - case KITEM_SUPERRING: - cmd->buttons |= BT_ATTACK; - player->kartstuff[k_botitemconfirm] = 0; - break; - default: - player->kartstuff[k_botitemconfirm] = 0; - break; + break; + case KITEM_ROCKETSNEAKER: + if (player->kartstuff[k_rocketsneakertimer] <= 0) + { + cmd->buttons |= BT_ATTACK; + player->kartstuff[k_botitemconfirm] = 0; + } + break; + case KITEM_INVINCIBILITY: + case KITEM_SPB: + case KITEM_GROW: + case KITEM_SHRINK: + case KITEM_HYUDORO: + case KITEM_SUPERRING: + cmd->buttons |= BT_ATTACK; + player->kartstuff[k_botitemconfirm] = 0; + break; + default: + player->kartstuff[k_botitemconfirm] = 0; + break; + } } } } diff --git a/src/k_bot.h b/src/k_bot.h index 071a4fb3e..797843dea 100644 --- a/src/k_bot.h +++ b/src/k_bot.h @@ -16,6 +16,7 @@ typedef struct botprediction_s { fixed_t x, y; fixed_t radius; + angle_t dir; } botprediction_t; void K_AddBots(SINT8 numbots); diff --git a/src/k_kart.c b/src/k_kart.c index 4c5d924c7..83d0354b3 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6455,9 +6455,9 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) { for (i = 0U; i < waypoint->numnextwaypoints; i++) { - if (K_GetWaypointIsShortcut(waypoint->nextwaypoints[i]) - && K_PlayerUsesBotMovement(player) - && !K_BotCanTakeCut(player)) + if (K_PlayerUsesBotMovement(player) + && K_GetWaypointIsShortcut(waypoint->nextwaypoints[i]) + && !K_BotCanTakeCut(player)) { continue; } @@ -6498,17 +6498,11 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) } } - if ((waypoint->prevwaypoints != NULL) && (waypoint->numprevwaypoints > 0U)) + if ((waypoint->prevwaypoints != NULL) && (waypoint->numprevwaypoints > 0U) + && !(K_PlayerUsesBotMovement(player))) // Bots do not need prev waypoints { for (i = 0U; i < waypoint->numprevwaypoints; i++) { - if (K_GetWaypointIsShortcut(waypoint->prevwaypoints[i]) - && K_PlayerUsesBotMovement(player) - && !K_BotCanTakeCut(player)) - { - continue; - } - angletowaypoint = R_PointToAngle2( player->mo->x, player->mo->y, waypoint->prevwaypoints[i]->mobj->x, waypoint->prevwaypoints[i]->mobj->y); @@ -6729,6 +6723,11 @@ static void K_UpdateDistanceFromFinishLine(player_t *const player) } } +INT32 K_GetKartRingPower(player_t *player) +{ + return (((9 - player->kartspeed) + (9 - player->kartweight)) / 2); +} + // Returns false if this player being placed here causes them to collide with any other player // Used in g_game.c for match etc. respawning // This does not check along the z because the z is not correctly set for the spawnee at this point diff --git a/src/k_kart.h b/src/k_kart.h index 26d6f6ecc..c9167a283 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -59,6 +59,7 @@ void K_UpdateHnextList(player_t *player, boolean clean); void K_DropHnextList(player_t *player); void K_RepairOrbitChain(mobj_t *orbit); player_t *K_FindJawzTarget(mobj_t *actor, player_t *source); +INT32 K_GetKartRingPower(player_t *player); boolean K_CheckPlayersRespawnColliding(INT32 playernum, fixed_t x, fixed_t y); INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue); INT32 K_GetKartDriftSparkValue(player_t *player); diff --git a/src/p_enemy.c b/src/p_enemy.c index 8f1fcb1be..c9d23d945 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -3570,7 +3570,6 @@ void A_AttractChase(mobj_t *actor) if (actor->extravalue1) // SRB2Kart { -#define RINGBOOSTPWR (((9 - actor->target->player->kartspeed) + (9 - actor->target->player->kartweight)) / 2) if (!actor->target || P_MobjWasRemoved(actor->target) || !actor->target->player) { P_RemoveMobj(actor); @@ -3588,7 +3587,7 @@ void A_AttractChase(mobj_t *actor) angle_t offset = FixedAngle(18<target->player->kartstuff[k_ringboost] += RINGBOOSTPWR+3; + actor->target->player->kartstuff[k_ringboost] += K_GetKartRingPower(actor->target->player)+3; S_StartSound(actor->target, sfx_s1b5); sparkle = P_SpawnMobj(actor->target->x, actor->target->y, actor->target->z, MT_RINGSPARKS); @@ -3616,7 +3615,7 @@ void A_AttractChase(mobj_t *actor) if (actor->extravalue1 >= 16) { if (actor->target->player->kartstuff[k_rings] >= 20) - actor->target->player->kartstuff[k_ringboost] += RINGBOOSTPWR+3; + actor->target->player->kartstuff[k_ringboost] += K_GetKartRingPower(actor->target->player)+3; else P_GivePlayerRings(actor->target->player, 1); @@ -3645,7 +3644,6 @@ void A_AttractChase(mobj_t *actor) actor->extravalue1++; } } -#undef RINGBOOSTPWR } else { From 0ee1628b513cd8494e1574320b9264c812041ea4 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 1 Apr 2020 20:10:50 -0700 Subject: [PATCH 10/85] Send bot status in servercfg --- src/d_clisrv.c | 3 +++ src/d_clisrv.h | 2 ++ 2 files changed, 5 insertions(+) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 5b43c2213..c73ee04ec 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1555,6 +1555,8 @@ static boolean SV_SendServerConfig(INT32 node) netbuffer->u.servercfg.playerskins[i] = (UINT8)players[i].skin; netbuffer->u.servercfg.playercolor[i] = (UINT8)players[i].skincolor; + + netbuffer->u.servercfg.playerisbot[i] = players[i].bot; } memcpy(netbuffer->u.servercfg.server_context, server_context, 8); @@ -4289,6 +4291,7 @@ static void HandlePacketFromAwayNode(SINT8 node) playeringame[j] = true; SetPlayerSkinByNum(j, (INT32)netbuffer->u.servercfg.playerskins[j]); players[j].skincolor = netbuffer->u.servercfg.playercolor[j]; + players[j].bot = netbuffer->u.servercfg.playerisbot[j]; } scp = netbuffer->u.servercfg.varlengthinputs; diff --git a/src/d_clisrv.h b/src/d_clisrv.h index 49945de7c..d39babd82 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -323,6 +323,8 @@ typedef struct UINT8 playerskins[MAXPLAYERS]; UINT8 playercolor[MAXPLAYERS]; + UINT8 playerisbot[MAXPLAYERS]; + UINT8 gametype; UINT8 modifiedgame; SINT8 adminplayers[MAXPLAYERS]; // Needs to be signed From b83ce4594170fda6c1151231ca252d40bd214469 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 1 Apr 2020 20:11:00 -0700 Subject: [PATCH 11/85] Set angleturn from player angle instead of relying on the previous state --- src/k_bot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_bot.c b/src/k_bot.c index 2e280ffba..ec99c982a 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -322,7 +322,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if (turnamt != 0) { cmd->driftturn = turnamt * turnsign; - cmd->angleturn += turnamt * turnsign; + cmd->angleturn = ( player->mo->angle >> 16 ) + turnamt * turnsign; } Z_Free(predict); From 8f450f2128675838c263c8389ff24a0fdd247cac Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Thu, 2 Apr 2020 00:43:42 -0400 Subject: [PATCH 12/85] Steer bots away from walls --- src/k_bot.c | 168 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 158 insertions(+), 10 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index 2e280ffba..b95346d04 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -22,6 +22,7 @@ #include "k_kart.h" #include "z_zone.h" #include "i_system.h" +#include "p_maputl.h" void K_AddBots(SINT8 numbots) { @@ -201,10 +202,145 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) return predictcoords; } +mobj_t *botmo = NULL; +INT16 badsteerglobal = 0; +INT32 predictx = 0, predicty = 0; + +static void K_SteerFromWall(mobj_t *bot, line_t *ld) +{ + const INT16 amount = KART_FULLTURN + (KART_FULLTURN/4); // KART_FULLTURN/4, but cancel out full turn from earlier turning + INT32 side = P_PointOnLineSide(bot->x, bot->y, ld); + angle_t lineangle = R_PointToAngle2(0, 0, ld->dx, ld->dy) - ANGLE_90; + angle_t destangle = R_PointToAngle2(bot->x, bot->y, predictx, predicty); + angle_t angle; + + if (side == 1) + { + lineangle += ANGLE_180; + } + + angle = (destangle - lineangle); + + if (angle < ANGLE_180) + { + if (bot->player == &players[displayplayers[0]]) + CONS_Printf("turn dir 2\n"); + badsteerglobal = -amount; + } + else + { + if (bot->player == &players[displayplayers[0]]) + CONS_Printf("turn dir 1\n"); + badsteerglobal = amount; + } +} + +static inline boolean K_FindBlockingWalls(line_t *ld) +{ + // Condensed version of PIT_CheckLine + const fixed_t maxstepmove = FixedMul(MAXSTEPMOVE, mapobjectscale); + fixed_t maxstep = maxstepmove; + + if (ld->polyobj && !(ld->polyobj->flags & POF_SOLID)) + { + return true; + } + + if (tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT] || tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT] + || tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM] || tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP]) + { + return true; + } + + if (P_BoxOnLineSide(tmbbox, ld) != -1) + { + return true; + } + + // one sided line + if (!ld->backsector) + { + if (P_PointOnLineSide(botmo->x, botmo->y, ld)) + { + // don't hit the back side + return true; + } + + K_SteerFromWall(botmo, ld); + return false; + } + + if ((ld->flags & ML_IMPASSABLE) || (ld->flags & ML_BLOCKPLAYERS)) + { + K_SteerFromWall(botmo, ld); + return false; + } + + // set openrange, opentop, openbottom + P_LineOpening(ld, botmo); + + if (botmo->player->kartstuff[k_waterskip]) + maxstep += maxstepmove; + + if (P_MobjTouchingSectorSpecial(botmo, 1, 13, false)) + maxstep <<= 1; + else if (P_MobjTouchingSectorSpecial(botmo, 1, 12, false)) + maxstep = 0; + + if ((openrange < botmo->height) // doesn't fit + || (opentop - botmo->z < botmo->height) // mobj is too high + || (openbottom - botmo->z > maxstep)) // too big a step up + { + K_SteerFromWall(botmo, ld); + return false; + } + + return true; +} + +static INT16 K_BotSteerFromWalls(player_t *player, botprediction_t *predict) +{ + INT32 xl, xh, yl, yh, bx, by; + fixed_t radius = predict->radius / 3; + + badsteerglobal = 0; + + botmo = player->mo; + tmx = player->mo->x + P_ReturnThrustX(NULL, player->mo->angle, player->speed); + tmy = player->mo->y + P_ReturnThrustY(NULL, player->mo->angle, player->speed); + + predictx = predict->x; + predicty = predict->y; + + xl = (unsigned)(tmx - radius - bmaporgx)>>MAPBLOCKSHIFT; + xh = (unsigned)(tmx + radius - bmaporgx)>>MAPBLOCKSHIFT; + yl = (unsigned)(tmy - radius - bmaporgy)>>MAPBLOCKSHIFT; + yh = (unsigned)(tmy + radius - bmaporgy)>>MAPBLOCKSHIFT; + + BMBOUNDFIX(xl, xh, yl, yh); + + tmbbox[BOXTOP] = tmy + radius; + tmbbox[BOXBOTTOM] = tmy - radius; + tmbbox[BOXRIGHT] = tmx + radius; + tmbbox[BOXLEFT] = tmx - radius; + + // check lines + for (bx = xl; bx <= xh; bx++) + { + for (by = yl; by <= yh; by++) + { + P_BlockLinesIterator(bx, by, K_FindBlockingWalls); + } + } + + return badsteerglobal; +} + void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) { + botprediction_t *predict = NULL; boolean ontrack = false; - INT16 turnamt = KART_FULLTURN; + INT16 turnamt = 0; // Can't build a ticcmd if we aren't spawned... if (!player->mo) @@ -235,11 +371,12 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if (player->nextwaypoint != NULL && player->nextwaypoint->mobj != NULL && !P_MobjWasRemoved(player->nextwaypoint->mobj)) { - botprediction_t *predict = K_CreateBotPrediction(player); SINT8 turnsign = 0; angle_t destangle, moveangle, angle; INT16 anglediff; + predict = K_CreateBotPrediction(player); + destangle = R_PointToAngle2(player->mo->x, player->mo->y, predict->x, predict->y); moveangle = player->mo->angle; @@ -257,6 +394,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } anglediff = abs(anglediff); + turnamt = KART_FULLTURN * turnsign; if (anglediff > 90) { @@ -319,17 +457,22 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } } - if (turnamt != 0) + turnamt += K_BotSteerFromWalls(player, predict); + } + + if (turnamt != 0) + { + if (turnamt > KART_FULLTURN) { - cmd->driftturn = turnamt * turnsign; - cmd->angleturn += turnamt * turnsign; + turnamt = KART_FULLTURN; + } + else if (turnamt < -KART_FULLTURN) + { + turnamt = -KART_FULLTURN; } - Z_Free(predict); - } - else - { - turnamt = 0; + cmd->driftturn = turnamt; + cmd->angleturn += turnamt; } (void)ontrack; @@ -420,5 +563,10 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } } } + + if (predict != NULL) + { + Z_Free(predict); + } } From 425220a10973269407a7205db05dd092ca8e7a7f Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Thu, 2 Apr 2020 00:45:01 -0400 Subject: [PATCH 13/85] Remove print --- src/k_bot.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index 1e0edf71c..3838b54a9 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -223,14 +223,10 @@ static void K_SteerFromWall(mobj_t *bot, line_t *ld) if (angle < ANGLE_180) { - if (bot->player == &players[displayplayers[0]]) - CONS_Printf("turn dir 2\n"); badsteerglobal = -amount; } else { - if (bot->player == &players[displayplayers[0]]) - CONS_Printf("turn dir 1\n"); badsteerglobal = amount; } } From ebbcf8d7fd75009964c0f7738094c594c84bcb01 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Thu, 2 Apr 2020 14:54:00 -0400 Subject: [PATCH 14/85] Make bots slow down on turns less often --- src/k_bot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_bot.c b/src/k_bot.c index 3838b54a9..98539f070 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -445,7 +445,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) turnamt /= 4; } } - else if (anglediff > 45) + else if (anglediff > 60) { // Actually, don't go too fast... cmd->forwardmove /= 2; From 52960459a3a3351bf6f6b4974a74e3416a6d8600 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Thu, 2 Apr 2020 15:08:48 -0400 Subject: [PATCH 15/85] Minor variable changes --- src/k_bot.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index 98539f070..da895d732 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -120,7 +120,7 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) const INT32 futuresight = (3*TICRATE/4); // How far ahead into the future to try and predict const INT32 distance = (player->speed / FRACUNIT) * futuresight; INT32 distanceleft = distance; - botprediction_t *predictcoords = Z_Calloc(sizeof(botprediction_t), PU_LEVEL, NULL); + botprediction_t *predict = Z_Calloc(sizeof(botprediction_t), PU_LEVEL, NULL); waypoint_t *wp = player->nextwaypoint; fixed_t smallestradius = wp->mobj->radius; size_t nwp; @@ -129,10 +129,10 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) if (distance <= 0) { - predictcoords->x = wp->mobj->x; - predictcoords->y = wp->mobj->y; - predictcoords->radius = smallestradius; - return predictcoords; + predict->x = wp->mobj->x; + predict->y = wp->mobj->y; + predict->radius = smallestradius; + return predict; } while (distanceleft > 0) @@ -187,24 +187,24 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) wp = wp->nextwaypoints[nwp]; } - predictcoords->x = wp->mobj->x; - predictcoords->y = wp->mobj->y; - predictcoords->radius = smallestradius; + predict->x = wp->mobj->x; + predict->y = wp->mobj->y; + predict->radius = smallestradius; if (distanceleft > 0) { angle_t a = R_PointToAngle2(wp->mobj->x, wp->mobj->y, wp->nextwaypoints[nwp]->mobj->x, wp->nextwaypoints[nwp]->mobj->y); - predictcoords->x += P_ReturnThrustX(NULL, a, distanceleft * FRACUNIT); - predictcoords->y += P_ReturnThrustY(NULL, a, distanceleft * FRACUNIT); + predict->x += P_ReturnThrustX(NULL, a, distanceleft * FRACUNIT); + predict->y += P_ReturnThrustY(NULL, a, distanceleft * FRACUNIT); } - return predictcoords; + return predict; } mobj_t *botmo = NULL; INT16 badsteerglobal = 0; -INT32 predictx = 0, predicty = 0; +fixed_t predictx = 0, predicty = 0; static void K_SteerFromWall(mobj_t *bot, line_t *ld) { From 3ae0cd03fb80926b23c8cb122f0beee937bfc68b Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Thu, 2 Apr 2020 15:15:00 -0400 Subject: [PATCH 16/85] Scale waypoint radius with mapobjectscale Fixes Marble Garden's bots --- src/p_mobj.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 0aed01589..6699d898c 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -12634,12 +12634,12 @@ ML_NOCLIMB : Direction not controllable { // Just like MT_SPINMACEPOINT, this now works here too! INT32 line = P_FindSpecialLineFromTag(2000, mthing->angle, -1); - mobj->radius = 384*FRACUNIT; + mobj->radius = 384 * mapobjectscale; // Set the radius, mobj z, and mthing z to match what the parameters want if (line != -1) { - fixed_t lineradius = sides[lines[line].sidenum[0]].textureoffset; - fixed_t linez = sides[lines[line].sidenum[0]].rowoffset; + fixed_t lineradius = FixedMul(sides[lines[line].sidenum[0]].textureoffset, mapobjectscale); + fixed_t linez = sides[lines[line].sidenum[0]].rowoffset; // mapthing z heights aren't affected by mapobjectscale, so I left this alone. if (lineradius > 0) mobj->radius = lineradius; @@ -12687,7 +12687,6 @@ ML_NOCLIMB : Direction not controllable mobj->extravalue2 = 0; } - // Sryder 2018-12-7: Grabbed this from the old MT_BOSS3WAYPOINT section so they'll be in the waypointcap instead P_SetTarget(&mobj->tracer, waypointcap); P_SetTarget(&waypointcap, mobj); From fafdb257b89e9a521a71931a161dabff1198aaac Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Thu, 2 Apr 2020 15:15:34 -0400 Subject: [PATCH 17/85] Actually, this should be left alone too --- src/p_mobj.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 6699d898c..59816430c 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -12638,8 +12638,8 @@ ML_NOCLIMB : Direction not controllable // Set the radius, mobj z, and mthing z to match what the parameters want if (line != -1) { - fixed_t lineradius = FixedMul(sides[lines[line].sidenum[0]].textureoffset, mapobjectscale); - fixed_t linez = sides[lines[line].sidenum[0]].rowoffset; // mapthing z heights aren't affected by mapobjectscale, so I left this alone. + fixed_t lineradius = sides[lines[line].sidenum[0]].textureoffset; + fixed_t linez = sides[lines[line].sidenum[0]].rowoffset; if (lineradius > 0) mobj->radius = lineradius; From 2e963a46f5e07ac514e3fdc0215aec6495a9c054 Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 2 Apr 2020 00:03:20 -0700 Subject: [PATCH 18/85] For fun, let the bots vote --- src/d_netcmd.c | 3 +-- src/d_netcmd.h | 2 +- src/y_inter.c | 25 ++++++++++++++++++++----- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 7b314e9b3..b3ad2f16f 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2801,11 +2801,10 @@ void D_SetupVote(void) SendNetXCmd(XD_SETUPVOTE, buf, p - buf); } -void D_ModifyClientVote(SINT8 voted, UINT8 splitplayer) +void D_ModifyClientVote(UINT8 player, SINT8 voted, UINT8 splitplayer) { char buf[2]; char *p = buf; - UINT8 player = consoleplayer; if (splitplayer > 0) player = g_localplayers[splitplayer]; diff --git a/src/d_netcmd.h b/src/d_netcmd.h index ef3548183..d765031d6 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -246,7 +246,7 @@ void Command_Retry_f(void); void D_GameTypeChanged(INT32 lastgametype); // not a real _OnChange function anymore void D_MapChange(INT32 pmapnum, INT32 pgametype, boolean pencoremode, boolean presetplayers, INT32 pdelay, boolean pskipprecutscene, boolean pfromlevelselect); void D_SetupVote(void); -void D_ModifyClientVote(SINT8 voted, UINT8 splitplayer); +void D_ModifyClientVote(UINT8 player, SINT8 voted, UINT8 splitplayer); void D_PickVote(void); void ObjectPlace_OnChange(void); boolean IsPlayerAdmin(INT32 playernum); diff --git a/src/y_inter.c b/src/y_inter.c index e9e243b04..c908f2143 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -1507,6 +1507,7 @@ static void Y_VoteStops(SINT8 pick, SINT8 level) void Y_VoteTicker(void) { INT32 i; + boolean everyone_voted; if (paused || P_AutoPause() || !voteclient.loaded) return; @@ -1655,7 +1656,7 @@ void Y_VoteTicker(void) if ((InputDown(gc_accelerate, i+1) || JoyAxis(AXISMOVE, i+1) > 0) && !pressed) { - D_ModifyClientVote(voteclient.playerinfo[i].selection, i); + D_ModifyClientVote(consoleplayer, voteclient.playerinfo[i].selection, i); pressed = true; } } @@ -1669,6 +1670,8 @@ void Y_VoteTicker(void) if (server) { + everyone_voted = true;/* the default condition */ + if (timer == 0) { for (i = 0; i < MAXPLAYERS; i++) @@ -1682,13 +1685,25 @@ void Y_VoteTicker(void) for (i = 0; i < MAXPLAYERS; i++) { if ((playeringame[i] && !players[i].spectator) && votes[i] == -1) - return; + { + if (players[i].bot) + { + if (( M_RandomFixed() % 100 ) == 0) + D_ModifyClientVote(i, M_RandomKey(4), 0); + } + + if (votes[i] == -1) + everyone_voted = false; + } } } - timer = 0; - if (voteendtic == -1) - D_PickVote(); + if (everyone_voted) + { + timer = 0; + if (voteendtic == -1) + D_PickVote(); + } } } } From ec9a023814de418eed3fbed966bd7fac4e08a94a Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Fri, 3 Apr 2020 00:38:24 -0400 Subject: [PATCH 19/85] Object steering Bots will steer *towards* rings, items, ring-stingable players, and players with lower weight. They will steer *away* from projectiles, traps, players using damaging items (such as invincibility), players with shields, and players with higher weight. --- src/k_bot.c | 291 ++++++++++++++++++++++++++++++++++++++++++++------- src/k_kart.c | 2 +- src/k_kart.h | 1 + 3 files changed, 254 insertions(+), 40 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index da895d732..cb567770b 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -107,10 +107,19 @@ static fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, fixed_t mag = FixedMul(v1tov2[0], v1tov2[0]) + FixedMul(v1tov2[1], v1tov2[1]); fixed_t dot = FixedMul(v1toc[0], v1tov2[0]) + FixedMul(v1toc[1], v1tov2[1]); - fixed_t t = FixedDiv(dot, mag); - fixed_t px = v1x + FixedMul(v1tov2[0], t); - fixed_t py = v1y + FixedMul(v1tov2[1], t); + fixed_t t; + fixed_t px, py; + + if (mag == 0) + { + return 0; + } + + t = FixedDiv(dot, mag); + + px = v1x + FixedMul(v1tov2[0], t); + py = v1y + FixedMul(v1tov2[1], t); return P_AproxDistance(cx - px, cy - py); } @@ -203,12 +212,13 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) } mobj_t *botmo = NULL; +fixed_t distancetocheck = 0; INT16 badsteerglobal = 0; fixed_t predictx = 0, predicty = 0; static void K_SteerFromWall(mobj_t *bot, line_t *ld) { - const INT16 amount = KART_FULLTURN + (KART_FULLTURN/4); // KART_FULLTURN/4, but cancel out full turn from earlier turning + const INT16 amount = 2*KART_FULLTURN; INT32 side = P_PointOnLineSide(bot->x, bot->y, ld); angle_t lineangle = R_PointToAngle2(0, 0, ld->dx, ld->dy) - ANGLE_90; angle_t destangle = R_PointToAngle2(bot->x, bot->y, predictx, predicty); @@ -223,11 +233,11 @@ static void K_SteerFromWall(mobj_t *bot, line_t *ld) if (angle < ANGLE_180) { - badsteerglobal = -amount; + badsteerglobal -= amount; } else { - badsteerglobal = amount; + badsteerglobal += amount; } } @@ -236,6 +246,12 @@ static inline boolean K_FindBlockingWalls(line_t *ld) // Condensed version of PIT_CheckLine const fixed_t maxstepmove = FixedMul(MAXSTEPMOVE, mapobjectscale); fixed_t maxstep = maxstepmove; + INT32 lineside = 0; + + if (!botmo || P_MobjWasRemoved(botmo) || !botmo->player) + { + return false; + } if (ld->polyobj && !(ld->polyobj->flags & POF_SOLID)) { @@ -253,23 +269,25 @@ static inline boolean K_FindBlockingWalls(line_t *ld) return true; } + lineside = P_PointOnLineSide(botmo->x, botmo->y, ld); + // one sided line if (!ld->backsector) { - if (P_PointOnLineSide(botmo->x, botmo->y, ld)) + if (lineside) { // don't hit the back side return true; } K_SteerFromWall(botmo, ld); - return false; + return true; } if ((ld->flags & ML_IMPASSABLE) || (ld->flags & ML_BLOCKPLAYERS)) { K_SteerFromWall(botmo, ld); - return false; + return true; } // set openrange, opentop, openbottom @@ -288,7 +306,7 @@ static inline boolean K_FindBlockingWalls(line_t *ld) || (openbottom - botmo->z > maxstep)) // too big a step up { K_SteerFromWall(botmo, ld); - return false; + return true; } return true; @@ -297,30 +315,31 @@ static inline boolean K_FindBlockingWalls(line_t *ld) static INT16 K_BotSteerFromWalls(player_t *player, botprediction_t *predict) { INT32 xl, xh, yl, yh, bx, by; - fixed_t radius = predict->radius / 3; badsteerglobal = 0; botmo = player->mo; + distancetocheck = player->mo->radius * 8; + tmx = player->mo->x + P_ReturnThrustX(NULL, player->mo->angle, player->speed); tmy = player->mo->y + P_ReturnThrustY(NULL, player->mo->angle, player->speed); predictx = predict->x; predicty = predict->y; - xl = (unsigned)(tmx - radius - bmaporgx)>>MAPBLOCKSHIFT; - xh = (unsigned)(tmx + radius - bmaporgx)>>MAPBLOCKSHIFT; - yl = (unsigned)(tmy - radius - bmaporgy)>>MAPBLOCKSHIFT; - yh = (unsigned)(tmy + radius - bmaporgy)>>MAPBLOCKSHIFT; + xl = (unsigned)(tmx - distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; + xh = (unsigned)(tmx + distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; + yl = (unsigned)(tmy - distancetocheck - bmaporgy)>>MAPBLOCKSHIFT; + yh = (unsigned)(tmy + distancetocheck - bmaporgy)>>MAPBLOCKSHIFT; BMBOUNDFIX(xl, xh, yl, yh); - tmbbox[BOXTOP] = tmy + radius; - tmbbox[BOXBOTTOM] = tmy - radius; - tmbbox[BOXRIGHT] = tmx + radius; - tmbbox[BOXLEFT] = tmx - radius; + tmbbox[BOXTOP] = tmy + distancetocheck; + tmbbox[BOXBOTTOM] = tmy - distancetocheck; + tmbbox[BOXRIGHT] = tmx + distancetocheck; + tmbbox[BOXLEFT] = tmx - distancetocheck; - // check lines + // Check for lines that the bot might collide with for (bx = xl; bx <= xh; bx++) { for (by = yl; by <= yh; by++) @@ -332,19 +351,210 @@ static INT16 K_BotSteerFromWalls(player_t *player, botprediction_t *predict) return badsteerglobal; } +static void K_SteerFromObject(mobj_t *bot, mobj_t *thing, boolean towards) +{ + INT16 amount = KART_FULLTURN; + angle_t destangle = R_PointToAngle2(bot->x, bot->y, thing->x, thing->y); + angle_t angle; + + if (towards) + { + fixed_t dist = K_DistanceOfLineFromPoint( + bot->x, bot->y, + bot->x + FINECOSINE(bot->angle >> ANGLETOFINESHIFT), bot->y + FINESINE(bot->angle >> ANGLETOFINESHIFT), + thing->x, thing->y + ); + + amount = -amount; + + if (dist <= (bot->radius + thing->radius) * 2) + { + // Don't need to turn any harder! + return; + } + } + + angle = (bot->angle - destangle); + + if (angle < ANGLE_180) + { + badsteerglobal -= amount; + } + else + { + badsteerglobal += amount; + } +} + +static inline boolean K_BotSteerObjects(mobj_t *thing) +{ + if (!botmo || P_MobjWasRemoved(botmo) || !botmo->player) + { + return false; + } + + if (!thing->health) + { + return true; + } + + if (P_AproxDistance(botmo->x - thing->x, botmo->y - thing->y) > distancetocheck) + { + return true; + } + + switch (thing->type) + { + case MT_BANANA: + case MT_EGGMANITEM: + case MT_ORBINAUT: + case MT_JAWZ: + case MT_JAWZ_DUD: + case MT_SSMINE: + case MT_BALLHOG: + case MT_SPB: + K_SteerFromObject(botmo, thing, false); + break; + case MT_RANDOMITEM: + if (P_CanPickupItem(botmo->player, 1)) + { + K_SteerFromObject(botmo, thing, true); + } + break; + case MT_FLOATINGITEM: + if (P_CanPickupItem(botmo->player, 3)) + { + K_SteerFromObject(botmo, thing, true); + } + break; + case MT_RING: + case MT_FLINGRING: + if (RINGTOTAL(botmo->player) < 20 && !botmo->player->kartstuff[k_ringlock]) + { + K_SteerFromObject(botmo, thing, true); + } + break; + case MT_PLAYER: + if (thing->player + && !thing->player->kartstuff[k_hyudorotimer] + && !botmo->player->kartstuff[k_hyudorotimer]) + { + // Squish + if (botmo->scale > thing->scale + (mapobjectscale/8)) + { + K_SteerFromObject(botmo, thing, true); + } + else if (thing->scale > botmo->scale + (mapobjectscale/8)) + { + K_SteerFromObject(botmo, thing, false); + } + // Invincibility + else if (botmo->player->kartstuff[k_invincibilitytimer] && !thing->player->kartstuff[k_invincibilitytimer]) + { + K_SteerFromObject(botmo, thing, true); + } + else if (thing->player->kartstuff[k_invincibilitytimer] && !botmo->player->kartstuff[k_invincibilitytimer]) + { + K_SteerFromObject(botmo, thing, false); + } + // Flame Shield + // Not a check for Flame Shield && flame dash, because they could potentially flame dash at any time! + else if (botmo->player->kartstuff[k_itemtype] == KITEM_FLAMESHIELD + && thing->player->kartstuff[k_itemtype] != KITEM_FLAMESHIELD) + { + K_SteerFromObject(botmo, thing, true); + } + else if (thing->player->kartstuff[k_itemtype] == KITEM_FLAMESHIELD + && botmo->player->kartstuff[k_itemtype] != KITEM_FLAMESHIELD) + { + K_SteerFromObject(botmo, thing, false); + } + // Has shield item + else if (botmo->player->kartstuff[k_itemheld] && !thing->player->kartstuff[k_itemheld]) + { + K_SteerFromObject(botmo, thing, true); + } + else if (thing->player->kartstuff[k_itemheld] && !botmo->player->kartstuff[k_itemheld]) + { + K_SteerFromObject(botmo, thing, false); + } + // Ring Sting + else if ((thing->player->kartstuff[k_rings] <= 0) + && !(botmo->player->kartstuff[k_rings] <= 0)) + { + K_SteerFromObject(botmo, thing, true); + } + else if ((botmo->player->kartstuff[k_rings] <= 0) + && !(thing->player->kartstuff[k_rings] <= 0)) + { + K_SteerFromObject(botmo, thing, false); + } + else + { + // After ALL of that, we can do standard bumping + fixed_t weightdiff = K_GetMobjWeight(thing, botmo) - K_GetMobjWeight(botmo, thing); + + if (weightdiff > mapobjectscale) + { + K_SteerFromObject(botmo, thing, false); + } + else if (weightdiff < -mapobjectscale) + { + K_SteerFromObject(botmo, thing, true); + } + } + } + break; + default: + if (thing->flags & (MF_ENEMY|MF_BOSS|MF_PAIN|MF_MISSILE|MF_FIRE)) + { + K_SteerFromObject(botmo, thing, false); + } + break; + } + + return true; +} + +static INT16 K_BotFindObjects(player_t *player) +{ + INT32 xl, xh, yl, yh, bx, by; + + badsteerglobal = 0; + + botmo = player->mo; + distancetocheck = (player->mo->radius * 8) + (player->speed * 2); + + xl = (unsigned)(botmo->x - distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; + xh = (unsigned)(botmo->x + distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; + yl = (unsigned)(botmo->y - distancetocheck - bmaporgy)>>MAPBLOCKSHIFT; + yh = (unsigned)(botmo->y + distancetocheck - bmaporgy)>>MAPBLOCKSHIFT; + + BMBOUNDFIX(xl, xh, yl, yh); + + for (bx = xl; bx <= xh; bx++) + { + for (by = yl; by <= yh; by++) + { + P_BlockThingsIterator(bx, by, K_BotSteerObjects); + } + } + + return badsteerglobal; +} + void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) { botprediction_t *predict = NULL; - boolean ontrack = false; INT16 turnamt = 0; // Can't build a ticcmd if we aren't spawned... if (!player->mo) return; - cmd->forwardmove = 0; - cmd->driftturn = 0; - cmd->buttons = 0; + // Remove any existing controls + memset(cmd, 0, sizeof(ticcmd_t)); + cmd->angleturn = (player->mo->angle >> 16); if (player->playerstate == PST_DEAD) { @@ -353,14 +563,15 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } #ifdef HAVE_BLUA - // Let Lua scripts build ticcmds + // Complete override of all ticcmd functionality if (LUAh_BotTiccmd(player, cmd)) return; #endif + // Start boost handler if (leveltime <= starttime) { - if (leveltime >= starttime-35) + if (leveltime >= starttime-40) cmd->buttons |= BT_ACCELERATE; return; } @@ -400,6 +611,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } else { + INT16 wallsteer = K_BotSteerFromWalls(player, predict); fixed_t rad = predict->radius - (player->mo->radius*4); fixed_t dirdist = K_DistanceOfLineFromPoint( player->mo->x, player->mo->y, @@ -417,12 +629,15 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) // Full speed ahead! cmd->forwardmove = 50; - if (dirdist <= rad) + if (wallsteer != 0) + { + turnamt = wallsteer; + } + else if (dirdist <= rad) { fixed_t speedmul = FixedMul(player->speed, K_GetKartSpeed(player, false)); fixed_t speedrad = rad/4; - - ontrack = true; + if (speedmul > FRACUNIT) { @@ -437,7 +652,8 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if (dirdist < speedrad) { // Don't need to turn! - turnamt = 0; + // Instead, lets turn based off of objects around us. + turnamt = K_BotFindObjects(player); } else { @@ -445,15 +661,14 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) turnamt /= 4; } } - else if (anglediff > 60) + + if (anglediff > 60) { // Actually, don't go too fast... cmd->forwardmove /= 2; cmd->buttons |= BT_BRAKE; } } - - turnamt += K_BotSteerFromWalls(player, predict); } if (turnamt != 0) @@ -468,11 +683,9 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } cmd->driftturn = turnamt; - cmd->angleturn = (player->mo->angle >> 16) + turnamt; + cmd->angleturn += turnamt; } - (void)ontrack; - if (player->kartstuff[k_userings] == 1) { if (!player->exiting) @@ -504,7 +717,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) { if (player->kartstuff[k_botitemconfirm] > TICRATE) { - if (player->kartstuff[k_sneakertimer] <= (TICRATE/3) && !(player->pflags & PF_ATTACKDOWN)) + if (!player->kartstuff[k_sneakertimer] && !(player->pflags & PF_ATTACKDOWN)) { cmd->buttons |= BT_ATTACK; player->kartstuff[k_botitemconfirm] = 0; @@ -526,10 +739,10 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) || player->kartstuff[k_speedboost] > 0 // Have another type of boost (tethering) || player->kartstuff[k_botitemconfirm] > 4*TICRATE) // Held onto it for too long { - if (player->kartstuff[k_sneakertimer] <= (TICRATE/3) && !(player->pflags & PF_ATTACKDOWN)) + if (!player->kartstuff[k_sneakertimer] && !(player->pflags & PF_ATTACKDOWN)) { cmd->buttons |= BT_ATTACK; - player->kartstuff[k_botitemconfirm] -= 2*TICRATE; + player->kartstuff[k_botitemconfirm] = 2*TICRATE; } } else diff --git a/src/k_kart.c b/src/k_kart.c index 83d0354b3..dd8c76ba2 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1394,7 +1394,7 @@ static fixed_t K_PlayerWeight(mobj_t *mobj, mobj_t *against) return weight; } -static fixed_t K_GetMobjWeight(mobj_t *mobj, mobj_t *against) +fixed_t K_GetMobjWeight(mobj_t *mobj, mobj_t *against) { fixed_t weight = 5*FRACUNIT; diff --git a/src/k_kart.h b/src/k_kart.h index c9167a283..6d11d7727 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -28,6 +28,7 @@ fixed_t K_GetKartGameSpeedScalar(SINT8 value); extern consvar_t *KartItemCVars[NUMKARTRESULTS-1]; INT32 K_GetShieldFromItem(INT32 item); +fixed_t K_GetMobjWeight(mobj_t *mobj, mobj_t *against); void K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean solid); void K_KartPainEnergyFling(player_t *player); void K_FlipFromObject(mobj_t *mo, mobj_t *master); From 5dec13c848428134f7a6982055d8c3e630d813c0 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Fri, 3 Apr 2020 13:36:33 -0400 Subject: [PATCH 20/85] Fix ring values being flipped --- src/k_bot.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index cb567770b..e079997bb 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -637,7 +637,6 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) { fixed_t speedmul = FixedMul(player->speed, K_GetKartSpeed(player, false)); fixed_t speedrad = rad/4; - if (speedmul > FRACUNIT) { @@ -690,7 +689,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) { if (!player->exiting) { - INT32 saferingsval = 8 + K_GetKartRingPower(player); + INT32 saferingsval = 16 - K_GetKartRingPower(player); if (player->speed < K_GetKartSpeed(player, false)/2 // Being slowed down too much || player->kartstuff[k_speedboost] > 0) // Have another type of boost (tethering) From 578f4ae2c101d38c937afc80bfa73f49b79c07c1 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Fri, 3 Apr 2020 13:42:27 -0400 Subject: [PATCH 21/85] Slight tweaks to object dodging --- src/k_bot.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index e079997bb..b55ab8341 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -367,7 +367,7 @@ static void K_SteerFromObject(mobj_t *bot, mobj_t *thing, boolean towards) amount = -amount; - if (dist <= (bot->radius + thing->radius) * 2) + if (dist <= (bot->radius + thing->radius)) { // Don't need to turn any harder! return; @@ -406,11 +406,16 @@ static inline boolean K_BotSteerObjects(mobj_t *thing) switch (thing->type) { case MT_BANANA: + case MT_BANANA_SHIELD: case MT_EGGMANITEM: + case MT_EGGMANITEM_SHIELD: case MT_ORBINAUT: + case MT_ORBINAUT_SHIELD: case MT_JAWZ: case MT_JAWZ_DUD: + case MT_JAWZ_SHIELD: case MT_SSMINE: + case MT_SSMINE_SHIELD: case MT_BALLHOG: case MT_SPB: K_SteerFromObject(botmo, thing, false); @@ -506,7 +511,7 @@ static inline boolean K_BotSteerObjects(mobj_t *thing) } break; default: - if (thing->flags & (MF_ENEMY|MF_BOSS|MF_PAIN|MF_MISSILE|MF_FIRE)) + if (thing->flags & (MF_SOLID|MF_ENEMY|MF_BOSS|MF_PAIN|MF_MISSILE|MF_FIRE)) { K_SteerFromObject(botmo, thing, false); } From ccc7ac8cad57dc57717b7d109890eedb6c0f42be Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Fri, 3 Apr 2020 16:39:43 -0400 Subject: [PATCH 22/85] More misc polish - Steer against objects more often - Reduce steering toward rings - Only steer towards items/rings in front of them - Be slightly less trigger happy with using rings/shoes while boosting --- src/k_bot.c | 111 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 80 insertions(+), 31 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index b55ab8341..5c671aedd 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -351,9 +351,9 @@ static INT16 K_BotSteerFromWalls(player_t *player, botprediction_t *predict) return badsteerglobal; } -static void K_SteerFromObject(mobj_t *bot, mobj_t *thing, boolean towards) +static void K_SteerFromObject(mobj_t *bot, mobj_t *thing, boolean towards, fixed_t turnmul) { - INT16 amount = KART_FULLTURN; + INT16 amount = 3*KART_FULLTURN; angle_t destangle = R_PointToAngle2(bot->x, bot->y, thing->x, thing->y); angle_t angle; @@ -365,13 +365,18 @@ static void K_SteerFromObject(mobj_t *bot, mobj_t *thing, boolean towards) thing->x, thing->y ); - amount = -amount; - if (dist <= (bot->radius + thing->radius)) { // Don't need to turn any harder! return; } + + amount = -amount; + } + + if (turnmul != FRACUNIT) + { + amount = FixedMul(amount * FRACUNIT, turnmul) / FRACUNIT; } angle = (bot->angle - destangle); @@ -388,6 +393,9 @@ static void K_SteerFromObject(mobj_t *bot, mobj_t *thing, boolean towards) static inline boolean K_BotSteerObjects(mobj_t *thing) { + INT16 anglediff; + angle_t destangle, angle; + if (!botmo || P_MobjWasRemoved(botmo) || !botmo->player) { return false; @@ -403,6 +411,20 @@ static inline boolean K_BotSteerObjects(mobj_t *thing) return true; } + destangle = R_PointToAngle2(botmo->x, botmo->y, thing->x, thing->y); + angle = (botmo->angle - destangle); + + if (angle < ANGLE_180) + { + anglediff = AngleFixed(angle)>>FRACBITS; + } + else + { + anglediff = 360-(AngleFixed(angle)>>FRACBITS); + } + + anglediff = abs(anglediff); + switch (thing->type) { case MT_BANANA: @@ -418,25 +440,42 @@ static inline boolean K_BotSteerObjects(mobj_t *thing) case MT_SSMINE_SHIELD: case MT_BALLHOG: case MT_SPB: - K_SteerFromObject(botmo, thing, false); + K_SteerFromObject(botmo, thing, false, FRACUNIT); break; case MT_RANDOMITEM: + if (anglediff > 25) + { + break; + } + if (P_CanPickupItem(botmo->player, 1)) { - K_SteerFromObject(botmo, thing, true); + K_SteerFromObject(botmo, thing, true, FRACUNIT); } break; case MT_FLOATINGITEM: + if (anglediff > 25) + { + break; + } + if (P_CanPickupItem(botmo->player, 3)) { - K_SteerFromObject(botmo, thing, true); + K_SteerFromObject(botmo, thing, true, FRACUNIT); } break; case MT_RING: case MT_FLINGRING: - if (RINGTOTAL(botmo->player) < 20 && !botmo->player->kartstuff[k_ringlock]) + if (anglediff > 25) { - K_SteerFromObject(botmo, thing, true); + break; + } + + if ((RINGTOTAL(botmo->player) < 20 && !botmo->player->kartstuff[k_ringlock] + && P_CanPickupItem(botmo->player, 0)) + && (!thing->extravalue1)) + { + K_SteerFromObject(botmo, thing, true, FRACUNIT/3); } break; case MT_PLAYER: @@ -447,65 +486,65 @@ static inline boolean K_BotSteerObjects(mobj_t *thing) // Squish if (botmo->scale > thing->scale + (mapobjectscale/8)) { - K_SteerFromObject(botmo, thing, true); + K_SteerFromObject(botmo, thing, true, FRACUNIT); } else if (thing->scale > botmo->scale + (mapobjectscale/8)) { - K_SteerFromObject(botmo, thing, false); + K_SteerFromObject(botmo, thing, false, FRACUNIT); } // Invincibility else if (botmo->player->kartstuff[k_invincibilitytimer] && !thing->player->kartstuff[k_invincibilitytimer]) { - K_SteerFromObject(botmo, thing, true); + K_SteerFromObject(botmo, thing, true, FRACUNIT); } else if (thing->player->kartstuff[k_invincibilitytimer] && !botmo->player->kartstuff[k_invincibilitytimer]) { - K_SteerFromObject(botmo, thing, false); + K_SteerFromObject(botmo, thing, false, FRACUNIT); } // Flame Shield // Not a check for Flame Shield && flame dash, because they could potentially flame dash at any time! else if (botmo->player->kartstuff[k_itemtype] == KITEM_FLAMESHIELD && thing->player->kartstuff[k_itemtype] != KITEM_FLAMESHIELD) { - K_SteerFromObject(botmo, thing, true); + K_SteerFromObject(botmo, thing, true, FRACUNIT); } else if (thing->player->kartstuff[k_itemtype] == KITEM_FLAMESHIELD && botmo->player->kartstuff[k_itemtype] != KITEM_FLAMESHIELD) { - K_SteerFromObject(botmo, thing, false); + K_SteerFromObject(botmo, thing, false, FRACUNIT); } // Has shield item else if (botmo->player->kartstuff[k_itemheld] && !thing->player->kartstuff[k_itemheld]) { - K_SteerFromObject(botmo, thing, true); + K_SteerFromObject(botmo, thing, true, FRACUNIT); } else if (thing->player->kartstuff[k_itemheld] && !botmo->player->kartstuff[k_itemheld]) { - K_SteerFromObject(botmo, thing, false); + K_SteerFromObject(botmo, thing, false, FRACUNIT); } // Ring Sting else if ((thing->player->kartstuff[k_rings] <= 0) && !(botmo->player->kartstuff[k_rings] <= 0)) { - K_SteerFromObject(botmo, thing, true); + K_SteerFromObject(botmo, thing, true, FRACUNIT); } else if ((botmo->player->kartstuff[k_rings] <= 0) && !(thing->player->kartstuff[k_rings] <= 0)) { - K_SteerFromObject(botmo, thing, false); + K_SteerFromObject(botmo, thing, false, FRACUNIT); } else { // After ALL of that, we can do standard bumping - fixed_t weightdiff = K_GetMobjWeight(thing, botmo) - K_GetMobjWeight(botmo, thing); + fixed_t weightdiff = K_GetMobjWeight(botmo, thing) - K_GetMobjWeight(thing, botmo); if (weightdiff > mapobjectscale) { - K_SteerFromObject(botmo, thing, false); + K_SteerFromObject(botmo, thing, true, FRACUNIT); } - else if (weightdiff < -mapobjectscale) + else { - K_SteerFromObject(botmo, thing, true); + K_SteerFromObject(botmo, thing, false, FRACUNIT); } } } @@ -513,7 +552,7 @@ static inline boolean K_BotSteerObjects(mobj_t *thing) default: if (thing->flags & (MF_SOLID|MF_ENEMY|MF_BOSS|MF_PAIN|MF_MISSILE|MF_FIRE)) { - K_SteerFromObject(botmo, thing, false); + K_SteerFromObject(botmo, thing, false, FRACUNIT); } break; } @@ -624,9 +663,17 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) predict->x, predict->y ); - if (rad < 0) + if (anglediff > 0) { - rad = 0; + // Become more precise based on how hard you need to turn + // This makes predictions into turns a little nicer + // Facing 90 degrees away from the predicted point gives you a 1/3 radius + rad = ((180 - anglediff) * rad) / 135; + } + + if (rad < 2*player->mo->radius) + { + rad = 2*player->mo->radius; } cmd->buttons |= BT_ACCELERATE; @@ -653,17 +700,19 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) // At high speed, they're more likely to lawnmower speedrad += FixedMul(speedmul, rad/2); - if (dirdist < speedrad) + if (dirdist <= speedrad) { // Don't need to turn! - // Instead, lets turn based off of objects around us. - turnamt = K_BotFindObjects(player); + turnamt = 0; } else { // Make minor adjustments turnamt /= 4; } + + // Steer towards or away from nearby objects! + turnamt += K_BotFindObjects(player); } if (anglediff > 60) @@ -697,7 +746,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) INT32 saferingsval = 16 - K_GetKartRingPower(player); if (player->speed < K_GetKartSpeed(player, false)/2 // Being slowed down too much - || player->kartstuff[k_speedboost] > 0) // Have another type of boost (tethering) + || player->kartstuff[k_speedboost] > (FRACUNIT/5)) // Have another type of boost (tethering) { saferingsval -= 5; } @@ -740,7 +789,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if ((player->kartstuff[k_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)/2 // Being slowed down too much - || player->kartstuff[k_speedboost] > 0 // Have another type of boost (tethering) + || player->kartstuff[k_speedboost] > (FRACUNIT/8) // Have another type of boost (tethering) || player->kartstuff[k_botitemconfirm] > 4*TICRATE) // Held onto it for too long { if (!player->kartstuff[k_sneakertimer] && !(player->pflags & PF_ATTACKDOWN)) From b983031587f3c20d67d25a4d06f17bd60a3d01a1 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sat, 4 Apr 2020 01:42:56 -0400 Subject: [PATCH 23/85] Yet more misc polish since I can't focus on orbis - Massive improvements to object steering -- they do it more often and more strongly - Steering towards hurtable players is now weighted on acceleration, and steering away from painful players is weighted on handling - Bots now can get & use Thunder Shield - Bots now know how to detontate Eggman Item explosions early - Top speed gets a buff to make up for them losing lots of speed without drifting, weighted more heavily towards low acceleration characters --- src/d_player.h | 1 + src/dehacked.c | 3 +- src/k_bot.c | 361 ++++++++++++++++++++++++++++++++++--------------- src/k_kart.c | 13 ++ 4 files changed, 267 insertions(+), 111 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index 24c692862..278361cfa 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -349,6 +349,7 @@ typedef enum k_botitemdelay, k_botitemconfirm, + k_botlastturn, NUMKARTSTUFF } kartstufftype_t; diff --git a/src/dehacked.c b/src/dehacked.c index 43a820c81..d55869434 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -8608,7 +8608,8 @@ static const char *const KARTSTUFF_LIST[] = { "WRONGWAY", "BOTITEMDELAY", - "BOTITEMCONFIRM" + "BOTITEMCONFIRM", + "BOTLASTTURN" }; #endif diff --git a/src/k_bot.c b/src/k_bot.c index 5c671aedd..2ef92c081 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -351,9 +351,8 @@ static INT16 K_BotSteerFromWalls(player_t *player, botprediction_t *predict) return badsteerglobal; } -static void K_SteerFromObject(mobj_t *bot, mobj_t *thing, boolean towards, fixed_t turnmul) +static void K_SteerFromObject(mobj_t *bot, mobj_t *thing, boolean towards, INT16 amount) { - INT16 amount = 3*KART_FULLTURN; angle_t destangle = R_PointToAngle2(bot->x, bot->y, thing->x, thing->y); angle_t angle; @@ -374,11 +373,6 @@ static void K_SteerFromObject(mobj_t *bot, mobj_t *thing, boolean towards, fixed amount = -amount; } - if (turnmul != FRACUNIT) - { - amount = FixedMul(amount * FRACUNIT, turnmul) / FRACUNIT; - } - angle = (bot->angle - destangle); if (angle < ANGLE_180) @@ -394,6 +388,7 @@ static void K_SteerFromObject(mobj_t *bot, mobj_t *thing, boolean towards, fixed static inline boolean K_BotSteerObjects(mobj_t *thing) { INT16 anglediff; + fixed_t dist; angle_t destangle, angle; if (!botmo || P_MobjWasRemoved(botmo) || !botmo->player) @@ -406,7 +401,23 @@ static inline boolean K_BotSteerObjects(mobj_t *thing) return true; } - if (P_AproxDistance(botmo->x - thing->x, botmo->y - thing->y) > distancetocheck) + if (botmo == thing) + { + return true; + } + + dist = P_AproxDistance(P_AproxDistance( + botmo->x - thing->x, + botmo->y - thing->y), + (botmo->z - thing->z) / 4 + ); + + if (dist > distancetocheck) + { + return true; + } + + if (!P_CheckSight(botmo, thing)) { return true; } @@ -440,33 +451,34 @@ static inline boolean K_BotSteerObjects(mobj_t *thing) case MT_SSMINE_SHIELD: case MT_BALLHOG: case MT_SPB: - K_SteerFromObject(botmo, thing, false, FRACUNIT); + case MT_BUBBLESHIELDTRAP: + K_SteerFromObject(botmo, thing, false, 2*KART_FULLTURN); break; case MT_RANDOMITEM: - if (anglediff > 25) + if (anglediff > 90) { break; } if (P_CanPickupItem(botmo->player, 1)) { - K_SteerFromObject(botmo, thing, true, FRACUNIT); + K_SteerFromObject(botmo, thing, true, 2*KART_FULLTURN); } break; case MT_FLOATINGITEM: - if (anglediff > 25) + if (anglediff > 90) { break; } if (P_CanPickupItem(botmo->player, 3)) { - K_SteerFromObject(botmo, thing, true, FRACUNIT); + K_SteerFromObject(botmo, thing, true, 2*KART_FULLTURN); } break; case MT_RING: case MT_FLINGRING: - if (anglediff > 25) + if (anglediff > 90) { break; } @@ -475,7 +487,7 @@ static inline boolean K_BotSteerObjects(mobj_t *thing) && P_CanPickupItem(botmo->player, 0)) && (!thing->extravalue1)) { - K_SteerFromObject(botmo, thing, true, FRACUNIT/3); + K_SteerFromObject(botmo, thing, true, (RINGTOTAL(botmo->player) <= 0 ? 2*KART_FULLTURN : KART_FULLTURN)); } break; case MT_PLAYER: @@ -483,68 +495,104 @@ static inline boolean K_BotSteerObjects(mobj_t *thing) && !thing->player->kartstuff[k_hyudorotimer] && !botmo->player->kartstuff[k_hyudorotimer]) { + INT16 attack = ((9 - botmo->player->kartspeed) * KART_FULLTURN) / 8; // Acceleration chars are more aggressive + INT16 dodge = ((9 - botmo->player->kartweight) * KART_FULLTURN) / 8; // Handling chars dodge better + // Squish if (botmo->scale > thing->scale + (mapobjectscale/8)) { - K_SteerFromObject(botmo, thing, true, FRACUNIT); + K_SteerFromObject(botmo, thing, true, KART_FULLTURN + attack); } else if (thing->scale > botmo->scale + (mapobjectscale/8)) { - K_SteerFromObject(botmo, thing, false, FRACUNIT); + K_SteerFromObject(botmo, thing, false, KART_FULLTURN + dodge); } // Invincibility else if (botmo->player->kartstuff[k_invincibilitytimer] && !thing->player->kartstuff[k_invincibilitytimer]) { - K_SteerFromObject(botmo, thing, true, FRACUNIT); + K_SteerFromObject(botmo, thing, true, KART_FULLTURN + attack); } else if (thing->player->kartstuff[k_invincibilitytimer] && !botmo->player->kartstuff[k_invincibilitytimer]) { - K_SteerFromObject(botmo, thing, false, FRACUNIT); + K_SteerFromObject(botmo, thing, false, KART_FULLTURN + dodge); + } + // Thunder Shield + else if (botmo->player->kartstuff[k_itemtype] == KITEM_THUNDERSHIELD + && thing->player->kartstuff[k_itemtype] != KITEM_THUNDERSHIELD) + { + K_SteerFromObject(botmo, thing, true, KART_FULLTURN + attack); + } + else if (thing->player->kartstuff[k_itemtype] == KITEM_THUNDERSHIELD + && botmo->player->kartstuff[k_itemtype] != KITEM_THUNDERSHIELD) + { + K_SteerFromObject(botmo, thing, false, KART_FULLTURN + dodge); + } + // Bubble Shield + else if (botmo->player->kartstuff[k_itemtype] == KITEM_BUBBLESHIELD + && thing->player->kartstuff[k_itemtype] != KITEM_BUBBLESHIELD) + { + K_SteerFromObject(botmo, thing, true, KART_FULLTURN + attack); + } + else if (thing->player->kartstuff[k_itemtype] == KITEM_BUBBLESHIELD + && botmo->player->kartstuff[k_itemtype] != KITEM_BUBBLESHIELD) + { + K_SteerFromObject(botmo, thing, false, KART_FULLTURN + dodge); } // Flame Shield // Not a check for Flame Shield && flame dash, because they could potentially flame dash at any time! else if (botmo->player->kartstuff[k_itemtype] == KITEM_FLAMESHIELD && thing->player->kartstuff[k_itemtype] != KITEM_FLAMESHIELD) { - K_SteerFromObject(botmo, thing, true, FRACUNIT); + K_SteerFromObject(botmo, thing, true, KART_FULLTURN + attack); } else if (thing->player->kartstuff[k_itemtype] == KITEM_FLAMESHIELD && botmo->player->kartstuff[k_itemtype] != KITEM_FLAMESHIELD) { - K_SteerFromObject(botmo, thing, false, FRACUNIT); + K_SteerFromObject(botmo, thing, false, KART_FULLTURN + dodge); } // Has shield item else if (botmo->player->kartstuff[k_itemheld] && !thing->player->kartstuff[k_itemheld]) { - K_SteerFromObject(botmo, thing, true, FRACUNIT); + K_SteerFromObject(botmo, thing, true, KART_FULLTURN + attack); } else if (thing->player->kartstuff[k_itemheld] && !botmo->player->kartstuff[k_itemheld]) { - K_SteerFromObject(botmo, thing, false, FRACUNIT); + K_SteerFromObject(botmo, thing, false, KART_FULLTURN + dodge); } // Ring Sting else if ((thing->player->kartstuff[k_rings] <= 0) && !(botmo->player->kartstuff[k_rings] <= 0)) { - K_SteerFromObject(botmo, thing, true, FRACUNIT); + K_SteerFromObject(botmo, thing, true, KART_FULLTURN + attack); } else if ((botmo->player->kartstuff[k_rings] <= 0) && !(thing->player->kartstuff[k_rings] <= 0)) { - K_SteerFromObject(botmo, thing, false, FRACUNIT); + K_SteerFromObject(botmo, thing, false, KART_FULLTURN + dodge); } else { // After ALL of that, we can do standard bumping - fixed_t weightdiff = K_GetMobjWeight(botmo, thing) - K_GetMobjWeight(thing, botmo); + const fixed_t ourweight = K_GetMobjWeight(botmo, thing); + const fixed_t theirweight = K_GetMobjWeight(thing, botmo); + fixed_t weightdiff = 0; - if (weightdiff > mapobjectscale) + if (anglediff > 90) { - K_SteerFromObject(botmo, thing, true, FRACUNIT); + weightdiff = theirweight - ourweight; } else { - K_SteerFromObject(botmo, thing, false, FRACUNIT); + weightdiff = ourweight - theirweight; + } + + if (weightdiff > mapobjectscale) + { + K_SteerFromObject(botmo, thing, true, (KART_FULLTURN + attack) / 2); + } + else + { + K_SteerFromObject(botmo, thing, false, (KART_FULLTURN + dodge) * 2); } } } @@ -552,7 +600,7 @@ static inline boolean K_BotSteerObjects(mobj_t *thing) default: if (thing->flags & (MF_SOLID|MF_ENEMY|MF_BOSS|MF_PAIN|MF_MISSILE|MF_FIRE)) { - K_SteerFromObject(botmo, thing, false, FRACUNIT); + K_SteerFromObject(botmo, thing, false, 2*KART_FULLTURN); } break; } @@ -567,7 +615,7 @@ static INT16 K_BotFindObjects(player_t *player) badsteerglobal = 0; botmo = player->mo; - distancetocheck = (player->mo->radius * 8) + (player->speed * 2); + distancetocheck = (player->mo->radius * 16) + (player->speed * 4); xl = (unsigned)(botmo->x - distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; xh = (unsigned)(botmo->x + distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; @@ -587,6 +635,50 @@ static INT16 K_BotFindObjects(player_t *player) return badsteerglobal; } +static boolean K_BotUseItemNearPlayer(player_t *player, ticcmd_t *cmd, fixed_t radius) +{ + UINT8 i; + + if (player->pflags & PF_ATTACKDOWN) + { + 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->powers[pw_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 + ); + + if (dist <= radius) + { + cmd->buttons |= BT_ATTACK; + return true; + } + } + + return false; +} + void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) { botprediction_t *predict = NULL; @@ -615,7 +707,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) // Start boost handler if (leveltime <= starttime) { - if (leveltime >= starttime-40) + if (leveltime >= starttime-35) cmd->buttons |= BT_ACCELERATE; return; } @@ -656,6 +748,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) else { INT16 wallsteer = K_BotSteerFromWalls(player, predict); + INT16 objectsteer = 0; fixed_t rad = predict->radius - (player->mo->radius*4); fixed_t dirdist = K_DistanceOfLineFromPoint( player->mo->x, player->mo->y, @@ -681,11 +774,18 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) // Full speed ahead! cmd->forwardmove = 50; - if (wallsteer != 0) + if (anglediff > 60) { - turnamt = wallsteer; + // Actually, don't go too fast... + cmd->forwardmove /= 2; + cmd->buttons |= BT_BRAKE; } - else if (dirdist <= rad) + else if (anglediff <= 15 || dirdist <= rad) + { + objectsteer = K_BotFindObjects(player); + } + + if (dirdist <= rad) { fixed_t speedmul = FixedMul(player->speed, K_GetKartSpeed(player, false)); fixed_t speedrad = rad/4; @@ -698,11 +798,16 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) // Increase radius with speed // At low speed, the CPU will try to be more accurate // At high speed, they're more likely to lawnmower - speedrad += FixedMul(speedmul, rad/2); + speedrad += FixedMul(speedmul, (3*rad/4) - speedrad); + + if (speedrad < 2*player->mo->radius) + { + speedrad = 2*player->mo->radius; + } if (dirdist <= speedrad) { - // Don't need to turn! + // Don't turn at all turnamt = 0; } else @@ -710,35 +815,20 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) // Make minor adjustments turnamt /= 4; } - - // Steer towards or away from nearby objects! - turnamt += K_BotFindObjects(player); } - if (anglediff > 60) + if (wallsteer != 0) { - // Actually, don't go too fast... - cmd->forwardmove /= 2; - cmd->buttons |= BT_BRAKE; + turnamt += wallsteer; + } + + if (objectsteer != 0) + { + turnamt += objectsteer; } } } - if (turnamt != 0) - { - if (turnamt > KART_FULLTURN) - { - turnamt = KART_FULLTURN; - } - else if (turnamt < -KART_FULLTURN) - { - turnamt = -KART_FULLTURN; - } - - cmd->driftturn = turnamt; - cmd->angleturn += turnamt; - } - if (player->kartstuff[k_userings] == 1) { if (!player->exiting) @@ -763,66 +853,117 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) { player->kartstuff[k_botitemdelay]--; player->kartstuff[k_botitemconfirm] = 0; - return; } - - if (player->kartstuff[k_rocketsneakertimer] > 0) + else if (player->kartstuff[k_stealingtimer] == 0 && player->kartstuff[k_stolentimer] == 0) { - if (player->kartstuff[k_botitemconfirm] > TICRATE) + if (player->kartstuff[k_eggmanexplode]) { - if (!player->kartstuff[k_sneakertimer] && !(player->pflags & PF_ATTACKDOWN)) + K_BotUseItemNearPlayer(player, cmd, 128*player->mo->scale); + } + else if (player->kartstuff[k_rocketsneakertimer] > 0) + { + if (player->kartstuff[k_botitemconfirm] > TICRATE) { - cmd->buttons |= BT_ATTACK; - player->kartstuff[k_botitemconfirm] = 0; - } - } - else - { - player->kartstuff[k_botitemconfirm]++; - } - } - else - { - switch (player->kartstuff[k_itemtype]) - { - case KITEM_SNEAKER: - if ((player->kartstuff[k_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)/2 // Being slowed down too much - || player->kartstuff[k_speedboost] > (FRACUNIT/8) // Have another type of boost (tethering) - || player->kartstuff[k_botitemconfirm] > 4*TICRATE) // Held onto it for too long - { - if (!player->kartstuff[k_sneakertimer] && !(player->pflags & PF_ATTACKDOWN)) - { - cmd->buttons |= BT_ATTACK; - player->kartstuff[k_botitemconfirm] = 2*TICRATE; - } - } - else - { - player->kartstuff[k_botitemconfirm]++; - } - break; - case KITEM_ROCKETSNEAKER: - if (player->kartstuff[k_rocketsneakertimer] <= 0) + if (!player->kartstuff[k_sneakertimer] && !(player->pflags & PF_ATTACKDOWN)) { cmd->buttons |= BT_ATTACK; player->kartstuff[k_botitemconfirm] = 0; } - break; - case KITEM_INVINCIBILITY: - case KITEM_SPB: - case KITEM_GROW: - case KITEM_SHRINK: - case KITEM_HYUDORO: - case KITEM_SUPERRING: - cmd->buttons |= BT_ATTACK; - player->kartstuff[k_botitemconfirm] = 0; - break; - default: - player->kartstuff[k_botitemconfirm] = 0; - break; + } + else + { + player->kartstuff[k_botitemconfirm]++; + } } + else + { + switch (player->kartstuff[k_itemtype]) + { + case KITEM_INVINCIBILITY: + case KITEM_SPB: + case KITEM_GROW: + case KITEM_SHRINK: + case KITEM_HYUDORO: + case KITEM_SUPERRING: + cmd->buttons |= BT_ATTACK; + player->kartstuff[k_botitemconfirm] = 0; + break; + case KITEM_SNEAKER: + if ((player->kartstuff[k_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)/2 // Being slowed down too much + || player->kartstuff[k_speedboost] > (FRACUNIT/8) // Have another type of boost (tethering) + || player->kartstuff[k_botitemconfirm] > 4*TICRATE) // Held onto it for too long + { + if (!player->kartstuff[k_sneakertimer] && !(player->pflags & PF_ATTACKDOWN)) + { + cmd->buttons |= BT_ATTACK; + player->kartstuff[k_botitemconfirm] = 2*TICRATE; + } + } + else + { + player->kartstuff[k_botitemconfirm]++; + } + break; + case KITEM_ROCKETSNEAKER: + if (player->kartstuff[k_rocketsneakertimer] <= 0) + { + cmd->buttons |= BT_ATTACK; + player->kartstuff[k_botitemconfirm] = 0; + } + break; + case KITEM_THUNDERSHIELD: + if (!K_BotUseItemNearPlayer(player, cmd, 192*player->mo->scale)) + { + if (player->kartstuff[k_botitemconfirm] > 10*TICRATE) + { + cmd->buttons |= BT_ATTACK; + player->kartstuff[k_botitemconfirm] = 0; + } + else + { + player->kartstuff[k_botitemconfirm]++; + } + } + break; + default: + player->kartstuff[k_botitemconfirm] = 0; + break; + } + } + } + } + + if (turnamt != 0) + { + if (turnamt > KART_FULLTURN) + { + turnamt = KART_FULLTURN; + } + else if (turnamt < -KART_FULLTURN) + { + turnamt = -KART_FULLTURN; + } + + if ((turnamt > 0 && player->kartstuff[k_botlastturn] >= 0) + || (turnamt < 0 && player->kartstuff[k_botlastturn] <= 0)) + { + if (turnamt > 0) + { + player->kartstuff[k_botlastturn] = 1; + } + else if (turnamt < 0) + { + player->kartstuff[k_botlastturn] = -1; + } + + cmd->driftturn = turnamt; + cmd->angleturn += turnamt; + } + else + { + player->kartstuff[k_botlastturn] = 0; } } diff --git a/src/k_kart.c b/src/k_kart.c index dd8c76ba2..3d9eac747 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -953,6 +953,7 @@ static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean sp case KITEM_SHRINK: case KITEM_HYUDORO: case KITEM_SUPERRING: + case KITEM_THUNDERSHIELD: case KRITEM_TRIPLESNEAKER: break; default: @@ -2680,6 +2681,18 @@ fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower) finalspeed = K_GetKartSpeedFromStat(kartspeed); + if (K_PlayerUsesBotMovement(player)) + { + fixed_t speedmul = FRACUNIT; + + // Give top speed a buff for bots, since it's a fairly weak stat without drifting + speedmul += ((kartspeed-1) * FRACUNIT / 8) / 10; // +10% for speed 9 + + // TODO: Rubberbanding goes here. Do it for accel too! + + finalspeed = FixedMul(finalspeed, speedmul); + } + if (player->mo && !P_MobjWasRemoved(player->mo)) finalspeed = FixedMul(finalspeed, player->mo->scale); From 6ecad2e73af842195fc77c98cc0fab5ff2090a66 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sun, 5 Apr 2020 02:13:39 -0400 Subject: [PATCH 24/85] Reduce steering code duplication --- src/k_bot.c | 114 ++++++++++++++++++++-------------------------------- 1 file changed, 44 insertions(+), 70 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index 2ef92c081..16272aa81 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -218,7 +218,7 @@ fixed_t predictx = 0, predicty = 0; static void K_SteerFromWall(mobj_t *bot, line_t *ld) { - const INT16 amount = 2*KART_FULLTURN; + const INT16 amount = 4*KART_FULLTURN; INT32 side = P_PointOnLineSide(bot->x, bot->y, ld); angle_t lineangle = R_PointToAngle2(0, 0, ld->dx, ld->dy) - ANGLE_90; angle_t destangle = R_PointToAngle2(bot->x, bot->y, predictx, predicty); @@ -498,78 +498,52 @@ static inline boolean K_BotSteerObjects(mobj_t *thing) INT16 attack = ((9 - botmo->player->kartspeed) * KART_FULLTURN) / 8; // Acceleration chars are more aggressive INT16 dodge = ((9 - botmo->player->kartweight) * KART_FULLTURN) / 8; // Handling chars dodge better - // Squish - if (botmo->scale > thing->scale + (mapobjectscale/8)) - { - K_SteerFromObject(botmo, thing, true, KART_FULLTURN + attack); - } - else if (thing->scale > botmo->scale + (mapobjectscale/8)) - { - K_SteerFromObject(botmo, thing, false, KART_FULLTURN + dodge); - } +#define PlayerAttackSteer(botcond, thingcond) \ + if ((botcond) && !(thingcond)) \ + { \ + K_SteerFromObject(botmo, thing, true, KART_FULLTURN + attack); \ + } \ + else if ((thingcond) && !(botcond)) \ + { \ + K_SteerFromObject(botmo, thing, false, KART_FULLTURN + dodge); \ + } + + // There REALLY ought to be a better way to handle this logic, right?! + // Squishing + PlayerAttackSteer( + botmo->scale > thing->scale + (mapobjectscale/8), + thing->scale > botmo->scale + (mapobjectscale/8) + ) // Invincibility - else if (botmo->player->kartstuff[k_invincibilitytimer] && !thing->player->kartstuff[k_invincibilitytimer]) - { - K_SteerFromObject(botmo, thing, true, KART_FULLTURN + attack); - } - else if (thing->player->kartstuff[k_invincibilitytimer] && !botmo->player->kartstuff[k_invincibilitytimer]) - { - K_SteerFromObject(botmo, thing, false, KART_FULLTURN + dodge); - } + else PlayerAttackSteer( + botmo->player->kartstuff[k_invincibilitytimer], + thing->player->kartstuff[k_invincibilitytimer] + ) // Thunder Shield - else if (botmo->player->kartstuff[k_itemtype] == KITEM_THUNDERSHIELD - && thing->player->kartstuff[k_itemtype] != KITEM_THUNDERSHIELD) - { - K_SteerFromObject(botmo, thing, true, KART_FULLTURN + attack); - } - else if (thing->player->kartstuff[k_itemtype] == KITEM_THUNDERSHIELD - && botmo->player->kartstuff[k_itemtype] != KITEM_THUNDERSHIELD) - { - K_SteerFromObject(botmo, thing, false, KART_FULLTURN + dodge); - } + else PlayerAttackSteer( + botmo->player->kartstuff[k_itemtype] == KITEM_THUNDERSHIELD, + thing->player->kartstuff[k_itemtype] == KITEM_THUNDERSHIELD + ) // Bubble Shield - else if (botmo->player->kartstuff[k_itemtype] == KITEM_BUBBLESHIELD - && thing->player->kartstuff[k_itemtype] != KITEM_BUBBLESHIELD) - { - K_SteerFromObject(botmo, thing, true, KART_FULLTURN + attack); - } - else if (thing->player->kartstuff[k_itemtype] == KITEM_BUBBLESHIELD - && botmo->player->kartstuff[k_itemtype] != KITEM_BUBBLESHIELD) - { - K_SteerFromObject(botmo, thing, false, KART_FULLTURN + dodge); - } + else PlayerAttackSteer( + botmo->player->kartstuff[k_itemtype] == KITEM_BUBBLESHIELD, + thing->player->kartstuff[k_itemtype] == KITEM_BUBBLESHIELD + ) // Flame Shield - // Not a check for Flame Shield && flame dash, because they could potentially flame dash at any time! - else if (botmo->player->kartstuff[k_itemtype] == KITEM_FLAMESHIELD - && thing->player->kartstuff[k_itemtype] != KITEM_FLAMESHIELD) - { - K_SteerFromObject(botmo, thing, true, KART_FULLTURN + attack); - } - else if (thing->player->kartstuff[k_itemtype] == KITEM_FLAMESHIELD - && botmo->player->kartstuff[k_itemtype] != KITEM_FLAMESHIELD) - { - K_SteerFromObject(botmo, thing, false, KART_FULLTURN + dodge); - } - // Has shield item - else if (botmo->player->kartstuff[k_itemheld] && !thing->player->kartstuff[k_itemheld]) - { - K_SteerFromObject(botmo, thing, true, KART_FULLTURN + attack); - } - else if (thing->player->kartstuff[k_itemheld] && !botmo->player->kartstuff[k_itemheld]) - { - K_SteerFromObject(botmo, thing, false, KART_FULLTURN + dodge); - } + else PlayerAttackSteer( + botmo->player->kartstuff[k_itemtype] == KITEM_FLAMESHIELD, + thing->player->kartstuff[k_itemtype] == KITEM_FLAMESHIELD + ) + // Has held item shield + else PlayerAttackSteer( + (botmo->player->kartstuff[k_itemheld] || botmo->player->kartstuff[k_eggmanheld]), + (thing->player->kartstuff[k_itemheld] || thing->player->kartstuff[k_eggmanheld]) + ) // Ring Sting - else if ((thing->player->kartstuff[k_rings] <= 0) - && !(botmo->player->kartstuff[k_rings] <= 0)) - { - K_SteerFromObject(botmo, thing, true, KART_FULLTURN + attack); - } - else if ((botmo->player->kartstuff[k_rings] <= 0) - && !(thing->player->kartstuff[k_rings] <= 0)) - { - K_SteerFromObject(botmo, thing, false, KART_FULLTURN + dodge); - } + else PlayerAttackSteer( + thing->player->kartstuff[k_rings] <= 0, + botmo->player->kartstuff[k_rings] <= 0 + ) else { // After ALL of that, we can do standard bumping @@ -666,7 +640,7 @@ static boolean K_BotUseItemNearPlayer(player_t *player, ticcmd_t *cmd, fixed_t r dist = P_AproxDistance(P_AproxDistance( player->mo->x - target->mo->x, player->mo->y - target->mo->y), - player->mo->z - target->mo->z + (player->mo->z - target->mo->z) / 4 ); if (dist <= radius) @@ -780,7 +754,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) cmd->forwardmove /= 2; cmd->buttons |= BT_BRAKE; } - else if (anglediff <= 15 || dirdist <= rad) + else if (anglediff <= 23 || dirdist <= rad) { objectsteer = K_BotFindObjects(player); } From dbb52e4aa531f446776fcbf8d829d48bce1f0c97 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sun, 5 Apr 2020 02:14:40 -0400 Subject: [PATCH 25/85] Use bananas, orbinauts, jawz, mines, and ballhog Yaaay Bananas and mines could definitely be improved, but works for now --- src/k_bot.c | 419 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/k_kart.c | 10 ++ 2 files changed, 429 insertions(+) diff --git a/src/k_bot.c b/src/k_bot.c index 16272aa81..5dbe6ea13 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -653,6 +653,43 @@ static boolean K_BotUseItemNearPlayer(player_t *player, ticcmd_t *cmd, fixed_t r return false; } +static boolean K_PlayerNearSpot(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->powers[pw_flashing]) + { + continue; + } + + dist = P_AproxDistance( + x - target->mo->x, + y - target->mo->y + ); + + if (dist <= radius) + { + return true; + } + } + + return false; +} + void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) { botprediction_t *predict = NULL; @@ -887,6 +924,388 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) player->kartstuff[k_botitemconfirm] = 0; } break; + case KITEM_BANANA: + if (!player->kartstuff[k_itemheld]) + { + cmd->buttons |= BT_ATTACK; + player->kartstuff[k_botitemconfirm] = 0; + } + else + { + SINT8 throwdir = -1; + UINT8 i; + + if (abs(turnamt) >= KART_FULLTURN/2) + { + player->kartstuff[k_botitemconfirm]++; + } + else + { + const UINT32 airtime = FixedDiv((30 * player->mo->scale) + 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); + + if (K_PlayerNearSpot(player, estx, esty, player->mo->radius * 16)) + { + player->kartstuff[k_botitemconfirm] += 4; + throwdir = 1; + } + } + + 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->powers[pw_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 <= (player->mo->radius * 16)) + { + angle_t a = player->mo->angle - R_PointToAngle2(player->mo->x, player->mo->y, target->mo->x, target->mo->y); + INT16 ad = 0; + const INT16 cone = 10; + + if (a < ANGLE_180) + { + ad = AngleFixed(a)>>FRACBITS; + } + else + { + ad = 360-(AngleFixed(a)>>FRACBITS); + } + + ad = abs(ad); + + if (ad >= 180-cone) + { + player->kartstuff[k_botitemconfirm] += 2; + throwdir = -1; + } + } + } + + if ((player->kartstuff[k_botitemconfirm] > 2*TICRATE) + || (player->kartstuff[k_bananadrag] >= TICRATE)) + { + if (throwdir == 1) + { + cmd->buttons |= BT_FORWARD; + } + else if (throwdir == -1) + { + cmd->buttons |= BT_BACKWARD; + } + + cmd->buttons |= BT_ATTACK; + player->kartstuff[k_botitemconfirm] = 0; + } + } + break; + case KITEM_ORBINAUT: + if (!player->kartstuff[k_itemheld]) + { + cmd->buttons |= BT_ATTACK; + player->kartstuff[k_botitemconfirm] = 0; + } + else if (player->kartstuff[k_position] != 1) // Hold onto orbiting items when in 1st :) + /* FALL-THRU */ + case KITEM_BALLHOG: + { + const fixed_t topspeed = K_GetKartSpeed(player, false); + fixed_t radius = (player->mo->radius * 32); + SINT8 throwdir = -1; + UINT8 i; + + if (player->speed > topspeed) + { + radius = FixedMul(radius, FixedDiv(player->speed, topspeed)); + } + + 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->powers[pw_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; + const INT16 cone = 10; + + if (a < ANGLE_180) + { + ad = AngleFixed(a)>>FRACBITS; + } + else + { + ad = 360-(AngleFixed(a)>>FRACBITS); + } + + ad = abs(ad); + + if (ad <= cone) + { + player->kartstuff[k_botitemconfirm] += 4; + throwdir = 1; + } + else if (ad >= 180-cone) + { + player->kartstuff[k_botitemconfirm] += 2; + } + } + } + + if (player->kartstuff[k_botitemconfirm] > 5*TICRATE) + { + if (throwdir == 1) + { + cmd->buttons |= BT_FORWARD; + } + else if (throwdir == -1) + { + cmd->buttons |= BT_BACKWARD; + } + + cmd->buttons |= BT_ATTACK; + player->kartstuff[k_botitemconfirm] = 0; + } + } + break; + case KITEM_JAWZ: + if (!player->kartstuff[k_itemheld]) + { + cmd->buttons |= BT_ATTACK; + player->kartstuff[k_botitemconfirm] = 0; + } + else if (player->kartstuff[k_position] != 1) // Hold onto orbiting items when in 1st :) + { + SINT8 throwdir = 1; + UINT8 i; + + player->kartstuff[k_botitemconfirm]++; + + 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->powers[pw_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 <= (player->mo->radius * 32)) + { + angle_t a = player->mo->angle - R_PointToAngle2(player->mo->x, player->mo->y, target->mo->x, target->mo->y); + INT16 ad = 0; + const INT16 cone = 10; + + if (a < ANGLE_180) + { + ad = AngleFixed(a)>>FRACBITS; + } + else + { + ad = 360-(AngleFixed(a)>>FRACBITS); + } + + ad = abs(ad); + + if (ad >= 180-cone) + { + player->kartstuff[k_botitemconfirm] += 2; + throwdir = -1; + } + } + } + + if (player->kartstuff[k_lastjawztarget] != -1) + { + player->kartstuff[k_botitemconfirm] += 4; + throwdir = 1; + } + + if (player->kartstuff[k_botitemconfirm] > 5*TICRATE) + { + if (throwdir == 1) + { + cmd->buttons |= BT_FORWARD; + } + else if (throwdir == -1) + { + cmd->buttons |= BT_BACKWARD; + } + + cmd->buttons |= BT_ATTACK; + player->kartstuff[k_botitemconfirm] = 0; + } + } + break; + case KITEM_MINE: + if (!player->kartstuff[k_itemheld]) + { + cmd->buttons |= BT_ATTACK; + player->kartstuff[k_botitemconfirm] = 0; + } + else + { + SINT8 throwdir = 0; + 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->powers[pw_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 <= (player->mo->radius * 16)) + { + angle_t a = player->mo->angle - R_PointToAngle2(player->mo->x, player->mo->y, target->mo->x, target->mo->y); + INT16 ad = 0; + const INT16 cone = 10; + + if (a < ANGLE_180) + { + ad = AngleFixed(a)>>FRACBITS; + } + else + { + ad = 360-(AngleFixed(a)>>FRACBITS); + } + + ad = abs(ad); + + if (ad >= 180-cone) + { + player->kartstuff[k_botitemconfirm] += 2; + throwdir = -1; + } + } + } + + if (abs(turnamt) >= KART_FULLTURN/2) + { + player->kartstuff[k_botitemconfirm]++; + } + else + { + UINT32 airtime = FixedDiv((30 * player->mo->scale) + player->mo->momz, gravity); + fixed_t throwspeed = FixedMul(82 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); + fixed_t estx = player->mo->x + P_ReturnThrustX(NULL, player->mo->angle, (throwspeed + player->speed) * airtime); + fixed_t esty = player->mo->y + P_ReturnThrustY(NULL, player->mo->angle, (throwspeed + player->speed) * airtime); + + if (K_PlayerNearSpot(player, estx, esty, player->mo->radius * 16)) + { + player->kartstuff[k_botitemconfirm] += 4; + throwdir = 0; + } + + airtime = FixedDiv((40 * player->mo->scale) + player->mo->momz, gravity); + throwspeed = FixedMul(82 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)) * 2; + estx = player->mo->x + P_ReturnThrustX(NULL, player->mo->angle, (throwspeed + player->speed) * airtime); + esty = player->mo->y + P_ReturnThrustY(NULL, player->mo->angle, (throwspeed + player->speed) * airtime); + + if (K_PlayerNearSpot(player, estx, esty, player->mo->radius * 16)) + { + player->kartstuff[k_botitemconfirm] += 4; + throwdir = 1; + } + } + + if ((player->kartstuff[k_botitemconfirm] > 2*TICRATE) + || (player->kartstuff[k_bananadrag] >= TICRATE)) + { + if (throwdir == 1) + { + cmd->buttons |= BT_FORWARD; + } + else if (throwdir == -1) + { + cmd->buttons |= BT_BACKWARD; + } + + cmd->buttons |= BT_ATTACK; + player->kartstuff[k_botitemconfirm] = 0; + } + } + break; case KITEM_THUNDERSHIELD: if (!K_BotUseItemNearPlayer(player, cmd, 192*player->mo->scale)) { diff --git a/src/k_kart.c b/src/k_kart.c index 3d9eac747..f2f979387 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -948,6 +948,11 @@ static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean sp case KITEM_SNEAKER: case KITEM_ROCKETSNEAKER: case KITEM_INVINCIBILITY: + case KITEM_BANANA: + case KITEM_ORBINAUT: + case KITEM_JAWZ: + case KITEM_MINE: + case KITEM_BALLHOG: case KITEM_SPB: case KITEM_GROW: case KITEM_SHRINK: @@ -955,6 +960,11 @@ static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean sp case KITEM_SUPERRING: case KITEM_THUNDERSHIELD: case KRITEM_TRIPLESNEAKER: + case KRITEM_TRIPLEBANANA: + case KRITEM_TENFOLDBANANA: + case KRITEM_TRIPLEORBINAUT: + case KRITEM_QUADORBINAUT: + case KRITEM_DUALJAWZ: break; default: return 0; From 3f639b4f8a74c31372b277cd88783d42d01e72aa Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Tue, 7 Apr 2020 00:40:44 -0400 Subject: [PATCH 26/85] Rubberbanding! --- src/k_bot.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/k_bot.h | 1 + src/k_kart.c | 25 +++++++++++++++++++------ 3 files changed, 70 insertions(+), 6 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index 5dbe6ea13..c882bc3e2 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -100,6 +100,56 @@ boolean K_BotCanTakeCut(player_t *player) return false; } +fixed_t K_BotRubberband(player_t *player) +{ + fixed_t rubberband = FRACUNIT; + player_t *besthumanplayer = NULL; + UINT8 i; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator || players[i].exiting) + { + continue; + } + + if (&players[i] == player || players[i].bot) + { + continue; + } + + if (besthumanplayer == NULL || players[i].distancetofinish < besthumanplayer->distancetofinish) + { + besthumanplayer = &players[i]; + } + } + + if (besthumanplayer != NULL) + { + UINT32 wanteddist = besthumanplayer->distancetofinish; // TODO: Add difficulty here + + if (wanteddist > player->distancetofinish) + { + rubberband = FRACUNIT + (2 * (player->distancetofinish - wanteddist)); + } + else + { + rubberband = FRACUNIT + (8 * (player->distancetofinish - wanteddist)); + } + } + + if (rubberband > 2*FRACUNIT) + { + rubberband = 2*FRACUNIT; + } + else if (rubberband < 2*FRACUNIT/3) + { + rubberband = 2*FRACUNIT/3; + } + + return rubberband; +} + static fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, fixed_t v2y, fixed_t cx, fixed_t cy) { fixed_t v1toc[2] = {cx - v1x, cy - v1y}; diff --git a/src/k_bot.h b/src/k_bot.h index 797843dea..38e3af27b 100644 --- a/src/k_bot.h +++ b/src/k_bot.h @@ -22,4 +22,5 @@ typedef struct botprediction_s { void K_AddBots(SINT8 numbots); boolean K_PlayerUsesBotMovement(player_t *player); boolean K_BotCanTakeCut(player_t *player); +fixed_t K_BotRubberband(player_t *player); void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd); diff --git a/src/k_kart.c b/src/k_kart.c index f2f979387..ee0b9d95c 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2693,13 +2693,11 @@ fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower) if (K_PlayerUsesBotMovement(player)) { - fixed_t speedmul = FRACUNIT; + fixed_t speedmul = K_BotRubberband(player); // Give top speed a buff for bots, since it's a fairly weak stat without drifting speedmul += ((kartspeed-1) * FRACUNIT / 8) / 10; // +10% for speed 9 - // TODO: Rubberbanding goes here. Do it for accel too! - finalspeed = FixedMul(finalspeed, speedmul); } @@ -2722,6 +2720,11 @@ fixed_t K_GetKartAccel(player_t *player) //k_accel += 3 * (9 - kartspeed); // 36 - 60 k_accel += 4 * (9 - kartspeed); // 32 - 64 + if (K_PlayerUsesBotMovement(player)) + { + k_accel = FixedMul(k_accel, K_BotRubberband(player)); + } + return FixedMul(k_accel, FRACUNIT+player->kartstuff[k_accelboost]); } @@ -6474,6 +6477,13 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) angle_t nextbestmomdelta = momdelta; size_t i = 0U; + if (K_PlayerUsesBotMovement(player)) + { + // Try to force bots to use a next waypoint + nextbestdelta = ANGLE_MAX; + nextbestmomdelta = ANGLE_MAX; + } + if ((waypoint->nextwaypoints != NULL) && (waypoint->numnextwaypoints > 0U)) { for (i = 0U; i < waypoint->numnextwaypoints; i++) @@ -6806,6 +6816,12 @@ INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue) if (player->spectator) return turnvalue; + if (K_PlayerUsesBotMovement(player)) + { + turnvalue = FixedMul(turnvalue, (5*FRACUNIT)/4); // Base increase to turning + turnvalue = FixedMul(turnvalue, K_BotRubberband(player)); + } + if (player->kartstuff[k_drift] != 0 && P_IsObjectOnGround(player->mo)) { // If we're drifting we have a completely different turning value @@ -6826,9 +6842,6 @@ INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue) if (EITHERSNEAKER(player) || player->kartstuff[k_invincibilitytimer] || player->kartstuff[k_growshrinktimer] > 0) turnvalue = FixedMul(turnvalue, (5*FRACUNIT)/4); - if (K_PlayerUsesBotMovement(player)) - turnvalue = FixedMul(turnvalue, (5*FRACUNIT)/4); - return turnvalue; } From fe376a388b3a7bbb4c6b0c0f937b8f8fb79137ec Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Tue, 7 Apr 2020 00:53:43 -0400 Subject: [PATCH 27/85] Slight adjustments --- src/k_bot.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/k_bot.c b/src/k_bot.c index c882bc3e2..583c6c9a2 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -130,10 +130,12 @@ fixed_t K_BotRubberband(player_t *player) if (wanteddist > player->distancetofinish) { - rubberband = FRACUNIT + (2 * (player->distancetofinish - wanteddist)); + // When ahead, they will rubberband much less than when behind + rubberband = FRACUNIT + (player->distancetofinish - wanteddist); } else { + // Catch up to 1st! rubberband = FRACUNIT + (8 * (player->distancetofinish - wanteddist)); } } From d0a6c43c8974f55edf5105d71e01417a009b2580 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Thu, 9 Apr 2020 02:52:41 -0400 Subject: [PATCH 28/85] Minor things --- src/d_clisrv.c | 3 ++- src/k_bot.c | 19 +++++-------------- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index c73ee04ec..0ca8eb2b8 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3785,7 +3785,8 @@ boolean SV_SpawnServer(void) CL_ConnectToServer(false); else doomcom->numslots = 1; - K_AddBots(7); // test + // TEST + K_AddBots(7); } return SV_AddWaitingPlayers(); diff --git a/src/k_bot.c b/src/k_bot.c index 583c6c9a2..445b4d250 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -59,9 +59,9 @@ void K_AddBots(SINT8 numbots) // test skins if (numbots == 6) - WRITEUINT8(buf_p, 0); - else if (numbots == 5) WRITEUINT8(buf_p, 1); + else if (numbots == 5) + WRITEUINT8(buf_p, 0); else if (numbots == 4) WRITEUINT8(buf_p, 2); else if (numbots == 3) @@ -128,12 +128,7 @@ fixed_t K_BotRubberband(player_t *player) { UINT32 wanteddist = besthumanplayer->distancetofinish; // TODO: Add difficulty here - if (wanteddist > player->distancetofinish) - { - // When ahead, they will rubberband much less than when behind - rubberband = FRACUNIT + (player->distancetofinish - wanteddist); - } - else + if (wanteddist < player->distancetofinish) { // Catch up to 1st! rubberband = FRACUNIT + (8 * (player->distancetofinish - wanteddist)); @@ -144,10 +139,6 @@ fixed_t K_BotRubberband(player_t *player) { rubberband = 2*FRACUNIT; } - else if (rubberband < 2*FRACUNIT/3) - { - rubberband = 2*FRACUNIT/3; - } return rubberband; } @@ -373,8 +364,8 @@ static INT16 K_BotSteerFromWalls(player_t *player, botprediction_t *predict) botmo = player->mo; distancetocheck = player->mo->radius * 8; - tmx = player->mo->x + P_ReturnThrustX(NULL, player->mo->angle, player->speed); - tmy = player->mo->y + P_ReturnThrustY(NULL, player->mo->angle, player->speed); + tmx = player->mo->x; + tmy = player->mo->y; predictx = predict->x; predicty = predict->y; From cc286fc8107ec269676d8402de5a4a939b753ac5 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Wed, 15 Apr 2020 22:11:25 -0400 Subject: [PATCH 29/85] If already on a shortcut, bots can continue to use shortcut waypoints --- src/k_kart.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index ee0b9d95c..9594dd4a0 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6488,11 +6488,18 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) { for (i = 0U; i < waypoint->numnextwaypoints; i++) { - if (K_PlayerUsesBotMovement(player) - && K_GetWaypointIsShortcut(waypoint->nextwaypoints[i]) - && !K_BotCanTakeCut(player)) + if (K_PlayerUsesBotMovement(player) == true + && K_GetWaypointIsShortcut(waypoint->nextwaypoints[i]) == true + && K_BotCanTakeCut(player) == false) { - continue; + // Bots that aren't able to take a shortcut will ignore shortcut waypoints. + // (However, if they're already on a shortcut, then we want them to keep going.) + + if (player->nextwaypoint == NULL + || K_GetWaypointIsShortcut(player->nextwaypoint) == false) + { + continue; + } } angletowaypoint = R_PointToAngle2( From 39964491e23615000b075cfb1ddd11c360e04780 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sun, 19 Apr 2020 05:27:44 -0400 Subject: [PATCH 30/85] Don't steer toward rings when you have Thunder Shield --- src/k_bot.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/k_bot.c b/src/k_bot.c index 445b4d250..f6014f152 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -528,7 +528,8 @@ static inline boolean K_BotSteerObjects(mobj_t *thing) if ((RINGTOTAL(botmo->player) < 20 && !botmo->player->kartstuff[k_ringlock] && P_CanPickupItem(botmo->player, 0)) - && (!thing->extravalue1)) + && !thing->extravalue1 + && (botmo->player->kartstuff[k_itemtype] != KITEM_THUNDERSHIELD)) { K_SteerFromObject(botmo, thing, true, (RINGTOTAL(botmo->player) <= 0 ? 2*KART_FULLTURN : KART_FULLTURN)); } From 4d3db89a1564caeea8ad372ae8a5ee150ea3df14 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sun, 19 Apr 2020 05:28:59 -0400 Subject: [PATCH 31/85] Reduce max catchup top speed --- src/k_kart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index af4837aeb..91f25e042 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2704,7 +2704,7 @@ fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower) if (K_PlayerUsesBotMovement(player)) { - fixed_t speedmul = K_BotRubberband(player); + fixed_t speedmul = FRACUNIT + ((K_BotRubberband(player) - FRACUNIT) / 2); // Give top speed a buff for bots, since it's a fairly weak stat without drifting speedmul += ((kartspeed-1) * FRACUNIT / 8) / 10; // +10% for speed 9 From 21bab775b139530884b34900eb1ed0b311c784a6 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sun, 19 Apr 2020 09:07:29 -0400 Subject: [PATCH 32/85] Add difficulty settings --- src/d_clisrv.c | 4 ++ src/d_player.h | 19 ++++++-- src/dehacked.c | 6 +-- src/g_game.c | 3 ++ src/k_bot.c | 121 +++++++++++++++++++++++++++---------------------- src/k_bot.h | 5 +- src/k_kart.c | 30 +++++++++--- 7 files changed, 117 insertions(+), 71 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 0ca8eb2b8..818eeb659 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3616,6 +3616,7 @@ static void Got_AddBot(UINT8 **p, INT32 playernum) { INT16 newplayernum; UINT8 skinnum = 0; + UINT8 difficulty = MAXBOTDIFFICULTY; if (playernum != serverplayer && !IsPlayerAdmin(playernum)) { @@ -3634,6 +3635,7 @@ static void Got_AddBot(UINT8 **p, INT32 playernum) newplayernum = (UINT8)READUINT8(*p); skinnum = (UINT8)READUINT8(*p); + difficulty = (UINT8)READUINT8(*p); CONS_Debug(DBG_NETPLAY, "addbot: %d\n", newplayernum); @@ -3647,6 +3649,8 @@ static void Got_AddBot(UINT8 **p, INT32 playernum) players[newplayernum].splitscreenindex = 0; players[newplayernum].bot = true; + players[newplayernum].botvars.difficulty = difficulty; + CONS_Printf("%d == %d\n", difficulty, players[newplayernum].botvars.difficulty); players[newplayernum].skincolor = skins[skinnum].prefcolor; sprintf(player_names[newplayernum], "%s", skins[skinnum].realname); diff --git a/src/d_player.h b/src/d_player.h index 278361cfa..f43c90f60 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -347,10 +347,6 @@ typedef enum k_killfield, // How long have you been in the kill field, stay in too long and lose a bumper k_wrongway, // Display WRONG WAY on screen - k_botitemdelay, - k_botitemconfirm, - k_botlastturn, - NUMKARTSTUFF } kartstufftype_t; @@ -414,6 +410,17 @@ typedef enum RW_RAIL = 32 } ringweapons_t; +// player_t struct for all bot variables +typedef struct botvars_s +{ + UINT8 difficulty; + + tic_t itemdelay; + tic_t itemconfirm; + + INT16 lastturn; +} botvars_t; + // ======================================================================== // PLAYER STRUCTURE // ======================================================================== @@ -587,7 +594,9 @@ typedef struct player_s angle_t awayviewaiming; // Used for cut-away view boolean spectator; - UINT8 bot; + + boolean bot; + botvars_t botvars; tic_t jointime; // Timer when player joins game to change skin/color diff --git a/src/dehacked.c b/src/dehacked.c index d55869434..60d61903a 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -8605,11 +8605,7 @@ static const char *const KARTSTUFF_LIST[] = { "SPRINGSTARS", "SPRINGCOLOR", "KILLFIELD", - "WRONGWAY", - - "BOTITEMDELAY", - "BOTITEMCONFIRM", - "BOTLASTTURN" + "WRONGWAY" }; #endif diff --git a/src/g_game.c b/src/g_game.c index fa4ae8b00..ac20c3c25 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2572,6 +2572,7 @@ void G_PlayerReborn(INT32 player) UINT8 splitscreenindex; boolean spectator; boolean bot; + UINT8 botdifficulty; SINT8 pity; // SRB2kart @@ -2624,6 +2625,7 @@ void G_PlayerReborn(INT32 player) mare = players[player].mare; bot = players[player].bot; + botdifficulty = players[player].botvars.difficulty; pity = players[player].pity; // SRB2kart @@ -2703,6 +2705,7 @@ void G_PlayerReborn(INT32 player) p->mare = mare; p->bot = bot; + p->botvars.difficulty = botdifficulty; p->pity = pity; // SRB2kart diff --git a/src/k_bot.c b/src/k_bot.c index f6014f152..093c43976 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -27,13 +27,14 @@ void K_AddBots(SINT8 numbots) { UINT8 newplayernum = 0; + UINT8 difficulty = MAXBOTDIFFICULTY; if (dedicated) newplayernum = 1; while (numbots > 0) { - UINT8 buf[2]; + UINT8 buf[3]; UINT8 *buf_p = buf; numbots--; @@ -73,8 +74,15 @@ void K_AddBots(SINT8 numbots) else WRITEUINT8(buf_p, 10); + WRITEUINT8(buf_p, difficulty); + SendNetXCmd(XD_ADDBOT, buf, buf_p - buf); + if (difficulty > 0) + { + difficulty--; + } + DEBFILE(va("Server added bot %d\n", newplayernum)); // use the next free slot (we can't put playeringame[newplayernum] = true here) newplayernum++; @@ -103,7 +111,7 @@ boolean K_BotCanTakeCut(player_t *player) fixed_t K_BotRubberband(player_t *player) { fixed_t rubberband = FRACUNIT; - player_t *besthumanplayer = NULL; + player_t *firstplace = NULL; UINT8 i; for (i = 0; i < MAXPLAYERS; i++) @@ -113,25 +121,32 @@ fixed_t K_BotRubberband(player_t *player) continue; } - if (&players[i] == player || players[i].bot) + /*if (players[i].bot) { continue; - } + }*/ - if (besthumanplayer == NULL || players[i].distancetofinish < besthumanplayer->distancetofinish) + if (firstplace == NULL || players[i].distancetofinish < firstplace->distancetofinish) { - besthumanplayer = &players[i]; + firstplace = &players[i]; } } - if (besthumanplayer != NULL) + if (firstplace != NULL) { - UINT32 wanteddist = besthumanplayer->distancetofinish; // TODO: Add difficulty here + const UINT32 spacing = 1024; + UINT32 easiness = (MAXBOTDIFFICULTY - player->botvars.difficulty); + UINT32 wanteddist = firstplace->distancetofinish + (spacing * easiness); if (wanteddist < player->distancetofinish) { // Catch up to 1st! - rubberband = FRACUNIT + (8 * (player->distancetofinish - wanteddist)); + rubberband = FRACUNIT + (player->botvars.difficulty * (player->distancetofinish - wanteddist)); + } + + if (P_IsDisplayPlayer(player)) + { + CONS_Printf("difficulty: %d, easiness: %d, wanted: %d, rubberband: %d\n", player->botvars.difficulty, easiness, wanteddist, rubberband); } } @@ -904,10 +919,10 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } else { - if (player->kartstuff[k_botitemdelay]) + if (player->botvars.itemdelay) { - player->kartstuff[k_botitemdelay]--; - player->kartstuff[k_botitemconfirm] = 0; + player->botvars.itemdelay--; + player->botvars.itemconfirm = 0; } else if (player->kartstuff[k_stealingtimer] == 0 && player->kartstuff[k_stolentimer] == 0) { @@ -917,17 +932,17 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } else if (player->kartstuff[k_rocketsneakertimer] > 0) { - if (player->kartstuff[k_botitemconfirm] > TICRATE) + if (player->botvars.itemconfirm > TICRATE) { if (!player->kartstuff[k_sneakertimer] && !(player->pflags & PF_ATTACKDOWN)) { cmd->buttons |= BT_ATTACK; - player->kartstuff[k_botitemconfirm] = 0; + player->botvars.itemconfirm = 0; } } else { - player->kartstuff[k_botitemconfirm]++; + player->botvars.itemconfirm++; } } else @@ -941,38 +956,38 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) case KITEM_HYUDORO: case KITEM_SUPERRING: cmd->buttons |= BT_ATTACK; - player->kartstuff[k_botitemconfirm] = 0; + player->botvars.itemconfirm = 0; break; case KITEM_SNEAKER: if ((player->kartstuff[k_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)/2 // Being slowed down too much || player->kartstuff[k_speedboost] > (FRACUNIT/8) // Have another type of boost (tethering) - || player->kartstuff[k_botitemconfirm] > 4*TICRATE) // Held onto it for too long + || player->botvars.itemconfirm > 4*TICRATE) // Held onto it for too long { if (!player->kartstuff[k_sneakertimer] && !(player->pflags & PF_ATTACKDOWN)) { cmd->buttons |= BT_ATTACK; - player->kartstuff[k_botitemconfirm] = 2*TICRATE; + player->botvars.itemconfirm = 2*TICRATE; } } else { - player->kartstuff[k_botitemconfirm]++; + player->botvars.itemconfirm++; } break; case KITEM_ROCKETSNEAKER: if (player->kartstuff[k_rocketsneakertimer] <= 0) { cmd->buttons |= BT_ATTACK; - player->kartstuff[k_botitemconfirm] = 0; + player->botvars.itemconfirm = 0; } break; case KITEM_BANANA: if (!player->kartstuff[k_itemheld]) { cmd->buttons |= BT_ATTACK; - player->kartstuff[k_botitemconfirm] = 0; + player->botvars.itemconfirm = 0; } else { @@ -981,7 +996,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if (abs(turnamt) >= KART_FULLTURN/2) { - player->kartstuff[k_botitemconfirm]++; + player->botvars.itemconfirm++; } else { @@ -992,7 +1007,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if (K_PlayerNearSpot(player, estx, esty, player->mo->radius * 16)) { - player->kartstuff[k_botitemconfirm] += 4; + player->botvars.itemconfirm += 4; throwdir = 1; } } @@ -1042,13 +1057,13 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if (ad >= 180-cone) { - player->kartstuff[k_botitemconfirm] += 2; + player->botvars.itemconfirm += 2; throwdir = -1; } } } - if ((player->kartstuff[k_botitemconfirm] > 2*TICRATE) + if ((player->botvars.itemconfirm > 2*TICRATE) || (player->kartstuff[k_bananadrag] >= TICRATE)) { if (throwdir == 1) @@ -1061,7 +1076,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } cmd->buttons |= BT_ATTACK; - player->kartstuff[k_botitemconfirm] = 0; + player->botvars.itemconfirm = 0; } } break; @@ -1069,7 +1084,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if (!player->kartstuff[k_itemheld]) { cmd->buttons |= BT_ATTACK; - player->kartstuff[k_botitemconfirm] = 0; + player->botvars.itemconfirm = 0; } else if (player->kartstuff[k_position] != 1) // Hold onto orbiting items when in 1st :) /* FALL-THRU */ @@ -1130,17 +1145,17 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if (ad <= cone) { - player->kartstuff[k_botitemconfirm] += 4; + player->botvars.itemconfirm += 4; throwdir = 1; } else if (ad >= 180-cone) { - player->kartstuff[k_botitemconfirm] += 2; + player->botvars.itemconfirm += 2; } } } - if (player->kartstuff[k_botitemconfirm] > 5*TICRATE) + if (player->botvars.itemconfirm > 5*TICRATE) { if (throwdir == 1) { @@ -1152,7 +1167,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } cmd->buttons |= BT_ATTACK; - player->kartstuff[k_botitemconfirm] = 0; + player->botvars.itemconfirm = 0; } } break; @@ -1160,14 +1175,14 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if (!player->kartstuff[k_itemheld]) { cmd->buttons |= BT_ATTACK; - player->kartstuff[k_botitemconfirm] = 0; + player->botvars.itemconfirm = 0; } else if (player->kartstuff[k_position] != 1) // Hold onto orbiting items when in 1st :) { SINT8 throwdir = 1; UINT8 i; - player->kartstuff[k_botitemconfirm]++; + player->botvars.itemconfirm++; for (i = 0; i < MAXPLAYERS; i++) { @@ -1214,7 +1229,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if (ad >= 180-cone) { - player->kartstuff[k_botitemconfirm] += 2; + player->botvars.itemconfirm += 2; throwdir = -1; } } @@ -1222,11 +1237,11 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if (player->kartstuff[k_lastjawztarget] != -1) { - player->kartstuff[k_botitemconfirm] += 4; + player->botvars.itemconfirm += 4; throwdir = 1; } - if (player->kartstuff[k_botitemconfirm] > 5*TICRATE) + if (player->botvars.itemconfirm > 5*TICRATE) { if (throwdir == 1) { @@ -1238,7 +1253,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } cmd->buttons |= BT_ATTACK; - player->kartstuff[k_botitemconfirm] = 0; + player->botvars.itemconfirm = 0; } } break; @@ -1246,7 +1261,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if (!player->kartstuff[k_itemheld]) { cmd->buttons |= BT_ATTACK; - player->kartstuff[k_botitemconfirm] = 0; + player->botvars.itemconfirm = 0; } else { @@ -1298,7 +1313,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if (ad >= 180-cone) { - player->kartstuff[k_botitemconfirm] += 2; + player->botvars.itemconfirm += 2; throwdir = -1; } } @@ -1306,7 +1321,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if (abs(turnamt) >= KART_FULLTURN/2) { - player->kartstuff[k_botitemconfirm]++; + player->botvars.itemconfirm++; } else { @@ -1317,7 +1332,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if (K_PlayerNearSpot(player, estx, esty, player->mo->radius * 16)) { - player->kartstuff[k_botitemconfirm] += 4; + player->botvars.itemconfirm += 4; throwdir = 0; } @@ -1328,12 +1343,12 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if (K_PlayerNearSpot(player, estx, esty, player->mo->radius * 16)) { - player->kartstuff[k_botitemconfirm] += 4; + player->botvars.itemconfirm += 4; throwdir = 1; } } - if ((player->kartstuff[k_botitemconfirm] > 2*TICRATE) + if ((player->botvars.itemconfirm > 2*TICRATE) || (player->kartstuff[k_bananadrag] >= TICRATE)) { if (throwdir == 1) @@ -1346,26 +1361,26 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } cmd->buttons |= BT_ATTACK; - player->kartstuff[k_botitemconfirm] = 0; + player->botvars.itemconfirm = 0; } } break; case KITEM_THUNDERSHIELD: if (!K_BotUseItemNearPlayer(player, cmd, 192*player->mo->scale)) { - if (player->kartstuff[k_botitemconfirm] > 10*TICRATE) + if (player->botvars.itemconfirm > 10*TICRATE) { cmd->buttons |= BT_ATTACK; - player->kartstuff[k_botitemconfirm] = 0; + player->botvars.itemconfirm = 0; } else { - player->kartstuff[k_botitemconfirm]++; + player->botvars.itemconfirm++; } } break; default: - player->kartstuff[k_botitemconfirm] = 0; + player->botvars.itemconfirm = 0; break; } } @@ -1383,16 +1398,16 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) turnamt = -KART_FULLTURN; } - if ((turnamt > 0 && player->kartstuff[k_botlastturn] >= 0) - || (turnamt < 0 && player->kartstuff[k_botlastturn] <= 0)) + if ((turnamt > 0 && player->botvars.lastturn >= 0) + || (turnamt < 0 && player->botvars.lastturn <= 0)) { if (turnamt > 0) { - player->kartstuff[k_botlastturn] = 1; + player->botvars.lastturn = 1; } else if (turnamt < 0) { - player->kartstuff[k_botlastturn] = -1; + player->botvars.lastturn = -1; } cmd->driftturn = turnamt; @@ -1400,7 +1415,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } else { - player->kartstuff[k_botlastturn] = 0; + player->botvars.lastturn = 0; } } diff --git a/src/k_bot.h b/src/k_bot.h index 38e3af27b..4a5af7122 100644 --- a/src/k_bot.h +++ b/src/k_bot.h @@ -7,10 +7,13 @@ // terms of the GNU General Public License, version 2. // See the 'LICENSE' file for more details. //----------------------------------------------------------------------------- -/// \file b_bot.h +/// \file k_bot.h /// \brief Basic bot handling #include "k_waypoint.h" +#include "d_player.h" + +#define MAXBOTDIFFICULTY 9 // Path that bot will attempt to take typedef struct botprediction_s { diff --git a/src/k_kart.c b/src/k_kart.c index 91f25e042..e36627ae3 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -801,8 +801,8 @@ static void K_KartGetItemResult(player_t *player, SINT8 getitem) if (getitem == KITEM_HYUDORO) // Hyudoro cooldown hyubgone = 5*TICRATE; - player->kartstuff[k_botitemdelay] = TICRATE; - player->kartstuff[k_botitemconfirm] = 0; + player->botvars.itemdelay = TICRATE; + player->botvars.itemconfirm = 0; switch (getitem) { @@ -2704,19 +2704,35 @@ fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower) if (K_PlayerUsesBotMovement(player)) { - fixed_t speedmul = FRACUNIT + ((K_BotRubberband(player) - FRACUNIT) / 2); - // Give top speed a buff for bots, since it's a fairly weak stat without drifting - speedmul += ((kartspeed-1) * FRACUNIT / 8) / 10; // +10% for speed 9 - - finalspeed = FixedMul(finalspeed, speedmul); + fixed_t speedmul = ((kartspeed-1) * FRACUNIT / 8) / 10; // +10% for speed 9 + finalspeed = FixedMul(finalspeed, FRACUNIT + speedmul); } if (player->mo && !P_MobjWasRemoved(player->mo)) finalspeed = FixedMul(finalspeed, player->mo->scale); if (doboostpower) + { + if (K_PlayerUsesBotMovement(player)) + { + fixed_t rubberband = K_BotRubberband(player); + + if (rubberband > 3*FRACUNIT/2) + { + rubberband = 3*FRACUNIT/2; + } + else if (rubberband < FRACUNIT) + { + rubberband = FRACUNIT; + } + + finalspeed = FixedMul(finalspeed, rubberband); + } + return FixedMul(finalspeed, player->kartstuff[k_boostpower]+player->kartstuff[k_speedboost]); + } + return finalspeed; } From 422f528056129c3bfc360140185780076059a93b Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sun, 19 Apr 2020 10:12:02 -0400 Subject: [PATCH 33/85] Remove prints, adjust things --- src/d_clisrv.c | 1 - src/k_bot.c | 33 +++++++++++++++++++++++++++------ 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 818eeb659..e4bf0e5df 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3650,7 +3650,6 @@ static void Got_AddBot(UINT8 **p, INT32 playernum) players[newplayernum].splitscreenindex = 0; players[newplayernum].bot = true; players[newplayernum].botvars.difficulty = difficulty; - CONS_Printf("%d == %d\n", difficulty, players[newplayernum].botvars.difficulty); players[newplayernum].skincolor = skins[skinnum].prefcolor; sprintf(player_names[newplayernum], "%s", skins[skinnum].realname); diff --git a/src/k_bot.c b/src/k_bot.c index 093c43976..3612cf97f 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -60,19 +60,40 @@ void K_AddBots(SINT8 numbots) // test skins if (numbots == 6) + { + difficulty = MAXBOTDIFFICULTY; WRITEUINT8(buf_p, 1); + } else if (numbots == 5) + { + difficulty = MAXBOTDIFFICULTY; WRITEUINT8(buf_p, 0); + } else if (numbots == 4) + { + difficulty = MAXBOTDIFFICULTY-1; WRITEUINT8(buf_p, 2); + } else if (numbots == 3) + { + difficulty = MAXBOTDIFFICULTY-2; WRITEUINT8(buf_p, 3); + } else if (numbots == 2) + { + difficulty = MAXBOTDIFFICULTY-4; WRITEUINT8(buf_p, 5); + } else if (numbots == 1) + { + difficulty = MAXBOTDIFFICULTY-4; WRITEUINT8(buf_p, 9); + } else + { + difficulty = MAXBOTDIFFICULTY-2; WRITEUINT8(buf_p, 10); + } WRITEUINT8(buf_p, difficulty); @@ -114,6 +135,11 @@ fixed_t K_BotRubberband(player_t *player) player_t *firstplace = NULL; UINT8 i; + if (player->exiting) + { + return FRACUNIT; + } + for (i = 0; i < MAXPLAYERS; i++) { if (!playeringame[i] || players[i].spectator || players[i].exiting) @@ -134,7 +160,7 @@ fixed_t K_BotRubberband(player_t *player) if (firstplace != NULL) { - const UINT32 spacing = 1024; + const UINT32 spacing = 2048; UINT32 easiness = (MAXBOTDIFFICULTY - player->botvars.difficulty); UINT32 wanteddist = firstplace->distancetofinish + (spacing * easiness); @@ -143,11 +169,6 @@ fixed_t K_BotRubberband(player_t *player) // Catch up to 1st! rubberband = FRACUNIT + (player->botvars.difficulty * (player->distancetofinish - wanteddist)); } - - if (P_IsDisplayPlayer(player)) - { - CONS_Printf("difficulty: %d, easiness: %d, wanted: %d, rubberband: %d\n", player->botvars.difficulty, easiness, wanteddist, rubberband); - } } if (rubberband > 2*FRACUNIT) From ccf1bdcb2961f50deb64c1ae1fbddb015bce708f Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 20 Apr 2020 22:09:01 -0700 Subject: [PATCH 34/85] Put botvars in savegame --- src/p_saveg.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/p_saveg.c b/src/p_saveg.c index 0220a09e8..097085b17 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -268,6 +268,11 @@ static void P_NetArchivePlayers(void) WRITEUINT32(save_p, players[i].distancetofinish); WRITEUINT32(save_p, K_GetWaypointHeapIndex(players[i].nextwaypoint)); + + WRITEUINT8(save_p, players[i].botvars.difficulty); + WRITEUINT32(save_p, players[i].botvars.itemdelay); + WRITEUINT32(save_p, players[i].botvars.itemconfirm); + WRITEINT16(save_p, players[i].botvars.lastturn); } } @@ -439,6 +444,11 @@ static void P_NetUnArchivePlayers(void) players[i].distancetofinish = READUINT32(save_p); players[i].nextwaypoint = (waypoint_t *)(size_t)READUINT32(save_p); + + players[i].botvars.difficulty = READUINT8(save_p); + players[i].botvars.itemdelay = READUINT32(save_p); + players[i].botvars.itemconfirm = READUINT32(save_p); + players[i].botvars.lastturn = READINT16(save_p); } } From 99cf8350781c4a30992ea1b26d334bf8ffa57ff6 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Wed, 22 Apr 2020 19:13:14 -0400 Subject: [PATCH 35/85] Improved banana usage --- src/k_bot.c | 147 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 127 insertions(+), 20 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index 3612cf97f..ff2f17a43 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -160,14 +160,14 @@ fixed_t K_BotRubberband(player_t *player) if (firstplace != NULL) { - const UINT32 spacing = 2048; + const UINT32 spacing = 4096; UINT32 easiness = (MAXBOTDIFFICULTY - player->botvars.difficulty); UINT32 wanteddist = firstplace->distancetofinish + (spacing * easiness); if (wanteddist < player->distancetofinish) { // Catch up to 1st! - rubberband = FRACUNIT + (player->botvars.difficulty * (player->distancetofinish - wanteddist)); + rubberband = FRACUNIT + ((3*player->botvars.difficulty/2) * (player->distancetofinish - wanteddist)); } } @@ -175,6 +175,10 @@ fixed_t K_BotRubberband(player_t *player) { rubberband = 2*FRACUNIT; } + else if (rubberband < FRACUNIT) + { + rubberband = FRACUNIT; + } return rubberband; } @@ -770,10 +774,99 @@ static boolean K_PlayerNearSpot(player_t *player, fixed_t x, fixed_t y, fixed_t return false; } +static boolean K_BotRevealsBanana(player_t *player, INT16 turnamt, boolean mine) +{ + UINT8 i; + + // Only get out bananas if you have a target + + if (abs(turnamt) >= KART_FULLTURN/2) + { + return false; + } + else + { + UINT32 airtime = FixedDiv((30 * player->mo->scale) + player->mo->momz, gravity); + fixed_t throwspeed = FixedMul(82 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); + fixed_t estx = player->mo->x + P_ReturnThrustX(NULL, player->mo->angle, (throwspeed + player->speed) * airtime); + fixed_t esty = player->mo->y + P_ReturnThrustY(NULL, player->mo->angle, (throwspeed + player->speed) * airtime); + + if (K_PlayerNearSpot(player, estx, esty, player->mo->radius * 16)) + { + return true; + } + + if (mine) + { + airtime = FixedDiv((40 * player->mo->scale) + player->mo->momz, gravity); + throwspeed = FixedMul(82 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)) * 2; + estx = player->mo->x + P_ReturnThrustX(NULL, player->mo->angle, (throwspeed + player->speed) * airtime); + esty = player->mo->y + P_ReturnThrustY(NULL, player->mo->angle, (throwspeed + player->speed) * airtime); + + if (K_PlayerNearSpot(player, estx, esty, player->mo->radius * 16)) + { + return true; + } + } + } + + 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->powers[pw_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 <= (player->mo->radius * 16)) + { + angle_t a = player->mo->angle - R_PointToAngle2(player->mo->x, player->mo->y, target->mo->x, target->mo->y); + INT16 ad = 0; + const INT16 cone = 10; + + if (a < ANGLE_180) + { + ad = AngleFixed(a)>>FRACBITS; + } + else + { + ad = 360-(AngleFixed(a)>>FRACBITS); + } + + ad = abs(ad); + + if (ad >= 180-cone) + { + return true; + } + } + } + + return false; +} + void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) { botprediction_t *predict = NULL; - INT16 turnamt = 0; + INT32 turnamt = 0; // Can't build a ticcmd if we aren't spawned... if (!player->mo) @@ -781,7 +874,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) // Remove any existing controls memset(cmd, 0, sizeof(ticcmd_t)); - cmd->angleturn = (player->mo->angle >> 16); + cmd->angleturn = (player->mo->angle >> 16) | TICCMD_RECEIVED; if (player->playerstate == PST_DEAD) { @@ -838,9 +931,12 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } else { + const fixed_t realrad = predict->radius - (player->mo->radius*2); + INT16 wallsteer = K_BotSteerFromWalls(player, predict); INT16 objectsteer = 0; - fixed_t rad = predict->radius - (player->mo->radius*4); + + fixed_t rad = realrad; fixed_t dirdist = K_DistanceOfLineFromPoint( player->mo->x, player->mo->y, player->mo->x + FINECOSINE(moveangle >> ANGLETOFINESHIFT), player->mo->y + FINESINE(moveangle >> ANGLETOFINESHIFT), @@ -871,7 +967,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) cmd->forwardmove /= 2; cmd->buttons |= BT_BRAKE; } - else if (anglediff <= 23 || dirdist <= rad) + else if (anglediff <= 23 || dirdist <= realrad) { objectsteer = K_BotFindObjects(player); } @@ -1007,17 +1103,22 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) case KITEM_BANANA: if (!player->kartstuff[k_itemheld]) { - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; + if (K_BotRevealsBanana(player, turnamt, false) || (player->botvars.itemconfirm++ > 5*TICRATE)) + { + cmd->buttons |= BT_ATTACK; + player->botvars.itemconfirm = 0; + } } else { SINT8 throwdir = -1; UINT8 i; + player->botvars.itemconfirm++; + if (abs(turnamt) >= KART_FULLTURN/2) { - player->botvars.itemconfirm++; + player->botvars.itemconfirm += 2; } else { @@ -1028,7 +1129,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if (K_PlayerNearSpot(player, estx, esty, player->mo->radius * 16)) { - player->botvars.itemconfirm += 4; + player->botvars.itemconfirm += 8; throwdir = 1; } } @@ -1078,7 +1179,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if (ad >= 180-cone) { - player->botvars.itemconfirm += 2; + player->botvars.itemconfirm += 4; throwdir = -1; } } @@ -1166,12 +1267,12 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if (ad <= cone) { - player->botvars.itemconfirm += 4; + player->botvars.itemconfirm += 8; throwdir = 1; } else if (ad >= 180-cone) { - player->botvars.itemconfirm += 2; + player->botvars.itemconfirm += 4; } } } @@ -1250,7 +1351,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if (ad >= 180-cone) { - player->botvars.itemconfirm += 2; + player->botvars.itemconfirm += 4; throwdir = -1; } } @@ -1258,7 +1359,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if (player->kartstuff[k_lastjawztarget] != -1) { - player->botvars.itemconfirm += 4; + player->botvars.itemconfirm += 8; throwdir = 1; } @@ -1281,14 +1382,19 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) case KITEM_MINE: if (!player->kartstuff[k_itemheld]) { - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; + if (K_BotRevealsBanana(player, turnamt, true) || (player->botvars.itemconfirm++ > 5*TICRATE)) + { + cmd->buttons |= BT_ATTACK; + player->botvars.itemconfirm = 0; + } } else { SINT8 throwdir = 0; UINT8 i; + player->botvars.itemconfirm++; + for (i = 0; i < MAXPLAYERS; i++) { player_t *target = NULL; @@ -1334,7 +1440,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if (ad >= 180-cone) { - player->botvars.itemconfirm += 2; + player->botvars.itemconfirm += 4; throwdir = -1; } } @@ -1342,7 +1448,8 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if (abs(turnamt) >= KART_FULLTURN/2) { - player->botvars.itemconfirm++; + player->botvars.itemconfirm += 2; + throwdir = -1; } else { @@ -1353,7 +1460,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if (K_PlayerNearSpot(player, estx, esty, player->mo->radius * 16)) { - player->botvars.itemconfirm += 4; + player->botvars.itemconfirm += 8; throwdir = 0; } From 4bd88e47e3d84099d8771e4e9f6b1bfe80450817 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Wed, 22 Apr 2020 21:53:41 -0400 Subject: [PATCH 36/85] Polishing turn stuff --- src/k_bot.c | 250 +++++++++++++-------------------------------------- src/k_kart.c | 3 +- 2 files changed, 61 insertions(+), 192 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index ff2f17a43..920556724 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -23,6 +23,7 @@ #include "z_zone.h" #include "i_system.h" #include "p_maputl.h" +#include "d_ticcmd.h" void K_AddBots(SINT8 numbots) { @@ -209,27 +210,28 @@ static fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, static botprediction_t *K_CreateBotPrediction(player_t *player) { - const INT32 futuresight = (3*TICRATE/4); // How far ahead into the future to try and predict const INT32 distance = (player->speed / FRACUNIT) * futuresight; INT32 distanceleft = distance; botprediction_t *predict = Z_Calloc(sizeof(botprediction_t), PU_LEVEL, NULL); waypoint_t *wp = player->nextwaypoint; - fixed_t smallestradius = wp->mobj->radius; + fixed_t smallestradius = INT32_MAX; size_t nwp; size_t i; - INT32 lp = 0; if (distance <= 0) { predict->x = wp->mobj->x; predict->y = wp->mobj->y; - predict->radius = smallestradius; + predict->radius = wp->mobj->radius; return predict; } while (distanceleft > 0) { - lp++; + if (wp->mobj->radius < smallestradius) + { + smallestradius = wp->mobj->radius; + } nwp = 0; @@ -271,11 +273,6 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) distanceleft -= wp->nextwaypointdistances[nwp]; - if (wp->nextwaypoints[nwp]->mobj->radius < smallestradius) - { - smallestradius = wp->nextwaypoints[nwp]->mobj->radius; - } - wp = wp->nextwaypoints[nwp]; } @@ -299,141 +296,6 @@ fixed_t distancetocheck = 0; INT16 badsteerglobal = 0; fixed_t predictx = 0, predicty = 0; -static void K_SteerFromWall(mobj_t *bot, line_t *ld) -{ - const INT16 amount = 4*KART_FULLTURN; - INT32 side = P_PointOnLineSide(bot->x, bot->y, ld); - angle_t lineangle = R_PointToAngle2(0, 0, ld->dx, ld->dy) - ANGLE_90; - angle_t destangle = R_PointToAngle2(bot->x, bot->y, predictx, predicty); - angle_t angle; - - if (side == 1) - { - lineangle += ANGLE_180; - } - - angle = (destangle - lineangle); - - if (angle < ANGLE_180) - { - badsteerglobal -= amount; - } - else - { - badsteerglobal += amount; - } -} - -static inline boolean K_FindBlockingWalls(line_t *ld) -{ - // Condensed version of PIT_CheckLine - const fixed_t maxstepmove = FixedMul(MAXSTEPMOVE, mapobjectscale); - fixed_t maxstep = maxstepmove; - INT32 lineside = 0; - - if (!botmo || P_MobjWasRemoved(botmo) || !botmo->player) - { - return false; - } - - if (ld->polyobj && !(ld->polyobj->flags & POF_SOLID)) - { - return true; - } - - if (tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT] || tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT] - || tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM] || tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP]) - { - return true; - } - - if (P_BoxOnLineSide(tmbbox, ld) != -1) - { - return true; - } - - lineside = P_PointOnLineSide(botmo->x, botmo->y, ld); - - // one sided line - if (!ld->backsector) - { - if (lineside) - { - // don't hit the back side - return true; - } - - K_SteerFromWall(botmo, ld); - return true; - } - - if ((ld->flags & ML_IMPASSABLE) || (ld->flags & ML_BLOCKPLAYERS)) - { - K_SteerFromWall(botmo, ld); - return true; - } - - // set openrange, opentop, openbottom - P_LineOpening(ld, botmo); - - if (botmo->player->kartstuff[k_waterskip]) - maxstep += maxstepmove; - - if (P_MobjTouchingSectorSpecial(botmo, 1, 13, false)) - maxstep <<= 1; - else if (P_MobjTouchingSectorSpecial(botmo, 1, 12, false)) - maxstep = 0; - - if ((openrange < botmo->height) // doesn't fit - || (opentop - botmo->z < botmo->height) // mobj is too high - || (openbottom - botmo->z > maxstep)) // too big a step up - { - K_SteerFromWall(botmo, ld); - return true; - } - - return true; -} - -static INT16 K_BotSteerFromWalls(player_t *player, botprediction_t *predict) -{ - INT32 xl, xh, yl, yh, bx, by; - - badsteerglobal = 0; - - botmo = player->mo; - distancetocheck = player->mo->radius * 8; - - tmx = player->mo->x; - tmy = player->mo->y; - - predictx = predict->x; - predicty = predict->y; - - xl = (unsigned)(tmx - distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; - xh = (unsigned)(tmx + distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; - yl = (unsigned)(tmy - distancetocheck - bmaporgy)>>MAPBLOCKSHIFT; - yh = (unsigned)(tmy + distancetocheck - bmaporgy)>>MAPBLOCKSHIFT; - - BMBOUNDFIX(xl, xh, yl, yh); - - tmbbox[BOXTOP] = tmy + distancetocheck; - tmbbox[BOXBOTTOM] = tmy - distancetocheck; - tmbbox[BOXRIGHT] = tmx + distancetocheck; - tmbbox[BOXLEFT] = tmx - distancetocheck; - - // Check for lines that the bot might collide with - for (bx = xl; bx <= xh; bx++) - { - for (by = yl; by <= yh; by++) - { - P_BlockLinesIterator(bx, by, K_FindBlockingWalls); - } - } - - return badsteerglobal; -} - static void K_SteerFromObject(mobj_t *bot, mobj_t *thing, boolean towards, INT16 amount) { angle_t destangle = R_PointToAngle2(bot->x, bot->y, thing->x, thing->y); @@ -468,11 +330,23 @@ static void K_SteerFromObject(mobj_t *bot, mobj_t *thing, boolean towards, INT16 } } -static inline boolean K_BotSteerObjects(mobj_t *thing) +static boolean K_BotSteerObjects(mobj_t *thing) { INT16 anglediff; fixed_t dist; angle_t destangle, angle; + INT16 attack = ((9 - botmo->player->kartspeed) * KART_FULLTURN) / 8; // Acceleration chars are more aggressive + INT16 dodge = ((9 - botmo->player->kartweight) * KART_FULLTURN) / 8; // Handling chars dodge better + +#define PlayerAttackSteer(botcond, thingcond) \ + if ((botcond) && !(thingcond)) \ + { \ + K_SteerFromObject(botmo, thing, true, KART_FULLTURN + attack); \ + } \ + else if ((thingcond) && !(botcond)) \ + { \ + K_SteerFromObject(botmo, thing, false, KART_FULLTURN + dodge); \ + } if (!botmo || P_MobjWasRemoved(botmo) || !botmo->player) { @@ -535,7 +409,7 @@ static inline boolean K_BotSteerObjects(mobj_t *thing) case MT_BALLHOG: case MT_SPB: case MT_BUBBLESHIELDTRAP: - K_SteerFromObject(botmo, thing, false, 2*KART_FULLTURN); + K_SteerFromObject(botmo, thing, false, KART_FULLTURN + dodge); break; case MT_RANDOMITEM: if (anglediff > 90) @@ -579,19 +453,6 @@ static inline boolean K_BotSteerObjects(mobj_t *thing) && !thing->player->kartstuff[k_hyudorotimer] && !botmo->player->kartstuff[k_hyudorotimer]) { - INT16 attack = ((9 - botmo->player->kartspeed) * KART_FULLTURN) / 8; // Acceleration chars are more aggressive - INT16 dodge = ((9 - botmo->player->kartweight) * KART_FULLTURN) / 8; // Handling chars dodge better - -#define PlayerAttackSteer(botcond, thingcond) \ - if ((botcond) && !(thingcond)) \ - { \ - K_SteerFromObject(botmo, thing, true, KART_FULLTURN + attack); \ - } \ - else if ((thingcond) && !(botcond)) \ - { \ - K_SteerFromObject(botmo, thing, false, KART_FULLTURN + dodge); \ - } - // There REALLY ought to be a better way to handle this logic, right?! // Squishing PlayerAttackSteer( @@ -631,8 +492,8 @@ static inline boolean K_BotSteerObjects(mobj_t *thing) else { // After ALL of that, we can do standard bumping - const fixed_t ourweight = K_GetMobjWeight(botmo, thing); - const fixed_t theirweight = K_GetMobjWeight(thing, botmo); + fixed_t ourweight = K_GetMobjWeight(botmo, thing); + fixed_t theirweight = K_GetMobjWeight(thing, botmo); fixed_t weightdiff = 0; if (anglediff > 90) @@ -646,11 +507,11 @@ static inline boolean K_BotSteerObjects(mobj_t *thing) if (weightdiff > mapobjectscale) { - K_SteerFromObject(botmo, thing, true, (KART_FULLTURN + attack) / 2); + K_SteerFromObject(botmo, thing, true, KART_FULLTURN + attack); } else { - K_SteerFromObject(botmo, thing, false, (KART_FULLTURN + dodge) * 2); + K_SteerFromObject(botmo, thing, false, KART_FULLTURN + dodge); } } } @@ -673,7 +534,7 @@ static INT16 K_BotFindObjects(player_t *player) badsteerglobal = 0; botmo = player->mo; - distancetocheck = (player->mo->radius * 16) + (player->speed * 4); + distancetocheck = (player->mo->radius * 32) + (player->speed * 4); xl = (unsigned)(botmo->x - distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; xh = (unsigned)(botmo->x + distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; @@ -931,9 +792,8 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } else { - const fixed_t realrad = predict->radius - (player->mo->radius*2); + const fixed_t realrad = predict->radius - player->mo->radius; - INT16 wallsteer = K_BotSteerFromWalls(player, predict); INT16 objectsteer = 0; fixed_t rad = realrad; @@ -948,10 +808,14 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) // Become more precise based on how hard you need to turn // This makes predictions into turns a little nicer // Facing 90 degrees away from the predicted point gives you a 1/3 radius - rad = ((180 - anglediff) * rad) / 135; + rad = FixedMul(rad, ((135 - anglediff) * FRACUNIT) / 135); } - if (rad < 2*player->mo->radius) + if (rad > realrad) + { + rad = realrad; + } + else if (rad < 2*player->mo->radius) { rad = 2*player->mo->radius; } @@ -1004,11 +868,6 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } } - if (wallsteer != 0) - { - turnamt += wallsteer; - } - if (objectsteer != 0) { turnamt += objectsteer; @@ -1072,8 +931,11 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) case KITEM_SHRINK: case KITEM_HYUDORO: case KITEM_SUPERRING: - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; + if (!(player->pflags & PF_ATTACKDOWN)) + { + cmd->buttons |= BT_ATTACK; + player->botvars.itemconfirm = 0; + } break; case KITEM_SNEAKER: if ((player->kartstuff[k_offroad] && K_ApplyOffroad(player)) // Stuck in offroad, use it NOW @@ -1094,7 +956,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } break; case KITEM_ROCKETSNEAKER: - if (player->kartstuff[k_rocketsneakertimer] <= 0) + if (player->kartstuff[k_rocketsneakertimer] <= 0 && !(player->pflags & PF_ATTACKDOWN)) { cmd->buttons |= BT_ATTACK; player->botvars.itemconfirm = 0; @@ -1103,7 +965,8 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) case KITEM_BANANA: if (!player->kartstuff[k_itemheld]) { - if (K_BotRevealsBanana(player, turnamt, false) || (player->botvars.itemconfirm++ > 5*TICRATE)) + if ((K_BotRevealsBanana(player, turnamt, false) || (player->botvars.itemconfirm++ > 5*TICRATE)) + && !(player->pflags & PF_ATTACKDOWN)) { cmd->buttons |= BT_ATTACK; player->botvars.itemconfirm = 0; @@ -1185,8 +1048,8 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } } - if ((player->botvars.itemconfirm > 2*TICRATE) - || (player->kartstuff[k_bananadrag] >= TICRATE)) + if ((player->botvars.itemconfirm > 2*TICRATE || player->kartstuff[k_bananadrag] >= TICRATE) + && !(player->pflags & PF_ATTACKDOWN)) { if (throwdir == 1) { @@ -1203,7 +1066,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } break; case KITEM_ORBINAUT: - if (!player->kartstuff[k_itemheld]) + if (!player->kartstuff[k_itemheld] && !(player->pflags & PF_ATTACKDOWN)) { cmd->buttons |= BT_ATTACK; player->botvars.itemconfirm = 0; @@ -1277,7 +1140,8 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } } - if (player->botvars.itemconfirm > 5*TICRATE) + if ((player->botvars.itemconfirm > 5*TICRATE) + && !(player->pflags & PF_ATTACKDOWN)) { if (throwdir == 1) { @@ -1294,7 +1158,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } break; case KITEM_JAWZ: - if (!player->kartstuff[k_itemheld]) + if (!player->kartstuff[k_itemheld] && !(player->pflags & PF_ATTACKDOWN)) { cmd->buttons |= BT_ATTACK; player->botvars.itemconfirm = 0; @@ -1363,7 +1227,8 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) throwdir = 1; } - if (player->botvars.itemconfirm > 5*TICRATE) + if ((player->botvars.itemconfirm > 5*TICRATE) + && !(player->pflags & PF_ATTACKDOWN)) { if (throwdir == 1) { @@ -1382,7 +1247,8 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) case KITEM_MINE: if (!player->kartstuff[k_itemheld]) { - if (K_BotRevealsBanana(player, turnamt, true) || (player->botvars.itemconfirm++ > 5*TICRATE)) + if ((K_BotRevealsBanana(player, turnamt, true) || (player->botvars.itemconfirm++ > 5*TICRATE)) + && !(player->pflags & PF_ATTACKDOWN)) { cmd->buttons |= BT_ATTACK; player->botvars.itemconfirm = 0; @@ -1476,8 +1342,9 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } } - if ((player->botvars.itemconfirm > 2*TICRATE) + if (((player->botvars.itemconfirm > 2*TICRATE) || (player->kartstuff[k_bananadrag] >= TICRATE)) + && !(player->pflags & PF_ATTACKDOWN)) { if (throwdir == 1) { @@ -1496,7 +1363,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) case KITEM_THUNDERSHIELD: if (!K_BotUseItemNearPlayer(player, cmd, 192*player->mo->scale)) { - if (player->botvars.itemconfirm > 10*TICRATE) + if (player->botvars.itemconfirm > 10*TICRATE && !(player->pflags & PF_ATTACKDOWN)) { cmd->buttons |= BT_ATTACK; player->botvars.itemconfirm = 0; @@ -1517,6 +1384,8 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if (turnamt != 0) { + const INT16 minturn = KART_FULLTURN/2; + if (turnamt > KART_FULLTURN) { turnamt = KART_FULLTURN; @@ -1526,8 +1395,8 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) turnamt = -KART_FULLTURN; } - if ((turnamt > 0 && player->botvars.lastturn >= 0) - || (turnamt < 0 && player->botvars.lastturn <= 0)) + if ((turnamt > minturn && player->botvars.lastturn >= 0) + || (turnamt < -minturn && player->botvars.lastturn <= 0)) { if (turnamt > 0) { @@ -1539,10 +1408,11 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } cmd->driftturn = turnamt; - cmd->angleturn += turnamt; + cmd->angleturn += K_GetKartTurnValue(player, turnamt); } else { + // Can reset turn dir player->botvars.lastturn = 0; } } diff --git a/src/k_kart.c b/src/k_kart.c index e36627ae3..bf371dbb1 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6863,8 +6863,7 @@ INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue) // If we're drifting we have a completely different turning value if (player->kartstuff[k_driftend] == 0) { - // 800 is the max set in g_game.c with angleturn - fixed_t countersteer = FixedDiv(turnvalue*FRACUNIT, 800*FRACUNIT); + fixed_t countersteer = FixedDiv(turnvalue*FRACUNIT, KART_FULLTURN*FRACUNIT); turnvalue = K_GetKartDriftValue(player, countersteer); } else From f920dd026d7abe49d04f10314cae71cb078142e8 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Wed, 22 Apr 2020 21:53:52 -0400 Subject: [PATCH 37/85] Minor things --- src/k_bot.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/k_bot.c b/src/k_bot.c index 920556724..c85a396df 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -123,6 +123,7 @@ boolean K_BotCanTakeCut(player_t *player) { if (!K_ApplyOffroad(player) || player->kartstuff[k_itemtype] == KITEM_SNEAKER + || player->kartstuff[k_itemtype] == KITEM_ROCKETSNEAKER || player->kartstuff[k_itemtype] == KITEM_INVINCIBILITY || player->kartstuff[k_itemtype] == KITEM_HYUDORO) return true; @@ -210,6 +211,7 @@ static fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, static botprediction_t *K_CreateBotPrediction(player_t *player) { + const tic_t futuresight = (3*TICRATE/4); // How far ahead into the future to try and predict const INT32 distance = (player->speed / FRACUNIT) * futuresight; INT32 distanceleft = distance; botprediction_t *predict = Z_Calloc(sizeof(botprediction_t), PU_LEVEL, NULL); From 4ec61a2e111bd14aa4cf6c9fb39cd7f9898348c4 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sun, 26 Apr 2020 13:06:53 -0400 Subject: [PATCH 38/85] Subtract your distance to the nextwaypoint from the distance to predict ahead Helps the GHZ turn out a LOT, and other instances with waypoints far apart from each other --- src/k_bot.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/k_bot.c b/src/k_bot.c index c85a396df..bc480a9d6 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -220,7 +220,9 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) size_t nwp; size_t i; - if (distance <= 0) + distanceleft -= P_AproxDistance(player->mo->x - wp->mobj->x, player->mo->y - wp->mobj->y) / FRACUNIT; + + if (distanceleft <= 0) { predict->x = wp->mobj->x; predict->y = wp->mobj->y; From 6b3e7c8e1611973d1a0340af19f20c020fb8b741 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sun, 26 Apr 2020 20:36:40 -0400 Subject: [PATCH 39/85] Adjust prediction future sight based on the bot's handling --- src/k_bot.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/k_bot.c b/src/k_bot.c index bc480a9d6..1c821c3e4 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -211,7 +211,9 @@ static fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, static botprediction_t *K_CreateBotPrediction(player_t *player) { - const tic_t futuresight = (3*TICRATE/4); // How far ahead into the future to try and predict + const INT16 handling = K_GetKartTurnValue(player, KART_FULLTURN); // Reduce prediction based on how fast you can turn + const INT16 normal = KART_FULLTURN; // "Standard" handling to compare to + const tic_t futuresight = (TICRATE * normal) / max(1, handling); // How far ahead into the future to try and predict const INT32 distance = (player->speed / FRACUNIT) * futuresight; INT32 distanceleft = distance; botprediction_t *predict = Z_Calloc(sizeof(botprediction_t), PU_LEVEL, NULL); From 6846d7c3a1a61d5b9d26d4c522b127a1cea7c110 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sun, 26 Apr 2020 20:36:57 -0400 Subject: [PATCH 40/85] Bring back strong rubberbands now that they can properly predict --- src/k_bot.c | 6 +++--- src/k_kart.c | 6 +----- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index 1c821c3e4..f9abde043 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -169,7 +169,7 @@ fixed_t K_BotRubberband(player_t *player) if (wanteddist < player->distancetofinish) { // Catch up to 1st! - rubberband = FRACUNIT + ((3*player->botvars.difficulty/2) * (player->distancetofinish - wanteddist)); + rubberband = FRACUNIT + ((2*player->botvars.difficulty) * (player->distancetofinish - wanteddist)); } } @@ -177,9 +177,9 @@ fixed_t K_BotRubberband(player_t *player) { rubberband = 2*FRACUNIT; } - else if (rubberband < FRACUNIT) + else if (rubberband < 7*FRACUNIT/8) { - rubberband = FRACUNIT; + rubberband = 7*FRACUNIT/8; } return rubberband; diff --git a/src/k_kart.c b/src/k_kart.c index 311c46df1..c44d74bd6 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2718,11 +2718,7 @@ fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower) { fixed_t rubberband = K_BotRubberband(player); - if (rubberband > 3*FRACUNIT/2) - { - rubberband = 3*FRACUNIT/2; - } - else if (rubberband < FRACUNIT) + if (rubberband < FRACUNIT) { rubberband = FRACUNIT; } From 80b59382f4905569cd3fb470b9691af5b5b82507 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Sun, 26 Apr 2020 22:41:00 -0400 Subject: [PATCH 41/85] Tighten up radius slightly --- src/k_bot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_bot.c b/src/k_bot.c index f9abde043..643b3abed 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -798,7 +798,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } else { - const fixed_t realrad = predict->radius - player->mo->radius; + const fixed_t realrad = predict->radius - (player->mo->radius * 2); INT16 objectsteer = 0; From be70afcbbd172456c247216943c749562f32fa0f Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Mon, 27 Apr 2020 13:42:01 -0400 Subject: [PATCH 42/85] Object steer polishing Notably, looks in an oval area instead of a circle --- src/k_bot.c | 102 ++++++++++++++++++++++++++++------------------------ 1 file changed, 56 insertions(+), 46 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index 643b3abed..52ab1b994 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -302,20 +302,16 @@ fixed_t distancetocheck = 0; INT16 badsteerglobal = 0; fixed_t predictx = 0, predicty = 0; -static void K_SteerFromObject(mobj_t *bot, mobj_t *thing, boolean towards, INT16 amount) +static void K_SteerFromObject(mobj_t *bot, mobj_t *thing, fixed_t fulldist, fixed_t xdist, boolean towards, INT16 amount) { angle_t destangle = R_PointToAngle2(bot->x, bot->y, thing->x, thing->y); angle_t angle; + amount = (amount * FixedDiv(distancetocheck - fulldist, distancetocheck)) / FRACUNIT; + if (towards) { - fixed_t dist = K_DistanceOfLineFromPoint( - bot->x, bot->y, - bot->x + FINECOSINE(bot->angle >> ANGLETOFINESHIFT), bot->y + FINESINE(bot->angle >> ANGLETOFINESHIFT), - thing->x, thing->y - ); - - if (dist <= (bot->radius + thing->radius)) + if (xdist < (bot->radius + thing->radius)) { // Don't need to turn any harder! return; @@ -339,21 +335,11 @@ static void K_SteerFromObject(mobj_t *bot, mobj_t *thing, boolean towards, INT16 static boolean K_BotSteerObjects(mobj_t *thing) { INT16 anglediff; - fixed_t dist; + fixed_t xdist, ydist, fulldist; angle_t destangle, angle; INT16 attack = ((9 - botmo->player->kartspeed) * KART_FULLTURN) / 8; // Acceleration chars are more aggressive INT16 dodge = ((9 - botmo->player->kartweight) * KART_FULLTURN) / 8; // Handling chars dodge better -#define PlayerAttackSteer(botcond, thingcond) \ - if ((botcond) && !(thingcond)) \ - { \ - K_SteerFromObject(botmo, thing, true, KART_FULLTURN + attack); \ - } \ - else if ((thingcond) && !(botcond)) \ - { \ - K_SteerFromObject(botmo, thing, false, KART_FULLTURN + dodge); \ - } - if (!botmo || P_MobjWasRemoved(botmo) || !botmo->player) { return false; @@ -369,13 +355,21 @@ static boolean K_BotSteerObjects(mobj_t *thing) return true; } - dist = P_AproxDistance(P_AproxDistance( - botmo->x - thing->x, - botmo->y - thing->y), - (botmo->z - thing->z) / 4 + xdist = K_DistanceOfLineFromPoint( + botmo->x, botmo->y, + botmo->x + FINECOSINE(botmo->angle >> ANGLETOFINESHIFT), botmo->y + FINESINE(botmo->angle >> ANGLETOFINESHIFT), + thing->x, thing->y + ) / 2; // weight x dist more heavily than y dist + + ydist = K_DistanceOfLineFromPoint( + botmo->x, botmo->y, + botmo->x + FINECOSINE((botmo->angle + ANGLE_90) >> ANGLETOFINESHIFT), botmo->y + FINESINE((botmo->angle + ANGLE_90) >> ANGLETOFINESHIFT), + thing->x, thing->y ); - if (dist > distancetocheck) + fulldist = FixedHypot(xdist, ydist); + + if (fulldist > distancetocheck) { return true; } @@ -399,6 +393,16 @@ static boolean K_BotSteerObjects(mobj_t *thing) anglediff = abs(anglediff); +#define PlayerAttackSteer(botcond, thingcond) \ + if ((botcond) && !(thingcond)) \ + { \ + K_SteerFromObject(botmo, thing, fulldist, xdist, true, 2 * (KART_FULLTURN + attack)); \ + } \ + else if ((thingcond) && !(botcond)) \ + { \ + K_SteerFromObject(botmo, thing, fulldist, xdist, false, 2 * (KART_FULLTURN + dodge)); \ + } + switch (thing->type) { case MT_BANANA: @@ -415,7 +419,7 @@ static boolean K_BotSteerObjects(mobj_t *thing) case MT_BALLHOG: case MT_SPB: case MT_BUBBLESHIELDTRAP: - K_SteerFromObject(botmo, thing, false, KART_FULLTURN + dodge); + K_SteerFromObject(botmo, thing, fulldist, xdist, false, 2 * (KART_FULLTURN + dodge)); break; case MT_RANDOMITEM: if (anglediff > 90) @@ -425,7 +429,7 @@ static boolean K_BotSteerObjects(mobj_t *thing) if (P_CanPickupItem(botmo->player, 1)) { - K_SteerFromObject(botmo, thing, true, 2*KART_FULLTURN); + K_SteerFromObject(botmo, thing, fulldist, xdist, true, KART_FULLTURN + attack); } break; case MT_FLOATINGITEM: @@ -436,7 +440,7 @@ static boolean K_BotSteerObjects(mobj_t *thing) if (P_CanPickupItem(botmo->player, 3)) { - K_SteerFromObject(botmo, thing, true, 2*KART_FULLTURN); + K_SteerFromObject(botmo, thing, fulldist, xdist, true, KART_FULLTURN + attack); } break; case MT_RING: @@ -451,7 +455,11 @@ static boolean K_BotSteerObjects(mobj_t *thing) && !thing->extravalue1 && (botmo->player->kartstuff[k_itemtype] != KITEM_THUNDERSHIELD)) { - K_SteerFromObject(botmo, thing, true, (RINGTOTAL(botmo->player) <= 0 ? 2*KART_FULLTURN : KART_FULLTURN)); + K_SteerFromObject(botmo, thing, fulldist, xdist, true, + (RINGTOTAL(botmo->player) < 3 + ? (2 * (KART_FULLTURN + attack)) + : ((KART_FULLTURN + attack) / 2)) + ); } break; case MT_PLAYER: @@ -513,11 +521,11 @@ static boolean K_BotSteerObjects(mobj_t *thing) if (weightdiff > mapobjectscale) { - K_SteerFromObject(botmo, thing, true, KART_FULLTURN + attack); + K_SteerFromObject(botmo, thing, fulldist, xdist, true, KART_FULLTURN + attack); } else { - K_SteerFromObject(botmo, thing, false, KART_FULLTURN + dodge); + K_SteerFromObject(botmo, thing, fulldist, xdist, false, KART_FULLTURN + dodge); } } } @@ -525,7 +533,7 @@ static boolean K_BotSteerObjects(mobj_t *thing) default: if (thing->flags & (MF_SOLID|MF_ENEMY|MF_BOSS|MF_PAIN|MF_MISSILE|MF_FIRE)) { - K_SteerFromObject(botmo, thing, false, 2*KART_FULLTURN); + K_SteerFromObject(botmo, thing, fulldist, xdist, false, 2 * (KART_FULLTURN + dodge)); } break; } @@ -737,12 +745,20 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) // Can't build a ticcmd if we aren't spawned... if (!player->mo) + { return; + } // Remove any existing controls memset(cmd, 0, sizeof(ticcmd_t)); cmd->angleturn = (player->mo->angle >> 16) | TICCMD_RECEIVED; + if (gamestate != GS_LEVEL) + { + // No need to do anything else. + return; + } + if (player->playerstate == PST_DEAD) { cmd->buttons |= BT_ACCELERATE; @@ -798,10 +814,8 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } else { - const fixed_t realrad = predict->radius - (player->mo->radius * 2); - - INT16 objectsteer = 0; - + const fixed_t playerwidth = (player->mo->radius * 2); + const fixed_t realrad = predict->radius - (playerwidth * 2); // Remove a "safe" distance away from the edges of the road fixed_t rad = realrad; fixed_t dirdist = K_DistanceOfLineFromPoint( player->mo->x, player->mo->y, @@ -821,9 +835,9 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) { rad = realrad; } - else if (rad < 2*player->mo->radius) + else if (rad < playerwidth) { - rad = 2*player->mo->radius; + rad = playerwidth; } cmd->buttons |= BT_ACCELERATE; @@ -837,9 +851,10 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) cmd->forwardmove /= 2; cmd->buttons |= BT_BRAKE; } - else if (anglediff <= 23 || dirdist <= realrad) + else if (dirdist <= realrad) { - objectsteer = K_BotFindObjects(player); + // Steer towards/away from objects! + turnamt += K_BotFindObjects(player); } if (dirdist <= rad) @@ -857,9 +872,9 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) // At high speed, they're more likely to lawnmower speedrad += FixedMul(speedmul, (3*rad/4) - speedrad); - if (speedrad < 2*player->mo->radius) + if (speedrad < playerwidth) { - speedrad = 2*player->mo->radius; + speedrad = playerwidth; } if (dirdist <= speedrad) @@ -873,11 +888,6 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) turnamt /= 4; } } - - if (objectsteer != 0) - { - turnamt += objectsteer; - } } } From 44334602e591828788d8726affcb51aec9fcedc1 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Mon, 27 Apr 2020 17:06:36 -0400 Subject: [PATCH 43/85] New "wall steering" Rather than trying to steer away from walls (and failing), just pull the predicted point back and make the radius stricter. Not an ideal solution compared to what the other method was trying to go for, but this has a better success rate -- and even in cases where false positives come up (like many instances Ezo pointed out), they will no longer be incapacitated by it. --- src/k_bot.c | 163 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 155 insertions(+), 8 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index 52ab1b994..928fb2a27 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -209,12 +209,153 @@ static fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, return P_AproxDistance(cx - px, cy - py); } +mobj_t *botmo = NULL; +fixed_t distancetocheck = 0; + +fixed_t closestlinedist = INT32_MAX; + +INT16 badsteerglobal = 0; + +static inline boolean K_FindBlockingWalls(line_t *line) +{ + // Condensed version of PIT_CheckLine + const fixed_t maxstepmove = FixedMul(MAXSTEPMOVE, mapobjectscale); + fixed_t maxstep = maxstepmove; + fixed_t linedist = INT32_MAX; + INT32 lineside = 0; + + if (!botmo || P_MobjWasRemoved(botmo) || !botmo->player) + { + return false; + } + + if (line->polyobj && !(line->polyobj->flags & POF_SOLID)) + { + return true; + } + + if (tmbbox[BOXRIGHT] <= line->bbox[BOXLEFT] || tmbbox[BOXLEFT] >= line->bbox[BOXRIGHT] + || tmbbox[BOXTOP] <= line->bbox[BOXBOTTOM] || tmbbox[BOXBOTTOM] >= line->bbox[BOXTOP]) + { + return true; + } + + if (P_BoxOnLineSide(tmbbox, line) != -1) + { + return true; + } + + lineside = P_PointOnLineSide(botmo->x, botmo->y, line); + + // one sided line + if (!line->backsector) + { + if (lineside) + { + // don't hit the back side + return true; + } + + goto blocked; + } + + if ((line->flags & ML_IMPASSABLE) || (line->flags & ML_BLOCKPLAYERS)) + { + goto blocked; + } + + // set openrange, opentop, openbottom + P_LineOpening(line, botmo); + + if (botmo->player->kartstuff[k_waterskip]) + maxstep += maxstepmove; + + if (P_MobjTouchingSectorSpecial(botmo, 1, 13, false)) + maxstep <<= 1; + else if (P_MobjTouchingSectorSpecial(botmo, 1, 12, false)) + maxstep = 0; + + if ((openrange < botmo->height) // doesn't fit + || (opentop - botmo->z < botmo->height) // mobj is too high + || (openbottom - botmo->z > maxstep)) // too big a step up + { + goto blocked; + } + + // We weren't blocked! + return true; + +blocked: + linedist = K_DistanceOfLineFromPoint(line->v1->x, line->v1->y, line->v2->x, line->v2->y, botmo->x, botmo->y); + linedist -= (botmo->radius * 4); // Maintain a reasonable distance away from it + + if (linedist > distancetocheck) + { + return true; + } + + if (linedist <= 0) + { + closestlinedist = 0; + return false; + } + + if (linedist < closestlinedist) + { + closestlinedist = linedist; + } + + return true; +} + +static fixed_t K_BotReducePrediction(player_t *player) +{ + INT32 xl, xh, yl, yh, bx, by; + + botmo = player->mo; + distancetocheck = player->mo->radius * 16; + closestlinedist = INT32_MAX; + + tmx = player->mo->x; + tmy = player->mo->y; + + xl = (unsigned)(tmx - distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; + xh = (unsigned)(tmx + distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; + yl = (unsigned)(tmy - distancetocheck - bmaporgy)>>MAPBLOCKSHIFT; + yh = (unsigned)(tmy + distancetocheck - bmaporgy)>>MAPBLOCKSHIFT; + + BMBOUNDFIX(xl, xh, yl, yh); + + tmbbox[BOXTOP] = tmy + distancetocheck; + tmbbox[BOXBOTTOM] = tmy - distancetocheck; + tmbbox[BOXRIGHT] = tmx + distancetocheck; + tmbbox[BOXLEFT] = tmx - distancetocheck; + + // Check for lines that the bot might collide with + for (bx = xl; bx <= xh; bx++) + { + for (by = yl; by <= yh; by++) + { + P_BlockLinesIterator(bx, by, K_FindBlockingWalls); + } + } + + if (closestlinedist == INT32_MAX) + { + return FRACUNIT; + } + + return FixedDiv(closestlinedist, distancetocheck); +} + static botprediction_t *K_CreateBotPrediction(player_t *player) { const INT16 handling = K_GetKartTurnValue(player, KART_FULLTURN); // Reduce prediction based on how fast you can turn const INT16 normal = KART_FULLTURN; // "Standard" handling to compare to const tic_t futuresight = (TICRATE * normal) / max(1, handling); // How far ahead into the future to try and predict - const INT32 distance = (player->speed / FRACUNIT) * futuresight; + const fixed_t distreduce = K_BotReducePrediction(player); + const fixed_t radreduce = min(distreduce + FRACUNIT/4, FRACUNIT); + const INT32 distance = (FixedMul(player->speed, distreduce) / FRACUNIT) * futuresight; INT32 distanceleft = distance; botprediction_t *predict = Z_Calloc(sizeof(botprediction_t), PU_LEVEL, NULL); waypoint_t *wp = player->nextwaypoint; @@ -228,7 +369,7 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) { predict->x = wp->mobj->x; predict->y = wp->mobj->y; - predict->radius = wp->mobj->radius; + predict->radius = FixedMul(wp->mobj->radius, radreduce); return predict; } @@ -284,7 +425,7 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) predict->x = wp->mobj->x; predict->y = wp->mobj->y; - predict->radius = smallestradius; + predict->radius = FixedMul(smallestradius, radreduce); if (distanceleft > 0) { @@ -297,11 +438,6 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) return predict; } -mobj_t *botmo = NULL; -fixed_t distancetocheck = 0; -INT16 badsteerglobal = 0; -fixed_t predictx = 0, predicty = 0; - static void K_SteerFromObject(mobj_t *bot, mobj_t *thing, fixed_t fulldist, fixed_t xdist, boolean towards, INT16 amount) { angle_t destangle = R_PointToAngle2(bot->x, bot->y, thing->x, thing->y); @@ -909,6 +1045,17 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } } } + else if (player->kartstuff[k_itemroulette] && !(player->pflags & PF_ATTACKDOWN)) + { + // Mashing behaviors + + if (player->kartstuff[k_rings] < 0 && cv_superring.value) + { + // Uh oh, we need a loan! + // It'll be better in the long run for bots to lose an item set for 10 free rings. + cmd->buttons |= BT_ATTACK; + } + } else { if (player->botvars.itemdelay) From c06c4049c441fb6bf2907aa1c4df51ee407b938d Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Tue, 28 Apr 2020 14:24:09 -0400 Subject: [PATCH 44/85] Bots play more careful around damage, insta-kill, and strong offroad --- src/k_bot.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/k_bot.c b/src/k_bot.c index 928fb2a27..9ad78bb7d 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -216,6 +216,24 @@ fixed_t closestlinedist = INT32_MAX; INT16 badsteerglobal = 0; +static boolean K_BotHatesThisSector(sector_t *sec) +{ + switch (GETSECSPECIAL(sec->special, 1)) + { + case 1: // Damage + //case 2: case 3: // Offroad (let's let them lawnmower) + case 4: // Offroad (Strong) + case 5: // Spikes + case 6: case 7: // Death Pit + case 8: // Instant Kill + return true; + default: + break; + } + + return false; +} + static inline boolean K_FindBlockingWalls(line_t *line) { // Condensed version of PIT_CheckLine @@ -282,6 +300,22 @@ static inline boolean K_FindBlockingWalls(line_t *line) goto blocked; } + if (!K_BotHatesThisSector(botmo->subsector->sector)) + { + // Treat damage sectors like walls + + if (lineside) + { + if (K_BotHatesThisSector(line->frontsector)) + goto blocked; + } + else + { + if (K_BotHatesThisSector(line->backsector)) + goto blocked; + } + } + // We weren't blocked! return true; From 1fcfdd5bae133b8fe3793a94faddec01aac1f780 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 30 Apr 2020 14:15:10 -0400 Subject: [PATCH 45/85] Bot hints Thing type 2004, adds a map-defined point for bots to gravitate towards or away from. Angle: Changes radius, defaults to a 32 radius (or 64x64 area) without. Ambush: When off, makes bots steer towards. When on, makes bots steer away. Parameter: Changes how much the bot steers away/towards this point. If 0, defaults to 2. (also, made bots ignore strong offroad if they could take a shortcut) --- src/dehacked.c | 2 ++ src/info.c | 27 +++++++++++++++++++++++++++ src/info.h | 2 ++ src/k_bot.c | 37 ++++++++++++++++++++++++------------- src/p_mobj.c | 33 +++++++++++++++++++++++++++++++++ 5 files changed, 88 insertions(+), 13 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index 8507a1b03..e8749931b 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -7901,6 +7901,8 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_WAYPOINT_RISER", "MT_WAYPOINT_ANCHOR", + "MT_BOTHINT", + "MT_RANDOMAUDIENCE", "MT_FLAYM", diff --git a/src/info.c b/src/info.c index 7bed0a78d..110704493 100644 --- a/src/info.c +++ b/src/info.c @@ -16582,6 +16582,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_BOTHINT + 2004, // doomednum + S_INVISIBLE, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 0, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 100, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 1*FRACUNIT, // radius + 2*FRACUNIT, // height + 0, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOCLIP|MF_NOGRAVITY|MF_SCENERY, // flags + S_NULL // raisestate + }, + { // MT_RANDOMAUDIENCE 1488, // doomednum S_RANDOMAUDIENCE, // spawnstate diff --git a/src/info.h b/src/info.h index f86fed973..d5b5ecf2c 100644 --- a/src/info.h +++ b/src/info.h @@ -4832,6 +4832,8 @@ typedef enum mobj_type MT_WAYPOINT_RISER, MT_WAYPOINT_ANCHOR, + MT_BOTHINT, + MT_RANDOMAUDIENCE, MT_FLAYM, diff --git a/src/k_bot.c b/src/k_bot.c index 9ad78bb7d..de12885ad 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -209,24 +209,19 @@ static fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, return P_AproxDistance(cx - px, cy - py); } -mobj_t *botmo = NULL; -fixed_t distancetocheck = 0; - -fixed_t closestlinedist = INT32_MAX; - -INT16 badsteerglobal = 0; - -static boolean K_BotHatesThisSector(sector_t *sec) +static boolean K_BotHatesThisSector(player_t *player, sector_t *sec) { switch (GETSECSPECIAL(sec->special, 1)) { case 1: // Damage - //case 2: case 3: // Offroad (let's let them lawnmower) - case 4: // Offroad (Strong) case 5: // Spikes case 6: case 7: // Death Pit case 8: // Instant Kill return true; + //case 2: case 3: // Offroad (let's let them lawnmower) + case 4: // Offroad (Strong) + if (!K_BotCanTakeCut(player)) + return true; default: break; } @@ -234,6 +229,13 @@ static boolean K_BotHatesThisSector(sector_t *sec) return false; } +mobj_t *botmo = NULL; +fixed_t distancetocheck = 0; + +fixed_t closestlinedist = INT32_MAX; + +INT16 badsteerglobal = 0; + static inline boolean K_FindBlockingWalls(line_t *line) { // Condensed version of PIT_CheckLine @@ -300,18 +302,18 @@ static inline boolean K_FindBlockingWalls(line_t *line) goto blocked; } - if (!K_BotHatesThisSector(botmo->subsector->sector)) + if (!K_BotHatesThisSector(botmo->player, botmo->subsector->sector)) { // Treat damage sectors like walls if (lineside) { - if (K_BotHatesThisSector(line->frontsector)) + if (K_BotHatesThisSector(botmo->player, line->frontsector)) goto blocked; } else { - if (K_BotHatesThisSector(line->backsector)) + if (K_BotHatesThisSector(botmo->player, line->backsector)) goto blocked; } } @@ -700,6 +702,15 @@ static boolean K_BotSteerObjects(mobj_t *thing) } } break; + case MT_BOTHINT: + if (thing->extravalue1 == 0) + { + K_SteerFromObject(botmo, thing, fulldist, xdist, false, thing->extravalue2 * (KART_FULLTURN + dodge)); + } + { + K_SteerFromObject(botmo, thing, fulldist, xdist, true, thing->extravalue2 * (KART_FULLTURN + attack)); + } + break; default: if (thing->flags & (MF_SOLID|MF_ENEMY|MF_BOSS|MF_PAIN|MF_MISSILE|MF_FIRE)) { diff --git a/src/p_mobj.c b/src/p_mobj.c index 4ec66ac9c..b9af1b91f 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -12691,6 +12691,39 @@ ML_NOCLIMB : Direction not controllable P_SetTarget(&waypointcap, mobj); break; } + case MT_BOTHINT: + { + // Change size + if (mthing->angle > 0) + { + mobj->radius = mthing->angle * FRACUNIT; + } + else + { + mobj->radius = 32 * mapobjectscale; + } + + // Steer away instead of towards + if (mthing->options & MTF_AMBUSH) + { + mobj->extravalue1 = 0; + } + else + { + mobj->extravalue1 = 1; + } + + // Steering amount + if (mthing->extrainfo == 0) + { + mobj->extravalue2 = 2; + } + else + { + mobj->extravalue2 = mthing->extrainfo; + } + break; + } // SRB2Kart case MT_BALLOON: mobj->color = (1 + (mthing->angle % (MAXSKINCOLORS-1))); From b9e24d921c18cfa96f398574ace6e42c01deddce Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 30 Apr 2020 17:28:34 -0400 Subject: [PATCH 46/85] Bots reduce their rubberbanding top speed if they're facing the wrong way --- src/k_bot.c | 2 +- src/k_kart.c | 90 ++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 71 insertions(+), 21 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index de12885ad..c330d25b2 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -169,7 +169,7 @@ fixed_t K_BotRubberband(player_t *player) if (wanteddist < player->distancetofinish) { // Catch up to 1st! - rubberband = FRACUNIT + ((2*player->botvars.difficulty) * (player->distancetofinish - wanteddist)); + rubberband += (2*player->botvars.difficulty) * (player->distancetofinish - wanteddist); } } diff --git a/src/k_kart.c b/src/k_kart.c index c44d74bd6..c2b0300a5 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2723,6 +2723,41 @@ fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower) rubberband = FRACUNIT; } + // Only allow you to go fast if you're facing the right direction + if (rubberband > FRACUNIT && player->mo != NULL && player->nextwaypoint != NULL) + { + const INT16 mindiff = 30; + const INT16 maxdiff = 60; + INT16 anglediff = 0; + fixed_t amt = rubberband - FRACUNIT; + angle_t destangle = R_PointToAngle2( + player->mo->x, player->mo->y, + player->nextwaypoint->mobj->x, player->nextwaypoint->mobj->y + ); + angle_t angle = player->mo->angle - destangle; + + if (angle < ANGLE_180) + { + anglediff = AngleFixed(angle) >> FRACBITS; + } + else + { + anglediff = 360 - (AngleFixed(angle) >> FRACBITS); + } + + anglediff = abs(anglediff); + + if (anglediff >= maxdiff) + { + rubberband = FRACUNIT; + } + else if (anglediff > mindiff) + { + amt = (amt * (maxdiff - anglediff)) / mindiff; + rubberband = FRACUNIT + amt; + } + } + finalspeed = FixedMul(finalspeed, rubberband); } @@ -6815,59 +6850,74 @@ boolean K_CheckPlayersRespawnColliding(INT32 playernum, fixed_t x, fixed_t y) // turndir is the direction the controls are telling us to turn, -1 if turning right and 1 if turning left static INT16 K_GetKartDriftValue(player_t *player, fixed_t countersteer) { - INT16 basedrift, driftangle; + INT16 basedrift, driftadjust; fixed_t driftweight = player->kartweight*14; // 12 - // If they aren't drifting or on the ground this doesn't apply if (player->kartstuff[k_drift] == 0 || !P_IsObjectOnGround(player->mo)) + { + // If they aren't drifting or on the ground, this doesn't apply return 0; + } if (player->kartstuff[k_driftend] != 0) - return -266*player->kartstuff[k_drift]; // Drift has ended and we are tweaking their angle back a bit + { + // Drift has ended and we are tweaking their angle back a bit + return -266*player->kartstuff[k_drift]; + } - //basedrift = 90*player->kartstuff[k_drift]; // 450 - //basedrift = 93*player->kartstuff[k_drift] - driftweight*3*player->kartstuff[k_drift]/10; // 447 - 303 - basedrift = 83*player->kartstuff[k_drift] - (driftweight - 14)*player->kartstuff[k_drift]/5; // 415 - 303 - driftangle = abs((252 - driftweight)*player->kartstuff[k_drift]/5); + basedrift = (83 * player->kartstuff[k_drift]) - (((driftweight - 14) * player->kartstuff[k_drift]) / 5); // 415 - 303 + driftadjust = abs((252 - driftweight) * player->kartstuff[k_drift] / 5); if (player->kartstuff[k_tiregrease] > 0) // Buff drift-steering while in greasemode + { basedrift += (basedrift / greasetics) * player->kartstuff[k_tiregrease]; + } - return basedrift + FixedMul(driftangle, countersteer); + return basedrift + (FixedMul(driftadjust * FRACUNIT, countersteer) / FRACUNIT); } INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue) { - fixed_t p_maxspeed = FixedMul(K_GetKartSpeed(player, false), 3*FRACUNIT); - fixed_t adjustangle = FixedDiv((p_maxspeed>>16) - (player->speed>>16), (p_maxspeed>>16) + player->kartweight); + fixed_t p_maxspeed = K_GetKartSpeed(player, false); + fixed_t p_speed = min(player->speed, (p_maxspeed * 2)); + fixed_t weightadjust = FixedDiv((p_maxspeed * 3) - p_speed, (p_maxspeed * 3) + (player->kartweight * FRACUNIT)); if (player->spectator) + { return turnvalue; + } if (K_PlayerUsesBotMovement(player)) { - turnvalue = FixedMul(turnvalue, (5*FRACUNIT)/4); // Base increase to turning - turnvalue = FixedMul(turnvalue, K_BotRubberband(player)); + turnvalue = 5*turnvalue/4; // Base increase to turning + turnvalue = FixedMul( + turnvalue * FRACUNIT, + K_BotRubberband(player) + ) / FRACUNIT; } if (player->kartstuff[k_drift] != 0 && P_IsObjectOnGround(player->mo)) { + fixed_t countersteer = FixedDiv(turnvalue*FRACUNIT, KART_FULLTURN*FRACUNIT); + // If we're drifting we have a completely different turning value - if (player->kartstuff[k_driftend] == 0) + + if (player->kartstuff[k_driftend] != 0) { - fixed_t countersteer = FixedDiv(turnvalue*FRACUNIT, KART_FULLTURN*FRACUNIT); - turnvalue = K_GetKartDriftValue(player, countersteer); + countersteer = FRACUNIT; } - else - turnvalue = (INT16)(turnvalue + K_GetKartDriftValue(player, FRACUNIT)); + + turnvalue = K_GetKartDriftValue(player, countersteer); return turnvalue; } - turnvalue = FixedMul(turnvalue, adjustangle); // Weight has a small effect on turning - if (EITHERSNEAKER(player) || player->kartstuff[k_invincibilitytimer] || player->kartstuff[k_growshrinktimer] > 0) - turnvalue = FixedMul(turnvalue, (5*FRACUNIT)/4); + { + turnvalue = 5*turnvalue/4; + } + + turnvalue = FixedMul(turnvalue * FRACUNIT, weightadjust) / FRACUNIT; // Weight has a small effect on turning return turnvalue; } From 664a9528edce9618124a0d266d1cec0877f50506 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 1 May 2020 22:55:58 -0400 Subject: [PATCH 47/85] Eggbox stuff - Bots can roll & use eggboxes now - Bots can be "tricked" by eggboxes and treat them like normal items, depending on their difficulty and how many normal items are around them. - If exploding and in 1st place, they'll slow down to try and find someone to explode next to. - Item usage is difficulty dependent, and thus far more aggressive for the rivals --- src/k_bot.c | 1166 +++++++++++++++++++++++++++++++------------------- src/k_kart.c | 1 + 2 files changed, 734 insertions(+), 433 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index c330d25b2..047fc917a 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -236,6 +236,96 @@ fixed_t closestlinedist = INT32_MAX; INT16 badsteerglobal = 0; +fixed_t eggboxx, eggboxy; +UINT8 randomitems = 0; +UINT8 eggboxes = 0; + +static boolean K_FindRandomItems(mobj_t *thing) +{ + fixed_t dist; + + if (thing->type != MT_RANDOMITEM) + { + return true; + } + + if (!thing->health) + { + return true; + } + + dist = P_AproxDistance(thing->x - eggboxx, thing->y - eggboxy); + + if (dist > distancetocheck) + { + return true; + } + + randomitems++; + return true; +} + +static boolean K_FindEggboxes(mobj_t *thing) +{ + fixed_t dist; + + if (thing->type != MT_EGGMANITEM) + { + return true; + } + + if (!thing->health) + { + return true; + } + + dist = P_AproxDistance(thing->x - eggboxx, thing->y - eggboxy); + + if (dist > distancetocheck) + { + return true; + } + + eggboxes++; + return true; +} + +static UINT8 K_EggboxStealth(fixed_t x, fixed_t y) +{ + INT32 xl, xh, yl, yh, bx, by; + + eggboxx = x; + eggboxy = y; + distancetocheck = (mapobjectscale * 256); + randomitems = 0; + eggboxes = 0; + + xl = (unsigned)(eggboxx - distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; + xh = (unsigned)(eggboxx + distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; + yl = (unsigned)(eggboxy - distancetocheck - bmaporgy)>>MAPBLOCKSHIFT; + yh = (unsigned)(eggboxy + distancetocheck - bmaporgy)>>MAPBLOCKSHIFT; + + BMBOUNDFIX(xl, xh, yl, yh); + + for (bx = xl; bx <= xh; bx++) + { + for (by = yl; by <= yh; by++) + { + P_BlockThingsIterator(bx, by, K_FindRandomItems); + } + } + + for (bx = xl; bx <= xh; bx++) + { + for (by = yl; by <= yh; by++) + { + P_BlockThingsIterator(bx, by, K_FindEggboxes); + } + } + + return randomitems * eggboxes; +} + static inline boolean K_FindBlockingWalls(line_t *line) { // Condensed version of PIT_CheckLine @@ -579,7 +669,6 @@ static boolean K_BotSteerObjects(mobj_t *thing) { case MT_BANANA: case MT_BANANA_SHIELD: - case MT_EGGMANITEM: case MT_EGGMANITEM_SHIELD: case MT_ORBINAUT: case MT_ORBINAUT_SHIELD: @@ -604,6 +693,27 @@ static boolean K_BotSteerObjects(mobj_t *thing) K_SteerFromObject(botmo, thing, fulldist, xdist, true, KART_FULLTURN + attack); } break; + case MT_EGGMANITEM: + if (anglediff > 90) + { + break; + } + + if (P_CanPickupItem(botmo->player, 1)) // Can pick up an actual item + { + const UINT8 stealth = K_EggboxStealth(thing->x, thing->y); + const UINT8 requiredstealth = (botmo->player->botvars.difficulty * botmo->player->botvars.difficulty); + + if (stealth >= requiredstealth) + { + K_SteerFromObject(botmo, thing, fulldist, xdist, true, 2 * (KART_FULLTURN + attack)); + } + else + { + K_SteerFromObject(botmo, thing, fulldist, xdist, false, 2 * (KART_FULLTURN + dodge)); + } + } + break; case MT_FLOATINGITEM: if (anglediff > 90) { @@ -844,10 +954,11 @@ static boolean K_BotRevealsBanana(player_t *player, INT16 turnamt, boolean mine) { UINT32 airtime = FixedDiv((30 * player->mo->scale) + player->mo->momz, gravity); fixed_t throwspeed = FixedMul(82 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); - fixed_t estx = player->mo->x + P_ReturnThrustX(NULL, player->mo->angle, (throwspeed + player->speed) * airtime); - fixed_t esty = player->mo->y + P_ReturnThrustY(NULL, player->mo->angle, (throwspeed + player->speed) * airtime); + const angle_t momangle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); + fixed_t estx = player->mo->x + P_ReturnThrustX(NULL, momangle, (throwspeed + player->speed) * airtime); + fixed_t esty = player->mo->y + P_ReturnThrustY(NULL, momangle, (throwspeed + player->speed) * airtime); - if (K_PlayerNearSpot(player, estx, esty, player->mo->radius * 16)) + if (K_PlayerNearSpot(player, estx, esty, player->mo->radius * 2)) { return true; } @@ -856,10 +967,10 @@ static boolean K_BotRevealsBanana(player_t *player, INT16 turnamt, boolean mine) { airtime = FixedDiv((40 * player->mo->scale) + player->mo->momz, gravity); throwspeed = FixedMul(82 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)) * 2; - estx = player->mo->x + P_ReturnThrustX(NULL, player->mo->angle, (throwspeed + player->speed) * airtime); - esty = player->mo->y + P_ReturnThrustY(NULL, player->mo->angle, (throwspeed + player->speed) * airtime); + estx = player->mo->x + P_ReturnThrustX(NULL, momangle, (throwspeed + player->speed) * airtime); + esty = player->mo->y + P_ReturnThrustY(NULL, momangle, (throwspeed + player->speed) * airtime); - if (K_PlayerNearSpot(player, estx, esty, player->mo->radius * 16)) + if (K_PlayerNearSpot(player, estx, esty, player->mo->radius * 2)) { return true; } @@ -919,6 +1030,79 @@ static boolean K_BotRevealsBanana(player_t *player, INT16 turnamt, boolean mine) return false; } +static boolean K_BotRevealsEggbox(player_t *player) +{ + const UINT32 airtime = FixedDiv((30 * player->mo->scale) + player->mo->momz, gravity); + const fixed_t throwspeed = FixedMul(82 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); + const angle_t momangle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); + const fixed_t estx = player->mo->x + P_ReturnThrustX(NULL, momangle, (throwspeed + player->speed) * airtime); + const fixed_t esty = player->mo->y + P_ReturnThrustY(NULL, momangle, (throwspeed + player->speed) * airtime); + const UINT8 stealth = K_EggboxStealth(player->mo->x, player->mo->y); + UINT8 i; + + if (stealth > 1) + { + return true; + } + + if (K_PlayerNearSpot(player, estx, esty, player->mo->radius * 2)) + { + return true; + } + + 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->powers[pw_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 <= (player->mo->radius * 16)) + { + angle_t a = player->mo->angle - R_PointToAngle2(player->mo->x, player->mo->y, target->mo->x, target->mo->y); + INT16 ad = 0; + const INT16 cone = 10; + + if (a < ANGLE_180) + { + ad = AngleFixed(a)>>FRACBITS; + } + else + { + ad = 360-(AngleFixed(a)>>FRACBITS); + } + + ad = abs(ad); + + if (ad >= 180-cone) + { + return true; + } + } + } + + return false; +} + void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) { botprediction_t *predict = NULL; @@ -1108,484 +1292,600 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) player->botvars.itemdelay--; player->botvars.itemconfirm = 0; } - else if (player->kartstuff[k_stealingtimer] == 0 && player->kartstuff[k_stolentimer] == 0) + else if (player->kartstuff[k_eggmanexplode]) { - if (player->kartstuff[k_eggmanexplode]) + if (player->kartstuff[k_position] == 1) { - K_BotUseItemNearPlayer(player, cmd, 128*player->mo->scale); + cmd->forwardmove /= 2; + cmd->buttons |= BT_BRAKE; } - else if (player->kartstuff[k_rocketsneakertimer] > 0) + + K_BotUseItemNearPlayer(player, cmd, 128*player->mo->scale); + } + else if (player->kartstuff[k_eggmanheld]) + { + const UINT32 airtime = FixedDiv((30 * player->mo->scale) + player->mo->momz, gravity); + const fixed_t throwspeed = FixedMul(82 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); + const angle_t momangle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); + const fixed_t estx = player->mo->x + P_ReturnThrustX(NULL, momangle, (throwspeed + player->speed) * airtime); + const fixed_t esty = player->mo->y + P_ReturnThrustY(NULL, momangle, (throwspeed + player->speed) * airtime); + const UINT8 stealth = K_EggboxStealth(player->mo->x, player->mo->y); + SINT8 throwdir = -1; + UINT8 i; + + player->botvars.itemconfirm++; + + if (K_PlayerNearSpot(player, estx, esty, player->mo->radius * 2)) { - if (player->botvars.itemconfirm > TICRATE) + player->botvars.itemconfirm += player->botvars.difficulty / 2; + throwdir = 1; + } + + for (i = 0; i < MAXPLAYERS; i++) + { + player_t *target = NULL; + fixed_t dist = INT32_MAX; + + if (!playeringame[i]) { - if (!player->kartstuff[k_sneakertimer] && !(player->pflags & PF_ATTACKDOWN)) + continue; + } + + target = &players[i]; + + if (target->mo == NULL || P_MobjWasRemoved(target->mo) + || player == target || target->spectator + || target->powers[pw_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 <= (player->mo->radius * 16)) + { + angle_t a = player->mo->angle - R_PointToAngle2(player->mo->x, player->mo->y, target->mo->x, target->mo->y); + INT16 ad = 0; + const INT16 cone = 10; + + if (a < ANGLE_180) { - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; + ad = AngleFixed(a)>>FRACBITS; + } + else + { + ad = 360-(AngleFixed(a)>>FRACBITS); + } + + ad = abs(ad); + + if (ad >= 180-cone) + { + player->botvars.itemconfirm += player->botvars.difficulty; + throwdir = -1; } } - else + } + + if (stealth > 1) + { + player->botvars.itemconfirm += player->botvars.difficulty * 4; + throwdir = -1; + } + + if (player->kartstuff[k_itemroulette] > 0) // Just grabbed an item + { + player->botvars.itemconfirm += player->botvars.difficulty * 4; + throwdir = -1; + } + + if ((player->botvars.itemconfirm > 2*TICRATE || player->kartstuff[k_bananadrag] >= TICRATE) + && !(player->pflags & PF_ATTACKDOWN)) + { + if (throwdir == 1) { - player->botvars.itemconfirm++; + cmd->buttons |= BT_FORWARD; + } + else if (throwdir == -1) + { + cmd->buttons |= BT_BACKWARD; + } + + cmd->buttons |= BT_ATTACK; + player->botvars.itemconfirm = 0; + } + } + else if (player->kartstuff[k_rocketsneakertimer] > 0) + { + if (player->botvars.itemconfirm > TICRATE) + { + if (!player->kartstuff[k_sneakertimer] && !(player->pflags & PF_ATTACKDOWN)) + { + cmd->buttons |= BT_ATTACK; + player->botvars.itemconfirm = 0; } } else { - switch (player->kartstuff[k_itemtype]) - { - case KITEM_INVINCIBILITY: - case KITEM_SPB: - case KITEM_GROW: - case KITEM_SHRINK: - case KITEM_HYUDORO: - case KITEM_SUPERRING: - if (!(player->pflags & PF_ATTACKDOWN)) + player->botvars.itemconfirm++; + } + } + else if (player->kartstuff[k_stealingtimer] == 0 && player->kartstuff[k_stolentimer] == 0) + { + switch (player->kartstuff[k_itemtype]) + { + case KITEM_INVINCIBILITY: + case KITEM_SPB: + case KITEM_GROW: + case KITEM_SHRINK: + case KITEM_HYUDORO: + case KITEM_SUPERRING: + if (!(player->pflags & PF_ATTACKDOWN)) + { + cmd->buttons |= BT_ATTACK; + player->botvars.itemconfirm = 0; + } + break; + case KITEM_SNEAKER: + if ((player->kartstuff[k_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)/2 // Being slowed down too much + || player->kartstuff[k_speedboost] > (FRACUNIT/8) // Have another type of boost (tethering) + || player->botvars.itemconfirm > 4*TICRATE) // Held onto it for too long + { + if (!player->kartstuff[k_sneakertimer] && !(player->pflags & PF_ATTACKDOWN)) + { + cmd->buttons |= BT_ATTACK; + player->botvars.itemconfirm = 2*TICRATE; + } + } + else + { + player->botvars.itemconfirm++; + } + break; + case KITEM_ROCKETSNEAKER: + if (player->kartstuff[k_rocketsneakertimer] <= 0 && !(player->pflags & PF_ATTACKDOWN)) + { + cmd->buttons |= BT_ATTACK; + player->botvars.itemconfirm = 0; + } + break; + case KITEM_BANANA: + if (!player->kartstuff[k_itemheld]) + { + if ((K_BotRevealsBanana(player, turnamt, false) || (player->botvars.itemconfirm++ > 5*TICRATE)) + && !(player->pflags & PF_ATTACKDOWN)) { cmd->buttons |= BT_ATTACK; player->botvars.itemconfirm = 0; } - break; - case KITEM_SNEAKER: - if ((player->kartstuff[k_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)/2 // Being slowed down too much - || player->kartstuff[k_speedboost] > (FRACUNIT/8) // Have another type of boost (tethering) - || player->botvars.itemconfirm > 4*TICRATE) // Held onto it for too long + } + else + { + SINT8 throwdir = -1; + UINT8 i; + + player->botvars.itemconfirm++; + + if (abs(turnamt) >= KART_FULLTURN/2) { - if (!player->kartstuff[k_sneakertimer] && !(player->pflags & PF_ATTACKDOWN)) - { - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 2*TICRATE; - } + player->botvars.itemconfirm += player->botvars.difficulty / 2; } else { - player->botvars.itemconfirm++; - } - break; - case KITEM_ROCKETSNEAKER: - if (player->kartstuff[k_rocketsneakertimer] <= 0 && !(player->pflags & PF_ATTACKDOWN)) - { - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; - } - break; - case KITEM_BANANA: - if (!player->kartstuff[k_itemheld]) - { - if ((K_BotRevealsBanana(player, turnamt, false) || (player->botvars.itemconfirm++ > 5*TICRATE)) - && !(player->pflags & PF_ATTACKDOWN)) + const UINT32 airtime = FixedDiv((30 * player->mo->scale) + player->mo->momz, gravity); + const fixed_t throwspeed = FixedMul(82 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); + const angle_t momangle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); + const fixed_t estx = player->mo->x + P_ReturnThrustX(NULL, momangle, (throwspeed + player->speed) * airtime); + const fixed_t esty = player->mo->y + P_ReturnThrustY(NULL, momangle, (throwspeed + player->speed) * airtime); + + if (K_PlayerNearSpot(player, estx, esty, player->mo->radius * 2)) { - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; - } - } - else - { - SINT8 throwdir = -1; - UINT8 i; - - player->botvars.itemconfirm++; - - if (abs(turnamt) >= KART_FULLTURN/2) - { - player->botvars.itemconfirm += 2; - } - else - { - const UINT32 airtime = FixedDiv((30 * player->mo->scale) + 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); - - if (K_PlayerNearSpot(player, estx, esty, player->mo->radius * 16)) - { - player->botvars.itemconfirm += 8; - throwdir = 1; - } - } - - 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->powers[pw_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 <= (player->mo->radius * 16)) - { - angle_t a = player->mo->angle - R_PointToAngle2(player->mo->x, player->mo->y, target->mo->x, target->mo->y); - INT16 ad = 0; - const INT16 cone = 10; - - if (a < ANGLE_180) - { - ad = AngleFixed(a)>>FRACBITS; - } - else - { - ad = 360-(AngleFixed(a)>>FRACBITS); - } - - ad = abs(ad); - - if (ad >= 180-cone) - { - player->botvars.itemconfirm += 4; - throwdir = -1; - } - } - } - - if ((player->botvars.itemconfirm > 2*TICRATE || player->kartstuff[k_bananadrag] >= TICRATE) - && !(player->pflags & PF_ATTACKDOWN)) - { - if (throwdir == 1) - { - cmd->buttons |= BT_FORWARD; - } - else if (throwdir == -1) - { - cmd->buttons |= BT_BACKWARD; - } - - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; - } - } - break; - case KITEM_ORBINAUT: - if (!player->kartstuff[k_itemheld] && !(player->pflags & PF_ATTACKDOWN)) - { - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; - } - else if (player->kartstuff[k_position] != 1) // Hold onto orbiting items when in 1st :) - /* FALL-THRU */ - case KITEM_BALLHOG: - { - const fixed_t topspeed = K_GetKartSpeed(player, false); - fixed_t radius = (player->mo->radius * 32); - SINT8 throwdir = -1; - UINT8 i; - - if (player->speed > topspeed) - { - radius = FixedMul(radius, FixedDiv(player->speed, topspeed)); - } - - 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->powers[pw_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; - const INT16 cone = 10; - - if (a < ANGLE_180) - { - ad = AngleFixed(a)>>FRACBITS; - } - else - { - ad = 360-(AngleFixed(a)>>FRACBITS); - } - - ad = abs(ad); - - if (ad <= cone) - { - player->botvars.itemconfirm += 8; - throwdir = 1; - } - else if (ad >= 180-cone) - { - player->botvars.itemconfirm += 4; - } - } - } - - if ((player->botvars.itemconfirm > 5*TICRATE) - && !(player->pflags & PF_ATTACKDOWN)) - { - if (throwdir == 1) - { - cmd->buttons |= BT_FORWARD; - } - else if (throwdir == -1) - { - cmd->buttons |= BT_BACKWARD; - } - - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; - } - } - break; - case KITEM_JAWZ: - if (!player->kartstuff[k_itemheld] && !(player->pflags & PF_ATTACKDOWN)) - { - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; - } - else if (player->kartstuff[k_position] != 1) // Hold onto orbiting items when in 1st :) - { - SINT8 throwdir = 1; - UINT8 i; - - player->botvars.itemconfirm++; - - 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->powers[pw_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 <= (player->mo->radius * 32)) - { - angle_t a = player->mo->angle - R_PointToAngle2(player->mo->x, player->mo->y, target->mo->x, target->mo->y); - INT16 ad = 0; - const INT16 cone = 10; - - if (a < ANGLE_180) - { - ad = AngleFixed(a)>>FRACBITS; - } - else - { - ad = 360-(AngleFixed(a)>>FRACBITS); - } - - ad = abs(ad); - - if (ad >= 180-cone) - { - player->botvars.itemconfirm += 4; - throwdir = -1; - } - } - } - - if (player->kartstuff[k_lastjawztarget] != -1) - { - player->botvars.itemconfirm += 8; + player->botvars.itemconfirm += player->botvars.difficulty * 2; throwdir = 1; } + } - if ((player->botvars.itemconfirm > 5*TICRATE) - && !(player->pflags & PF_ATTACKDOWN)) + for (i = 0; i < MAXPLAYERS; i++) + { + player_t *target = NULL; + fixed_t dist = INT32_MAX; + + if (!playeringame[i]) { - if (throwdir == 1) + continue; + } + + target = &players[i]; + + if (target->mo == NULL || P_MobjWasRemoved(target->mo) + || player == target || target->spectator + || target->powers[pw_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 <= (player->mo->radius * 16)) + { + angle_t a = player->mo->angle - R_PointToAngle2(player->mo->x, player->mo->y, target->mo->x, target->mo->y); + INT16 ad = 0; + const INT16 cone = 10; + + if (a < ANGLE_180) { - cmd->buttons |= BT_FORWARD; + ad = AngleFixed(a)>>FRACBITS; } - else if (throwdir == -1) + else { - cmd->buttons |= BT_BACKWARD; + ad = 360-(AngleFixed(a)>>FRACBITS); } - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; + ad = abs(ad); + + if (ad >= 180-cone) + { + player->botvars.itemconfirm += player->botvars.difficulty; + throwdir = -1; + } } } - break; - case KITEM_MINE: - if (!player->kartstuff[k_itemheld]) + + if ((player->botvars.itemconfirm > 2*TICRATE || player->kartstuff[k_bananadrag] >= TICRATE) + && !(player->pflags & PF_ATTACKDOWN)) { - if ((K_BotRevealsBanana(player, turnamt, true) || (player->botvars.itemconfirm++ > 5*TICRATE)) - && !(player->pflags & PF_ATTACKDOWN)) + if (throwdir == 1) { - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; + cmd->buttons |= BT_FORWARD; } + else if (throwdir == -1) + { + cmd->buttons |= BT_BACKWARD; + } + + cmd->buttons |= BT_ATTACK; + player->botvars.itemconfirm = 0; + } + } + break; + case KITEM_EGGMAN: + if (!player->kartstuff[k_eggmanheld]) + { + if ((K_BotRevealsEggbox(player) || (player->botvars.itemconfirm++ > 20*TICRATE)) + && !(player->pflags & PF_ATTACKDOWN)) + { + cmd->buttons |= BT_ATTACK; + player->botvars.itemconfirm = 0; + } + } + break; + case KITEM_ORBINAUT: + if (!player->kartstuff[k_itemheld] && !(player->pflags & PF_ATTACKDOWN)) + { + cmd->buttons |= BT_ATTACK; + player->botvars.itemconfirm = 0; + } + else if (player->kartstuff[k_position] != 1) // Hold onto orbiting items when in 1st :) + /* FALL-THRU */ + case KITEM_BALLHOG: + { + const fixed_t topspeed = K_GetKartSpeed(player, false); + fixed_t radius = (player->mo->radius * 32); + SINT8 throwdir = -1; + UINT8 i; + + if (player->speed > topspeed) + { + radius = FixedMul(radius, FixedDiv(player->speed, topspeed)); + } + + 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->powers[pw_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; + const INT16 cone = 10; + + if (a < ANGLE_180) + { + ad = AngleFixed(a)>>FRACBITS; + } + else + { + ad = 360-(AngleFixed(a)>>FRACBITS); + } + + ad = abs(ad); + + if (ad <= cone) + { + player->botvars.itemconfirm += player->botvars.difficulty * 2; + throwdir = 1; + } + else if (ad >= 180-cone) + { + player->botvars.itemconfirm += player->botvars.difficulty; + } + } + } + + if ((player->botvars.itemconfirm > 5*TICRATE) + && !(player->pflags & PF_ATTACKDOWN)) + { + if (throwdir == 1) + { + cmd->buttons |= BT_FORWARD; + } + else if (throwdir == -1) + { + cmd->buttons |= BT_BACKWARD; + } + + cmd->buttons |= BT_ATTACK; + player->botvars.itemconfirm = 0; + } + } + break; + case KITEM_JAWZ: + if (!player->kartstuff[k_itemheld] && !(player->pflags & PF_ATTACKDOWN)) + { + cmd->buttons |= BT_ATTACK; + player->botvars.itemconfirm = 0; + } + else if (player->kartstuff[k_position] != 1) // Hold onto orbiting items when in 1st :) + { + SINT8 throwdir = 1; + UINT8 i; + + player->botvars.itemconfirm++; + + 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->powers[pw_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 <= (player->mo->radius * 32)) + { + angle_t a = player->mo->angle - R_PointToAngle2(player->mo->x, player->mo->y, target->mo->x, target->mo->y); + INT16 ad = 0; + const INT16 cone = 10; + + if (a < ANGLE_180) + { + ad = AngleFixed(a)>>FRACBITS; + } + else + { + ad = 360-(AngleFixed(a)>>FRACBITS); + } + + ad = abs(ad); + + if (ad >= 180-cone) + { + player->botvars.itemconfirm += player->botvars.difficulty; + throwdir = -1; + } + } + } + + if (player->kartstuff[k_lastjawztarget] != -1) + { + player->botvars.itemconfirm += player->botvars.difficulty * 2; + throwdir = 1; + } + + if ((player->botvars.itemconfirm > 5*TICRATE) + && !(player->pflags & PF_ATTACKDOWN)) + { + if (throwdir == 1) + { + cmd->buttons |= BT_FORWARD; + } + else if (throwdir == -1) + { + cmd->buttons |= BT_BACKWARD; + } + + cmd->buttons |= BT_ATTACK; + player->botvars.itemconfirm = 0; + } + } + break; + case KITEM_MINE: + if (!player->kartstuff[k_itemheld]) + { + if ((K_BotRevealsBanana(player, turnamt, true) || (player->botvars.itemconfirm++ > 5*TICRATE)) + && !(player->pflags & PF_ATTACKDOWN)) + { + cmd->buttons |= BT_ATTACK; + player->botvars.itemconfirm = 0; + } + } + else + { + SINT8 throwdir = 0; + UINT8 i; + + player->botvars.itemconfirm++; + + 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->powers[pw_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 <= (player->mo->radius * 16)) + { + angle_t a = player->mo->angle - R_PointToAngle2(player->mo->x, player->mo->y, target->mo->x, target->mo->y); + INT16 ad = 0; + const INT16 cone = 10; + + if (a < ANGLE_180) + { + ad = AngleFixed(a)>>FRACBITS; + } + else + { + ad = 360-(AngleFixed(a)>>FRACBITS); + } + + ad = abs(ad); + + if (ad >= 180-cone) + { + player->botvars.itemconfirm += player->botvars.difficulty; + throwdir = -1; + } + } + } + + if (abs(turnamt) >= KART_FULLTURN/2) + { + player->botvars.itemconfirm += player->botvars.difficulty / 2; + throwdir = -1; } else { - SINT8 throwdir = 0; - UINT8 i; + UINT32 airtime = FixedDiv((30 * player->mo->scale) + player->mo->momz, gravity); + fixed_t throwspeed = FixedMul(82 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); + angle_t momangle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); + fixed_t estx = player->mo->x + P_ReturnThrustX(NULL, momangle, (throwspeed + player->speed) * airtime); + fixed_t esty = player->mo->y + P_ReturnThrustY(NULL, momangle, (throwspeed + player->speed) * airtime); - player->botvars.itemconfirm++; - - for (i = 0; i < MAXPLAYERS; i++) + if (K_PlayerNearSpot(player, estx, esty, player->mo->radius * 2)) { - 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->powers[pw_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 <= (player->mo->radius * 16)) - { - angle_t a = player->mo->angle - R_PointToAngle2(player->mo->x, player->mo->y, target->mo->x, target->mo->y); - INT16 ad = 0; - const INT16 cone = 10; - - if (a < ANGLE_180) - { - ad = AngleFixed(a)>>FRACBITS; - } - else - { - ad = 360-(AngleFixed(a)>>FRACBITS); - } - - ad = abs(ad); - - if (ad >= 180-cone) - { - player->botvars.itemconfirm += 4; - throwdir = -1; - } - } + player->botvars.itemconfirm += player->botvars.difficulty * 2; + throwdir = 0; } - if (abs(turnamt) >= KART_FULLTURN/2) + airtime = FixedDiv((40 * player->mo->scale) + player->mo->momz, gravity); + throwspeed = FixedMul(82 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)) * 2; + estx = player->mo->x + P_ReturnThrustX(NULL, player->mo->angle, (throwspeed + player->speed) * airtime); + esty = player->mo->y + P_ReturnThrustY(NULL, player->mo->angle, (throwspeed + player->speed) * airtime); + + if (K_PlayerNearSpot(player, estx, esty, player->mo->radius * 2)) { - player->botvars.itemconfirm += 2; - throwdir = -1; - } - else - { - UINT32 airtime = FixedDiv((30 * player->mo->scale) + player->mo->momz, gravity); - fixed_t throwspeed = FixedMul(82 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); - fixed_t estx = player->mo->x + P_ReturnThrustX(NULL, player->mo->angle, (throwspeed + player->speed) * airtime); - fixed_t esty = player->mo->y + P_ReturnThrustY(NULL, player->mo->angle, (throwspeed + player->speed) * airtime); - - if (K_PlayerNearSpot(player, estx, esty, player->mo->radius * 16)) - { - player->botvars.itemconfirm += 8; - throwdir = 0; - } - - airtime = FixedDiv((40 * player->mo->scale) + player->mo->momz, gravity); - throwspeed = FixedMul(82 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)) * 2; - estx = player->mo->x + P_ReturnThrustX(NULL, player->mo->angle, (throwspeed + player->speed) * airtime); - esty = player->mo->y + P_ReturnThrustY(NULL, player->mo->angle, (throwspeed + player->speed) * airtime); - - if (K_PlayerNearSpot(player, estx, esty, player->mo->radius * 16)) - { - player->botvars.itemconfirm += 4; - throwdir = 1; - } - } - - if (((player->botvars.itemconfirm > 2*TICRATE) - || (player->kartstuff[k_bananadrag] >= TICRATE)) - && !(player->pflags & PF_ATTACKDOWN)) - { - if (throwdir == 1) - { - cmd->buttons |= BT_FORWARD; - } - else if (throwdir == -1) - { - cmd->buttons |= BT_BACKWARD; - } - - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; + player->botvars.itemconfirm += player->botvars.difficulty / 2; + throwdir = 1; } } - break; - case KITEM_THUNDERSHIELD: - if (!K_BotUseItemNearPlayer(player, cmd, 192*player->mo->scale)) + + if (((player->botvars.itemconfirm > 2*TICRATE) + || (player->kartstuff[k_bananadrag] >= TICRATE)) + && !(player->pflags & PF_ATTACKDOWN)) { - if (player->botvars.itemconfirm > 10*TICRATE && !(player->pflags & PF_ATTACKDOWN)) + if (throwdir == 1) { - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; + cmd->buttons |= BT_FORWARD; } - else + else if (throwdir == -1) { - player->botvars.itemconfirm++; + cmd->buttons |= BT_BACKWARD; } + + cmd->buttons |= BT_ATTACK; + player->botvars.itemconfirm = 0; } - break; - default: - player->botvars.itemconfirm = 0; - break; - } + } + break; + case KITEM_THUNDERSHIELD: + if (!K_BotUseItemNearPlayer(player, cmd, 192*player->mo->scale)) + { + if (player->botvars.itemconfirm > 10*TICRATE && !(player->pflags & PF_ATTACKDOWN)) + { + cmd->buttons |= BT_ATTACK; + player->botvars.itemconfirm = 0; + } + else + { + player->botvars.itemconfirm++; + } + } + break; + default: + if (player->kartstuff[k_itemtype] != KITEM_NONE && !(player->pflags & PF_ATTACKDOWN)) + cmd->buttons |= BT_ATTACK; + player->botvars.itemconfirm = 0; + break; } } } diff --git a/src/k_kart.c b/src/k_kart.c index c2b0300a5..2f53570ce 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -950,6 +950,7 @@ static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean sp case KITEM_ROCKETSNEAKER: case KITEM_INVINCIBILITY: case KITEM_BANANA: + case KITEM_EGGMAN: case KITEM_ORBINAUT: case KITEM_JAWZ: case KITEM_MINE: From 69e5cf82055cc7b00cf984ccebbc25c51466e0f3 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 1 May 2020 23:44:03 -0400 Subject: [PATCH 48/85] Bots can use bubble shield and flame shield, albiet a bit simplisticly This means they can now use ALL items! --- src/k_bot.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/k_kart.c | 4 +++ 2 files changed, 88 insertions(+) diff --git a/src/k_bot.c b/src/k_bot.c index 047fc917a..7af7869cc 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -1881,6 +1881,90 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } } break; + case KITEM_BUBBLESHIELD: + { + boolean hold = false; + + if (player->kartstuff[k_bubbleblowup] <= 0) + { + UINT8 i; + + player->botvars.itemconfirm++; + + if (player->kartstuff[k_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->powers[pw_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->kartstuff[k_bubbleblowup] >= bubbletime) + { + if (player->botvars.itemconfirm >= 10*TICRATE) + { + hold = true; + } + } + else if (player->kartstuff[k_bubbleblowup] < bubbletime) + { + hold = true; + } + + if (hold && player->kartstuff[k_holdready]) + { + cmd->buttons |= BT_ATTACK; + } + } + break; + case KITEM_FLAMESHIELD: + if (player->botvars.itemconfirm > 0) + { + player->botvars.itemconfirm--; + } + else if (player->kartstuff[k_holdready]) + { + INT32 flamemax = player->kartstuff[k_flamelength] * flameseg; + + if (player->kartstuff[k_flamemeter] < flamemax || flamemax == 0) + { + cmd->buttons |= BT_ATTACK; + } + else + { + player->botvars.itemconfirm = 3*flamemax/4; + } + } + break; default: if (player->kartstuff[k_itemtype] != KITEM_NONE && !(player->pflags & PF_ATTACKDOWN)) cmd->buttons |= BT_ATTACK; diff --git a/src/k_kart.c b/src/k_kart.c index 2f53570ce..7a394399b 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -938,6 +938,7 @@ static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean sp #define COOLDOWNONSTART (leveltime < (30*TICRATE)+starttime) + /* if (bot) { // TODO: Item use on bots should all be passed-in functions. @@ -961,6 +962,8 @@ static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean sp case KITEM_HYUDORO: case KITEM_SUPERRING: case KITEM_THUNDERSHIELD: + case KITEM_BUBBLESHIELD: + case KITEM_FLAMESHIELD: case KRITEM_TRIPLESNEAKER: case KRITEM_TRIPLEBANANA: case KRITEM_TENFOLDBANANA: @@ -972,6 +975,7 @@ static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean sp return 0; } } + */ switch (item) { From df099864b60ccd8a3544ac606b7f03a258afd8b7 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 1 May 2020 23:45:12 -0400 Subject: [PATCH 49/85] Whoops --- src/k_kart.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/k_kart.c b/src/k_kart.c index 7a394399b..4a6d1852f 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -976,6 +976,7 @@ static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean sp } } */ + (void)bot; switch (item) { From e6e2cd5d820af10e8d79e31a776588a0891c25fe Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 2 May 2020 00:54:54 -0400 Subject: [PATCH 50/85] Adjust air speed cap easing with bot rubberbanding --- src/p_user.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/p_user.c b/src/p_user.c index dd6415918..b0432cc93 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -4139,11 +4139,21 @@ static void P_3dMovement(player_t *player) if (!onground) { - fixed_t airspeedcap = (50*mapobjectscale); - fixed_t speed = R_PointToDist2(0, 0, player->mo->momx, player->mo->momy); + const fixed_t airspeedcap = (50*mapobjectscale); + const fixed_t speed = R_PointToDist2(0, 0, player->mo->momx, player->mo->momy); + if (speed > airspeedcap) { - fixed_t newspeed = speed - ((speed - airspeedcap) / 32); + fixed_t div = 32*FRACUNIT; + fixed_t newspeed; + + if (K_PlayerUsesBotMovement(player)) + { + div = FixedMul(div, K_BotRubberband(player)); + } + + newspeed = speed - FixedDiv((speed - airspeedcap), div); + player->mo->momx = FixedMul(FixedDiv(player->mo->momx, speed), newspeed); player->mo->momy = FixedMul(FixedDiv(player->mo->momy, speed), newspeed); } From cb34ea138822abbdc16d31a12c59c3c7a44b0f43 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 2 May 2020 02:01:39 -0400 Subject: [PATCH 51/85] Consider FOFs when figuring out sectors to be careful near --- src/k_bot.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/src/k_bot.c b/src/k_bot.c index 7af7869cc..e3c1bfd08 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -209,7 +209,7 @@ static fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, return P_AproxDistance(cx - px, cy - py); } -static boolean K_BotHatesThisSector(player_t *player, sector_t *sec) +static boolean K_BotHatesThisSectorsSpecial(player_t *player, sector_t *sec) { switch (GETSECSPECIAL(sec->special, 1)) { @@ -229,6 +229,54 @@ static boolean K_BotHatesThisSector(player_t *player, sector_t *sec) return false; } +static boolean K_BotHatesThisSector(player_t *player, sector_t *sec) +{ + const boolean flip = (player->mo->eflags & MFE_VERTICALFLIP); + INT32 flag; + ffloor_t *rover; + + if (flip) + { + flag = SF_FLIPSPECIAL_CEILING; + } + else + { + flag = SF_FLIPSPECIAL_FLOOR; + } + + if (sec->flags & flag) + { + if (K_BotHatesThisSectorsSpecial(player, sec)) + { + return true; + } + } + + for (rover = sec->ffloors; rover; rover = rover->next) + { + if (!(rover->flags & FF_EXISTS)) + { + continue; + } + + if (!(rover->master->frontsector->flags & flag)) + { + continue; + } + + if (((*rover->bottomheight >= player->mo->z + player->mo->height) && (flip)) + || ((*rover->topheight <= player->mo->z) && (!flip))) + { + if (K_BotHatesThisSectorsSpecial(player, sec)) + { + return true; + } + } + } + + return false; +} + mobj_t *botmo = NULL; fixed_t distancetocheck = 0; From 766c6da005c5f6313da61904a3f5ba1eed88b27f Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 4 May 2020 19:49:18 -0400 Subject: [PATCH 52/85] Give bots servernode, D_NumPlayers no longer counts bots --- src/d_clisrv.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 06ab19e44..60ad406cf 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3647,6 +3647,8 @@ static void Got_AddBot(UINT8 **p, INT32 playernum) if (newplayernum+1 > doomcom->numslots) doomcom->numslots = (INT16)(newplayernum+1); + playernode[newplayernum] = servernode; + players[newplayernum].splitscreenindex = 0; players[newplayernum].bot = true; players[newplayernum].botvars.difficulty = difficulty; @@ -5778,9 +5780,15 @@ FILESTAMP INT32 D_NumPlayers(void) { INT32 num = 0, ix; + for (ix = 0; ix < MAXPLAYERS; ix++) - if (playeringame[ix]) + { + if (playeringame[ix] && !players[ix].bot) + { num++; + } + } + return num; } From 2188391d421243a2d76bb532bb7e01d557d788e3 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 4 May 2020 20:37:52 -0400 Subject: [PATCH 53/85] add botvars to resynch packet --- src/d_clisrv.c | 12 ++++++++++++ src/d_clisrv.h | 6 ++++++ 2 files changed, 18 insertions(+) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 60ad406cf..45627d3a2 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -646,6 +646,12 @@ static inline void resynch_write_player(resynch_pak *rsp, const size_t i) rsp->splitscreenindex = players[i].splitscreenindex; + rsp->bot = players[i].bot; + rsp->bot_difficulty = players[i].botvars.difficulty; + rsp->bot_itemdelay = players[i].botvars.itemdelay; + rsp->bot_itemconfirm = players[i].botvars.itemconfirm; + rsp->bot_lastturn = players[i].botvars.lastturn; + rsp->hasmo = false; //Transfer important mo information if the player has a body. //This lets us resync players even if they are dead. @@ -769,6 +775,12 @@ static void resynch_read_player(resynch_pak *rsp) players[i].splitscreenindex = rsp->splitscreenindex; + players[i].bot = rsp->bot; + players[i].botvars.difficulty = rsp->bot_difficulty; + players[i].botvars.itemdelay = rsp->bot_itemdelay; + players[i].botvars.itemconfirm = rsp->bot_itemconfirm; + players[i].botvars.lastturn = rsp->bot_lastturn; + //We get a packet for each player in game. if (!playeringame[i]) return; diff --git a/src/d_clisrv.h b/src/d_clisrv.h index d39babd82..6e0a88c6a 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -280,6 +280,12 @@ typedef struct UINT8 splitscreenindex; + boolean bot; + UINT8 bot_difficulty; + tic_t bot_itemdelay; + tic_t bot_itemconfirm; + INT16 bot_lastturn; + //player->mo stuff UINT8 hasmo; // Boolean From 3b6a2fed77b0940ea71baba9e4e00958a7cc8d2e Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 4 May 2020 21:46:00 -0400 Subject: [PATCH 54/85] Make the safe distance from the road farther --- src/k_bot.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index e3c1bfd08..52731769b 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -461,7 +461,7 @@ static inline boolean K_FindBlockingWalls(line_t *line) blocked: linedist = K_DistanceOfLineFromPoint(line->v1->x, line->v1->y, line->v2->x, line->v2->y, botmo->x, botmo->y); - linedist -= (botmo->radius * 4); // Maintain a reasonable distance away from it + linedist -= (botmo->radius * 8); // Maintain a reasonable distance away from it if (linedist > distancetocheck) { @@ -1228,7 +1228,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) else { const fixed_t playerwidth = (player->mo->radius * 2); - const fixed_t realrad = predict->radius - (playerwidth * 2); // Remove a "safe" distance away from the edges of the road + const fixed_t realrad = predict->radius - (playerwidth * 4); // Remove a "safe" distance away from the edges of the road fixed_t rad = realrad; fixed_t dirdist = K_DistanceOfLineFromPoint( player->mo->x, player->mo->y, From 636553d1a26795e5db409ad8760709c6a04b956d Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 4 May 2020 22:27:55 -0400 Subject: [PATCH 55/85] Don't show minimap icons for players who have finished the race --- src/k_kart.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 5fe758128..68c5c63da 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -10559,7 +10559,7 @@ static void K_drawKartMinimap(void) g = g->next; } - if (!stplyr->mo || stplyr->spectator) // do we need the latter..? + if (!stplyr->mo || stplyr->spectator || stplyr->exiting) return; localplayers[numlocalplayers] = stplyr-players; @@ -10571,7 +10571,7 @@ static void K_drawKartMinimap(void) { if (!playeringame[i]) continue; - if (!players[i].mo || players[i].spectator) + if (!players[i].mo || players[i].spectator || players[i].exiting) continue; if (i != displayplayers[0] || r_splitscreen) From d2172fc54b64852448007b370111632015fd7498 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 7 May 2020 02:37:06 -0400 Subject: [PATCH 56/85] Bot amount & difficulty is now controlled by cvars --- src/d_clisrv.c | 21 +++- src/d_netcmd.c | 8 ++ src/d_netcmd.h | 1 + src/k_bot.c | 335 ++++++++++++++++++++++++++++++++++++++++--------- src/k_bot.h | 3 +- src/k_kart.c | 1 + src/p_mobj.c | 13 +- src/p_setup.c | 4 + 8 files changed, 324 insertions(+), 62 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 45627d3a2..406c07150 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2687,7 +2687,7 @@ void CL_RemovePlayer(INT32 playernum, INT32 reason) demo_extradata[playernum] |= DXD_PLAYSTATE; - if (server && !demo.playback) + if (server && !demo.playback && !players[playernum].bot) { INT32 node = playernode[playernum]; //playerpernode[node] = 0; // It'd be better to remove them all at once, but ghosting happened, so continue to let CL_RemovePlayer do it one-by-one @@ -3696,6 +3696,7 @@ static boolean SV_AddWaitingPlayers(void) { UINT8 buf[4]; UINT8 *buf_p = buf; + UINT8 nobotoverwrite; newplayer = true; @@ -3713,6 +3714,21 @@ static boolean SV_AddWaitingPlayers(void) break; } + nobotoverwrite = newplayernum; + + while (playeringame[nobotoverwrite] + && players[nobotoverwrite].bot + && nobotoverwrite < MAXPLAYERS) + { + // Only overwrite bots if there are NO other slots available. + nobotoverwrite++; + } + + if (nobotoverwrite < MAXPLAYERS) + { + newplayernum = nobotoverwrite; + } + // should never happen since we check the playernum // before accepting the join I_Assert(newplayernum < MAXPLAYERS); @@ -3801,9 +3817,6 @@ boolean SV_SpawnServer(void) if (!dedicated) CL_ConnectToServer(false); else doomcom->numslots = 1; - - // TEST - K_AddBots(7); } return SV_AddWaitingPlayers(); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 82892c9c2..12d246405 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -392,6 +392,14 @@ consvar_t cv_kartspeedometer = {"kartdisplayspeed", "Off", CV_SAVE, kartspeedome static CV_PossibleValue_t kartvoices_cons_t[] = {{0, "Never"}, {1, "Tasteful"}, {2, "Meme"}, {0, NULL}}; consvar_t cv_kartvoices = {"kartvoices", "Tasteful", CV_SAVE, kartvoices_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +static CV_PossibleValue_t kartbot_cons_t[] = { + {1, "MIN"}, + {9, "MAX"}, + {0, "Off"}, + {0, NULL} +}; +consvar_t cv_kartbot = {"kartbot", "5", CV_NETVAR|CV_CHEAT, kartbot_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; + consvar_t cv_karteliminatelast = {"karteliminatelast", "Yes", CV_NETVAR|CV_CHEAT|CV_CALL|CV_NOSHOWHELP, CV_YesNo, KartEliminateLast_OnChange, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_kartusepwrlv = {"kartusepwrlv", "Yes", CV_NETVAR|CV_CHEAT, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL}; diff --git a/src/d_netcmd.h b/src/d_netcmd.h index b5d723756..cdd54bd87 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -121,6 +121,7 @@ extern consvar_t cv_kartencore; extern consvar_t cv_kartvoterulechanges; extern consvar_t cv_kartspeedometer; extern consvar_t cv_kartvoices; +extern consvar_t cv_kartbot; extern consvar_t cv_karteliminatelast; extern consvar_t cv_kartusepwrlv; diff --git a/src/k_bot.c b/src/k_bot.c index 52731769b..13f5873ea 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -24,92 +24,315 @@ #include "i_system.h" #include "p_maputl.h" #include "d_ticcmd.h" +#include "m_random.h" +#include "r_things.h" // numskins -void K_AddBots(SINT8 numbots) +boolean K_AddBot(UINT8 skin, UINT8 difficulty, UINT8 *p) { - UINT8 newplayernum = 0; - UINT8 difficulty = MAXBOTDIFFICULTY; + UINT8 buf[3]; + UINT8 *buf_p = buf; + UINT8 newplayernum = *p; - if (dedicated) - newplayernum = 1; - - while (numbots > 0) + // search for a free playernum + // we can't use playeringame since it is not updated here + for (; newplayernum < MAXPLAYERS; newplayernum++) { - UINT8 buf[3]; - UINT8 *buf_p = buf; + UINT8 n; - numbots--; - - // search for a free playernum - // we can't use playeringame since it is not updated here - for (; newplayernum < MAXPLAYERS; newplayernum++) - { - UINT8 n; - - for (n = 0; n < MAXNETNODES; n++) - if (nodetoplayer[n] == newplayernum - || nodetoplayer2[n] == newplayernum - || nodetoplayer3[n] == newplayernum - || nodetoplayer4[n] == newplayernum) - break; - - if (n == MAXNETNODES) + for (n = 0; n < MAXNETNODES; n++) + if (nodetoplayer[n] == newplayernum + || nodetoplayer2[n] == newplayernum + || nodetoplayer3[n] == newplayernum + || nodetoplayer4[n] == newplayernum) break; + + if (n == MAXNETNODES) + break; + } + + while (playeringame[newplayernum] + && players[newplayernum].bot + && newplayernum < MAXPLAYERS) + { + newplayernum++; + } + + if (newplayernum >= MAXPLAYERS) + { + *p = newplayernum; + return false; + } + + WRITEUINT8(buf_p, newplayernum); + + if (skin > numskins) + { + skin = numskins; + } + + WRITEUINT8(buf_p, skin); + + if (difficulty < 1) + { + difficulty = 1; + } + else if (difficulty > MAXBOTDIFFICULTY) + { + difficulty = MAXBOTDIFFICULTY; + } + + WRITEUINT8(buf_p, difficulty); + + SendNetXCmd(XD_ADDBOT, buf, buf_p - buf); + + DEBFILE(va("Server added bot %d\n", newplayernum)); + // use the next free slot (we can't put playeringame[newplayernum] = true here) + newplayernum++; + + *p = newplayernum; + return true; +} + +void K_UpdateMatchRaceBots(void) +{ + const UINT8 difficulty = cv_kartbot.value; + UINT8 pmax = min((dedicated ? MAXPLAYERS-1 : MAXPLAYERS), cv_maxplayers.value); + UINT8 numplayers = 0; + UINT8 numbots = 0; + UINT8 numwaiting = 0; + SINT8 wantedbots = 0; + UINT8 i; + + if (difficulty != 0) + { + if (cv_ingamecap.value > 0) + { + pmax = min(pmax, cv_ingamecap.value); } - WRITEUINT8(buf_p, newplayernum); + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + if (!players[i].spectator) + { + if (players[i].bot) + { + numbots++; - // test skins - if (numbots == 6) - { - difficulty = MAXBOTDIFFICULTY; - WRITEUINT8(buf_p, 1); + // While we're here, we should update bot difficulty to the proper value. + players[i].botvars.difficulty = difficulty; + } + else + { + numplayers++; + } + } + else if (players[i].pflags & PF_WANTSTOJOIN) + { + numwaiting++; + } + } } - else if (numbots == 5) + + wantedbots = pmax - numplayers - numwaiting; + + if (wantedbots < 0) { - difficulty = MAXBOTDIFFICULTY; - WRITEUINT8(buf_p, 0); + wantedbots = 0; } - else if (numbots == 4) + } + else + { + wantedbots = 0; + } + + if (numbots < wantedbots) + { + // We require MORE bots! + UINT8 newplayernum = 0; + + if (dedicated) { - difficulty = MAXBOTDIFFICULTY-1; - WRITEUINT8(buf_p, 2); + newplayernum = 1; } - else if (numbots == 3) + + while (numbots < wantedbots) { - difficulty = MAXBOTDIFFICULTY-2; - WRITEUINT8(buf_p, 3); + if (!K_AddBot(M_RandomKey(numskins), difficulty, &newplayernum)) + { + // Not enough player slots to add the bot, break the loop. + break; + } + + numbots++; } - else if (numbots == 2) + } + else if (numbots > wantedbots) + { + UINT8 buf[2]; + + i = 0; + + while (numbots > wantedbots && i < MAXPLAYERS) { - difficulty = MAXBOTDIFFICULTY-4; - WRITEUINT8(buf_p, 5); + if (playeringame[i] && players[i].bot) + { + buf[0] = i; + buf[1] = KR_LEAVE; + SendNetXCmd(XD_REMOVEPLAYER, &buf, 2); + + CONS_Printf("Removed bot %s\n", player_names[i]); + numbots--; + } + + i++; } - else if (numbots == 1) + } + + // We should have enough bots now :) +} + +#if 0 +// This is mostly just pesudo code right now... +void K_InitGrandPrixBots(void) +{ + const UINT8 defaultbotskin = 9; // eggrobo + + // startingdifficulty: Easy = 3, Normal = 6, Hard = 9 + const UINT8 startingdifficulty = min(MAXBOTDIFFICULTY, (cv_kartspeed.value + 1) * 3); + UINT8 difficultylevels[MAXPLAYERS]; + + UINT8 playercount = 8; + UINT8 wantedbots = 0; + + UINT8 numplayers = 0; + UINT8 competitors[4]; + + boolean skinusable[MAXSKINS]; + UINT8 botskinlist[MAXPLAYERS]; + UINT8 botskinlistpos = 0; + + UINT8 i; + + memset(difficultylevels, MAXBOTDIFFICULTY, sizeof (difficultylevels)); + memset(competitors, MAXPLAYERS, sizeof (competitors)); + memset(botskinlist, defaultbotskin, sizeof (botskinlist)); + + // init usable bot skins list + for (i = 0; i < MAXSKINS; i++) + { + if (i < numskins) { - difficulty = MAXBOTDIFFICULTY-4; - WRITEUINT8(buf_p, 9); + skinusable[i] = true; } else { - difficulty = MAXBOTDIFFICULTY-2; - WRITEUINT8(buf_p, 10); + skinusable[i] = false; } + } - WRITEUINT8(buf_p, difficulty); + // init difficulty levels list + //if (!mastermodebots) { + difficultylevels[MAXPLAYERS] = { + max(1, startingdifficulty), + max(1, startingdifficulty-1), + max(1, startingdifficulty-2), + max(1, startingdifficulty-3), + max(1, startingdifficulty-3), + max(1, startingdifficulty-4), + max(1, startingdifficulty-4), + max(1, startingdifficulty-4), + max(1, startingdifficulty-5), + max(1, startingdifficulty-5), + max(1, startingdifficulty-6), + max(1, startingdifficulty-6), + max(1, startingdifficulty-7), + max(1, startingdifficulty-7), + max(1, startingdifficulty-8), + max(1, startingdifficulty-8), + }; - SendNetXCmd(XD_ADDBOT, buf, buf_p - buf); - - if (difficulty > 0) + for (i = 0; i < MAXPLAYERS; i++) + { + if (numplayers < MAXSPLITSCREENPLAYERS) { - difficulty--; + if (playeringame[i] && !players[i].spectator) + { + competitors[numplayers] = i; + numplayers++; + } } + else + { + if (playeringame[i]) + { + players[i].spectator = true; // force spectate for all other players, if they happen to exist? + } + } + } - DEBFILE(va("Server added bot %d\n", newplayernum)); - // use the next free slot (we can't put playeringame[newplayernum] = true here) - newplayernum++; + if (numplayers > 2) + { + // Add 3 bots per player beyond 2P + playercount += (numplayers-2) * 3; + } + + wantedbots = playercount - numplayers; + + // Create rival list + + // TODO: Use player skin's set rivals + // Starting with P1's rival1, P2's rival1, P3's rival1, P4's rival1, + // then P1's rival2, P2's rival2, etc etc etc etc....... + // then skip over any duplicates. + + // Pad the remaining list with random skins if we need to + if (botskinlistpos < wantedbots) + { + for (i = botskinlistpos; i < wantedbots; i++) + { + UINT8 val = M_RandomKey(numskins); + UINT8 loops = 0; + + while (!skinusable[val]) + { + if (loops >= numskins) + { + // no more skins + break; + } + + val++; + + if (val >= numskins) + { + val = 0; + } + + loops++; + } + + if (loops >= numskins) + { + // leave the rest of the table as the default skin + break; + } + + botskinlist[i] = val; + skinusable[val] = false; + } + } + + for (i = 0; i < wantedbots; i++) + { + if (!K_AddBot(botskinlist[i], difficultylevels[i], &newplayernum)) + { + break; + } } } +#endif boolean K_PlayerUsesBotMovement(player_t *player) { diff --git a/src/k_bot.h b/src/k_bot.h index 4a5af7122..ef7409f97 100644 --- a/src/k_bot.h +++ b/src/k_bot.h @@ -22,7 +22,8 @@ typedef struct botprediction_s { angle_t dir; } botprediction_t; -void K_AddBots(SINT8 numbots); +boolean K_AddBot(UINT8 skin, UINT8 difficulty, UINT8 *newplayernum); +void K_UpdateMatchRaceBots(void); boolean K_PlayerUsesBotMovement(player_t *player); boolean K_BotCanTakeCut(player_t *player); fixed_t K_BotRubberband(player_t *player); diff --git a/src/k_kart.c b/src/k_kart.c index 68c5c63da..83fffe056 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -623,6 +623,7 @@ void K_RegisterKartStuff(void) CV_RegisterVar(&cv_kartvoterulechanges); CV_RegisterVar(&cv_kartspeedometer); CV_RegisterVar(&cv_kartvoices); + CV_RegisterVar(&cv_kartbot); CV_RegisterVar(&cv_karteliminatelast); CV_RegisterVar(&cv_kartusepwrlv); CV_RegisterVar(&cv_votetime); diff --git a/src/p_mobj.c b/src/p_mobj.c index c1eed6327..3d70b3c38 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -11762,9 +11762,20 @@ void P_SpawnPlayer(INT32 playernum) } // spawn as spectator determination - if (multiplayer && demo.playback); // Don't mess with spectator values since the demo setup handles them already. + if (multiplayer && demo.playback) + { + ; // Don't mess with spectator values since the demo setup handles them already. + } else if (!G_GametypeHasSpectators()) + { + // We don't have spectators p->spectator = false; + } + else if (p->bot) + { + // No point in a spectating bot! + p->spectator = false; + } else if (netgame && p->jointime <= 1 && pcount) { p->spectator = true; diff --git a/src/p_setup.c b/src/p_setup.c index bc0e00d0c..d5891e84a 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -87,6 +87,7 @@ #include "k_battle.h" // K_SpawnBattleCapsules #include "k_pwrlv.h" #include "k_waypoint.h" +#include "k_bot.h" // // Map MD5, calculated on level load. @@ -2425,6 +2426,9 @@ static void P_LevelInitStuff(void) memset(&battleovertime, 0, sizeof(struct battleovertime)); speedscramble = encorescramble = -1; + + //if (!grandprix) + K_UpdateMatchRaceBots(); } // From efc8ce335c6709c774bef31955b3ece48c0d70d8 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 7 May 2020 04:07:13 -0400 Subject: [PATCH 57/85] Rubberbanding spacing was reworked Instead of the spacing being completely difficulty dependent (which would cause them not to rubberband to 1st place if they were any difficulty lower than 9), it's based on their difficulty & overall race standings & a little bit of port priority instead. This enables you to see a pesudo "rival" even when every bot is the same difficulty. --- src/k_bot.c | 51 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index 13f5873ea..3c66a0092 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -354,6 +354,41 @@ boolean K_BotCanTakeCut(player_t *player) return false; } +static UINT32 K_BotRubberbandDistance(player_t *player) +{ + const UINT32 spacing = 2048; + const UINT8 portpriority = player - players; + UINT8 pos = 0; + UINT8 i; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (i == portpriority) + { + continue; + } + + if (playeringame[i] && players[i].bot) + { + // First check difficulty levels, then score, then settle it with port priority! + if (player->botvars.difficulty < players[i].botvars.difficulty) + { + pos++; + } + else if (player->score < players[i].score) + { + pos++; + } + else if (i < portpriority) + { + pos++; + } + } + } + + return (pos * spacing); +} + fixed_t K_BotRubberband(player_t *player) { fixed_t rubberband = FRACUNIT; @@ -385,14 +420,18 @@ fixed_t K_BotRubberband(player_t *player) if (firstplace != NULL) { - const UINT32 spacing = 4096; - UINT32 easiness = (MAXBOTDIFFICULTY - player->botvars.difficulty); - UINT32 wanteddist = firstplace->distancetofinish + (spacing * easiness); + const UINT32 wanteddist = firstplace->distancetofinish + K_BotRubberbandDistance(player); + const INT32 distdiff = player->distancetofinish - wanteddist; - if (wanteddist < player->distancetofinish) + if (wanteddist > player->distancetofinish) { - // Catch up to 1st! - rubberband += (2*player->botvars.difficulty) * (player->distancetofinish - wanteddist); + // Whoa, you're too far ahead! + rubberband += (MAXBOTDIFFICULTY - player->botvars.difficulty) * distdiff; + } + else + { + // Catch up to your position! + rubberband += (2*player->botvars.difficulty) * distdiff; } } From 9429ae80e156b2277f62a21c6b245f407d1a39f0 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 7 May 2020 22:19:32 -0400 Subject: [PATCH 58/85] Only do this if you're the server :VVV --- src/k_bot.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/k_bot.c b/src/k_bot.c index 3c66a0092..285207caa 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -103,6 +103,11 @@ void K_UpdateMatchRaceBots(void) SINT8 wantedbots = 0; UINT8 i; + if (!server) + { + return; + } + if (difficulty != 0) { if (cv_ingamecap.value > 0) @@ -182,7 +187,6 @@ void K_UpdateMatchRaceBots(void) buf[1] = KR_LEAVE; SendNetXCmd(XD_REMOVEPLAYER, &buf, 2); - CONS_Printf("Removed bot %s\n", player_names[i]); numbots--; } From ccfc326885033dab30dbc1b74f11a1ef89a782bf Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 12 May 2020 00:20:59 -0400 Subject: [PATCH 59/85] Add to CMakeLists --- src/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4447614d3..91155fc8a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -164,6 +164,7 @@ set(SRB2_CORE_GAME_SOURCES k_pathfind.c k_pwrlv.c k_waypoint.c + k_bot.c p_local.h p_maputl.h @@ -182,6 +183,7 @@ set(SRB2_CORE_GAME_SOURCES k_pathfind.h k_pwrlv.h k_waypoint.h + k_bot.h ) if(NOT (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) From f903ca960d393d9045cfb2064d473788bc16afef Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 12 May 2020 01:41:00 -0400 Subject: [PATCH 60/85] Not in modeattacking --- src/p_setup.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/p_setup.c b/src/p_setup.c index d5891e84a..7ce4944f9 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -2427,8 +2427,10 @@ static void P_LevelInitStuff(void) memset(&battleovertime, 0, sizeof(struct battleovertime)); speedscramble = encorescramble = -1; - //if (!grandprix) + if (!modeattacking) + { K_UpdateMatchRaceBots(); + } } // From c3a51931504984eee32e0b3e0338dd67a5e9685e Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 18 May 2020 15:19:48 -0400 Subject: [PATCH 61/85] Remove this code, it's in grand-pricks & functional now --- src/k_bot.c | 141 ---------------------------------------------------- 1 file changed, 141 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index 285207caa..bc35bc21d 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -197,147 +197,6 @@ void K_UpdateMatchRaceBots(void) // We should have enough bots now :) } -#if 0 -// This is mostly just pesudo code right now... -void K_InitGrandPrixBots(void) -{ - const UINT8 defaultbotskin = 9; // eggrobo - - // startingdifficulty: Easy = 3, Normal = 6, Hard = 9 - const UINT8 startingdifficulty = min(MAXBOTDIFFICULTY, (cv_kartspeed.value + 1) * 3); - UINT8 difficultylevels[MAXPLAYERS]; - - UINT8 playercount = 8; - UINT8 wantedbots = 0; - - UINT8 numplayers = 0; - UINT8 competitors[4]; - - boolean skinusable[MAXSKINS]; - UINT8 botskinlist[MAXPLAYERS]; - UINT8 botskinlistpos = 0; - - UINT8 i; - - memset(difficultylevels, MAXBOTDIFFICULTY, sizeof (difficultylevels)); - memset(competitors, MAXPLAYERS, sizeof (competitors)); - memset(botskinlist, defaultbotskin, sizeof (botskinlist)); - - // init usable bot skins list - for (i = 0; i < MAXSKINS; i++) - { - if (i < numskins) - { - skinusable[i] = true; - } - else - { - skinusable[i] = false; - } - } - - // init difficulty levels list - //if (!mastermodebots) { - difficultylevels[MAXPLAYERS] = { - max(1, startingdifficulty), - max(1, startingdifficulty-1), - max(1, startingdifficulty-2), - max(1, startingdifficulty-3), - max(1, startingdifficulty-3), - max(1, startingdifficulty-4), - max(1, startingdifficulty-4), - max(1, startingdifficulty-4), - max(1, startingdifficulty-5), - max(1, startingdifficulty-5), - max(1, startingdifficulty-6), - max(1, startingdifficulty-6), - max(1, startingdifficulty-7), - max(1, startingdifficulty-7), - max(1, startingdifficulty-8), - max(1, startingdifficulty-8), - }; - - for (i = 0; i < MAXPLAYERS; i++) - { - if (numplayers < MAXSPLITSCREENPLAYERS) - { - if (playeringame[i] && !players[i].spectator) - { - competitors[numplayers] = i; - numplayers++; - } - } - else - { - if (playeringame[i]) - { - players[i].spectator = true; // force spectate for all other players, if they happen to exist? - } - } - } - - if (numplayers > 2) - { - // Add 3 bots per player beyond 2P - playercount += (numplayers-2) * 3; - } - - wantedbots = playercount - numplayers; - - // Create rival list - - // TODO: Use player skin's set rivals - // Starting with P1's rival1, P2's rival1, P3's rival1, P4's rival1, - // then P1's rival2, P2's rival2, etc etc etc etc....... - // then skip over any duplicates. - - // Pad the remaining list with random skins if we need to - if (botskinlistpos < wantedbots) - { - for (i = botskinlistpos; i < wantedbots; i++) - { - UINT8 val = M_RandomKey(numskins); - UINT8 loops = 0; - - while (!skinusable[val]) - { - if (loops >= numskins) - { - // no more skins - break; - } - - val++; - - if (val >= numskins) - { - val = 0; - } - - loops++; - } - - if (loops >= numskins) - { - // leave the rest of the table as the default skin - break; - } - - botskinlist[i] = val; - skinusable[val] = false; - } - } - - for (i = 0; i < wantedbots; i++) - { - if (!K_AddBot(botskinlist[i], difficultylevels[i], &newplayernum)) - { - break; - } - } -} -#endif - boolean K_PlayerUsesBotMovement(player_t *player) { if (player->bot || player->exiting) From 1ec651ff00f4b842033f0367da8b16b90e68ccf9 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 18 May 2020 15:30:55 -0400 Subject: [PATCH 62/85] Combine K_FindRandomItems & K_FindEggboxes --- src/k_bot.c | 53 +++++++++++++++-------------------------------------- 1 file changed, 15 insertions(+), 38 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index bc35bc21d..fe9e73df0 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -270,10 +270,13 @@ fixed_t K_BotRubberband(player_t *player) continue; } - /*if (players[i].bot) +#if 0 + // Only rubberband up to players. + if (players[i].bot) { continue; - }*/ + } +#endif if (firstplace == NULL || players[i].distancetofinish < firstplace->distancetofinish) { @@ -413,36 +416,11 @@ fixed_t eggboxx, eggboxy; UINT8 randomitems = 0; UINT8 eggboxes = 0; -static boolean K_FindRandomItems(mobj_t *thing) -{ - fixed_t dist; - - if (thing->type != MT_RANDOMITEM) - { - return true; - } - - if (!thing->health) - { - return true; - } - - dist = P_AproxDistance(thing->x - eggboxx, thing->y - eggboxy); - - if (dist > distancetocheck) - { - return true; - } - - randomitems++; - return true; -} - static boolean K_FindEggboxes(mobj_t *thing) { fixed_t dist; - if (thing->type != MT_EGGMANITEM) + if (thing->type != MT_RANDOMITEM && thing->type != MT_EGGMANITEM) { return true; } @@ -459,7 +437,14 @@ static boolean K_FindEggboxes(mobj_t *thing) return true; } - eggboxes++; + if (thing->type == MT_RANDOMITEM) + { + randomitems++; + } + else + { + eggboxes++; + } return true; } @@ -480,14 +465,6 @@ static UINT8 K_EggboxStealth(fixed_t x, fixed_t y) BMBOUNDFIX(xl, xh, yl, yh); - for (bx = xl; bx <= xh; bx++) - { - for (by = yl; by <= yh; by++) - { - P_BlockThingsIterator(bx, by, K_FindRandomItems); - } - } - for (bx = xl; bx <= xh; bx++) { for (by = yl; by <= yh; by++) @@ -496,7 +473,7 @@ static UINT8 K_EggboxStealth(fixed_t x, fixed_t y) } } - return randomitems * eggboxes; + return (randomitems * eggboxes); } static inline boolean K_FindBlockingWalls(line_t *line) From 97e261f443946fa63f95b6c7fcdc4eefca35f4f8 Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 22 May 2020 21:27:55 +0100 Subject: [PATCH 63/85] Fix some issues with the tab rankings and end screen that bots exposed. * Laps now have an initial zero instead of being directly indexed from 1, so start counting from there. * Clean up the circumstances under which LAGLESS! is shown next to a name - specifically removing it from both non-netgames in general, and bots in netgames. * Fix `(powertype == -1)` being broken on the intermission drawer, which was especially obvious with bots outside of netgames. --- src/hu_stuff.c | 2 +- src/k_kart.c | 2 +- src/y_inter.c | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/hu_stuff.c b/src/hu_stuff.c index a4acabf79..7338a232b 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -3095,7 +3095,7 @@ static void HU_DrawRankings(void) if (G_RaceGametype()) { if (circuitmap) - tab[scorelines].count = players[i].laps+1; + tab[scorelines].count = players[i].laps; else tab[scorelines].count = players[i].realtime; } diff --git a/src/k_kart.c b/src/k_kart.c index de653e9b0..a8ddef8f0 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -9434,7 +9434,7 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I y2 = y; - if (playerconsole[tab[i].num] == 0 && server_lagless) + if (netgame && playerconsole[tab[i].num] == 0 && server_lagless && !players[tab[i].num].bot) { y2 = ( y - 4 ); diff --git a/src/y_inter.c b/src/y_inter.c index d3ef26fdd..1051a7e55 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -440,7 +440,7 @@ void Y_IntermissionDrawer(void) int y2; if (data.match.rankingsmode) - timeheader = "PWR.LV"; + timeheader = (powertype != -1 ? "PWR.LV" : "RANK"); else timeheader = ((intertype == int_race || (intertype == int_match && battlecapsules)) ? "TIME" : "SCORE"); @@ -497,7 +497,7 @@ void Y_IntermissionDrawer(void) y2 = y; - if (playerconsole[data.match.num[i]] == 0 && server_lagless) + if (netgame && playerconsole[data.match.num[i]] == 0 && server_lagless && !players[data.match.num[i]].bot) { static int alagles_timer = 0; patch_t *alagles; @@ -533,7 +533,7 @@ void Y_IntermissionDrawer(void) if (data.match.rankingsmode) { - if (!clientpowerlevels[data.match.num[i]][powertype]) // No power level (splitscreen guests) + if (powertype != -1 && !clientpowerlevels[data.match.num[i]][powertype]) // No power level (splitscreen guests) STRBUFCPY(strtime, "----"); else { From 84f277c3239d7e6d23aeab62c31c9213b10148ac Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 22 May 2020 21:34:05 +0100 Subject: [PATCH 64/85] Adjust the finish line detection to exclude a handful of circumstances I've tripped before. --- src/p_spec.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/p_spec.c b/src/p_spec.c index 0c75051ad..82dad34a6 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -2209,7 +2209,8 @@ static void K_HandleLapDecrement(player_t *player) void P_CrossSpecialLine(line_t *line, INT32 side, mobj_t *thing) { // only used for the players currently - if (thing && thing->player) + if (!(thing && thing->player && !thing->player->spectator && !(thing->player->pflags & PF_TIMEOVER))) + return; { player_t *player = thing->player; switch (line->special) From 56226fae458d1ea38be79e5d1fd8a23e609643cd Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 22 May 2020 21:51:05 +0100 Subject: [PATCH 65/85] Only iterate blockmap once for Random Items and Eggboxes. --- src/k_bot.c | 45 ++++++++------------------------------------- 1 file changed, 8 insertions(+), 37 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index 285207caa..fd75adca5 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -554,11 +554,12 @@ fixed_t eggboxx, eggboxy; UINT8 randomitems = 0; UINT8 eggboxes = 0; -static boolean K_FindRandomItems(mobj_t *thing) +static boolean K_FindRandomItemsAndEggboxes(mobj_t *thing) { fixed_t dist; + boolean egg = (thing->type == MT_EGGMANITEM); - if (thing->type != MT_RANDOMITEM) + if (!egg && thing->type != MT_RANDOMITEM) { return true; } @@ -575,32 +576,10 @@ static boolean K_FindRandomItems(mobj_t *thing) return true; } - randomitems++; - return true; -} - -static boolean K_FindEggboxes(mobj_t *thing) -{ - fixed_t dist; - - if (thing->type != MT_EGGMANITEM) - { - return true; - } - - if (!thing->health) - { - return true; - } - - dist = P_AproxDistance(thing->x - eggboxx, thing->y - eggboxy); - - if (dist > distancetocheck) - { - return true; - } - - eggboxes++; + if (egg) + eggboxes++; + else + randomitems++; return true; } @@ -625,15 +604,7 @@ static UINT8 K_EggboxStealth(fixed_t x, fixed_t y) { for (by = yl; by <= yh; by++) { - P_BlockThingsIterator(bx, by, K_FindRandomItems); - } - } - - for (bx = xl; bx <= xh; bx++) - { - for (by = yl; by <= yh; by++) - { - P_BlockThingsIterator(bx, by, K_FindEggboxes); + P_BlockThingsIterator(bx, by, K_FindRandomItemsAndEggboxes); } } From 19f352e4a450ad28a8466026885669af9bf9a8bb Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 22 May 2020 20:19:58 -0400 Subject: [PATCH 66/85] Commentate K_BotReducePrediction better, use angle rather than distance for path split picking --- src/k_bot.c | 79 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 61 insertions(+), 18 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index fe9e73df0..fe7b65e2b 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -628,19 +628,29 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) { const INT16 handling = K_GetKartTurnValue(player, KART_FULLTURN); // Reduce prediction based on how fast you can turn const INT16 normal = KART_FULLTURN; // "Standard" handling to compare to - const tic_t futuresight = (TICRATE * normal) / max(1, handling); // How far ahead into the future to try and predict + const fixed_t distreduce = K_BotReducePrediction(player); const fixed_t radreduce = min(distreduce + FRACUNIT/4, FRACUNIT); - const INT32 distance = (FixedMul(player->speed, distreduce) / FRACUNIT) * futuresight; - INT32 distanceleft = distance; + + const tic_t futuresight = (TICRATE * normal) / max(1, handling); // How far ahead into the future to try and predict + const fixed_t speed = P_AproxDistance(player->mo->momx, player->mo->momy) + (K_GetKartSpeed(player, false) / 2); + const INT32 distance = (FixedMul(speed, distreduce) / FRACUNIT) * futuresight; + botprediction_t *predict = Z_Calloc(sizeof(botprediction_t), PU_LEVEL, NULL); waypoint_t *wp = player->nextwaypoint; + + INT32 distanceleft = distance; fixed_t smallestradius = INT32_MAX; + angle_t angletonext = ANGLE_MAX; + size_t nwp; size_t i; + // Reduce distance left by your distance to the starting waypoint. + // This prevents looking too far ahead if the closest waypoint is really far away. distanceleft -= P_AproxDistance(player->mo->x - wp->mobj->x, player->mo->y - wp->mobj->y) / FRACUNIT; + // We don't want to look ahead at all, just go to the first waypoint. if (distanceleft <= 0) { predict->x = wp->mobj->x; @@ -649,66 +659,99 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) return predict; } + angletonext = R_PointToAngle2( + player->mo->x, player->mo->y, + wp->mobj->x, wp->mobj->y + ); + + // Go through waypoints until we've traveled the distance we wanted to predict ahead! while (distanceleft > 0) { + INT32 disttonext = INT32_MAX; + if (wp->mobj->radius < smallestradius) { smallestradius = wp->mobj->radius; } - nwp = 0; - if (wp->numnextwaypoints == 0) { + // Well, this is where I get off. distanceleft = 0; break; } + // Calculate nextwaypoints index to use + // nextwaypoints[0] by default + nwp = 0; + + // There are multiple nextwaypoints, + // so we need to find the most convenient one to us. + // Let's compare the angle to the player's! if (wp->numnextwaypoints > 1) { - fixed_t closest = INT32_MAX; - fixed_t dist = INT32_MAX; + angle_t delta = ANGLE_MAX; + angle_t a = ANGLE_MAX; for (i = 0; i < wp->numnextwaypoints; i++) { + if (K_GetWaypointIsShortcut(wp->nextwaypoints[i]) && !K_BotCanTakeCut(player)) { continue; } - dist = P_AproxDistance( - player->mo->x - wp->nextwaypoints[i]->mobj->x, - player->mo->y - wp->nextwaypoints[i]->mobj->y + // Unlike the other parts of this function, we're comparing the player's physical position, NOT the position of the waypoint!! + // This should roughly correspond with how players will think about path splits. + a = R_PointToAngle2( + player->mo->x, player->mo->y, + wp->nextwaypoints[i]->mobj->x, wp->nextwaypoints[i]->mobj->y ); + if (a > ANGLE_180) + { + a = InvAngle(a); + } - if (dist < closest) + a = player->mo->angle - a; + + if (a < delta) { nwp = i; - closest = dist; + delta = a; } } } - if ((INT32)(wp->nextwaypointdistances[nwp]) > distanceleft) + // Save angle for the next loop's oldangle + angletonext = R_PointToAngle2( + wp->mobj->x, wp->mobj->y, + wp->nextwaypoints[nwp]->mobj->x, wp->nextwaypoints[nwp]->mobj->y + ); + + disttonext = (wp->nextwaypointdistances[nwp] * FRACUNIT) / FRACUNIT; + + if (disttonext > distanceleft) { break; } - distanceleft -= wp->nextwaypointdistances[nwp]; + distanceleft -= disttonext; wp = wp->nextwaypoints[nwp]; } + // Set our predicted point's coordinates, + // and use the smallest radius of all of the waypoints in the chain! predict->x = wp->mobj->x; predict->y = wp->mobj->y; predict->radius = FixedMul(smallestradius, radreduce); + // Set the prediction coordinates between the 2 waypoints if there's still distance left. if (distanceleft > 0) { - angle_t a = R_PointToAngle2(wp->mobj->x, wp->mobj->y, wp->nextwaypoints[nwp]->mobj->x, wp->nextwaypoints[nwp]->mobj->y); - - predict->x += P_ReturnThrustX(NULL, a, distanceleft * FRACUNIT); - predict->y += P_ReturnThrustY(NULL, a, distanceleft * FRACUNIT); + // Scaled with the leftover anglemul! + predict->x += P_ReturnThrustX(NULL, angletonext, distanceleft * FRACUNIT); + predict->y += P_ReturnThrustY(NULL, angletonext, distanceleft * FRACUNIT); } return predict; From f442b934cccf0f6690d5ace88c8830a6ef25490d Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 22 May 2020 20:20:17 -0400 Subject: [PATCH 67/85] Adjust wall steer radius with speed --- src/k_bot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_bot.c b/src/k_bot.c index fe7b65e2b..6633e5e9e 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -589,7 +589,7 @@ static fixed_t K_BotReducePrediction(player_t *player) INT32 xl, xh, yl, yh, bx, by; botmo = player->mo; - distancetocheck = player->mo->radius * 16; + distancetocheck = (player->mo->radius * 8) + (player->speed * 4); closestlinedist = INT32_MAX; tmx = player->mo->x; From 38ec153e901f71a081635f63c9ae1d38c21af47b Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 22 May 2020 20:49:33 -0400 Subject: [PATCH 68/85] A bit redundant --- src/k_bot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_bot.c b/src/k_bot.c index 6633e5e9e..b22fd5946 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -728,7 +728,7 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) wp->nextwaypoints[nwp]->mobj->x, wp->nextwaypoints[nwp]->mobj->y ); - disttonext = (wp->nextwaypointdistances[nwp] * FRACUNIT) / FRACUNIT; + disttonext = (INT32)wp->nextwaypointdistances[nwp]; if (disttonext > distanceleft) { From ca7154f521423b57b9577cbb4feed6d71af61531 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 22 May 2020 21:19:50 -0400 Subject: [PATCH 69/85] Make rubberband air speed cap even harsher --- src/p_user.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/p_user.c b/src/p_user.c index 6062d0d63..f91e8dc25 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -4170,7 +4170,12 @@ static void P_3dMovement(player_t *player) if (K_PlayerUsesBotMovement(player)) { - div = FixedMul(div, K_BotRubberband(player)); + fixed_t rubberband = K_BotRubberband(player); + + if (rubberband > FRACUNIT) + { + div = FixedMul(div, 4*rubberband); + } } newspeed = speed - FixedDiv((speed - airspeedcap), div); From edfc14c506fbd8064c01faf72ae75b02fa787c06 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 24 May 2020 11:12:38 -0400 Subject: [PATCH 70/85] Better steering - If they're already turning in one direction, they are more likely to steer in that direction for objects - Bots have to want to turn in 1 direction for a few frames in a row before it'll let them Prevents twitching & makes them less indecisive in general --- src/d_clisrv.c | 4 +- src/d_clisrv.h | 2 +- src/d_player.h | 2 +- src/k_bot.c | 119 ++++++++++++++++++++++++++++++++++--------------- src/k_bot.h | 5 +++ src/p_saveg.c | 4 +- 6 files changed, 95 insertions(+), 41 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index f73f436ae..37e39aadf 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -650,7 +650,7 @@ static inline void resynch_write_player(resynch_pak *rsp, const size_t i) rsp->bot_difficulty = players[i].botvars.difficulty; rsp->bot_itemdelay = players[i].botvars.itemdelay; rsp->bot_itemconfirm = players[i].botvars.itemconfirm; - rsp->bot_lastturn = players[i].botvars.lastturn; + rsp->bot_turnconfirm = players[i].botvars.turnconfirm; rsp->hasmo = false; //Transfer important mo information if the player has a body. @@ -779,7 +779,7 @@ static void resynch_read_player(resynch_pak *rsp) players[i].botvars.difficulty = rsp->bot_difficulty; players[i].botvars.itemdelay = rsp->bot_itemdelay; players[i].botvars.itemconfirm = rsp->bot_itemconfirm; - players[i].botvars.lastturn = rsp->bot_lastturn; + players[i].botvars.turnconfirm = rsp->bot_turnconfirm; //We get a packet for each player in game. if (!playeringame[i]) diff --git a/src/d_clisrv.h b/src/d_clisrv.h index abf43e5f9..1ef85b4f9 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -289,7 +289,7 @@ typedef struct UINT8 bot_difficulty; tic_t bot_itemdelay; tic_t bot_itemconfirm; - INT16 bot_lastturn; + SINT8 bot_turnconfirm; //player->mo stuff UINT8 hasmo; // Boolean diff --git a/src/d_player.h b/src/d_player.h index d67c4137b..6ed9fd719 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -428,7 +428,7 @@ typedef struct botvars_s tic_t itemdelay; tic_t itemconfirm; - INT16 lastturn; + SINT8 turnconfirm; } botvars_t; // ======================================================================== diff --git a/src/k_bot.c b/src/k_bot.c index b22fd5946..edd46b7d0 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -410,6 +410,7 @@ fixed_t distancetocheck = 0; fixed_t closestlinedist = INT32_MAX; +INT16 curturn = 0; INT16 badsteerglobal = 0; fixed_t eggboxx, eggboxy; @@ -761,30 +762,60 @@ static void K_SteerFromObject(mobj_t *bot, mobj_t *thing, fixed_t fulldist, fixe { angle_t destangle = R_PointToAngle2(bot->x, bot->y, thing->x, thing->y); angle_t angle; + SINT8 flip = 1; amount = (amount * FixedDiv(distancetocheck - fulldist, distancetocheck)) / FRACUNIT; + if (amount == 0) + { + // Shouldn't happen + return; + } + if (towards) { - if (xdist < (bot->radius + thing->radius)) + if (xdist < FixedHypot(bot->radius, thing->radius)) { // Don't need to turn any harder! + + if (abs(badsteerglobal) <= amount) + { + badsteerglobal = 0; + } + else + { + if (badsteerglobal > 0) + { + badsteerglobal -= amount; + } + else if (badsteerglobal < 0) + { + badsteerglobal += amount; + } + } + return; } - amount = -amount; + // Still turning towards it, flip. + flip = -flip; } angle = (bot->angle - destangle); - if (angle < ANGLE_180) { - badsteerglobal -= amount; + flip = -flip; } - else + + // If going in the opposite direction of where you wanted to turn, + // then reduce the amount that you can turn in that direction. + if ((flip == 1 && curturn < 0) + || (flip == -1 && curturn > 0)) { - badsteerglobal += amount; + amount /= 4; } + + badsteerglobal += amount * flip; } static boolean K_BotSteerObjects(mobj_t *thing) @@ -876,7 +907,7 @@ static boolean K_BotSteerObjects(mobj_t *thing) K_SteerFromObject(botmo, thing, fulldist, xdist, false, 2 * (KART_FULLTURN + dodge)); break; case MT_RANDOMITEM: - if (anglediff > 90) + if (anglediff >= 60) { break; } @@ -887,7 +918,7 @@ static boolean K_BotSteerObjects(mobj_t *thing) } break; case MT_EGGMANITEM: - if (anglediff > 90) + if (anglediff >= 60) { break; } @@ -908,7 +939,7 @@ static boolean K_BotSteerObjects(mobj_t *thing) } break; case MT_FLOATINGITEM: - if (anglediff > 90) + if (anglediff >= 60) { break; } @@ -920,7 +951,7 @@ static boolean K_BotSteerObjects(mobj_t *thing) break; case MT_RING: case MT_FLINGRING: - if (anglediff > 90) + if (anglediff >= 60) { break; } @@ -932,8 +963,8 @@ static boolean K_BotSteerObjects(mobj_t *thing) { K_SteerFromObject(botmo, thing, fulldist, xdist, true, (RINGTOTAL(botmo->player) < 3 - ? (2 * (KART_FULLTURN + attack)) - : ((KART_FULLTURN + attack) / 2)) + ? (4 * (KART_FULLTURN + attack)) + : (KART_FULLTURN + attack)) ); } break; @@ -985,7 +1016,7 @@ static boolean K_BotSteerObjects(mobj_t *thing) fixed_t theirweight = K_GetMobjWeight(thing, botmo); fixed_t weightdiff = 0; - if (anglediff > 90) + if (anglediff >= 90) { weightdiff = theirweight - ourweight; } @@ -1006,6 +1037,11 @@ static boolean K_BotSteerObjects(mobj_t *thing) } break; case MT_BOTHINT: + if (anglediff >= 60) + { + break; + } + if (thing->extravalue1 == 0) { K_SteerFromObject(botmo, thing, fulldist, xdist, false, thing->extravalue2 * (KART_FULLTURN + dodge)); @@ -1025,14 +1061,15 @@ static boolean K_BotSteerObjects(mobj_t *thing) return true; } -static INT16 K_BotFindObjects(player_t *player) +static INT16 K_BotFindObjects(player_t *player, INT16 turn) { INT32 xl, xh, yl, yh, bx, by; badsteerglobal = 0; botmo = player->mo; - distancetocheck = (player->mo->radius * 32) + (player->speed * 4); + curturn = turn; + distancetocheck = 2048*mapobjectscale; xl = (unsigned)(botmo->x - distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; xh = (unsigned)(botmo->x + distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; @@ -1409,11 +1446,6 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) cmd->forwardmove /= 2; cmd->buttons |= BT_BRAKE; } - else if (dirdist <= realrad) - { - // Steer towards/away from objects! - turnamt += K_BotFindObjects(player); - } if (dirdist <= rad) { @@ -1446,6 +1478,12 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) turnamt /= 4; } } + + if (anglediff < 60) + { + // Steer towards/away from objects! + turnamt += K_BotFindObjects(player, turnamt); + } } } @@ -2169,8 +2207,6 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if (turnamt != 0) { - const INT16 minturn = KART_FULLTURN/2; - if (turnamt > KART_FULLTURN) { turnamt = KART_FULLTURN; @@ -2180,26 +2216,39 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) turnamt = -KART_FULLTURN; } - if ((turnamt > minturn && player->botvars.lastturn >= 0) - || (turnamt < -minturn && player->botvars.lastturn <= 0)) + if (turnamt > 0) { - if (turnamt > 0) + if (player->botvars.turnconfirm < 0) { - player->botvars.lastturn = 1; - } - else if (turnamt < 0) - { - player->botvars.lastturn = -1; + // Reset turn confirm + player->botvars.turnconfirm = 0; } + if (player->botvars.turnconfirm < BOTTURNCONFIRM) + { + player->botvars.turnconfirm++; + } + } + else if (turnamt < 0) + { + if (player->botvars.turnconfirm > 0) + { + // Reset turn confirm + player->botvars.turnconfirm = 0; + } + + if (player->botvars.turnconfirm > -BOTTURNCONFIRM) + { + player->botvars.turnconfirm--; + } + } + + if (abs(player->botvars.turnconfirm) >= BOTTURNCONFIRM) + { + // You're probably commiting to your turn, here you go. cmd->driftturn = turnamt; cmd->angleturn += K_GetKartTurnValue(player, turnamt); } - else - { - // Can reset turn dir - player->botvars.lastturn = 0; - } } if (predict != NULL) diff --git a/src/k_bot.h b/src/k_bot.h index ef7409f97..0525480b4 100644 --- a/src/k_bot.h +++ b/src/k_bot.h @@ -13,8 +13,13 @@ #include "k_waypoint.h" #include "d_player.h" +// Maximum value of botvars.difficulty #define MAXBOTDIFFICULTY 9 +// How many tics in a row do you need to turn in this direction before we'll let you turn. +// Made it as small as possible without making it look like the bots are twitching constantly. +#define BOTTURNCONFIRM 7 + // Path that bot will attempt to take typedef struct botprediction_s { fixed_t x, y; diff --git a/src/p_saveg.c b/src/p_saveg.c index 007ce919c..5aa988075 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -272,7 +272,7 @@ static void P_NetArchivePlayers(void) WRITEUINT8(save_p, players[i].botvars.difficulty); WRITEUINT32(save_p, players[i].botvars.itemdelay); WRITEUINT32(save_p, players[i].botvars.itemconfirm); - WRITEINT16(save_p, players[i].botvars.lastturn); + WRITESINT8(save_p, players[i].botvars.turnconfirm); } } @@ -448,7 +448,7 @@ static void P_NetUnArchivePlayers(void) players[i].botvars.difficulty = READUINT8(save_p); players[i].botvars.itemdelay = READUINT32(save_p); players[i].botvars.itemconfirm = READUINT32(save_p); - players[i].botvars.lastturn = READINT16(save_p); + players[i].botvars.turnconfirm = READSINT8(save_p); } } From 56867fb203a569e9a215db58275c039c29f3278d Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 24 May 2020 11:13:20 -0400 Subject: [PATCH 71/85] Going above top speed makes radius smaller --- src/k_bot.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index edd46b7d0..da4f432a5 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -630,11 +630,13 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) const INT16 handling = K_GetKartTurnValue(player, KART_FULLTURN); // Reduce prediction based on how fast you can turn const INT16 normal = KART_FULLTURN; // "Standard" handling to compare to + const fixed_t topspeed = K_GetKartSpeed(player, false); + const fixed_t speed = P_AproxDistance(player->mo->momx, player->mo->momy) + (topspeed / 4); + const fixed_t distreduce = K_BotReducePrediction(player); - const fixed_t radreduce = min(distreduce + FRACUNIT/4, FRACUNIT); + fixed_t radreduce = min(distreduce + FRACUNIT/4, FRACUNIT); const tic_t futuresight = (TICRATE * normal) / max(1, handling); // How far ahead into the future to try and predict - const fixed_t speed = P_AproxDistance(player->mo->momx, player->mo->momy) + (K_GetKartSpeed(player, false) / 2); const INT32 distance = (FixedMul(speed, distreduce) / FRACUNIT) * futuresight; botprediction_t *predict = Z_Calloc(sizeof(botprediction_t), PU_LEVEL, NULL); @@ -651,6 +653,12 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) // This prevents looking too far ahead if the closest waypoint is really far away. distanceleft -= P_AproxDistance(player->mo->x - wp->mobj->x, player->mo->y - wp->mobj->y) / FRACUNIT; + if (speed > topspeed) + { + // Play more in the center when going fast. + radreduce = FixedDiv(radreduce, speed / topspeed); + } + // We don't want to look ahead at all, just go to the first waypoint. if (distanceleft <= 0) { From 02605a5aceb4282b435c52cb002bebb480169925 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 24 May 2020 15:08:10 -0400 Subject: [PATCH 72/85] Split bot code into multiple files & clean up --- src/CMakeLists.txt | 2 + src/Makefile | 2 + src/k_bot.c | 1655 ++------------------------------------------ src/k_bot.h | 196 +++++- src/k_botitem.c | 1095 +++++++++++++++++++++++++++++ src/k_botsearch.c | 749 ++++++++++++++++++++ 6 files changed, 2109 insertions(+), 1590 deletions(-) create mode 100644 src/k_botitem.c create mode 100644 src/k_botsearch.c diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 59cb146c0..2cba3c836 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -166,6 +166,8 @@ set(SRB2_CORE_GAME_SOURCES k_waypoint.c k_color.c k_bot.c + k_botitem.c + k_botsearch.c p_local.h p_maputl.h diff --git a/src/Makefile b/src/Makefile index 21a08eaab..84d47da5c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -563,6 +563,8 @@ OBJS:=$(i_main_o) \ $(OBJDIR)/lzf.o \ $(OBJDIR)/vid_copy.o \ $(OBJDIR)/k_bot.o \ + $(OBJDIR)/k_botitem.o \ + $(OBJDIR)/k_botsearch.o \ $(i_cdmus_o) \ $(i_net_o) \ $(i_system_o) \ diff --git a/src/k_bot.c b/src/k_bot.c index da4f432a5..927fc357a 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -1,14 +1,13 @@ -// SONIC ROBO BLAST 2 +// SONIC ROBO BLAST 2 KART //----------------------------------------------------------------------------- -// Copyright (C) 2007-2016 by John "JTE" Muniz. -// Copyright (C) 2011-2018 by Sonic Team Junior. +// 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_bot.c -/// \brief Basic bot handling +/// \brief Bot logic & ticcmd generation code #include "doomdef.h" #include "d_player.h" @@ -27,6 +26,12 @@ #include "m_random.h" #include "r_things.h" // numskins + +/*-------------------------------------------------- + boolean K_AddBot(UINT8 skin, UINT8 difficulty, UINT8 *p) + + See header file for description. +--------------------------------------------------*/ boolean K_AddBot(UINT8 skin, UINT8 difficulty, UINT8 *p) { UINT8 buf[3]; @@ -93,6 +98,11 @@ boolean K_AddBot(UINT8 skin, UINT8 difficulty, UINT8 *p) return true; } +/*-------------------------------------------------- + void K_UpdateMatchRaceBots(void) + + See header file for description. +--------------------------------------------------*/ void K_UpdateMatchRaceBots(void) { const UINT8 difficulty = cv_kartbot.value; @@ -197,6 +207,11 @@ void K_UpdateMatchRaceBots(void) // We should have enough bots now :) } +/*-------------------------------------------------- + boolean K_PlayerUsesBotMovement(player_t *player) + + See header file for description. +--------------------------------------------------*/ boolean K_PlayerUsesBotMovement(player_t *player) { if (player->bot || player->exiting) @@ -205,6 +220,11 @@ boolean K_PlayerUsesBotMovement(player_t *player) return false; } +/*-------------------------------------------------- + boolean K_BotCanTakeCut(player_t *player) + + See header file for description. +--------------------------------------------------*/ boolean K_BotCanTakeCut(player_t *player) { if (!K_ApplyOffroad(player) @@ -217,6 +237,18 @@ boolean K_BotCanTakeCut(player_t *player) return false; } +/*-------------------------------------------------- + static UINT32 K_BotRubberbandDistance(player_t *player) + + Calculates the distance away from 1st place that the + bot should rubberband to. + + Input Arguments:- + player - Player to compare. + + Return:- + Distance to add, as an integer. +--------------------------------------------------*/ static UINT32 K_BotRubberbandDistance(player_t *player) { const UINT32 spacing = 2048; @@ -252,6 +284,11 @@ static UINT32 K_BotRubberbandDistance(player_t *player) return (pos * spacing); } +/*-------------------------------------------------- + fixed_t K_BotRubberband(player_t *player) + + See header file for description. +--------------------------------------------------*/ fixed_t K_BotRubberband(player_t *player) { fixed_t rubberband = FRACUNIT; @@ -313,7 +350,12 @@ fixed_t K_BotRubberband(player_t *player) return rubberband; } -static fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, fixed_t v2y, fixed_t cx, fixed_t cy) +/*-------------------------------------------------- + fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, fixed_t v2y, fixed_t cx, fixed_t cy) + + See header file for description. +--------------------------------------------------*/ +fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, fixed_t v2y, fixed_t cx, fixed_t cy) { fixed_t v1toc[2] = {cx - v1x, cy - v1y}; fixed_t v1tov2[2] = {v2x - v1x, v2y - v1y}; @@ -337,294 +379,17 @@ static fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, return P_AproxDistance(cx - px, cy - py); } -static boolean K_BotHatesThisSectorsSpecial(player_t *player, sector_t *sec) -{ - switch (GETSECSPECIAL(sec->special, 1)) - { - case 1: // Damage - case 5: // Spikes - case 6: case 7: // Death Pit - case 8: // Instant Kill - return true; - //case 2: case 3: // Offroad (let's let them lawnmower) - case 4: // Offroad (Strong) - if (!K_BotCanTakeCut(player)) - return true; - default: - break; - } +/*-------------------------------------------------- + static botprediction_t *K_CreateBotPrediction(player_t *player) - return false; -} + Calculates a point further along the track to attempt to drive towards. -static boolean K_BotHatesThisSector(player_t *player, sector_t *sec) -{ - const boolean flip = (player->mo->eflags & MFE_VERTICALFLIP); - INT32 flag; - ffloor_t *rover; - - if (flip) - { - flag = SF_FLIPSPECIAL_CEILING; - } - else - { - flag = SF_FLIPSPECIAL_FLOOR; - } - - if (sec->flags & flag) - { - if (K_BotHatesThisSectorsSpecial(player, sec)) - { - return true; - } - } - - for (rover = sec->ffloors; rover; rover = rover->next) - { - if (!(rover->flags & FF_EXISTS)) - { - continue; - } - - if (!(rover->master->frontsector->flags & flag)) - { - continue; - } - - if (((*rover->bottomheight >= player->mo->z + player->mo->height) && (flip)) - || ((*rover->topheight <= player->mo->z) && (!flip))) - { - if (K_BotHatesThisSectorsSpecial(player, sec)) - { - return true; - } - } - } - - return false; -} - -mobj_t *botmo = NULL; -fixed_t distancetocheck = 0; - -fixed_t closestlinedist = INT32_MAX; - -INT16 curturn = 0; -INT16 badsteerglobal = 0; - -fixed_t eggboxx, eggboxy; -UINT8 randomitems = 0; -UINT8 eggboxes = 0; - -static boolean K_FindEggboxes(mobj_t *thing) -{ - fixed_t dist; - - if (thing->type != MT_RANDOMITEM && thing->type != MT_EGGMANITEM) - { - return true; - } - - if (!thing->health) - { - return true; - } - - dist = P_AproxDistance(thing->x - eggboxx, thing->y - eggboxy); - - if (dist > distancetocheck) - { - return true; - } - - if (thing->type == MT_RANDOMITEM) - { - randomitems++; - } - else - { - eggboxes++; - } - return true; -} - -static UINT8 K_EggboxStealth(fixed_t x, fixed_t y) -{ - INT32 xl, xh, yl, yh, bx, by; - - eggboxx = x; - eggboxy = y; - distancetocheck = (mapobjectscale * 256); - randomitems = 0; - eggboxes = 0; - - xl = (unsigned)(eggboxx - distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; - xh = (unsigned)(eggboxx + distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; - yl = (unsigned)(eggboxy - distancetocheck - bmaporgy)>>MAPBLOCKSHIFT; - yh = (unsigned)(eggboxy + distancetocheck - bmaporgy)>>MAPBLOCKSHIFT; - - BMBOUNDFIX(xl, xh, yl, yh); - - for (bx = xl; bx <= xh; bx++) - { - for (by = yl; by <= yh; by++) - { - P_BlockThingsIterator(bx, by, K_FindEggboxes); - } - } - - return (randomitems * eggboxes); -} - -static inline boolean K_FindBlockingWalls(line_t *line) -{ - // Condensed version of PIT_CheckLine - const fixed_t maxstepmove = FixedMul(MAXSTEPMOVE, mapobjectscale); - fixed_t maxstep = maxstepmove; - fixed_t linedist = INT32_MAX; - INT32 lineside = 0; - - if (!botmo || P_MobjWasRemoved(botmo) || !botmo->player) - { - return false; - } - - if (line->polyobj && !(line->polyobj->flags & POF_SOLID)) - { - return true; - } - - if (tmbbox[BOXRIGHT] <= line->bbox[BOXLEFT] || tmbbox[BOXLEFT] >= line->bbox[BOXRIGHT] - || tmbbox[BOXTOP] <= line->bbox[BOXBOTTOM] || tmbbox[BOXBOTTOM] >= line->bbox[BOXTOP]) - { - return true; - } - - if (P_BoxOnLineSide(tmbbox, line) != -1) - { - return true; - } - - lineside = P_PointOnLineSide(botmo->x, botmo->y, line); - - // one sided line - if (!line->backsector) - { - if (lineside) - { - // don't hit the back side - return true; - } - - goto blocked; - } - - if ((line->flags & ML_IMPASSABLE) || (line->flags & ML_BLOCKPLAYERS)) - { - goto blocked; - } - - // set openrange, opentop, openbottom - P_LineOpening(line, botmo); - - if (botmo->player->kartstuff[k_waterskip]) - maxstep += maxstepmove; - - if (P_MobjTouchingSectorSpecial(botmo, 1, 13, false)) - maxstep <<= 1; - else if (P_MobjTouchingSectorSpecial(botmo, 1, 12, false)) - maxstep = 0; - - if ((openrange < botmo->height) // doesn't fit - || (opentop - botmo->z < botmo->height) // mobj is too high - || (openbottom - botmo->z > maxstep)) // too big a step up - { - goto blocked; - } - - if (!K_BotHatesThisSector(botmo->player, botmo->subsector->sector)) - { - // Treat damage sectors like walls - - if (lineside) - { - if (K_BotHatesThisSector(botmo->player, line->frontsector)) - goto blocked; - } - else - { - if (K_BotHatesThisSector(botmo->player, line->backsector)) - goto blocked; - } - } - - // We weren't blocked! - return true; - -blocked: - linedist = K_DistanceOfLineFromPoint(line->v1->x, line->v1->y, line->v2->x, line->v2->y, botmo->x, botmo->y); - linedist -= (botmo->radius * 8); // Maintain a reasonable distance away from it - - if (linedist > distancetocheck) - { - return true; - } - - if (linedist <= 0) - { - closestlinedist = 0; - return false; - } - - if (linedist < closestlinedist) - { - closestlinedist = linedist; - } - - return true; -} - -static fixed_t K_BotReducePrediction(player_t *player) -{ - INT32 xl, xh, yl, yh, bx, by; - - botmo = player->mo; - distancetocheck = (player->mo->radius * 8) + (player->speed * 4); - closestlinedist = INT32_MAX; - - tmx = player->mo->x; - tmy = player->mo->y; - - xl = (unsigned)(tmx - distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; - xh = (unsigned)(tmx + distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; - yl = (unsigned)(tmy - distancetocheck - bmaporgy)>>MAPBLOCKSHIFT; - yh = (unsigned)(tmy + distancetocheck - bmaporgy)>>MAPBLOCKSHIFT; - - BMBOUNDFIX(xl, xh, yl, yh); - - tmbbox[BOXTOP] = tmy + distancetocheck; - tmbbox[BOXBOTTOM] = tmy - distancetocheck; - tmbbox[BOXRIGHT] = tmx + distancetocheck; - tmbbox[BOXLEFT] = tmx - distancetocheck; - - // Check for lines that the bot might collide with - for (bx = xl; bx <= xh; bx++) - { - for (by = yl; by <= yh; by++) - { - P_BlockLinesIterator(bx, by, K_FindBlockingWalls); - } - } - - if (closestlinedist == INT32_MAX) - { - return FRACUNIT; - } - - return FixedDiv(closestlinedist, distancetocheck); -} + Input Arguments:- + player - Player to compare. + Return:- + Bot prediction struct. +--------------------------------------------------*/ static botprediction_t *K_CreateBotPrediction(player_t *player) { const INT16 handling = K_GetKartTurnValue(player, KART_FULLTURN); // Reduce prediction based on how fast you can turn @@ -766,581 +531,11 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) return predict; } -static void K_SteerFromObject(mobj_t *bot, mobj_t *thing, fixed_t fulldist, fixed_t xdist, boolean towards, INT16 amount) -{ - angle_t destangle = R_PointToAngle2(bot->x, bot->y, thing->x, thing->y); - angle_t angle; - SINT8 flip = 1; - - amount = (amount * FixedDiv(distancetocheck - fulldist, distancetocheck)) / FRACUNIT; - - if (amount == 0) - { - // Shouldn't happen - return; - } - - if (towards) - { - if (xdist < FixedHypot(bot->radius, thing->radius)) - { - // Don't need to turn any harder! - - if (abs(badsteerglobal) <= amount) - { - badsteerglobal = 0; - } - else - { - if (badsteerglobal > 0) - { - badsteerglobal -= amount; - } - else if (badsteerglobal < 0) - { - badsteerglobal += amount; - } - } - - return; - } - - // Still turning towards it, flip. - flip = -flip; - } - - angle = (bot->angle - destangle); - if (angle < ANGLE_180) - { - flip = -flip; - } - - // If going in the opposite direction of where you wanted to turn, - // then reduce the amount that you can turn in that direction. - if ((flip == 1 && curturn < 0) - || (flip == -1 && curturn > 0)) - { - amount /= 4; - } - - badsteerglobal += amount * flip; -} - -static boolean K_BotSteerObjects(mobj_t *thing) -{ - INT16 anglediff; - fixed_t xdist, ydist, fulldist; - angle_t destangle, angle; - INT16 attack = ((9 - botmo->player->kartspeed) * KART_FULLTURN) / 8; // Acceleration chars are more aggressive - INT16 dodge = ((9 - botmo->player->kartweight) * KART_FULLTURN) / 8; // Handling chars dodge better - - if (!botmo || P_MobjWasRemoved(botmo) || !botmo->player) - { - return false; - } - - if (!thing->health) - { - return true; - } - - if (botmo == thing) - { - return true; - } - - xdist = K_DistanceOfLineFromPoint( - botmo->x, botmo->y, - botmo->x + FINECOSINE(botmo->angle >> ANGLETOFINESHIFT), botmo->y + FINESINE(botmo->angle >> ANGLETOFINESHIFT), - thing->x, thing->y - ) / 2; // weight x dist more heavily than y dist - - ydist = K_DistanceOfLineFromPoint( - botmo->x, botmo->y, - botmo->x + FINECOSINE((botmo->angle + ANGLE_90) >> ANGLETOFINESHIFT), botmo->y + FINESINE((botmo->angle + ANGLE_90) >> ANGLETOFINESHIFT), - thing->x, thing->y - ); - - fulldist = FixedHypot(xdist, ydist); - - if (fulldist > distancetocheck) - { - return true; - } - - if (!P_CheckSight(botmo, thing)) - { - return true; - } - - destangle = R_PointToAngle2(botmo->x, botmo->y, thing->x, thing->y); - angle = (botmo->angle - destangle); - - if (angle < ANGLE_180) - { - anglediff = AngleFixed(angle)>>FRACBITS; - } - else - { - anglediff = 360-(AngleFixed(angle)>>FRACBITS); - } - - anglediff = abs(anglediff); - -#define PlayerAttackSteer(botcond, thingcond) \ - if ((botcond) && !(thingcond)) \ - { \ - K_SteerFromObject(botmo, thing, fulldist, xdist, true, 2 * (KART_FULLTURN + attack)); \ - } \ - else if ((thingcond) && !(botcond)) \ - { \ - K_SteerFromObject(botmo, thing, fulldist, xdist, false, 2 * (KART_FULLTURN + dodge)); \ - } - - switch (thing->type) - { - case MT_BANANA: - case MT_BANANA_SHIELD: - case MT_EGGMANITEM_SHIELD: - case MT_ORBINAUT: - case MT_ORBINAUT_SHIELD: - case MT_JAWZ: - case MT_JAWZ_DUD: - case MT_JAWZ_SHIELD: - case MT_SSMINE: - case MT_SSMINE_SHIELD: - case MT_BALLHOG: - case MT_SPB: - case MT_BUBBLESHIELDTRAP: - K_SteerFromObject(botmo, thing, fulldist, xdist, false, 2 * (KART_FULLTURN + dodge)); - break; - case MT_RANDOMITEM: - if (anglediff >= 60) - { - break; - } - - if (P_CanPickupItem(botmo->player, 1)) - { - K_SteerFromObject(botmo, thing, fulldist, xdist, true, KART_FULLTURN + attack); - } - break; - case MT_EGGMANITEM: - if (anglediff >= 60) - { - break; - } - - if (P_CanPickupItem(botmo->player, 1)) // Can pick up an actual item - { - const UINT8 stealth = K_EggboxStealth(thing->x, thing->y); - const UINT8 requiredstealth = (botmo->player->botvars.difficulty * botmo->player->botvars.difficulty); - - if (stealth >= requiredstealth) - { - K_SteerFromObject(botmo, thing, fulldist, xdist, true, 2 * (KART_FULLTURN + attack)); - } - else - { - K_SteerFromObject(botmo, thing, fulldist, xdist, false, 2 * (KART_FULLTURN + dodge)); - } - } - break; - case MT_FLOATINGITEM: - if (anglediff >= 60) - { - break; - } - - if (P_CanPickupItem(botmo->player, 3)) - { - K_SteerFromObject(botmo, thing, fulldist, xdist, true, KART_FULLTURN + attack); - } - break; - case MT_RING: - case MT_FLINGRING: - if (anglediff >= 60) - { - break; - } - - if ((RINGTOTAL(botmo->player) < 20 && !botmo->player->kartstuff[k_ringlock] - && P_CanPickupItem(botmo->player, 0)) - && !thing->extravalue1 - && (botmo->player->kartstuff[k_itemtype] != KITEM_THUNDERSHIELD)) - { - K_SteerFromObject(botmo, thing, fulldist, xdist, true, - (RINGTOTAL(botmo->player) < 3 - ? (4 * (KART_FULLTURN + attack)) - : (KART_FULLTURN + attack)) - ); - } - break; - case MT_PLAYER: - if (thing->player - && !thing->player->kartstuff[k_hyudorotimer] - && !botmo->player->kartstuff[k_hyudorotimer]) - { - // There REALLY ought to be a better way to handle this logic, right?! - // Squishing - PlayerAttackSteer( - botmo->scale > thing->scale + (mapobjectscale/8), - thing->scale > botmo->scale + (mapobjectscale/8) - ) - // Invincibility - else PlayerAttackSteer( - botmo->player->kartstuff[k_invincibilitytimer], - thing->player->kartstuff[k_invincibilitytimer] - ) - // Thunder Shield - else PlayerAttackSteer( - botmo->player->kartstuff[k_itemtype] == KITEM_THUNDERSHIELD, - thing->player->kartstuff[k_itemtype] == KITEM_THUNDERSHIELD - ) - // Bubble Shield - else PlayerAttackSteer( - botmo->player->kartstuff[k_itemtype] == KITEM_BUBBLESHIELD, - thing->player->kartstuff[k_itemtype] == KITEM_BUBBLESHIELD - ) - // Flame Shield - else PlayerAttackSteer( - botmo->player->kartstuff[k_itemtype] == KITEM_FLAMESHIELD, - thing->player->kartstuff[k_itemtype] == KITEM_FLAMESHIELD - ) - // Has held item shield - else PlayerAttackSteer( - (botmo->player->kartstuff[k_itemheld] || botmo->player->kartstuff[k_eggmanheld]), - (thing->player->kartstuff[k_itemheld] || thing->player->kartstuff[k_eggmanheld]) - ) - // Ring Sting - else PlayerAttackSteer( - thing->player->kartstuff[k_rings] <= 0, - botmo->player->kartstuff[k_rings] <= 0 - ) - else - { - // After ALL of that, we can do standard bumping - fixed_t ourweight = K_GetMobjWeight(botmo, thing); - fixed_t theirweight = K_GetMobjWeight(thing, botmo); - fixed_t weightdiff = 0; - - if (anglediff >= 90) - { - weightdiff = theirweight - ourweight; - } - else - { - weightdiff = ourweight - theirweight; - } - - if (weightdiff > mapobjectscale) - { - K_SteerFromObject(botmo, thing, fulldist, xdist, true, KART_FULLTURN + attack); - } - else - { - K_SteerFromObject(botmo, thing, fulldist, xdist, false, KART_FULLTURN + dodge); - } - } - } - break; - case MT_BOTHINT: - if (anglediff >= 60) - { - break; - } - - if (thing->extravalue1 == 0) - { - K_SteerFromObject(botmo, thing, fulldist, xdist, false, thing->extravalue2 * (KART_FULLTURN + dodge)); - } - { - K_SteerFromObject(botmo, thing, fulldist, xdist, true, thing->extravalue2 * (KART_FULLTURN + attack)); - } - break; - default: - if (thing->flags & (MF_SOLID|MF_ENEMY|MF_BOSS|MF_PAIN|MF_MISSILE|MF_FIRE)) - { - K_SteerFromObject(botmo, thing, fulldist, xdist, false, 2 * (KART_FULLTURN + dodge)); - } - break; - } - - return true; -} - -static INT16 K_BotFindObjects(player_t *player, INT16 turn) -{ - INT32 xl, xh, yl, yh, bx, by; - - badsteerglobal = 0; - - botmo = player->mo; - curturn = turn; - distancetocheck = 2048*mapobjectscale; - - xl = (unsigned)(botmo->x - distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; - xh = (unsigned)(botmo->x + distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; - yl = (unsigned)(botmo->y - distancetocheck - bmaporgy)>>MAPBLOCKSHIFT; - yh = (unsigned)(botmo->y + distancetocheck - bmaporgy)>>MAPBLOCKSHIFT; - - BMBOUNDFIX(xl, xh, yl, yh); - - for (bx = xl; bx <= xh; bx++) - { - for (by = yl; by <= yh; by++) - { - P_BlockThingsIterator(bx, by, K_BotSteerObjects); - } - } - - return badsteerglobal; -} - -static boolean K_BotUseItemNearPlayer(player_t *player, ticcmd_t *cmd, fixed_t radius) -{ - UINT8 i; - - if (player->pflags & PF_ATTACKDOWN) - { - 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->powers[pw_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 boolean K_PlayerNearSpot(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->powers[pw_flashing]) - { - continue; - } - - dist = P_AproxDistance( - x - target->mo->x, - y - target->mo->y - ); - - if (dist <= radius) - { - return true; - } - } - - return false; -} - -static boolean K_BotRevealsBanana(player_t *player, INT16 turnamt, boolean mine) -{ - UINT8 i; - - // Only get out bananas if you have a target - - if (abs(turnamt) >= KART_FULLTURN/2) - { - return false; - } - else - { - UINT32 airtime = FixedDiv((30 * player->mo->scale) + player->mo->momz, gravity); - fixed_t throwspeed = FixedMul(82 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); - const angle_t momangle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); - fixed_t estx = player->mo->x + P_ReturnThrustX(NULL, momangle, (throwspeed + player->speed) * airtime); - fixed_t esty = player->mo->y + P_ReturnThrustY(NULL, momangle, (throwspeed + player->speed) * airtime); - - if (K_PlayerNearSpot(player, estx, esty, player->mo->radius * 2)) - { - return true; - } - - if (mine) - { - airtime = FixedDiv((40 * player->mo->scale) + player->mo->momz, gravity); - throwspeed = FixedMul(82 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)) * 2; - estx = player->mo->x + P_ReturnThrustX(NULL, momangle, (throwspeed + player->speed) * airtime); - esty = player->mo->y + P_ReturnThrustY(NULL, momangle, (throwspeed + player->speed) * airtime); - - if (K_PlayerNearSpot(player, estx, esty, player->mo->radius * 2)) - { - return true; - } - } - } - - 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->powers[pw_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 <= (player->mo->radius * 16)) - { - angle_t a = player->mo->angle - R_PointToAngle2(player->mo->x, player->mo->y, target->mo->x, target->mo->y); - INT16 ad = 0; - const INT16 cone = 10; - - if (a < ANGLE_180) - { - ad = AngleFixed(a)>>FRACBITS; - } - else - { - ad = 360-(AngleFixed(a)>>FRACBITS); - } - - ad = abs(ad); - - if (ad >= 180-cone) - { - return true; - } - } - } - - return false; -} - -static boolean K_BotRevealsEggbox(player_t *player) -{ - const UINT32 airtime = FixedDiv((30 * player->mo->scale) + player->mo->momz, gravity); - const fixed_t throwspeed = FixedMul(82 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); - const angle_t momangle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); - const fixed_t estx = player->mo->x + P_ReturnThrustX(NULL, momangle, (throwspeed + player->speed) * airtime); - const fixed_t esty = player->mo->y + P_ReturnThrustY(NULL, momangle, (throwspeed + player->speed) * airtime); - const UINT8 stealth = K_EggboxStealth(player->mo->x, player->mo->y); - UINT8 i; - - if (stealth > 1) - { - return true; - } - - if (K_PlayerNearSpot(player, estx, esty, player->mo->radius * 2)) - { - return true; - } - - 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->powers[pw_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 <= (player->mo->radius * 16)) - { - angle_t a = player->mo->angle - R_PointToAngle2(player->mo->x, player->mo->y, target->mo->x, target->mo->y); - INT16 ad = 0; - const INT16 cone = 10; - - if (a < ANGLE_180) - { - ad = AngleFixed(a)>>FRACBITS; - } - else - { - ad = 360-(AngleFixed(a)>>FRACBITS); - } - - ad = abs(ad); - - if (ad >= 180-cone) - { - return true; - } - } - } - - return false; -} +/*-------------------------------------------------- + void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) + See header file for description. +--------------------------------------------------*/ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) { botprediction_t *predict = NULL; @@ -1377,11 +572,19 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) // Start boost handler if (leveltime <= starttime) { - if (leveltime >= starttime-35) + tic_t boosthold = starttime - TICRATE; + + boosthold -= (MAXBOTDIFFICULTY - player->botvars.difficulty); + + if (leveltime >= boosthold) + { cmd->buttons |= BT_ACCELERATE; + } + return; } + // Handle steering towards waypoints! if (player->nextwaypoint != NULL && player->nextwaypoint->mobj != NULL && !P_MobjWasRemoved(player->nextwaypoint->mobj)) { SINT8 turnsign = 0; @@ -1495,723 +698,8 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } } - if (player->kartstuff[k_userings] == 1) - { - if (!player->exiting) - { - INT32 saferingsval = 16 - K_GetKartRingPower(player); - - if (player->speed < K_GetKartSpeed(player, false)/2 // Being slowed down too much - || player->kartstuff[k_speedboost] > (FRACUNIT/5)) // Have another type of boost (tethering) - { - saferingsval -= 5; - } - - if (player->kartstuff[k_rings] > saferingsval) - { - cmd->buttons |= BT_ATTACK; - } - } - } - else if (player->kartstuff[k_itemroulette] && !(player->pflags & PF_ATTACKDOWN)) - { - // Mashing behaviors - - if (player->kartstuff[k_rings] < 0 && cv_superring.value) - { - // Uh oh, we need a loan! - // It'll be better in the long run for bots to lose an item set for 10 free rings. - cmd->buttons |= BT_ATTACK; - } - } - else - { - if (player->botvars.itemdelay) - { - player->botvars.itemdelay--; - player->botvars.itemconfirm = 0; - } - else if (player->kartstuff[k_eggmanexplode]) - { - if (player->kartstuff[k_position] == 1) - { - cmd->forwardmove /= 2; - cmd->buttons |= BT_BRAKE; - } - - K_BotUseItemNearPlayer(player, cmd, 128*player->mo->scale); - } - else if (player->kartstuff[k_eggmanheld]) - { - const UINT32 airtime = FixedDiv((30 * player->mo->scale) + player->mo->momz, gravity); - const fixed_t throwspeed = FixedMul(82 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); - const angle_t momangle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); - const fixed_t estx = player->mo->x + P_ReturnThrustX(NULL, momangle, (throwspeed + player->speed) * airtime); - const fixed_t esty = player->mo->y + P_ReturnThrustY(NULL, momangle, (throwspeed + player->speed) * airtime); - const UINT8 stealth = K_EggboxStealth(player->mo->x, player->mo->y); - SINT8 throwdir = -1; - UINT8 i; - - player->botvars.itemconfirm++; - - if (K_PlayerNearSpot(player, estx, esty, player->mo->radius * 2)) - { - player->botvars.itemconfirm += player->botvars.difficulty / 2; - throwdir = 1; - } - - 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->powers[pw_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 <= (player->mo->radius * 16)) - { - angle_t a = player->mo->angle - R_PointToAngle2(player->mo->x, player->mo->y, target->mo->x, target->mo->y); - INT16 ad = 0; - const INT16 cone = 10; - - if (a < ANGLE_180) - { - ad = AngleFixed(a)>>FRACBITS; - } - else - { - ad = 360-(AngleFixed(a)>>FRACBITS); - } - - ad = abs(ad); - - if (ad >= 180-cone) - { - player->botvars.itemconfirm += player->botvars.difficulty; - throwdir = -1; - } - } - } - - if (stealth > 1) - { - player->botvars.itemconfirm += player->botvars.difficulty * 4; - throwdir = -1; - } - - if (player->kartstuff[k_itemroulette] > 0) // Just grabbed an item - { - player->botvars.itemconfirm += player->botvars.difficulty * 4; - throwdir = -1; - } - - if ((player->botvars.itemconfirm > 2*TICRATE || player->kartstuff[k_bananadrag] >= TICRATE) - && !(player->pflags & PF_ATTACKDOWN)) - { - if (throwdir == 1) - { - cmd->buttons |= BT_FORWARD; - } - else if (throwdir == -1) - { - cmd->buttons |= BT_BACKWARD; - } - - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; - } - } - else if (player->kartstuff[k_rocketsneakertimer] > 0) - { - if (player->botvars.itemconfirm > TICRATE) - { - if (!player->kartstuff[k_sneakertimer] && !(player->pflags & PF_ATTACKDOWN)) - { - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; - } - } - else - { - player->botvars.itemconfirm++; - } - } - else if (player->kartstuff[k_stealingtimer] == 0 && player->kartstuff[k_stolentimer] == 0) - { - switch (player->kartstuff[k_itemtype]) - { - case KITEM_INVINCIBILITY: - case KITEM_SPB: - case KITEM_GROW: - case KITEM_SHRINK: - case KITEM_HYUDORO: - case KITEM_SUPERRING: - if (!(player->pflags & PF_ATTACKDOWN)) - { - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; - } - break; - case KITEM_SNEAKER: - if ((player->kartstuff[k_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)/2 // Being slowed down too much - || player->kartstuff[k_speedboost] > (FRACUNIT/8) // Have another type of boost (tethering) - || player->botvars.itemconfirm > 4*TICRATE) // Held onto it for too long - { - if (!player->kartstuff[k_sneakertimer] && !(player->pflags & PF_ATTACKDOWN)) - { - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 2*TICRATE; - } - } - else - { - player->botvars.itemconfirm++; - } - break; - case KITEM_ROCKETSNEAKER: - if (player->kartstuff[k_rocketsneakertimer] <= 0 && !(player->pflags & PF_ATTACKDOWN)) - { - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; - } - break; - case KITEM_BANANA: - if (!player->kartstuff[k_itemheld]) - { - if ((K_BotRevealsBanana(player, turnamt, false) || (player->botvars.itemconfirm++ > 5*TICRATE)) - && !(player->pflags & PF_ATTACKDOWN)) - { - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; - } - } - else - { - SINT8 throwdir = -1; - UINT8 i; - - player->botvars.itemconfirm++; - - if (abs(turnamt) >= KART_FULLTURN/2) - { - player->botvars.itemconfirm += player->botvars.difficulty / 2; - } - else - { - const UINT32 airtime = FixedDiv((30 * player->mo->scale) + player->mo->momz, gravity); - const fixed_t throwspeed = FixedMul(82 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); - const angle_t momangle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); - const fixed_t estx = player->mo->x + P_ReturnThrustX(NULL, momangle, (throwspeed + player->speed) * airtime); - const fixed_t esty = player->mo->y + P_ReturnThrustY(NULL, momangle, (throwspeed + player->speed) * airtime); - - if (K_PlayerNearSpot(player, estx, esty, player->mo->radius * 2)) - { - player->botvars.itemconfirm += player->botvars.difficulty * 2; - throwdir = 1; - } - } - - 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->powers[pw_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 <= (player->mo->radius * 16)) - { - angle_t a = player->mo->angle - R_PointToAngle2(player->mo->x, player->mo->y, target->mo->x, target->mo->y); - INT16 ad = 0; - const INT16 cone = 10; - - if (a < ANGLE_180) - { - ad = AngleFixed(a)>>FRACBITS; - } - else - { - ad = 360-(AngleFixed(a)>>FRACBITS); - } - - ad = abs(ad); - - if (ad >= 180-cone) - { - player->botvars.itemconfirm += player->botvars.difficulty; - throwdir = -1; - } - } - } - - if ((player->botvars.itemconfirm > 2*TICRATE || player->kartstuff[k_bananadrag] >= TICRATE) - && !(player->pflags & PF_ATTACKDOWN)) - { - if (throwdir == 1) - { - cmd->buttons |= BT_FORWARD; - } - else if (throwdir == -1) - { - cmd->buttons |= BT_BACKWARD; - } - - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; - } - } - break; - case KITEM_EGGMAN: - if (!player->kartstuff[k_eggmanheld]) - { - if ((K_BotRevealsEggbox(player) || (player->botvars.itemconfirm++ > 20*TICRATE)) - && !(player->pflags & PF_ATTACKDOWN)) - { - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; - } - } - break; - case KITEM_ORBINAUT: - if (!player->kartstuff[k_itemheld] && !(player->pflags & PF_ATTACKDOWN)) - { - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; - } - else if (player->kartstuff[k_position] != 1) // Hold onto orbiting items when in 1st :) - /* FALL-THRU */ - case KITEM_BALLHOG: - { - const fixed_t topspeed = K_GetKartSpeed(player, false); - fixed_t radius = (player->mo->radius * 32); - SINT8 throwdir = -1; - UINT8 i; - - if (player->speed > topspeed) - { - radius = FixedMul(radius, FixedDiv(player->speed, topspeed)); - } - - 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->powers[pw_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; - const INT16 cone = 10; - - if (a < ANGLE_180) - { - ad = AngleFixed(a)>>FRACBITS; - } - else - { - ad = 360-(AngleFixed(a)>>FRACBITS); - } - - ad = abs(ad); - - if (ad <= cone) - { - player->botvars.itemconfirm += player->botvars.difficulty * 2; - throwdir = 1; - } - else if (ad >= 180-cone) - { - player->botvars.itemconfirm += player->botvars.difficulty; - } - } - } - - if ((player->botvars.itemconfirm > 5*TICRATE) - && !(player->pflags & PF_ATTACKDOWN)) - { - if (throwdir == 1) - { - cmd->buttons |= BT_FORWARD; - } - else if (throwdir == -1) - { - cmd->buttons |= BT_BACKWARD; - } - - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; - } - } - break; - case KITEM_JAWZ: - if (!player->kartstuff[k_itemheld] && !(player->pflags & PF_ATTACKDOWN)) - { - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; - } - else if (player->kartstuff[k_position] != 1) // Hold onto orbiting items when in 1st :) - { - SINT8 throwdir = 1; - UINT8 i; - - player->botvars.itemconfirm++; - - 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->powers[pw_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 <= (player->mo->radius * 32)) - { - angle_t a = player->mo->angle - R_PointToAngle2(player->mo->x, player->mo->y, target->mo->x, target->mo->y); - INT16 ad = 0; - const INT16 cone = 10; - - if (a < ANGLE_180) - { - ad = AngleFixed(a)>>FRACBITS; - } - else - { - ad = 360-(AngleFixed(a)>>FRACBITS); - } - - ad = abs(ad); - - if (ad >= 180-cone) - { - player->botvars.itemconfirm += player->botvars.difficulty; - throwdir = -1; - } - } - } - - if (player->kartstuff[k_lastjawztarget] != -1) - { - player->botvars.itemconfirm += player->botvars.difficulty * 2; - throwdir = 1; - } - - if ((player->botvars.itemconfirm > 5*TICRATE) - && !(player->pflags & PF_ATTACKDOWN)) - { - if (throwdir == 1) - { - cmd->buttons |= BT_FORWARD; - } - else if (throwdir == -1) - { - cmd->buttons |= BT_BACKWARD; - } - - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; - } - } - break; - case KITEM_MINE: - if (!player->kartstuff[k_itemheld]) - { - if ((K_BotRevealsBanana(player, turnamt, true) || (player->botvars.itemconfirm++ > 5*TICRATE)) - && !(player->pflags & PF_ATTACKDOWN)) - { - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; - } - } - else - { - SINT8 throwdir = 0; - UINT8 i; - - player->botvars.itemconfirm++; - - 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->powers[pw_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 <= (player->mo->radius * 16)) - { - angle_t a = player->mo->angle - R_PointToAngle2(player->mo->x, player->mo->y, target->mo->x, target->mo->y); - INT16 ad = 0; - const INT16 cone = 10; - - if (a < ANGLE_180) - { - ad = AngleFixed(a)>>FRACBITS; - } - else - { - ad = 360-(AngleFixed(a)>>FRACBITS); - } - - ad = abs(ad); - - if (ad >= 180-cone) - { - player->botvars.itemconfirm += player->botvars.difficulty; - throwdir = -1; - } - } - } - - if (abs(turnamt) >= KART_FULLTURN/2) - { - player->botvars.itemconfirm += player->botvars.difficulty / 2; - throwdir = -1; - } - else - { - UINT32 airtime = FixedDiv((30 * player->mo->scale) + player->mo->momz, gravity); - fixed_t throwspeed = FixedMul(82 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); - angle_t momangle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); - fixed_t estx = player->mo->x + P_ReturnThrustX(NULL, momangle, (throwspeed + player->speed) * airtime); - fixed_t esty = player->mo->y + P_ReturnThrustY(NULL, momangle, (throwspeed + player->speed) * airtime); - - if (K_PlayerNearSpot(player, estx, esty, player->mo->radius * 2)) - { - player->botvars.itemconfirm += player->botvars.difficulty * 2; - throwdir = 0; - } - - airtime = FixedDiv((40 * player->mo->scale) + player->mo->momz, gravity); - throwspeed = FixedMul(82 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)) * 2; - estx = player->mo->x + P_ReturnThrustX(NULL, player->mo->angle, (throwspeed + player->speed) * airtime); - esty = player->mo->y + P_ReturnThrustY(NULL, player->mo->angle, (throwspeed + player->speed) * airtime); - - if (K_PlayerNearSpot(player, estx, esty, player->mo->radius * 2)) - { - player->botvars.itemconfirm += player->botvars.difficulty / 2; - throwdir = 1; - } - } - - if (((player->botvars.itemconfirm > 2*TICRATE) - || (player->kartstuff[k_bananadrag] >= TICRATE)) - && !(player->pflags & PF_ATTACKDOWN)) - { - if (throwdir == 1) - { - cmd->buttons |= BT_FORWARD; - } - else if (throwdir == -1) - { - cmd->buttons |= BT_BACKWARD; - } - - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; - } - } - break; - case KITEM_THUNDERSHIELD: - if (!K_BotUseItemNearPlayer(player, cmd, 192*player->mo->scale)) - { - if (player->botvars.itemconfirm > 10*TICRATE && !(player->pflags & PF_ATTACKDOWN)) - { - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; - } - else - { - player->botvars.itemconfirm++; - } - } - break; - case KITEM_BUBBLESHIELD: - { - boolean hold = false; - - if (player->kartstuff[k_bubbleblowup] <= 0) - { - UINT8 i; - - player->botvars.itemconfirm++; - - if (player->kartstuff[k_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->powers[pw_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->kartstuff[k_bubbleblowup] >= bubbletime) - { - if (player->botvars.itemconfirm >= 10*TICRATE) - { - hold = true; - } - } - else if (player->kartstuff[k_bubbleblowup] < bubbletime) - { - hold = true; - } - - if (hold && player->kartstuff[k_holdready]) - { - cmd->buttons |= BT_ATTACK; - } - } - break; - case KITEM_FLAMESHIELD: - if (player->botvars.itemconfirm > 0) - { - player->botvars.itemconfirm--; - } - else if (player->kartstuff[k_holdready]) - { - INT32 flamemax = player->kartstuff[k_flamelength] * flameseg; - - if (player->kartstuff[k_flamemeter] < flamemax || flamemax == 0) - { - cmd->buttons |= BT_ATTACK; - } - else - { - player->botvars.itemconfirm = 3*flamemax/4; - } - } - break; - default: - if (player->kartstuff[k_itemtype] != KITEM_NONE && !(player->pflags & PF_ATTACKDOWN)) - cmd->buttons |= BT_ATTACK; - player->botvars.itemconfirm = 0; - break; - } - } - } + // Handle item usage + K_BotItemUsage(player, cmd, turnamt); if (turnamt != 0) { @@ -2253,12 +741,13 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if (abs(player->botvars.turnconfirm) >= BOTTURNCONFIRM) { - // You're probably commiting to your turn, here you go. + // You're commiting to your turn, you're allowed! cmd->driftturn = turnamt; cmd->angleturn += K_GetKartTurnValue(player, turnamt); } } + // Free the prediction we made earlier if (predict != NULL) { Z_Free(predict); diff --git a/src/k_bot.h b/src/k_bot.h index 0525480b4..81257ecbf 100644 --- a/src/k_bot.h +++ b/src/k_bot.h @@ -1,14 +1,16 @@ -// SONIC ROBO BLAST 2 +// SONIC ROBO BLAST 2 KART //----------------------------------------------------------------------------- -// Copyright (C) 2007-2016 by John "JTE" Muniz. -// Copyright (C) 2012-2018 by Sonic Team Junior. +// 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_bot.h -/// \brief Basic bot handling +/// \brief Bot logic & ticcmd generation code + +#ifndef __K_BOT__ +#define __K_BOT__ #include "k_waypoint.h" #include "d_player.h" @@ -20,16 +22,196 @@ // Made it as small as possible without making it look like the bots are twitching constantly. #define BOTTURNCONFIRM 7 -// Path that bot will attempt to take +// Point for bots to aim for typedef struct botprediction_s { fixed_t x, y; fixed_t radius; angle_t dir; } botprediction_t; -boolean K_AddBot(UINT8 skin, UINT8 difficulty, UINT8 *newplayernum); -void K_UpdateMatchRaceBots(void); + +// AVAILABLE FOR LUA + + +/*-------------------------------------------------- + boolean K_PlayerUsesBotMovement(player_t *player); + + Tells if this player is being controlled via bot movement code (is a bot, or is exiting). + + Input Arguments:- + player - Player to check. + + Return:- + true if using bot movement code, otherwise false. +--------------------------------------------------*/ + boolean K_PlayerUsesBotMovement(player_t *player); + + +/*-------------------------------------------------- + boolean K_BotCanTakeCut(player_t *player); + + Tells if this bot is able to take shortcuts (currently unaffected by offroad, + or has certain items ready). + + Input Arguments:- + player - Player to check. + + Return:- + true if able to take shortcuts, otherwise false. +--------------------------------------------------*/ + boolean K_BotCanTakeCut(player_t *player); + + +/*-------------------------------------------------- + fixed_t K_BotRubberband(player_t *player); + + Gives a multiplier for a bot's rubberbanding. Meant to be used for top speed, + acceleration, and handling. + + Input Arguments:- + player - Player to check. + + Return:- + A multiplier in fixed point scale, between 0.875 and 2.0. +--------------------------------------------------*/ + fixed_t K_BotRubberband(player_t *player); + + +/*-------------------------------------------------- + fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, fixed_t v2y, fixed_t cx, fixed_t cy); + + Gets the distance of a point away from a line. + TODO: Could go in another file? + + Input Arguments:- + v1x - Line's first vertex x position. + v1y - Line's first vertex y position. + v2x - Line's second vertex x position. + v2y - Line's second vertex y position. + cx - Point's x position. + cy - Point's y position. + + Return:- + The distance between the point and the line. +--------------------------------------------------*/ + +fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, fixed_t v2y, fixed_t cx, fixed_t cy); + + +// NOT AVAILABLE FOR LUA + + +/*-------------------------------------------------- + boolean K_AddBot(UINT8 skin, UINT8 difficulty, UINT8 *newplayernum); + + Returns the waypoint actually being used as the finish line. + + Input Arguments:- + skin - Skin number that the bot will use. + difficulty - Difficulty level this bot will use. + newplayernum - Pointer to the last valid player slot number. + Is a pointer so that this function can be called multiple times to add more than one bot. + + Return:- + true if a bot packet can be sent, otherwise false. +--------------------------------------------------*/ + +boolean K_AddBot(UINT8 skin, UINT8 difficulty, UINT8 *newplayernum); + + +/*-------------------------------------------------- + void K_UpdateMatchRaceBots(void); + + Updates the number of bots in the server and their difficulties for Match Race. +--------------------------------------------------*/ + +void K_UpdateMatchRaceBots(void); + + +/*-------------------------------------------------- + UINT8 K_EggboxStealth(fixed_t x, fixed_t y); + + Gets a "stealth" value for a position, to figure out how + well Eggman boxes blend into random items. + + Input Arguments:- + x - X coordinate to check. + y - Y coordinate to check. + + Return:- + Stealth value for the position. +--------------------------------------------------*/ + +UINT8 K_EggboxStealth(fixed_t x, fixed_t y); + + +/*-------------------------------------------------- + fixed_t K_BotReducePrediction(player_t *player); + + Finds walls nearby the specified player, to create a multiplier + to pull bot predictions back by. + + Input Arguments:- + player - Player to compare. + + Return:- + Multiplier in fixed point scale. +--------------------------------------------------*/ + +fixed_t K_BotReducePrediction(player_t *player); + + +/*-------------------------------------------------- + INT16 K_BotFindObjects(player_t *player, INT16 turn); + + Generates a sum for objects to steer towards/away from. + + Input Arguments:- + player - Player to compare. + turn - Turn value before object steering. + + Return:- + Turn amount sum to add to final product. +--------------------------------------------------*/ + +INT16 K_BotFindObjects(player_t *player, INT16 turn); + + +/*-------------------------------------------------- + void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd); + + Gives a multiplier for a bot's rubberbanding. Meant to be used for top speed, + acceleration, and handling. + + Input Arguments:- + player - Player to generate the ticcmd for. + cmd - The player's ticcmd to modify. + + Return:- + None +--------------------------------------------------*/ + void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd); + + +/*-------------------------------------------------- + void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt); + + Item usage part of ticcmd generation. + + Input Arguments:- + player - Player to generate the ticcmd for. + cmd - The player's ticcmd to modify. + turnamt - How hard the bot is turning. + + Return:- + None +--------------------------------------------------*/ + +void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt); + + +#endif diff --git a/src/k_botitem.c b/src/k_botitem.c new file mode 100644 index 000000000..55f8e15e4 --- /dev/null +++ b/src/k_botitem.c @@ -0,0 +1,1095 @@ +// SONIC ROBO BLAST 2 KART +//----------------------------------------------------------------------------- +// 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 + +/*-------------------------------------------------- + static boolean K_BotUseItemNearPlayer(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(player_t *player, ticcmd_t *cmd, fixed_t radius) +{ + UINT8 i; + + if (player->pflags & PF_ATTACKDOWN) + { + 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->powers[pw_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 boolean K_PlayerNearSpot(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:- + true if a player was found around the coordinate, otherwise false. +--------------------------------------------------*/ +static boolean K_PlayerNearSpot(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->powers[pw_flashing]) + { + continue; + } + + dist = P_AproxDistance( + x - target->mo->x, + y - target->mo->y + ); + + if (dist <= radius) + { + return true; + } + } + + return false; +} + +/*-------------------------------------------------- + static boolean K_PlayerPredictThrow(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:- + true if a player was found around the coordinate, otherwise false. +--------------------------------------------------*/ +static boolean K_PlayerPredictThrow(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 boolean K_PlayerInCone(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 boolean K_PlayerInCone(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->powers[pw_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 true; + } + } + else + { + if (ad <= cone) + { + return true; + } + } + } + } + + return false; +} + +/*-------------------------------------------------- + static boolean K_BotGenericPressItem(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(player_t *player, ticcmd_t *cmd, SINT8 dir) +{ + if (player->pflags & PF_ATTACKDOWN) + { + return false; + } + + if (dir == 1) + { + cmd->buttons |= BT_FORWARD; + } + else if (dir == -1) + { + cmd->buttons |= BT_BACKWARD; + } + + cmd->buttons |= BT_ATTACK; + player->botvars.itemconfirm = 0; + return true; +} + +/*-------------------------------------------------- + static void K_BotItemGenericTap(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(player_t *player, ticcmd_t *cmd) +{ + if (!(player->pflags & PF_ATTACKDOWN)) + { + cmd->buttons |= BT_ATTACK; + player->botvars.itemconfirm = 0; + } +} + +/*-------------------------------------------------- + static boolean K_BotRevealsGenericTrap(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(player_t *player, INT16 turnamt, boolean mine) +{ + 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)) + { + return true; + } + + if (mine) + { + if (K_PlayerPredictThrow(player, 1)) + { + return true; + } + } + + // Check your behind. + if (K_PlayerInCone(player, player->mo->radius * 16, 10, true)) + { + return true; + } + + return false; +} + +/*-------------------------------------------------- + static void K_BotItemGenericTrapShield(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(player_t *player, ticcmd_t *cmd, INT16 turnamt, boolean mine) +{ + if (player->kartstuff[k_itemheld]) + { + return; + } + + if (K_BotRevealsGenericTrap(player, turnamt, mine) || (player->botvars.itemconfirm++ > 5*TICRATE)) + { + K_BotGenericPressItem(player, cmd, 0); + } +} + +/*-------------------------------------------------- + static void K_BotItemGenericOrbitShield(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(player_t *player, ticcmd_t *cmd) +{ + if (player->kartstuff[k_itemheld]) + { + return; + } + + K_BotGenericPressItem(player, cmd, 0); +} + +/*-------------------------------------------------- + static void K_BotItemSneaker(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(player_t *player, ticcmd_t *cmd) +{ + if ((player->kartstuff[k_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)/2 // Being slowed down too much + || player->kartstuff[k_speedboost] > (FRACUNIT/8) // Have another type of boost (tethering) + || player->botvars.itemconfirm > 4*TICRATE) // Held onto it for too long + { + if (!player->kartstuff[k_sneakertimer] && !(player->pflags & PF_ATTACKDOWN)) + { + cmd->buttons |= BT_ATTACK; + player->botvars.itemconfirm = 2*TICRATE; + } + } + else + { + player->botvars.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(player_t *player, ticcmd_t *cmd) +{ + if (player->botvars.itemconfirm > TICRATE) + { + if (!player->kartstuff[k_sneakertimer] && !(player->pflags & PF_ATTACKDOWN)) + { + cmd->buttons |= BT_ATTACK; + player->botvars.itemconfirm = 0; + } + } + else + { + player->botvars.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(player_t *player, ticcmd_t *cmd, INT16 turnamt) +{ + SINT8 throwdir = -1; + + player->botvars.itemconfirm++; + + if (abs(turnamt) >= KART_FULLTURN/2) + { + player->botvars.itemconfirm += player->botvars.difficulty / 2; + throwdir = -1; + } + else + { + if (K_PlayerPredictThrow(player, 0)) + { + player->botvars.itemconfirm += player->botvars.difficulty * 2; + throwdir = 1; + } + } + + if (K_PlayerInCone(player, player->mo->radius * 16, 10, true)) + { + player->botvars.itemconfirm += player->botvars.difficulty; + throwdir = -1; + } + + if (player->botvars.itemconfirm > 2*TICRATE || player->kartstuff[k_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(player_t *player, ticcmd_t *cmd, INT16 turnamt) +{ + SINT8 throwdir = 0; + + player->botvars.itemconfirm++; + + if (K_PlayerInCone(player, player->mo->radius * 16, 10, true)) + { + player->botvars.itemconfirm += player->botvars.difficulty; + throwdir = -1; + } + + if (abs(turnamt) >= KART_FULLTURN/2) + { + player->botvars.itemconfirm += player->botvars.difficulty / 2; + throwdir = -1; + } + else + { + if (K_PlayerPredictThrow(player, 0)) + { + player->botvars.itemconfirm += player->botvars.difficulty * 2; + throwdir = 0; + } + + if (K_PlayerPredictThrow(player, 1)) + { + player->botvars.itemconfirm += player->botvars.difficulty * 2; + throwdir = 1; + } + } + + + + if (player->botvars.itemconfirm > 2*TICRATE || player->kartstuff[k_bananadrag] >= TICRATE) + { + K_BotGenericPressItem(player, cmd, throwdir); + } +} + +/*-------------------------------------------------- + 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(player_t *player, ticcmd_t *cmd) +{ + const UINT8 stealth = K_EggboxStealth(player->mo->x, player->mo->y); + SINT8 throwdir = -1; + + player->botvars.itemconfirm++; + + if (K_PlayerPredictThrow(player, 0)) + { + player->botvars.itemconfirm += player->botvars.difficulty / 2; + throwdir = 1; + } + + if (K_PlayerInCone(player, player->mo->radius * 16, 10, true)) + { + player->botvars.itemconfirm += player->botvars.difficulty; + throwdir = -1; + } + + if (stealth > 1 || player->kartstuff[k_itemroulette] > 0) + { + player->botvars.itemconfirm += player->botvars.difficulty * 4; + throwdir = -1; + } + + if (player->botvars.itemconfirm > 2*TICRATE || player->kartstuff[k_bananadrag] >= TICRATE) + { + K_BotGenericPressItem(player, cmd, throwdir); + } +} + +/*-------------------------------------------------- + static boolean K_BotRevealsEggbox(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(player_t *player) +{ + const UINT8 stealth = K_EggboxStealth(player->mo->x, player->mo->y); + + // This is a stealthy spot for an eggbox, lets reveal it! + if (stealth > 1) + { + return true; + } + + // Check the predicted throws. + if (K_PlayerPredictThrow(player, 0)) + { + return true; + } + + // Check your behind. + if (K_PlayerInCone(player, player->mo->radius * 16, 10, true)) + { + return true; + } + + return false; +} + +/*-------------------------------------------------- + static void K_BotItemEggmanShield(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(player_t *player, ticcmd_t *cmd) +{ + if (player->kartstuff[k_eggmanheld]) + { + return; + } + + if (K_BotRevealsEggbox(player) || (player->botvars.itemconfirm++ > 20*TICRATE)) + { + K_BotGenericPressItem(player, cmd, 0); + } +} + +/*-------------------------------------------------- + static void K_BotItemEggmanExplosion(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(player_t *player, ticcmd_t *cmd) +{ + if (player->kartstuff[k_position] == 1) + { + cmd->forwardmove /= 2; + cmd->buttons |= BT_BRAKE; + } + + K_BotUseItemNearPlayer(player, cmd, 128*player->mo->scale); +} + +/*-------------------------------------------------- + static void K_BotItemOrbinaut(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(player_t *player, ticcmd_t *cmd) +{ + const fixed_t topspeed = K_GetKartSpeed(player, false); + fixed_t radius = (player->mo->radius * 32); + SINT8 throwdir = -1; + + if (player->speed > topspeed) + { + radius = FixedMul(radius, FixedDiv(player->speed, topspeed)); + } + + player->botvars.itemconfirm++; + + if (K_PlayerInCone(player, radius, 10, false)) + { + player->botvars.itemconfirm += player->botvars.difficulty * 2; + throwdir = 1; + } + else if (K_PlayerInCone(player, radius, 10, true)) + { + player->botvars.itemconfirm += player->botvars.difficulty; + throwdir = -1; + } + + if (player->botvars.itemconfirm > 5*TICRATE) + { + K_BotGenericPressItem(player, cmd, throwdir); + } +} + +/*-------------------------------------------------- + static void K_BotItemJawz(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(player_t *player, ticcmd_t *cmd) +{ + const fixed_t topspeed = K_GetKartSpeed(player, false); + fixed_t radius = (player->mo->radius * 32); + SINT8 throwdir = 1; + + if (player->speed > topspeed) + { + radius = FixedMul(radius, FixedDiv(player->speed, topspeed)); + } + + player->botvars.itemconfirm++; + + if (K_PlayerInCone(player, radius, 10, true)) + { + player->botvars.itemconfirm += player->botvars.difficulty; + throwdir = -1; + } + + if (player->kartstuff[k_lastjawztarget] != -1) + { + player->botvars.itemconfirm += player->botvars.difficulty * 2; + throwdir = 1; + } + + if (player->botvars.itemconfirm > 5*TICRATE) + { + K_BotGenericPressItem(player, cmd, throwdir); + } +} + +/*-------------------------------------------------- + static void K_BotItemThunder(player_t *player, ticcmd_t *cmd) + + Item usage for Thunder Shield. + + Input Arguments:- + player - Bot to do this for. + cmd - Bot's ticcmd to edit. + + Return:- + None +--------------------------------------------------*/ +static void K_BotItemThunder(player_t *player, ticcmd_t *cmd) +{ + if (!K_BotUseItemNearPlayer(player, cmd, 192*player->mo->scale)) + { + if (player->botvars.itemconfirm > 10*TICRATE) + { + K_BotGenericPressItem(player, cmd, 0); + } + else + { + player->botvars.itemconfirm++; + } + } +} + +/*-------------------------------------------------- + static void K_BotItemBubble(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(player_t *player, ticcmd_t *cmd) +{ + boolean hold = false; + + if (player->kartstuff[k_bubbleblowup] <= 0) + { + UINT8 i; + + player->botvars.itemconfirm++; + + if (player->kartstuff[k_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->powers[pw_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->kartstuff[k_bubbleblowup] >= bubbletime) + { + if (player->botvars.itemconfirm >= 10*TICRATE) + { + hold = true; + } + } + else if (player->kartstuff[k_bubbleblowup] < bubbletime) + { + hold = true; + } + + if (hold && player->kartstuff[k_holdready]) + { + cmd->buttons |= BT_ATTACK; + } +} + +/*-------------------------------------------------- + static void K_BotItemFlame(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(player_t *player, ticcmd_t *cmd) +{ + if (player->botvars.itemconfirm > 0) + { + player->botvars.itemconfirm--; + } + else if (player->kartstuff[k_holdready]) + { + INT32 flamemax = player->kartstuff[k_flamelength] * flameseg; + + if (player->kartstuff[k_flamemeter] < flamemax || flamemax == 0) + { + cmd->buttons |= BT_ATTACK; + } + else + { + player->botvars.itemconfirm = 3*flamemax/4; + } + } +} + +/*-------------------------------------------------- + static void K_BotItemRings(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(player_t *player, ticcmd_t *cmd) +{ + INT32 saferingsval = 16 - K_GetKartRingPower(player); + + if (player->speed < K_GetKartSpeed(player, false)/2 // Being slowed down too much + || player->kartstuff[k_speedboost] > (FRACUNIT/5)) // Have another type of boost (tethering) + { + saferingsval -= 5; + } + + if (player->kartstuff[k_rings] > saferingsval) + { + cmd->buttons |= BT_ATTACK; + } +} + +/*-------------------------------------------------- + static void K_BotItemRouletteMash(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(player_t *player, ticcmd_t *cmd) +{ + boolean mash = false; + + if (player->pflags & PF_ATTACKDOWN) + { + return; + } + + if (player->kartstuff[k_rings] < 0 && cv_superring.value) + { + // Uh oh, we need a loan! + // It'll be better in the long run for bots to lose an item set for 10 free rings. + mash = true; + } + + // TODO: Mash based on how far behind you are, when items are + // almost garantueed to be in your favor. + + if (mash == true) + { + cmd->buttons |= BT_ATTACK; + } +} + +/*-------------------------------------------------- + void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt) + + See header file for description. +--------------------------------------------------*/ +void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt) +{ + if (player->kartstuff[k_userings] == 1) + { + // Use rings! + + if (!player->exiting) + { + K_BotItemRings(player, cmd); + } + } + else + { + if (player->botvars.itemdelay) + { + player->botvars.itemdelay--; + player->botvars.itemconfirm = 0; + return; + } + + if (player->kartstuff[k_itemroulette]) + { + // Mashing behaviors + K_BotItemRouletteMash(player, cmd); + return; + } + + if (player->kartstuff[k_stealingtimer] == 0 && player->kartstuff[k_stolentimer] == 0) + { + if (player->kartstuff[k_eggmanexplode]) + { + K_BotItemEggmanExplosion(player, cmd); + } + else if (player->kartstuff[k_eggmanheld]) + { + K_BotItemEggman(player, cmd); + } + else if (player->kartstuff[k_rocketsneakertimer] > 0) + { + K_BotItemRocketSneaker(player, cmd); + } + else + { + switch (player->kartstuff[k_itemtype]) + { + default: + if (player->kartstuff[k_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_HYUDORO: + case KITEM_SUPERRING: + K_BotItemGenericTap(player, cmd); + break; + case KITEM_ROCKETSNEAKER: + if (player->kartstuff[k_rocketsneakertimer] <= 0) + { + K_BotItemGenericTap(player, cmd); + } + break; + case KITEM_SNEAKER: + K_BotItemSneaker(player, cmd); + break; + case KITEM_BANANA: + if (!player->kartstuff[k_itemheld]) + { + 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->kartstuff[k_itemheld]) + { + K_BotItemGenericOrbitShield(player, cmd); + } + else if (player->kartstuff[k_position] != 1) // Hold onto orbiting items when in 1st :) + /* FALL-THRU */ + case KITEM_BALLHOG: + { + K_BotItemOrbinaut(player, cmd); + } + break; + case KITEM_JAWZ: + if (!player->kartstuff[k_itemheld]) + { + K_BotItemGenericOrbitShield(player, cmd); + } + else if (player->kartstuff[k_position] != 1) // Hold onto orbiting items when in 1st :) + { + K_BotItemJawz(player, cmd); + } + break; + case KITEM_MINE: + if (!player->kartstuff[k_itemheld]) + { + K_BotItemGenericTrapShield(player, cmd, turnamt, true); + } + else + { + K_BotItemMine(player, cmd, turnamt); + } + break; + case KITEM_THUNDERSHIELD: + K_BotItemThunder(player, cmd); + break; + case KITEM_BUBBLESHIELD: + K_BotItemBubble(player, cmd); + break; + case KITEM_FLAMESHIELD: + K_BotItemFlame(player, cmd); + break; + } + } + } + } +} diff --git a/src/k_botsearch.c b/src/k_botsearch.c new file mode 100644 index 000000000..28bef819e --- /dev/null +++ b/src/k_botsearch.c @@ -0,0 +1,749 @@ +// SONIC ROBO BLAST 2 KART +//----------------------------------------------------------------------------- +// 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_botsearch.c +/// \brief Bot blockmap search functions + +#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 + +struct globalsmuggle +{ + mobj_t *botmo; + fixed_t distancetocheck; + + fixed_t closestlinedist; + + INT16 curturn; + INT16 steer; + + fixed_t eggboxx, eggboxy; + UINT8 randomitems; + UINT8 eggboxes; +} globalsmuggle; + +/*-------------------------------------------------- + static boolean K_FindEggboxes(mobj_t *thing) + + Blockmap search function. + Increments the random items and egg boxes counters. + + Input Arguments:- + thing - Object passed in from iteration. + + Return:- + true continues searching, false ends the search early. +--------------------------------------------------*/ +static boolean K_FindEggboxes(mobj_t *thing) +{ + fixed_t dist; + + if (thing->type != MT_RANDOMITEM && thing->type != MT_EGGMANITEM) + { + return true; + } + + if (!thing->health) + { + return true; + } + + dist = P_AproxDistance(thing->x - globalsmuggle.eggboxx, thing->y - globalsmuggle.eggboxy); + + if (dist > globalsmuggle.distancetocheck) + { + return true; + } + + if (thing->type == MT_RANDOMITEM) + { + globalsmuggle.randomitems++; + } + else + { + globalsmuggle.eggboxes++; + } + + return true; +} + +/*-------------------------------------------------- + UINT8 K_EggboxStealth(fixed_t x, fixed_t y) + + See header file for description. +--------------------------------------------------*/ +UINT8 K_EggboxStealth(fixed_t x, fixed_t y) +{ + INT32 xl, xh, yl, yh, bx, by; + + memset(&globalsmuggle, 0, sizeof (struct globalsmuggle)); + globalsmuggle.eggboxx = x; + globalsmuggle.eggboxy = y; + globalsmuggle.distancetocheck = (mapobjectscale * 256); + globalsmuggle.randomitems = 0; + globalsmuggle.eggboxes = 0; + + xl = (unsigned)(globalsmuggle.eggboxx - globalsmuggle.distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; + xh = (unsigned)(globalsmuggle.eggboxx + globalsmuggle.distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; + yl = (unsigned)(globalsmuggle.eggboxy - globalsmuggle.distancetocheck - bmaporgy)>>MAPBLOCKSHIFT; + yh = (unsigned)(globalsmuggle.eggboxy + globalsmuggle.distancetocheck - bmaporgy)>>MAPBLOCKSHIFT; + + BMBOUNDFIX(xl, xh, yl, yh); + + for (bx = xl; bx <= xh; bx++) + { + for (by = yl; by <= yh; by++) + { + P_BlockThingsIterator(bx, by, K_FindEggboxes); + } + } + + return (globalsmuggle.randomitems * globalsmuggle.eggboxes); +} + +/*-------------------------------------------------- + static boolean K_BotHatesThisSectorsSpecial(player_t *player, sector_t *sec) + + Tells us if a bot will play more careful around + this sector's special type. + + Input Arguments:- + player - Player to check against. + sec - Sector to check the specials of. + + Return:- + true if avoiding this sector special, false otherwise. +--------------------------------------------------*/ +static boolean K_BotHatesThisSectorsSpecial(player_t *player, sector_t *sec) +{ + switch (GETSECSPECIAL(sec->special, 1)) + { + case 1: // Damage + case 5: // Spikes + case 6: case 7: // Death Pit + case 8: // Instant Kill + return true; + //case 2: case 3: // Offroad (let's let them lawnmower) + case 4: // Offroad (Strong) + if (!K_BotCanTakeCut(player)) + { + return true; + } + break; + default: + break; + } + + return false; +} + +/*-------------------------------------------------- + static boolean K_BotHatesThisSector(player_t *player, sector_t *sec) + + Tells us if a bot will play more careful around + this sector. + + Input Arguments:- + player - Player to check against. + sec - Sector to check against. + + Return:- + true if avoiding this sector, false otherwise. +--------------------------------------------------*/ +static boolean K_BotHatesThisSector(player_t *player, sector_t *sec) +{ + const boolean flip = (player->mo->eflags & MFE_VERTICALFLIP); + INT32 flag; + ffloor_t *rover; + + if (flip) + { + flag = SF_FLIPSPECIAL_CEILING; + } + else + { + flag = SF_FLIPSPECIAL_FLOOR; + } + + if (sec->flags & flag) + { + if (K_BotHatesThisSectorsSpecial(player, sec)) + { + return true; + } + } + + for (rover = sec->ffloors; rover; rover = rover->next) + { + if (!(rover->flags & FF_EXISTS)) + { + continue; + } + + if (!(rover->master->frontsector->flags & flag)) + { + continue; + } + + if (((*rover->bottomheight >= player->mo->z + player->mo->height) && (flip)) + || ((*rover->topheight <= player->mo->z) && (!flip))) + { + if (K_BotHatesThisSectorsSpecial(player, sec)) + { + return true; + } + } + } + + return false; +} + +/*-------------------------------------------------- + static boolean K_FindBlockingWalls(line_t *line) + + Blockmap search function. + Reels the bot prediction back in based on solid walls + or other obstacles surrounding the bot. + + Input Arguments:- + line - Linedef passed in from iteration. + + Return:- + true continues searching, false ends the search early. +--------------------------------------------------*/ +static boolean K_FindBlockingWalls(line_t *line) +{ + // Condensed version of PIT_CheckLine + const fixed_t maxstepmove = FixedMul(MAXSTEPMOVE, mapobjectscale); + fixed_t maxstep = maxstepmove; + fixed_t linedist = INT32_MAX; + INT32 lineside = 0; + + if (!globalsmuggle.botmo || P_MobjWasRemoved(globalsmuggle.botmo) || !globalsmuggle.botmo->player) + { + return false; + } + + if (line->polyobj && !(line->polyobj->flags & POF_SOLID)) + { + return true; + } + + if (tmbbox[BOXRIGHT] <= line->bbox[BOXLEFT] || tmbbox[BOXLEFT] >= line->bbox[BOXRIGHT] + || tmbbox[BOXTOP] <= line->bbox[BOXBOTTOM] || tmbbox[BOXBOTTOM] >= line->bbox[BOXTOP]) + { + return true; + } + + if (P_BoxOnLineSide(tmbbox, line) != -1) + { + return true; + } + + lineside = P_PointOnLineSide(globalsmuggle.botmo->x, globalsmuggle.botmo->y, line); + + // one sided line + if (!line->backsector) + { + if (lineside) + { + // don't hit the back side + return true; + } + + goto blocked; + } + + if ((line->flags & ML_IMPASSABLE) || (line->flags & ML_BLOCKPLAYERS)) + { + goto blocked; + } + + // set openrange, opentop, openbottom + P_LineOpening(line, globalsmuggle.botmo); + + if (globalsmuggle.botmo->player->kartstuff[k_waterskip]) + maxstep += maxstepmove; + + if (P_MobjTouchingSectorSpecial(globalsmuggle.botmo, 1, 13, false)) + maxstep <<= 1; + else if (P_MobjTouchingSectorSpecial(globalsmuggle.botmo, 1, 12, false)) + maxstep = 0; + + if ((openrange < globalsmuggle.botmo->height) // doesn't fit + || (opentop - globalsmuggle.botmo->z < globalsmuggle.botmo->height) // mobj is too high + || (openbottom - globalsmuggle.botmo->z > maxstep)) // too big a step up + { + goto blocked; + } + + if (!K_BotHatesThisSector(globalsmuggle.botmo->player, globalsmuggle.botmo->subsector->sector)) + { + // Treat damage sectors like walls + + if (lineside) + { + if (K_BotHatesThisSector(globalsmuggle.botmo->player, line->frontsector)) + goto blocked; + } + else + { + if (K_BotHatesThisSector(globalsmuggle.botmo->player, line->backsector)) + goto blocked; + } + } + + // We weren't blocked! + return true; + +blocked: + linedist = K_DistanceOfLineFromPoint(line->v1->x, line->v1->y, line->v2->x, line->v2->y, globalsmuggle.botmo->x, globalsmuggle.botmo->y); + linedist -= (globalsmuggle.botmo->radius * 8); // Maintain a reasonable distance away from it + + if (linedist > globalsmuggle.distancetocheck) + { + return true; + } + + if (linedist <= 0) + { + globalsmuggle.closestlinedist = 0; + return false; + } + + if (linedist < globalsmuggle.closestlinedist) + { + globalsmuggle.closestlinedist = linedist; + } + + return true; +} + +/*-------------------------------------------------- + fixed_t K_BotReducePrediction(player_t *player) + + See header file for description. +--------------------------------------------------*/ +fixed_t K_BotReducePrediction(player_t *player) +{ + INT32 xl, xh, yl, yh, bx, by; + + memset(&globalsmuggle, 0, sizeof (struct globalsmuggle)); + globalsmuggle.botmo = player->mo; + globalsmuggle.distancetocheck = (player->mo->radius * 8) + (player->speed * 4); + globalsmuggle.closestlinedist = INT32_MAX; + + tmx = player->mo->x; + tmy = player->mo->y; + + xl = (unsigned)(tmx - globalsmuggle.distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; + xh = (unsigned)(tmx + globalsmuggle.distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; + yl = (unsigned)(tmy - globalsmuggle.distancetocheck - bmaporgy)>>MAPBLOCKSHIFT; + yh = (unsigned)(tmy + globalsmuggle.distancetocheck - bmaporgy)>>MAPBLOCKSHIFT; + + BMBOUNDFIX(xl, xh, yl, yh); + + tmbbox[BOXTOP] = tmy + globalsmuggle.distancetocheck; + tmbbox[BOXBOTTOM] = tmy - globalsmuggle.distancetocheck; + tmbbox[BOXRIGHT] = tmx + globalsmuggle.distancetocheck; + tmbbox[BOXLEFT] = tmx - globalsmuggle.distancetocheck; + + // Check for lines that the bot might collide with + for (bx = xl; bx <= xh; bx++) + { + for (by = yl; by <= yh; by++) + { + P_BlockLinesIterator(bx, by, K_FindBlockingWalls); + } + } + + if (globalsmuggle.closestlinedist == INT32_MAX) + { + return FRACUNIT; + } + + return FixedDiv(globalsmuggle.closestlinedist, globalsmuggle.distancetocheck); +} + +/*-------------------------------------------------- + static void K_SteerFromObject(mobj_t *bot, mobj_t *thing, fixed_t fulldist, fixed_t xdist, boolean towards, INT16 amount) + + Handles steering away/towards the specified object. + + Input Arguments:- + bot - Bot's mobj. + thing - Mobj to steer towards/away from. + fulldist - Distance away from object. + xdist - Horizontal distance away from object. + towards - If true, steer towards the object. Otherwise, steer away. + amount - How hard to turn. + + Return:- + None +--------------------------------------------------*/ +static void K_SteerFromObject(mobj_t *bot, mobj_t *thing, fixed_t fulldist, fixed_t xdist, boolean towards, INT16 amount) +{ + angle_t destangle = R_PointToAngle2(bot->x, bot->y, thing->x, thing->y); + angle_t angle; + SINT8 flip = 1; + + amount = (amount * FixedDiv(globalsmuggle.distancetocheck - fulldist, globalsmuggle.distancetocheck)) / FRACUNIT; + + if (amount == 0) + { + // Shouldn't happen + return; + } + + if (towards) + { + if (xdist < FixedHypot(bot->radius, thing->radius)) + { + // Don't need to turn any harder! + + if (abs(globalsmuggle.steer) <= amount) + { + globalsmuggle.steer = 0; + } + else + { + if (globalsmuggle.steer > 0) + { + globalsmuggle.steer -= amount; + } + else if (globalsmuggle.steer < 0) + { + globalsmuggle.steer += amount; + } + } + + return; + } + + // Still turning towards it, flip. + flip = -flip; + } + + angle = (bot->angle - destangle); + if (angle < ANGLE_180) + { + flip = -flip; + } + + // If going in the opposite direction of where you wanted to turn, + // then reduce the amount that you can turn in that direction. + if ((flip == 1 && globalsmuggle.curturn < 0) + || (flip == -1 && globalsmuggle.curturn > 0)) + { + amount /= 4; + } + + globalsmuggle.steer += amount * flip; +} + +/*-------------------------------------------------- + static boolean K_BotSteerObjects(mobj_t *thing) + + Blockmap search function. + Finds objects around the bot to steer towards/away from. + + Input Arguments:- + thing - Object passed in from iteration. + + Return:- + true continues searching, false ends the search early. +--------------------------------------------------*/ +static boolean K_BotSteerObjects(mobj_t *thing) +{ + INT16 anglediff; + fixed_t xdist, ydist, fulldist; + angle_t destangle, angle; + INT16 attack = ((9 - globalsmuggle.botmo->player->kartspeed) * KART_FULLTURN) / 8; // Acceleration chars are more aggressive + INT16 dodge = ((9 - globalsmuggle.botmo->player->kartweight) * KART_FULLTURN) / 8; // Handling chars dodge better + + if (!globalsmuggle.botmo || P_MobjWasRemoved(globalsmuggle.botmo) || !globalsmuggle.botmo->player) + { + return false; + } + + if (!thing->health) + { + return true; + } + + if (globalsmuggle.botmo == thing) + { + return true; + } + + xdist = K_DistanceOfLineFromPoint( + globalsmuggle.botmo->x, globalsmuggle.botmo->y, + globalsmuggle.botmo->x + FINECOSINE(globalsmuggle.botmo->angle >> ANGLETOFINESHIFT), globalsmuggle.botmo->y + FINESINE(globalsmuggle.botmo->angle >> ANGLETOFINESHIFT), + thing->x, thing->y + ) / 2; // weight x dist more heavily than y dist + + ydist = K_DistanceOfLineFromPoint( + globalsmuggle.botmo->x, globalsmuggle.botmo->y, + globalsmuggle.botmo->x + FINECOSINE((globalsmuggle.botmo->angle + ANGLE_90) >> ANGLETOFINESHIFT), globalsmuggle.botmo->y + FINESINE((globalsmuggle.botmo->angle + ANGLE_90) >> ANGLETOFINESHIFT), + thing->x, thing->y + ); + + fulldist = FixedHypot(xdist, ydist); + + if (fulldist > globalsmuggle.distancetocheck) + { + return true; + } + + if (!P_CheckSight(globalsmuggle.botmo, thing)) + { + return true; + } + + destangle = R_PointToAngle2(globalsmuggle.botmo->x, globalsmuggle.botmo->y, thing->x, thing->y); + angle = (globalsmuggle.botmo->angle - destangle); + + if (angle < ANGLE_180) + { + anglediff = AngleFixed(angle)>>FRACBITS; + } + else + { + anglediff = 360-(AngleFixed(angle)>>FRACBITS); + } + + anglediff = abs(anglediff); + +#define PlayerAttackSteer(botcond, thingcond) \ + if ((botcond) && !(thingcond)) \ + { \ + K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, true, 2 * (KART_FULLTURN + attack)); \ + } \ + else if ((thingcond) && !(botcond)) \ + { \ + K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, false, 2 * (KART_FULLTURN + dodge)); \ + } + + switch (thing->type) + { + case MT_BANANA: + case MT_BANANA_SHIELD: + case MT_EGGMANITEM_SHIELD: + case MT_ORBINAUT: + case MT_ORBINAUT_SHIELD: + case MT_JAWZ: + case MT_JAWZ_DUD: + case MT_JAWZ_SHIELD: + case MT_SSMINE: + case MT_SSMINE_SHIELD: + case MT_BALLHOG: + case MT_SPB: + case MT_BUBBLESHIELDTRAP: + K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, false, 2 * (KART_FULLTURN + dodge)); + break; + case MT_RANDOMITEM: + if (anglediff >= 60) + { + break; + } + + if (P_CanPickupItem(globalsmuggle.botmo->player, 1)) + { + K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, true, KART_FULLTURN + attack); + } + break; + case MT_EGGMANITEM: + if (anglediff >= 60) + { + break; + } + + if (P_CanPickupItem(globalsmuggle.botmo->player, 1)) // Can pick up an actual item + { + const UINT8 stealth = K_EggboxStealth(thing->x, thing->y); + const UINT8 requiredstealth = (globalsmuggle.botmo->player->botvars.difficulty * globalsmuggle.botmo->player->botvars.difficulty); + + if (stealth >= requiredstealth) + { + K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, true, 2 * (KART_FULLTURN + attack)); + } + else + { + K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, false, 2 * (KART_FULLTURN + dodge)); + } + } + break; + case MT_FLOATINGITEM: + if (anglediff >= 60) + { + break; + } + + if (P_CanPickupItem(globalsmuggle.botmo->player, 3)) + { + K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, true, KART_FULLTURN + attack); + } + break; + case MT_RING: + case MT_FLINGRING: + if (anglediff >= 60) + { + break; + } + + if ((RINGTOTAL(globalsmuggle.botmo->player) < 20 && !globalsmuggle.botmo->player->kartstuff[k_ringlock] + && P_CanPickupItem(globalsmuggle.botmo->player, 0)) + && !thing->extravalue1 + && (globalsmuggle.botmo->player->kartstuff[k_itemtype] != KITEM_THUNDERSHIELD)) + { + K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, true, + (RINGTOTAL(globalsmuggle.botmo->player) < 3 + ? (4 * (KART_FULLTURN + attack)) + : (KART_FULLTURN + attack)) + ); + } + break; + case MT_PLAYER: + if (thing->player + && !thing->player->kartstuff[k_hyudorotimer] + && !globalsmuggle.botmo->player->kartstuff[k_hyudorotimer]) + { + // There REALLY ought to be a better way to handle this logic, right?! + // Squishing + PlayerAttackSteer( + globalsmuggle.botmo->scale > thing->scale + (mapobjectscale/8), + thing->scale > globalsmuggle.botmo->scale + (mapobjectscale/8) + ) + // Invincibility + else PlayerAttackSteer( + globalsmuggle.botmo->player->kartstuff[k_invincibilitytimer], + thing->player->kartstuff[k_invincibilitytimer] + ) + // Thunder Shield + else PlayerAttackSteer( + globalsmuggle.botmo->player->kartstuff[k_itemtype] == KITEM_THUNDERSHIELD, + thing->player->kartstuff[k_itemtype] == KITEM_THUNDERSHIELD + ) + // Bubble Shield + else PlayerAttackSteer( + globalsmuggle.botmo->player->kartstuff[k_itemtype] == KITEM_BUBBLESHIELD, + thing->player->kartstuff[k_itemtype] == KITEM_BUBBLESHIELD + ) + // Flame Shield + else PlayerAttackSteer( + globalsmuggle.botmo->player->kartstuff[k_itemtype] == KITEM_FLAMESHIELD, + thing->player->kartstuff[k_itemtype] == KITEM_FLAMESHIELD + ) + // Has held item shield + else PlayerAttackSteer( + (globalsmuggle.botmo->player->kartstuff[k_itemheld] || globalsmuggle.botmo->player->kartstuff[k_eggmanheld]), + (thing->player->kartstuff[k_itemheld] || thing->player->kartstuff[k_eggmanheld]) + ) + // Ring Sting + else PlayerAttackSteer( + thing->player->kartstuff[k_rings] <= 0, + globalsmuggle.botmo->player->kartstuff[k_rings] <= 0 + ) + else + { + // After ALL of that, we can do standard bumping + fixed_t ourweight = K_GetMobjWeight(globalsmuggle.botmo, thing); + fixed_t theirweight = K_GetMobjWeight(thing, globalsmuggle.botmo); + fixed_t weightdiff = 0; + + if (anglediff >= 90) + { + weightdiff = theirweight - ourweight; + } + else + { + weightdiff = ourweight - theirweight; + } + + if (weightdiff > mapobjectscale) + { + K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, true, KART_FULLTURN + attack); + } + else + { + K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, false, KART_FULLTURN + dodge); + } + } + } + break; + case MT_BOTHINT: + if (anglediff >= 60) + { + break; + } + + if (thing->extravalue1 == 0) + { + K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, false, thing->extravalue2 * (KART_FULLTURN + dodge)); + } + { + K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, true, thing->extravalue2 * (KART_FULLTURN + attack)); + } + break; + default: + if (thing->flags & (MF_SOLID|MF_ENEMY|MF_BOSS|MF_PAIN|MF_MISSILE|MF_FIRE)) + { + K_SteerFromObject(globalsmuggle.botmo, thing, fulldist, xdist, false, 2 * (KART_FULLTURN + dodge)); + } + break; + } + + return true; +} + +/*-------------------------------------------------- + INT16 K_BotFindObjects(player_t *player, INT16 turn) + + See header file for description. +--------------------------------------------------*/ +INT16 K_BotFindObjects(player_t *player, INT16 turn) +{ + INT32 xl, xh, yl, yh, bx, by; + + memset(&globalsmuggle, 0, sizeof (struct globalsmuggle)); + globalsmuggle.steer = 0; + globalsmuggle.botmo = player->mo; + globalsmuggle.curturn = turn; + globalsmuggle.distancetocheck = 2048*mapobjectscale; + + xl = (unsigned)(globalsmuggle.botmo->x - globalsmuggle.distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; + xh = (unsigned)(globalsmuggle.botmo->x + globalsmuggle.distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; + yl = (unsigned)(globalsmuggle.botmo->y - globalsmuggle.distancetocheck - bmaporgy)>>MAPBLOCKSHIFT; + yh = (unsigned)(globalsmuggle.botmo->y + globalsmuggle.distancetocheck - bmaporgy)>>MAPBLOCKSHIFT; + + BMBOUNDFIX(xl, xh, yl, yh); + + for (bx = xl; bx <= xh; bx++) + { + for (by = yl; by <= yh; by++) + { + P_BlockThingsIterator(bx, by, K_BotSteerObjects); + } + } + + return globalsmuggle.steer; +} From dbc30da69ff8284309e7444f6423a82acdf31d47 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 24 May 2020 15:55:10 -0400 Subject: [PATCH 73/85] Dial back the bot steering changes a bit --- src/k_bot.c | 40 +++++++++------------------------------- src/k_bot.h | 2 +- src/k_botsearch.c | 7 ++----- 3 files changed, 12 insertions(+), 37 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index 927fc357a..01767054f 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -395,13 +395,11 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) const INT16 handling = K_GetKartTurnValue(player, KART_FULLTURN); // Reduce prediction based on how fast you can turn const INT16 normal = KART_FULLTURN; // "Standard" handling to compare to - const fixed_t topspeed = K_GetKartSpeed(player, false); - const fixed_t speed = P_AproxDistance(player->mo->momx, player->mo->momy) + (topspeed / 4); - const fixed_t distreduce = K_BotReducePrediction(player); fixed_t radreduce = min(distreduce + FRACUNIT/4, FRACUNIT); const tic_t futuresight = (TICRATE * normal) / max(1, handling); // How far ahead into the future to try and predict + const fixed_t speed = P_AproxDistance(player->mo->momx, player->mo->momy); const INT32 distance = (FixedMul(speed, distreduce) / FRACUNIT) * futuresight; botprediction_t *predict = Z_Calloc(sizeof(botprediction_t), PU_LEVEL, NULL); @@ -418,12 +416,6 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) // This prevents looking too far ahead if the closest waypoint is really far away. distanceleft -= P_AproxDistance(player->mo->x - wp->mobj->x, player->mo->y - wp->mobj->y) / FRACUNIT; - if (speed > topspeed) - { - // Play more in the center when going fast. - radreduce = FixedDiv(radreduce, speed / topspeed); - } - // We don't want to look ahead at all, just go to the first waypoint. if (distanceleft <= 0) { @@ -495,8 +487,7 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) } } } - - // Save angle for the next loop's oldangle + angletonext = R_PointToAngle2( wp->mobj->x, wp->mobj->y, wp->nextwaypoints[nwp]->mobj->x, wp->nextwaypoints[nwp]->mobj->y @@ -651,13 +642,6 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) // Full speed ahead! cmd->forwardmove = 50; - if (anglediff > 60) - { - // Actually, don't go too fast... - cmd->forwardmove /= 2; - cmd->buttons |= BT_BRAKE; - } - if (dirdist <= rad) { fixed_t speedmul = FixedMul(player->speed, K_GetKartSpeed(player, false)); @@ -690,7 +674,13 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } } - if (anglediff < 60) + if (anglediff > 60) + { + // Actually, don't go too fast... + cmd->forwardmove /= 2; + cmd->buttons |= BT_BRAKE; + } + else if (dirdist <= realrad) { // Steer towards/away from objects! turnamt += K_BotFindObjects(player, turnamt); @@ -714,12 +704,6 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if (turnamt > 0) { - if (player->botvars.turnconfirm < 0) - { - // Reset turn confirm - player->botvars.turnconfirm = 0; - } - if (player->botvars.turnconfirm < BOTTURNCONFIRM) { player->botvars.turnconfirm++; @@ -727,12 +711,6 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } else if (turnamt < 0) { - if (player->botvars.turnconfirm > 0) - { - // Reset turn confirm - player->botvars.turnconfirm = 0; - } - if (player->botvars.turnconfirm > -BOTTURNCONFIRM) { player->botvars.turnconfirm--; diff --git a/src/k_bot.h b/src/k_bot.h index 81257ecbf..bbd2dedcd 100644 --- a/src/k_bot.h +++ b/src/k_bot.h @@ -20,7 +20,7 @@ // How many tics in a row do you need to turn in this direction before we'll let you turn. // Made it as small as possible without making it look like the bots are twitching constantly. -#define BOTTURNCONFIRM 7 +#define BOTTURNCONFIRM 4 // Point for bots to aim for typedef struct botprediction_s { diff --git a/src/k_botsearch.c b/src/k_botsearch.c index 28bef819e..2a95da738 100644 --- a/src/k_botsearch.c +++ b/src/k_botsearch.c @@ -95,7 +95,6 @@ UINT8 K_EggboxStealth(fixed_t x, fixed_t y) { INT32 xl, xh, yl, yh, bx, by; - memset(&globalsmuggle, 0, sizeof (struct globalsmuggle)); globalsmuggle.eggboxx = x; globalsmuggle.eggboxy = y; globalsmuggle.distancetocheck = (mapobjectscale * 256); @@ -347,9 +346,8 @@ fixed_t K_BotReducePrediction(player_t *player) { INT32 xl, xh, yl, yh, bx, by; - memset(&globalsmuggle, 0, sizeof (struct globalsmuggle)); globalsmuggle.botmo = player->mo; - globalsmuggle.distancetocheck = (player->mo->radius * 8) + (player->speed * 4); + globalsmuggle.distancetocheck = (player->mo->radius * 16); globalsmuggle.closestlinedist = INT32_MAX; tmx = player->mo->x; @@ -724,11 +722,10 @@ INT16 K_BotFindObjects(player_t *player, INT16 turn) { INT32 xl, xh, yl, yh, bx, by; - memset(&globalsmuggle, 0, sizeof (struct globalsmuggle)); globalsmuggle.steer = 0; globalsmuggle.botmo = player->mo; globalsmuggle.curturn = turn; - globalsmuggle.distancetocheck = 2048*mapobjectscale; + globalsmuggle.distancetocheck = (player->mo->radius * 32) + (player->speed * 4); xl = (unsigned)(globalsmuggle.botmo->x - globalsmuggle.distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; xh = (unsigned)(globalsmuggle.botmo->x + globalsmuggle.distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; From 597baf212b0381beb452f38c43421347634868ec Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 24 May 2020 15:55:26 -0400 Subject: [PATCH 74/85] Allow rubberbanding up to exiting players --- src/k_bot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_bot.c b/src/k_bot.c index 01767054f..541acd78f 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -302,7 +302,7 @@ fixed_t K_BotRubberband(player_t *player) for (i = 0; i < MAXPLAYERS; i++) { - if (!playeringame[i] || players[i].spectator || players[i].exiting) + if (!playeringame[i] || players[i].spectator) { continue; } From 5d77807a78bd3915c5583db8db4f0da6d83969d3 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 24 May 2020 16:16:01 -0400 Subject: [PATCH 75/85] const radreduce, removed unused "dir" property --- src/k_bot.c | 2 +- src/k_bot.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index 541acd78f..4405bee44 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -396,7 +396,7 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) const INT16 normal = KART_FULLTURN; // "Standard" handling to compare to const fixed_t distreduce = K_BotReducePrediction(player); - fixed_t radreduce = min(distreduce + FRACUNIT/4, FRACUNIT); + const fixed_t radreduce = min(distreduce + FRACUNIT/4, FRACUNIT); const tic_t futuresight = (TICRATE * normal) / max(1, handling); // How far ahead into the future to try and predict const fixed_t speed = P_AproxDistance(player->mo->momx, player->mo->momy); diff --git a/src/k_bot.h b/src/k_bot.h index bbd2dedcd..48756e2c8 100644 --- a/src/k_bot.h +++ b/src/k_bot.h @@ -26,7 +26,6 @@ typedef struct botprediction_s { fixed_t x, y; fixed_t radius; - angle_t dir; } botprediction_t; From 4916516e2e5d6fa4b067799c8c9bbf33c0ffe6cb Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 24 May 2020 17:12:58 -0400 Subject: [PATCH 76/85] Prevent duplicate skins, fix bots not being able to be disabled, change default to "off" --- src/d_netcmd.c | 13 +++++-- src/k_bot.c | 98 ++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 81 insertions(+), 30 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index fe931a70f..18c9db93b 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -393,12 +393,19 @@ static CV_PossibleValue_t kartvoices_cons_t[] = {{0, "Never"}, {1, "Tasteful"}, consvar_t cv_kartvoices = {"kartvoices", "Tasteful", CV_SAVE, kartvoices_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; static CV_PossibleValue_t kartbot_cons_t[] = { - {1, "MIN"}, - {9, "MAX"}, {0, "Off"}, + {1, "Lv.1"}, + {2, "Lv.2"}, + {3, "Lv.3"}, + {4, "Lv.4"}, + {5, "Lv.5"}, + {6, "Lv.6"}, + {7, "Lv.7"}, + {8, "Lv.8"}, + {9, "Lv.9"}, {0, NULL} }; -consvar_t cv_kartbot = {"kartbot", "5", CV_NETVAR|CV_CHEAT, kartbot_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_kartbot = {"kartbot", "0", CV_NETVAR|CV_CHEAT, kartbot_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_karteliminatelast = {"karteliminatelast", "Yes", CV_NETVAR|CV_CHEAT|CV_CALL|CV_NOSHOWHELP, CV_YesNo, KartEliminateLast_OnChange, 0, NULL, NULL, 0, 0, NULL}; diff --git a/src/k_bot.c b/src/k_bot.c index 4405bee44..3103facdb 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -111,6 +111,7 @@ void K_UpdateMatchRaceBots(void) UINT8 numbots = 0; UINT8 numwaiting = 0; SINT8 wantedbots = 0; + boolean skinusable[MAXSKINS]; UINT8 i; if (!server) @@ -118,38 +119,57 @@ void K_UpdateMatchRaceBots(void) return; } - if (difficulty != 0) + // init usable bot skins list + for (i = 0; i < MAXSKINS; i++) { - if (cv_ingamecap.value > 0) + if (i < numskins) { - pmax = min(pmax, cv_ingamecap.value); + skinusable[i] = true; } - - for (i = 0; i < MAXPLAYERS; i++) + else { - if (playeringame[i]) - { - if (!players[i].spectator) - { - if (players[i].bot) - { - numbots++; + skinusable[i] = false; + } + } - // While we're here, we should update bot difficulty to the proper value. - players[i].botvars.difficulty = difficulty; - } - else - { - numplayers++; - } - } - else if (players[i].pflags & PF_WANTSTOJOIN) + if (cv_ingamecap.value > 0) + { + pmax = min(pmax, cv_ingamecap.value); + } + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + if (!players[i].spectator) + { + skinusable[players[i].skin] = false; + + if (players[i].bot) { - numwaiting++; + numbots++; + + // While we're here, we should update bot difficulty to the proper value. + players[i].botvars.difficulty = difficulty; + } + else + { + numplayers++; } } + else if (players[i].pflags & PF_WANTSTOJOIN) + { + numwaiting++; + } } + } + if (difficulty == 0) + { + wantedbots = 0; + } + else + { wantedbots = pmax - numplayers - numwaiting; if (wantedbots < 0) @@ -157,15 +177,12 @@ void K_UpdateMatchRaceBots(void) wantedbots = 0; } } - else - { - wantedbots = 0; - } if (numbots < wantedbots) { // We require MORE bots! UINT8 newplayernum = 0; + boolean usedallskins = false; if (dedicated) { @@ -174,12 +191,39 @@ void K_UpdateMatchRaceBots(void) while (numbots < wantedbots) { - if (!K_AddBot(M_RandomKey(numskins), difficulty, &newplayernum)) + UINT8 skin = M_RandomKey(numskins); + + if (usedallskins == false) + { + UINT8 loops = 0; + + while (!skinusable[skin]) + { + if (loops >= numskins) + { + // no more skins, stick to our first choice + usedallskins = true; + break; + } + + skin++; + + if (skin >= numskins) + { + skin = 0; + } + + loops++; + } + } + + if (!K_AddBot(skin, difficulty, &newplayernum)) { // Not enough player slots to add the bot, break the loop. break; } + skinusable[skin] = false; numbots++; } } From 5ad6812f1738e4cbacdc5e4cbb338d554b089c4f Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 28 May 2020 13:42:11 -0400 Subject: [PATCH 77/85] No modify power level for beating/losing to players with no power level set This mainly is intended for bots, but also applies to splitscreen guests who also probably shouldn't have any power level --- src/k_pwrlv.c | 7 ++++--- src/y_inter.c | 16 ++++++++++------ 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/k_pwrlv.c b/src/k_pwrlv.c index b8d78e0ee..8101a013e 100644 --- a/src/k_pwrlv.c +++ b/src/k_pwrlv.c @@ -305,9 +305,10 @@ void K_PlayerForfeit(UINT8 playernum, boolean pointloss) if (i == playernum) continue; - theirpower = PWRLVRECORD_DEF; - if (clientpowerlevels[i][powertype] != 0) // No power level acts as 5000 (used for splitscreen guests) - theirpower = clientpowerlevels[i][powertype]; + if (clientpowerlevels[i][powertype] == 0) // No power level (splitscreen guests, bots) + continue; + + theirpower = clientpowerlevels[i][powertype]; diff = yourpower - theirpower; inc -= K_CalculatePowerLevelInc(diff); diff --git a/src/y_inter.c b/src/y_inter.c index 1051a7e55..94e24aed1 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -939,9 +939,11 @@ static void K_UpdatePowerLevels(void) continue; } - theirpower = PWRLVRECORD_DEF; - if (clientpowerlevels[jpnum][powertype] != 0) // No power level acts as 5000 (used for splitscreen guests) - theirpower = clientpowerlevels[jpnum][powertype]; + if (clientpowerlevels[jpnum][powertype] == 0) // No power level (splitscreen guests, bots) + continue; + + theirpower = clientpowerlevels[jpnum][powertype]; + CONS_Debug(DBG_GAMELOGIC, "Player %d's PWR.LV: %d\n", jpnum, theirpower); if (G_RaceGametype()) @@ -981,9 +983,11 @@ static void K_UpdatePowerLevels(void) CONS_Debug(DBG_GAMELOGIC, "Player %d VS Player %d (griefer):\n", ipnum, jpnum); - theirpower = PWRLVRECORD_DEF; - if (nospectategrief[jpnum] != 0) // No power level acts as 5000 (used for splitscreen guests) - theirpower = nospectategrief[jpnum]; + if (nospectategrief[jpnum] == 0) // No power level (splitscreen guests, bots) + continue; + + theirpower = nospectategrief[jpnum]; + CONS_Debug(DBG_GAMELOGIC, "Player %d's PWR.LV: %d\n", jpnum, theirpower); diff = theirpower - yourpower; From b8b4d913a8f2e596c8290bc83f3ad5fa83bdd4a2 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 28 May 2020 13:44:53 -0400 Subject: [PATCH 78/85] Reverse order bots are removed --- src/k_bot.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index 3103facdb..7bf8a22e7 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -231,9 +231,9 @@ void K_UpdateMatchRaceBots(void) { UINT8 buf[2]; - i = 0; + i = MAXPLAYERS; - while (numbots > wantedbots && i < MAXPLAYERS) + while (numbots > wantedbots && i > 0) { if (playeringame[i] && players[i].bot) { @@ -244,7 +244,7 @@ void K_UpdateMatchRaceBots(void) numbots--; } - i++; + i--; } } From 08198ef1b4ecfac5cacdba5248085aff59600a2c Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 28 May 2020 13:50:47 -0400 Subject: [PATCH 79/85] Don't display ping for bots --- src/k_kart.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 7c7cd387c..94d5aba0f 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -9461,9 +9461,17 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I if (players[tab[i].num].spectator || !players[tab[i].num].mo) continue; //ignore them. - if (netgame // don't draw it offline - && ( tab[i].num != serverplayer || ! server_lagless )) - HU_drawPing(x + ((i < 8) ? -17 : rightoffset + 11), y-4, playerpingtable[tab[i].num], 0); + if (netgame) // don't draw ping offline + { + if (players[tab[i].num].bot) + { + ; // TODO: Put a graphic here to indicate this player is a bot! + } + else if (tab[i].num != serverplayer || !server_lagless) + { + HU_drawPing(x + ((i < 8) ? -17 : rightoffset + 11), y-4, playerpingtable[tab[i].num], 0); + } + } STRBUFCPY(strtime, tab[i].name); From fdb13b76d1c33ce77f37d35b617524eb0b4a2610 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 28 May 2020 16:46:21 -0400 Subject: [PATCH 80/85] Make K_BotTopSpeedRubberband a function --- src/k_bot.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/k_bot.h | 22 +++++++++++++++++++--- src/k_kart.c | 44 +------------------------------------------ src/p_user.c | 9 +++++++-- 4 files changed, 80 insertions(+), 48 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index 7bf8a22e7..8fed8dc4f 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -394,6 +394,59 @@ fixed_t K_BotRubberband(player_t *player) return rubberband; } +/*-------------------------------------------------- + fixed_t K_BotTopSpeedRubberband(player_t *player) + + See header file for description. +--------------------------------------------------*/ +fixed_t K_BotTopSpeedRubberband(player_t *player) +{ + fixed_t rubberband = K_BotRubberband(player); + + if (rubberband < FRACUNIT) + { + // Never go below your regular top speed + rubberband = FRACUNIT; + } + + // Only allow you to go faster than your regular top speed if you're facing the right direction + if (rubberband > FRACUNIT && player->mo != NULL && player->nextwaypoint != NULL) + { + const INT16 mindiff = 30; + const INT16 maxdiff = 60; + INT16 anglediff = 0; + fixed_t amt = rubberband - FRACUNIT; + angle_t destangle = R_PointToAngle2( + player->mo->x, player->mo->y, + player->nextwaypoint->mobj->x, player->nextwaypoint->mobj->y + ); + angle_t angle = player->mo->angle - destangle; + + if (angle < ANGLE_180) + { + anglediff = AngleFixed(angle) >> FRACBITS; + } + else + { + anglediff = 360 - (AngleFixed(angle) >> FRACBITS); + } + + anglediff = abs(anglediff); + + if (anglediff >= maxdiff) + { + rubberband = FRACUNIT; + } + else if (anglediff > mindiff) + { + amt = (amt * (maxdiff - anglediff)) / mindiff; + rubberband = FRACUNIT + amt; + } + } + + return rubberband; +} + /*-------------------------------------------------- fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, fixed_t v2y, fixed_t cx, fixed_t cy) diff --git a/src/k_bot.h b/src/k_bot.h index 48756e2c8..48472b5fd 100644 --- a/src/k_bot.h +++ b/src/k_bot.h @@ -66,19 +66,35 @@ boolean K_BotCanTakeCut(player_t *player); /*-------------------------------------------------- fixed_t K_BotRubberband(player_t *player); - Gives a multiplier for a bot's rubberbanding. Meant to be used for top speed, - acceleration, and handling. + Gives a multiplier for a bot's rubberbanding. + Meant to be used for acceleration and handling. Input Arguments:- player - Player to check. Return:- - A multiplier in fixed point scale, between 0.875 and 2.0. + A multiplier in fixed point scale. --------------------------------------------------*/ fixed_t K_BotRubberband(player_t *player); +/*-------------------------------------------------- + fixed_t K_BotTopSpeedRubberband(player_t *player); + + Gives a multiplier for a bot's rubberbanding. + Adjusted from K_BotRubberband to be used for top speed. + + Input Arguments:- + player - Player to check. + + Return:- + A multiplier in fixed point scale. +--------------------------------------------------*/ + +fixed_t K_BotTopSpeedRubberband(player_t *player); + + /*-------------------------------------------------- fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, fixed_t v2y, fixed_t cx, fixed_t cy); diff --git a/src/k_kart.c b/src/k_kart.c index 94d5aba0f..e00013128 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2357,49 +2357,7 @@ fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower) { if (K_PlayerUsesBotMovement(player)) { - fixed_t rubberband = K_BotRubberband(player); - - if (rubberband < FRACUNIT) - { - rubberband = FRACUNIT; - } - - // Only allow you to go fast if you're facing the right direction - if (rubberband > FRACUNIT && player->mo != NULL && player->nextwaypoint != NULL) - { - const INT16 mindiff = 30; - const INT16 maxdiff = 60; - INT16 anglediff = 0; - fixed_t amt = rubberband - FRACUNIT; - angle_t destangle = R_PointToAngle2( - player->mo->x, player->mo->y, - player->nextwaypoint->mobj->x, player->nextwaypoint->mobj->y - ); - angle_t angle = player->mo->angle - destangle; - - if (angle < ANGLE_180) - { - anglediff = AngleFixed(angle) >> FRACBITS; - } - else - { - anglediff = 360 - (AngleFixed(angle) >> FRACBITS); - } - - anglediff = abs(anglediff); - - if (anglediff >= maxdiff) - { - rubberband = FRACUNIT; - } - else if (anglediff > mindiff) - { - amt = (amt * (maxdiff - anglediff)) / mindiff; - rubberband = FRACUNIT + amt; - } - } - - finalspeed = FixedMul(finalspeed, rubberband); + finalspeed = FixedMul(finalspeed, K_BotTopSpeedRubberband(player)); } return FixedMul(finalspeed, player->kartstuff[k_boostpower]+player->kartstuff[k_speedboost]); diff --git a/src/p_user.c b/src/p_user.c index f91e8dc25..28273ca82 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -4170,11 +4170,16 @@ static void P_3dMovement(player_t *player) if (K_PlayerUsesBotMovement(player)) { - fixed_t rubberband = K_BotRubberband(player); + fixed_t baserubberband = K_BotRubberband(player); + fixed_t rubberband = FixedMul(baserubberband, + FixedMul(baserubberband, + FixedMul(baserubberband, + baserubberband + ))); // This looks extremely goofy, but we need this really high, but at the same time, proportional. if (rubberband > FRACUNIT) { - div = FixedMul(div, 4*rubberband); + div = FixedMul(div, rubberband); } } From 896c6b39527010385dc66cba1367ae5584ed0ff6 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 28 May 2020 17:58:35 -0400 Subject: [PATCH 81/85] Rewrite K_BotHatesThisSector slightly Fixed a lot of its false positives, as well as some copy-paste errors --- src/k_botsearch.c | 97 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 66 insertions(+), 31 deletions(-) diff --git a/src/k_botsearch.c b/src/k_botsearch.c index 2a95da738..28e08f3cf 100644 --- a/src/k_botsearch.c +++ b/src/k_botsearch.c @@ -25,6 +25,7 @@ #include "d_ticcmd.h" #include "m_random.h" #include "r_things.h" // numskins +#include "p_slopes.h" // P_GetZAt struct globalsmuggle { @@ -156,64 +157,99 @@ static boolean K_BotHatesThisSectorsSpecial(player_t *player, sector_t *sec) } /*-------------------------------------------------- - static boolean K_BotHatesThisSector(player_t *player, sector_t *sec) + static boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t y) Tells us if a bot will play more careful around - this sector. + this sector. Checks FOFs in the sector, as well. Input Arguments:- player - Player to check against. sec - Sector to check against. + x - Linedef cross X position, for slopes + y - Linedef cross Y position, for slopes Return:- true if avoiding this sector, false otherwise. --------------------------------------------------*/ -static boolean K_BotHatesThisSector(player_t *player, sector_t *sec) +static boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t y) { const boolean flip = (player->mo->eflags & MFE_VERTICALFLIP); - INT32 flag; + INT32 specialflag = 0; + fixed_t highestfloor = INT32_MAX; + sector_t *bestsector = NULL; ffloor_t *rover; - if (flip) + if (flip == true) { - flag = SF_FLIPSPECIAL_CEILING; + specialflag = SF_FLIPSPECIAL_CEILING; + highestfloor = (sec->c_slope ? P_GetZAt(sec->c_slope, x, y) : sec->ceilingheight); } else { - flag = SF_FLIPSPECIAL_FLOOR; + specialflag = SF_FLIPSPECIAL_FLOOR; + highestfloor = (sec->f_slope ? P_GetZAt(sec->f_slope, x, y) : sec->floorheight); } - if (sec->flags & flag) + if (sec->flags & specialflag) { - if (K_BotHatesThisSectorsSpecial(player, sec)) - { - return true; - } + bestsector = sec; } for (rover = sec->ffloors; rover; rover = rover->next) { + fixed_t top = INT32_MAX; + fixed_t bottom = INT32_MAX; + if (!(rover->flags & FF_EXISTS)) { continue; } - if (!(rover->master->frontsector->flags & flag)) + top = (*rover->t_slope ? P_GetZAt(*rover->t_slope, x, y) : *rover->topheight); + bottom = (*rover->b_slope ? P_GetZAt(*rover->b_slope, x, y) : *rover->bottomheight); + + if (!(rover->flags & FF_BLOCKPLAYER)) + { + if ((top >= player->mo->z) && (bottom <= player->mo->z + player->mo->height) + && K_BotHatesThisSectorsSpecial(player, rover->master->frontsector)) + { + // Bad intangible sector at our height, so we DEFINITELY want to avoid + return true; + } + } + + if ((rover->flags & FF_BLOCKPLAYER) && !(rover->master->frontsector->flags & specialflag)) { continue; } - if (((*rover->bottomheight >= player->mo->z + player->mo->height) && (flip)) - || ((*rover->topheight <= player->mo->z) && (!flip))) + // Find the highest FOF floor beneath the player, and check it at the end. + if (flip == true) { - if (K_BotHatesThisSectorsSpecial(player, sec)) + if (bottom < highestfloor + && bottom >= player->mo->z + player->mo->height) { - return true; + bestsector = rover->master->frontsector; + highestfloor = bottom; + } + } + else + { + if (top > highestfloor + && top <= player->mo->z) + { + bestsector = rover->master->frontsector; + highestfloor = top; } } } - return false; + if (bestsector == NULL) + { + return false; + } + + return K_BotHatesThisSectorsSpecial(player, bestsector); } /*-------------------------------------------------- @@ -236,6 +272,7 @@ static boolean K_FindBlockingWalls(line_t *line) fixed_t maxstep = maxstepmove; fixed_t linedist = INT32_MAX; INT32 lineside = 0; + vertex_t pos; if (!globalsmuggle.botmo || P_MobjWasRemoved(globalsmuggle.botmo) || !globalsmuggle.botmo->player) { @@ -295,20 +332,18 @@ static boolean K_FindBlockingWalls(line_t *line) goto blocked; } - if (!K_BotHatesThisSector(globalsmuggle.botmo->player, globalsmuggle.botmo->subsector->sector)) - { - // Treat damage sectors like walls + // Treat damage sectors like walls + P_ClosestPointOnLine(globalsmuggle.botmo->x, globalsmuggle.botmo->y, line, &pos); - if (lineside) - { - if (K_BotHatesThisSector(globalsmuggle.botmo->player, line->frontsector)) - goto blocked; - } - else - { - if (K_BotHatesThisSector(globalsmuggle.botmo->player, line->backsector)) - goto blocked; - } + if (lineside) + { + if (K_BotHatesThisSector(globalsmuggle.botmo->player, line->frontsector, pos.x, pos.y)) + goto blocked; + } + else + { + if (K_BotHatesThisSector(globalsmuggle.botmo->player, line->backsector, pos.x, pos.y)) + goto blocked; } // We weren't blocked! From 5f97070a471dfa604c9282cea564053dfaea9f60 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 28 May 2020 18:29:00 -0400 Subject: [PATCH 82/85] FixedDiv should be here instead of FixedMul --- src/k_bot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_bot.c b/src/k_bot.c index 8fed8dc4f..a38090f6a 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -741,7 +741,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if (dirdist <= rad) { - fixed_t speedmul = FixedMul(player->speed, K_GetKartSpeed(player, false)); + fixed_t speedmul = FixedDiv(player->speed, K_GetKartSpeed(player, false)); fixed_t speedrad = rad/4; if (speedmul > FRACUNIT) From d2aa03b44ed007ce99dafcdc71cce36bc7185b41 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 28 May 2020 18:45:14 -0400 Subject: [PATCH 83/85] Change 3*rad/4 to rad Should let them use slightly more of the track --- src/k_bot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_bot.c b/src/k_bot.c index a38090f6a..7bbd8baf9 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -752,7 +752,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) // Increase radius with speed // At low speed, the CPU will try to be more accurate // At high speed, they're more likely to lawnmower - speedrad += FixedMul(speedmul, (3*rad/4) - speedrad); + speedrad += FixedMul(speedmul, rad - speedrad); if (speedrad < playerwidth) { From 990733a5ccaec26beac51926f6dc53094f97cf14 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 28 May 2020 18:46:33 -0400 Subject: [PATCH 84/85] Minimum prediction distance Lets them properly steer while respawning --- src/k_bot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_bot.c b/src/k_bot.c index 7bbd8baf9..6beb6fe9e 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -496,7 +496,7 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) const fixed_t radreduce = min(distreduce + FRACUNIT/4, FRACUNIT); const tic_t futuresight = (TICRATE * normal) / max(1, handling); // How far ahead into the future to try and predict - const fixed_t speed = P_AproxDistance(player->mo->momx, player->mo->momy); + const fixed_t speed = max(P_AproxDistance(player->mo->momx, player->mo->momy), K_GetKartSpeed(player, false) / 4); const INT32 distance = (FixedMul(speed, distreduce) / FRACUNIT) * futuresight; botprediction_t *predict = Z_Calloc(sizeof(botprediction_t), PU_LEVEL, NULL); From 08a87d7e7e10bcab3a7db2311c0983f05fc5fab8 Mon Sep 17 00:00:00 2001 From: lachwright Date: Sun, 31 May 2020 18:31:33 +0800 Subject: [PATCH 85/85] Remove b_bot.c and b_bot.h from CMakeLists --- src/CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2cba3c836..5c3a87d87 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,7 +3,6 @@ # Core sources set(SRB2_CORE_SOURCES am_map.c - b_bot.c command.c comptime.c console.c @@ -50,7 +49,6 @@ set(SRB2_CORE_SOURCES set(SRB2_CORE_HEADERS am_map.h - b_bot.h byteptr.h command.h console.h