From 6e73adff2025f91f2d50fc4d65b377040e9c6ae3 Mon Sep 17 00:00:00 2001 From: Rachael Alexanderson Date: Tue, 26 Jan 2021 14:30:26 -0500 Subject: [PATCH] - add TDBots 2.7 to replace ZCajun --- CMakeLists.txt | 1 + bot-config/bots.cfg | 381 +++++++++++++ src/CMakeLists.txt | 2 - src/common/engine/startupinfo.h | 1 + src/d_main.cpp | 19 +- src/gamedata/info.cpp | 2 - src/playsim/bots/b_bot.cpp | 73 --- src/playsim/bots/b_bot.h | 26 - src/playsim/bots/b_func.cpp | 531 ------------------ src/playsim/bots/b_game.cpp | 8 +- src/playsim/bots/b_move.cpp | 390 -------------- src/playsim/bots/b_think.cpp | 439 --------------- src/playsim/p_map.cpp | 15 - src/playsim/p_mobj.cpp | 6 - wadsrc_tdbots/CMakeLists.txt | 3 + wadsrc_tdbots/static/BOTINFO.zandro | 322 +++++++++++ wadsrc_tdbots/static/CHANGELOG.txt | 29 + wadsrc_tdbots/static/CVARINFO | 25 + wadsrc_tdbots/static/DECORATE.dec | 482 +++++++++++++++++ wadsrc_tdbots/static/KEYCONF | 19 + wadsrc_tdbots/static/KEYCONF.nstudio | 1 + wadsrc_tdbots/static/LANGUAGE.pm | 3 + wadsrc_tdbots/static/LICENSE.txt | 21 + wadsrc_tdbots/static/LOADACS | 2 + wadsrc_tdbots/static/MENUDEF | 103 ++++ wadsrc_tdbots/static/TO_MODDERS.txt | 227 ++++++++ wadsrc_tdbots/static/acs/NODESTUDIO.o | Bin 0 -> 4092 bytes wadsrc_tdbots/static/acs/TDB_Main.o | Bin 0 -> 20264 bytes wadsrc_tdbots/static/scripts/NODESTUDIO.acs | 288 ++++++++++ wadsrc_tdbots/static/scripts/TDB_Chat.acs | 173 ++++++ wadsrc_tdbots/static/scripts/TDB_Defn.acs | 33 ++ wadsrc_tdbots/static/scripts/TDB_Inv.acs | 48 ++ wadsrc_tdbots/static/scripts/TDB_Main.acs | 330 ++++++++++++ wadsrc_tdbots/static/scripts/TDB_Misc.acs | 417 +++++++++++++++ wadsrc_tdbots/static/scripts/TDB_Node.acs | 478 +++++++++++++++++ wadsrc_tdbots/static/scripts/TDB_RA.acs | 287 ++++++++++ wadsrc_tdbots/static/scripts/TDB_Zan.acs | 25 + wadsrc_tdbots/static/sprites/TBNDA0.png | Bin 0 -> 135 bytes wadsrc_tdbots/static/sprites/TBNDB0.png | Bin 0 -> 93 bytes wadsrc_tdbots/static/sprites/TBNDC0.png | Bin 0 -> 120 bytes wadsrc_tdbots/static/sprites/TBNDD0.png | Bin 0 -> 120 bytes wadsrc_tdbots/static/sprites/TBNDZ0.png | Bin 0 -> 120 bytes wadsrc_tdbots/static/tdbots/CHEXWEAPONS.dec | 111 ++++ wadsrc_tdbots/static/tdbots/DOOMWEAPONS.dec | 468 ++++++++++++++++ wadsrc_tdbots/static/tdbots/HEXNWEAPONS.dec | 464 ++++++++++++++++ wadsrc_tdbots/static/tdbots/HTICWEAPONS.dec | 564 ++++++++++++++++++++ wadsrc_tdbots/static/tdbots/NODESTUDIO.dec | 115 ++++ wadsrc_tdbots/static/tdbots/PATHNODING.dec | 177 ++++++ wadsrc_tdbots/static/tdbots/STRFWEAPONS.dec | 327 ++++++++++++ 49 files changed, 5947 insertions(+), 1489 deletions(-) create mode 100644 bot-config/bots.cfg delete mode 100644 src/playsim/bots/b_move.cpp delete mode 100644 src/playsim/bots/b_think.cpp create mode 100644 wadsrc_tdbots/CMakeLists.txt create mode 100644 wadsrc_tdbots/static/BOTINFO.zandro create mode 100644 wadsrc_tdbots/static/CHANGELOG.txt create mode 100644 wadsrc_tdbots/static/CVARINFO create mode 100644 wadsrc_tdbots/static/DECORATE.dec create mode 100644 wadsrc_tdbots/static/KEYCONF create mode 100644 wadsrc_tdbots/static/KEYCONF.nstudio create mode 100644 wadsrc_tdbots/static/LANGUAGE.pm create mode 100644 wadsrc_tdbots/static/LICENSE.txt create mode 100644 wadsrc_tdbots/static/LOADACS create mode 100644 wadsrc_tdbots/static/MENUDEF create mode 100644 wadsrc_tdbots/static/TO_MODDERS.txt create mode 100644 wadsrc_tdbots/static/acs/NODESTUDIO.o create mode 100644 wadsrc_tdbots/static/acs/TDB_Main.o create mode 100644 wadsrc_tdbots/static/scripts/NODESTUDIO.acs create mode 100644 wadsrc_tdbots/static/scripts/TDB_Chat.acs create mode 100644 wadsrc_tdbots/static/scripts/TDB_Defn.acs create mode 100644 wadsrc_tdbots/static/scripts/TDB_Inv.acs create mode 100644 wadsrc_tdbots/static/scripts/TDB_Main.acs create mode 100644 wadsrc_tdbots/static/scripts/TDB_Misc.acs create mode 100644 wadsrc_tdbots/static/scripts/TDB_Node.acs create mode 100644 wadsrc_tdbots/static/scripts/TDB_RA.acs create mode 100644 wadsrc_tdbots/static/scripts/TDB_Zan.acs create mode 100644 wadsrc_tdbots/static/sprites/TBNDA0.png create mode 100644 wadsrc_tdbots/static/sprites/TBNDB0.png create mode 100644 wadsrc_tdbots/static/sprites/TBNDC0.png create mode 100644 wadsrc_tdbots/static/sprites/TBNDD0.png create mode 100644 wadsrc_tdbots/static/sprites/TBNDZ0.png create mode 100644 wadsrc_tdbots/static/tdbots/CHEXWEAPONS.dec create mode 100644 wadsrc_tdbots/static/tdbots/DOOMWEAPONS.dec create mode 100644 wadsrc_tdbots/static/tdbots/HEXNWEAPONS.dec create mode 100644 wadsrc_tdbots/static/tdbots/HTICWEAPONS.dec create mode 100644 wadsrc_tdbots/static/tdbots/NODESTUDIO.dec create mode 100644 wadsrc_tdbots/static/tdbots/PATHNODING.dec create mode 100644 wadsrc_tdbots/static/tdbots/STRFWEAPONS.dec diff --git a/CMakeLists.txt b/CMakeLists.txt index 98116460a0..4b8ede499f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -404,6 +404,7 @@ add_subdirectory( wadsrc ) add_subdirectory( wadsrc_bm ) add_subdirectory( wadsrc_lights ) add_subdirectory( wadsrc_extra ) +add_subdirectory( wadsrc_tdbots ) add_subdirectory( wadsrc_widescreen ) add_subdirectory( src ) diff --git a/bot-config/bots.cfg b/bot-config/bots.cfg new file mode 100644 index 0000000000..15f4b60c26 --- /dev/null +++ b/bot-config/bots.cfg @@ -0,0 +1,381 @@ +{ + name "Mact Man" + aiming 100 + perfection 100 + reaction 1 + isp 50 + color "00 00 00" + weaponpref 012345373 + voice "Arabian" +} + +{ + name "Slayer" + aiming 72 + perfection 35 + reaction 46 + isp 77 + color "ff ff ff" + weaponpref 012345876 +} + +{ + name "Larry" + aiming 67 + perfection 15 + reaction 76 + isp 37 + color "50 50 60" + + weaponpref 012345678 + voice "Babe" +} + +{ + name "Karen" + aiming 25 + perfection 55 + reaction 32 + isp 70 + color "00 00 ff" + skin illucia + weaponpref 012345678 +} + +{ + name "Joel" + aiming 67 + perfection 50 + reaction 10 + isp 50 + color "40 cf 00" + weaponpref 012385687 + voice "Bitch" +} + +{ + name "Gustavo" + aiming 99 + perfection 95 + reaction 98 + isp 32 + color "ff ff ff" + + weaponpref 35787120 + voice "Bond" +} + +{ + name "James" + aiming 90 + perfection 90 + reaction 10 + isp 10 + color "b0 b0 b0" + + weaponpref 012345678 + voice "Bugs" +} + +{ + name "Tyler" + aiming 90 + perfection 90 + reaction 20 + isp 90 + color "ff ff ff" + + weaponpref 012345678 + voice "Daffy" +} + +{ + name "Thomas" + aiming 10 + perfection 10 + reaction 10 + isp 30 + color "ff ff 00" + + weaponpref 012345678 + voice "Darth" +} + +{ + name "Kyle" + aiming 90 + perfection 90 + reaction 70 + isp 60 + color "ff af 3f" + + weaponpref 012345678 + voice "Doom" +} + +{ + name "Louis" + aiming 80 + perfection 50 + reaction 20 + isp 37 + color "40 cf 00" + + weaponpref 012345678 + voice "Duke" +} + +{ + name "Robert" + aiming 90 + perfection 90 + reaction 72 + isp 87 + color "50 50 60" + + weaponpref 012345678 + voice "Elexis" +} + +{ + name "Jean Romeo" + aiming 90 + perfection 50 + reaction 50 + isp 50 + color "8f 00 00" + + weaponpref 082345678 + voice "Homer" +} + +{ + name "Steve" + aiming 80 + perfection 80 + reaction 15 + isp 90 + color "b0 b0 b0" + + weaponpref 012345678 + voice "LoonyMix" +} + +{ + name "Gaz" + aiming 10 + perfection 10 + reaction 23 + isp 32 + color "00 00 7f" + + weaponpref 012345678 + voice "Picard" +} + +{ + name "Josh" + aiming 52 + perfection 35 + reaction 50 + isp 37 + color "8f 00 00" + + weaponpref 012345678 + voice "Protoss" +} + +{ + name "Alex" + aiming 70 + perfection 60 + reaction 10 + isp 50 + color "bf 00 00" + + weaponpref 012345678 + voice "Snide" +} + +{ + name "Patrick" + aiming 100 + perfection 100 + reaction 100 + isp 100 + color "ff 00 00" + skin Crash + gender other + team "blue" + //weaponpref 012385687 +} + +{ + name Sarah + aiming 100 + perfection 100 + reaction 100 + isp 100 + color "ff 00 00" + skin Crash + gender female + team "blue" + //weaponpref 012385687 +} + +{ + name "Sgt. Nigel" + aiming 100 + perfection 100 + reaction 100 + isp 100 + color "b0 b0 b0" + skin "Doom 64 Guy" + team "red" + //weaponpref 012345678 +} + +{ + name Flynn + aiming 100 + perfection 100 + reaction 100 + isp 100 + color "00 ff 00" + skin base + team "blue" + //weaponpref 012345678 +} + +{ + name Richard + aiming 100 + perfection 100 + reaction 100 + isp 100 + color "00 ff 00" + skin "Big fat doomguy" + team "red" + //weaponpref 082345678 +} + +{ + name "Carl" + aiming 100 + perfection 100 + reaction 100 + isp 100 + color brown + skin "Chaingun marine" + team "blue" + //weaponpref 012345678 +} + +{ + name Illucia + aiming 100 + perfection 100 + reaction 100 + isp 100 + color "ff ff 00" + skin Illucia + gender female + team "red" + //weaponpref 012345678 +} + +{ + name NitroActive + aiming 100 + perfection 100 + reaction 100 + isp 100 + color green + skin "base iii" + team "blue" + //weaponpref 012345678 +} + +{ + name Javier + aiming 100 + perfection 100 + reaction 100 + isp 100 + color "00 00 ff" + skin Phobos + team "red" + //weaponpref 012345678 +} + +{ + name Procyon + aiming 100 + perfection 100 + reaction 100 + isp 100 + color "ff ff 00" + skin Procyon + team "blue" + //weaponpref 012345678 +} + +{ + name Seenas + aiming 100 + perfection 100 + reaction 100 + isp 100 + color red + skin Seenas + team "red" + //weaponpref 012345678 +} + +{ + name "Oscar" + aiming 100 + perfection 100 + reaction 100 + isp 100 + color "bf 8f 5f" + skin "Strife Guy" + team "blue" + //weaponpref 012345678 +} + +{ + name Synas + aiming 100 + perfection 100 + reaction 100 + isp 100 + color "00 b0 ff" + skin Synas + team "red" + //weaponpref 012345678 +} + +{ + name Linguica + aiming 100 + perfection 100 + reaction 100 + isp 100 + color red + skin base + team "blue" + //weaponpref 012345678 +} + +{ + name H4X0R + aiming 100 + perfection 100 + reaction 100 + isp 100 + color "8f 01 ff" + skin base + team "red" + //weaponpref 012345678 +} \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f131de3b58..3601c9a129 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -815,8 +815,6 @@ set (PCH_SOURCES playsim/bots/b_bot.cpp playsim/bots/b_func.cpp playsim/bots/b_game.cpp - playsim/bots/b_move.cpp - playsim/bots/b_think.cpp bbannouncer.cpp console/c_cmds.cpp console/c_notifybuffer.cpp diff --git a/src/common/engine/startupinfo.h b/src/common/engine/startupinfo.h index f47fa351f4..c4304b8405 100644 --- a/src/common/engine/startupinfo.h +++ b/src/common/engine/startupinfo.h @@ -13,6 +13,7 @@ struct FStartupInfo int LoadLights = -1; int LoadBrightmaps = -1; int LoadWidescreen = -1; + int LoadBots = -1; int modern = 0; enum { diff --git a/src/d_main.cpp b/src/d_main.cpp index 4f0916a728..f40104ead6 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -288,6 +288,7 @@ CVAR (Bool, disableautoload, false, CVAR_ARCHIVE | CVAR_NOINITCALL | CVAR_GLOBAL CVAR (Bool, autoloadbrightmaps, false, CVAR_ARCHIVE | CVAR_NOINITCALL | CVAR_GLOBALCONFIG) CVAR (Bool, autoloadlights, false, CVAR_ARCHIVE | CVAR_NOINITCALL | CVAR_GLOBALCONFIG) CVAR (Bool, autoloadwidescreen, true, CVAR_ARCHIVE | CVAR_NOINITCALL | CVAR_GLOBALCONFIG) +CVAR (Bool, autoloadbots, true, CVAR_ARCHIVE | CVAR_NOINITCALL | CVAR_GLOBALCONFIG) CVAR (Bool, r_debug_disable_vis_filter, false, 0) CVAR(Bool, vid_fps, false, 0) CVAR(Int, vid_showpalette, 0, 0) @@ -1945,6 +1946,11 @@ static FString ParseGameInfo(TArray &pwads, const char *fn, const char sc.MustGetNumber(); GameStartupInfo.LoadWidescreen = !!sc.Number; } + else if (!nextKey.CompareNoCase("LOADBOTS")) + { + sc.MustGetNumber(); + GameStartupInfo.LoadBots = !!sc.Number; + } else { // Silently ignore unknown properties @@ -2085,6 +2091,12 @@ static void AddAutoloadFiles(const char *autoname) if (wswad) D_AddFile (allwads, wswad, true, -1, GameConfig); } + if (GameStartupInfo.LoadBots == 1 || (GameStartupInfo.LoadBots != 0 && autoloadbots)) + { + const char *wswad = BaseFileSearch ("gzdoom-tdbots.pk3", NULL, true, GameConfig); + if (wswad) + D_AddFile (allwads, wswad, true, -1, GameConfig); + } } if (!(gameinfo.flags & GI_SHAREWARE) && !Args->CheckParm("-noautoload") && !disableautoload) @@ -3742,7 +3754,12 @@ void D_Cleanup() // delete GameStartupInfo data GameStartupInfo.Name = ""; GameStartupInfo.BkColor = GameStartupInfo.FgColor = GameStartupInfo.Type = 0; - GameStartupInfo.LoadWidescreen = GameStartupInfo.LoadLights = GameStartupInfo.LoadBrightmaps = -1; + + // this statement is starting to get a little messy! + GameStartupInfo.LoadBots = + GameStartupInfo.LoadWidescreen = + GameStartupInfo.LoadLights = + GameStartupInfo.LoadBrightmaps = -1; GC::FullGC(); // clean up before taking down the object list. diff --git a/src/gamedata/info.cpp b/src/gamedata/info.cpp index 455a4cb22b..cc262b232a 100644 --- a/src/gamedata/info.cpp +++ b/src/gamedata/info.cpp @@ -55,7 +55,6 @@ #include "texturemanager.h" extern void LoadActors (); -extern void InitBotStuff(); extern void ClearStrifeTypes(); TArray PClassActor::AllActorClasses; @@ -402,7 +401,6 @@ void PClassActor::StaticInit() } LoadAltHudStuff(); - InitBotStuff(); // reinit GLOBAL static stuff from gameinfo, once classes are loaded. staticEventManager.InitStaticHandlers(primaryLevel, false); diff --git a/src/playsim/bots/b_bot.cpp b/src/playsim/bots/b_bot.cpp index 97eb378291..b937ac7d57 100644 --- a/src/playsim/bots/b_bot.cpp +++ b/src/playsim/bots/b_bot.cpp @@ -143,12 +143,6 @@ void DBot::Tick () { return; } - - BotThinkCycles.Clock(); - Level->BotInfo.m_Thinking = true; - Think (); - Level->BotInfo.m_Thinking = false; - BotThinkCycles.Unclock(); } CVAR (Int, bot_next_color, 11, 0) @@ -245,70 +239,3 @@ CCMD (listbots) Printf ("> %d bots\n", count); } -// set the bot specific weapon information -// This is intentionally not in the weapon definition anymore. - -BotInfoMap BotInfo; - -void InitBotStuff() -{ - int lump; - int lastlump = 0; - while (-1 != (lump = fileSystem.FindLump("BOTSUPP", &lastlump))) - { - FScanner sc(lump); - sc.SetCMode(true); - while (sc.GetString()) - { - PClassActor *wcls = PClass::FindActor(sc.String); - if (wcls != nullptr && wcls->IsDescendantOf(NAME_Weapon)) - { - BotInfoData bi = {}; - sc.MustGetStringName(","); - sc.MustGetNumber(); - bi.MoveCombatDist = sc.Number; - while (sc.CheckString(",")) - { - sc.MustGetString(); - if (sc.Compare("BOT_REACTION_SKILL_THING")) - { - bi.flags |= BIF_BOT_REACTION_SKILL_THING; - } - else if (sc.Compare("BOT_EXPLOSIVE")) - { - bi.flags |= BIF_BOT_EXPLOSIVE; - } - else if (sc.Compare("BOT_BFG")) - { - bi.flags |= BIF_BOT_BFG; - } - else - { - PClassActor *cls = PClass::FindActor(sc.String); - bi.projectileType = cls; - if (cls == nullptr) - { - sc.ScriptError("Unknown token %s", sc.String); - } - } - } - BotInfo[wcls->TypeName] = bi; - } - else - { - sc.ScriptError("%s is not a weapon type", sc.String); - } - } - } - - // Fixme: Export these, too. - static const char *warnbotmissiles[] = { "PlasmaBall", "Ripper", "HornRodFX1" }; - for(unsigned i=0;iflags3|=MF3_WARNBOT; - } - } -} diff --git a/src/playsim/bots/b_bot.h b/src/playsim/bots/b_bot.h index 2f214bbd9f..6ea4f9b29a 100644 --- a/src/playsim/bots/b_bot.h +++ b/src/playsim/bots/b_bot.h @@ -128,15 +128,8 @@ public: //(b_func.cpp) void StartTravel (); void FinishTravel (); - bool IsLeader (player_t *player); - void SetBodyAt (FLevelLocals *Level, const DVector3 &pos, int hostnum); - double FakeFire (AActor *source, AActor *dest, ticcmd_t *cmd); - bool SafeCheckPosition (AActor *actor, double x, double y, FCheckPosition &tm); - void BotTick(AActor *mo); //(b_move.cpp) - bool CleanAhead (AActor *thing, double x, double y, ticcmd_t *cmd); - bool IsDangerous (sector_t *sec); TArray getspawned; //Array of bots (their names) which should be spawned when starting a game. @@ -214,25 +207,6 @@ public: DVector2 old; private: - //(b_think.cpp) - void Think (); - void ThinkForMove (ticcmd_t *cmd); - void Set_enemy (); - - //(b_func.cpp) - bool Reachable (AActor *target); - void Dofire (ticcmd_t *cmd); - AActor *Choose_Mate (); - AActor *Find_enemy (); - DAngle FireRox (AActor *enemy, ticcmd_t *cmd); - - //(b_move.cpp) - void Roam (ticcmd_t *cmd); - bool Move (ticcmd_t *cmd); - bool TryWalk (ticcmd_t *cmd); - void NewChaseDir (ticcmd_t *cmd); - void TurnToAng (); - void Pitch (AActor *target); }; diff --git a/src/playsim/bots/b_func.cpp b/src/playsim/bots/b_func.cpp index f9925f5343..779a59fe13 100644 --- a/src/playsim/bots/b_func.cpp +++ b/src/playsim/bots/b_func.cpp @@ -44,539 +44,8 @@ #include "doomtype.h" #include "doomdef.h" #include "doomstat.h" -#include "p_local.h" -#include "p_maputl.h" #include "b_bot.h" -#include "g_game.h" -#include "d_event.h" #include "d_player.h" -#include "p_spec.h" -#include "p_checkposition.h" -#include "actorinlines.h" - -static FRandom pr_botdofire ("BotDoFire"); - - -//Checks TRUE reachability from bot to a looker. -bool DBot::Reachable (AActor *rtarget) -{ - if (player->mo == rtarget) - return false; - - if ((rtarget->Sector->ceilingplane.ZatPoint (rtarget) - - rtarget->Sector->floorplane.ZatPoint (rtarget)) - < player->mo->Height) //Where rtarget is, player->mo can't be. - return false; - - sector_t *last_s = player->mo->Sector; - double last_z = last_s->floorplane.ZatPoint (player->mo); - double estimated_dist = player->mo->Distance2D(rtarget); - bool reachable = true; - - FPathTraverse it(Level, player->mo->X()+player->mo->Vel.X, player->mo->Y()+player->mo->Vel.Y, rtarget->X(), rtarget->Y(), PT_ADDLINES|PT_ADDTHINGS); - intercept_t *in; - while ((in = it.Next())) - { - double hitx, hity; - double frac; - line_t *line; - AActor *thing; - double dist; - sector_t *s; - - frac = in->frac - 4 /MAX_TRAVERSE_DIST; - dist = frac * MAX_TRAVERSE_DIST; - - hitx = it.Trace().x + player->mo->Vel.X * frac; - hity = it.Trace().y + player->mo->Vel.Y * frac; - - if (in->isaline) - { - line = in->d.line; - - if (!(line->flags & ML_TWOSIDED) || (line->flags & (ML_BLOCKING|ML_BLOCKEVERYTHING|ML_BLOCK_PLAYERS))) - { - return false; //Cannot continue. - } - else - { - //Determine if going to use backsector/frontsector. - s = (line->backsector == last_s) ? line->frontsector : line->backsector; - double ceilingheight = s->ceilingplane.ZatPoint (hitx, hity); - double floorheight = s->floorplane.ZatPoint (hitx, hity); - - if (!Level->BotInfo.IsDangerous (s) && //Any nukage/lava? - (floorheight <= (last_z+MAXMOVEHEIGHT) - && ((ceilingheight == floorheight && line->special) - || (ceilingheight - floorheight) >= player->mo->Height))) //Does it fit? - { - last_z = floorheight; - last_s = s; - continue; - } - else - { - return false; - } - } - } - - if (dist > estimated_dist) - { - return true; - } - - thing = in->d.thing; - if (thing == player->mo) //Can't reach self in this case. - continue; - if (thing == rtarget && (rtarget->Sector->floorplane.ZatPoint (rtarget) <= (last_z+MAXMOVEHEIGHT))) - { - return true; - } - - reachable = false; - } - return reachable; -} - -//doesnt check LOS, checks visibility with a set view angle. -//B_Checksight checks LOS (straight line) -//---------------------------------------------------------------------- -//Check if mo1 has free line to mo2 -//and if mo2 is within mo1 viewangle (vangle) given with normal degrees. -//if these conditions are true, the function returns true. -//GOOD TO KNOW is that the player's view angle -//in doom is 90 degrees infront. -bool DBot::Check_LOS (AActor *to, DAngle vangle) -{ - if (!P_CheckSight (player->mo, to, SF_SEEPASTBLOCKEVERYTHING)) - return false; // out of sight - if (vangle >= 360.) - return true; - if (vangle == 0) - return false; //Looker seems to be blind. - - return absangle(player->mo->AngleTo(to), player->mo->Angles.Yaw) <= (vangle/2); -} - -//------------------------------------- -//Bot_Dofire() -//------------------------------------- -//The bot will check if it's time to fire -//and do so if that is the case. -void DBot::Dofire (ticcmd_t *cmd) -{ - bool no_fire; //used to prevent bot from pumping rockets into nearby walls. - int aiming_penalty=0; //For shooting at shading target, if screen is red, MAKEME: When screen red. - int aiming_value; //The final aiming value. - double Dist; - DAngle an; - DAngle m; - double fm; - - if (!enemy || !(enemy->flags & MF_SHOOTABLE) || enemy->health <= 0) - return; - - if (player->ReadyWeapon == NULL) - return; - - if (player->damagecount > skill.isp) - { - first_shot = true; - return; - } - - //Reaction skill thing. - if (first_shot && - !(GetBotInfo(player->ReadyWeapon).flags & BIF_BOT_REACTION_SKILL_THING)) - { - t_react = (100-skill.reaction+1)/((pr_botdofire()%3)+3); - } - first_shot = false; - if (t_react) - return; - - //MAKEME: Decrease the rocket suicides even more. - - no_fire = true; - //Distance to enemy. - Dist = player->mo->Distance2D(enemy, player->mo->Vel.X - enemy->Vel.X, player->mo->Vel.Y - enemy->Vel.Y); - - //FIRE EACH TYPE OF WEAPON DIFFERENT: Here should all the different weapons go. - if (GetBotInfo(player->ReadyWeapon).MoveCombatDist == 0) - { - //*4 is for atmosphere, the chainsaws sounding and all.. - no_fire = (Dist > DEFMELEERANGE*4); - } - else if (GetBotInfo(player->ReadyWeapon).flags & BIF_BOT_BFG) - { - //MAKEME: This should be smarter. - if ((pr_botdofire()%200)<=skill.reaction) - if(Check_LOS(enemy, SHOOTFOV)) - no_fire = false; - } - else if (GetBotInfo(player->ReadyWeapon).projectileType != NULL) - { - if (GetBotInfo(player->ReadyWeapon).flags & BIF_BOT_EXPLOSIVE) - { - //Special rules for RL - an = FireRox (enemy, cmd); - if(an != 0) - { - Angle = an; - //have to be somewhat precise. to avoid suicide. - if (absangle(an, player->mo->Angles.Yaw) < 12.) - { - t_rocket = 9; - no_fire = false; - } - } - } - // prediction aiming - Dist = player->mo->Distance2D(enemy); - fm = Dist / GetDefaultByType (GetBotInfo(player->ReadyWeapon).projectileType)->Speed; - Level->BotInfo.SetBodyAt(Level, enemy->Pos() + enemy->Vel.XY() * fm * 2, 1); - Angle = player->mo->AngleTo(Level->BotInfo.body1); - if (Check_LOS (enemy, SHOOTFOV)) - no_fire = false; - } - else - { - //Other weapons, mostly instant hit stuff. - Angle = player->mo->AngleTo(enemy); - aiming_penalty = 0; - if (enemy->flags & MF_SHADOW) - aiming_penalty += (pr_botdofire()%25)+10; - if (enemy->Sector->lightlevelpowers & PW_INFRARED)*/) - aiming_penalty += pr_botdofire()%40;//Dark - if (player->damagecount) - aiming_penalty += player->damagecount; //Blood in face makes it hard to aim - aiming_value = skill.aiming - aiming_penalty; - if (aiming_value <= 0) - aiming_value = 1; - m = ((SHOOTFOV/2)-(aiming_value*SHOOTFOV/200)); //Higher skill is more accurate - if (m <= 0) - m = 1.; //Prevents lock. - - if (m != 0) - { - if (increase) - Angle += m; - else - Angle -= m; - } - - if (absangle(Angle, player->mo->Angles.Yaw) < 4.) - { - increase = !increase; - } - - if (Check_LOS (enemy, (SHOOTFOV/2))) - no_fire = false; - } - if (!no_fire) //If going to fire weapon - { - cmd->ucmd.buttons |= BT_ATTACK; - } - //Prevents bot from jerking, when firing automatic things with low skill. -} - -bool FCajunMaster::IsLeader (player_t *player) -{ - for (int count = 0; count < MAXPLAYERS; count++) - { - if (players[count].Bot != NULL - && players[count].Bot->mate == player->mo) - { - return true; - } - } - - return false; -} - -extern int BotWTG; - -void FCajunMaster::BotTick(AActor *mo) -{ - BotSupportCycles.Clock(); - m_Thinking = true; - for (int i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].Bot == NULL) - continue; - - if (mo->flags3 & MF3_ISMONSTER) - { - if (mo->health > 0 - && !players[i].Bot->enemy - && mo->player ? !mo->IsTeammate(players[i].mo) : true - && mo->Distance2D(players[i].mo) < MAX_MONSTER_TARGET_DIST - && P_CheckSight(players[i].mo, mo, SF_SEEPASTBLOCKEVERYTHING)) - { //Probably a monster, so go kill it. - players[i].Bot->enemy = mo; - } - } - else if (mo->flags & MF_SPECIAL) - { //Item pickup time - //clock (BotWTG); - players[i].Bot->WhatToGet(mo); - //unclock (BotWTG); - BotWTG++; - } - else if (mo->flags & MF_MISSILE) - { - if (!players[i].Bot->missile && (mo->flags3 & MF3_WARNBOT)) - { //warn for incoming missiles. - if (mo->target != players[i].mo && players[i].Bot->Check_LOS(mo, 90.)) - players[i].Bot->missile = mo; - } - } - } - m_Thinking = false; - BotSupportCycles.Unclock(); -} - -//This function is called every -//tick (for each bot) to set -//the mate (teammate coop mate). -AActor *DBot::Choose_Mate () -{ - int count; - double closest_dist, test; - AActor *target; - - //is mate alive? - if (mate) - { - if (mate->health <= 0) - mate = nullptr; - else - last_mate = mate; - } - if (mate) //Still is.. - return mate; - - //Check old_mates status. - if (last_mate) - if (last_mate->health <= 0) - last_mate = nullptr; - - target = NULL; - closest_dist = FLT_MAX; - - //Check for player friends - for (count = 0; count < MAXPLAYERS; count++) - { - player_t *client = &players[count]; - - if (playeringame[count] - && client->mo - && player->mo != client->mo - && (player->mo->IsTeammate (client->mo) || !deathmatch) - && client->mo->health > 0 - && ((player->mo->health/2) <= client->mo->health || !deathmatch) - && !Level->BotInfo.IsLeader(client)) //taken? - { - if (P_CheckSight (player->mo, client->mo, SF_IGNOREVISIBILITY)) - { - test = client->mo->Distance2D(player->mo); - - if (test < closest_dist) - { - closest_dist = test; - target = client->mo; - } - } - } - } - -/* - //Make a introducing to mate. - if(target && target!=last_mate) - { - if((P_Random()%(200*Level->BotInfo.botnum))<3) - { - chat = c_teamup; - if(target->bot) - strcpy(c_target, botsingame[target->bot_id]); - else if(target->player) - strcpy(c_target, player_names[target->play_id]); - } - } -*/ - - return target; - -} - -//MAKEME: Make this a smart decision -AActor *DBot::Find_enemy () -{ - int count; - double closest_dist, temp; //To target. - AActor *target; - DAngle vangle; - - if (!deathmatch) - { // [RH] Take advantage of the Heretic/Hexen code to be a little smarter - return P_RoughMonsterSearch (player->mo, 20); - } - - //Note: It's hard to ambush a bot who is not alone - if (allround || mate) - vangle = 360.; - else - vangle = ENEMY_SCAN_FOV; - allround = false; - - target = NULL; - closest_dist = FLT_MAX; - - for (count = 0; count < MAXPLAYERS; count++) - { - player_t *client = &players[count]; - if (playeringame[count] - && !player->mo->IsTeammate (client->mo) - && client->mo->health > 0 - && player->mo != client->mo) - { - if (Check_LOS (client->mo, vangle)) //Here's a strange one, when bot is standing still, the P_CheckSight within Check_LOS almost always returns false. tought it should be the same checksight as below but.. (below works) something must be fuckin wierd screded up. - //if(P_CheckSight(player->mo, players[count].mo)) - { - temp = client->mo->Distance2D(player->mo); - - //Too dark? - if (temp > DARK_DIST && - client->mo->Sector->lightlevel < WHATS_DARK /*&& - player->Powers & PW_INFRARED*/) - continue; - - if (temp < closest_dist) - { - closest_dist = temp; - target = client->mo; - } - } - } - } - - return target; -} - - - -//Creates a temporary mobj (invisible) at the given location. -void FCajunMaster::SetBodyAt (FLevelLocals *Level, const DVector3 &pos, int hostnum) -{ - if (hostnum == 1) - { - if (body1) - { - body1->SetOrigin (pos, false); - } - else - { - body1 = Spawn (Level, "CajunBodyNode", pos, NO_REPLACE); - } - } - else if (hostnum == 2) - { - if (body2) - { - body2->SetOrigin (pos, false); - } - else - { - body2 = Spawn (Level, "CajunBodyNode", pos, NO_REPLACE); - } - } -} - -//------------------------------------------ -// FireRox() -// -//Returns NULL if shouldn't fire -//else an angle (in degrees) are given -//This function assumes actor->player->angle -//has been set an is the main aiming angle. - - -//Emulates missile travel. Returns distance travelled. -double FCajunMaster::FakeFire (AActor *source, AActor *dest, ticcmd_t *cmd) -{ - AActor *th = Spawn (source->Level, "CajunTrace", source->PosPlusZ(4*8.), NO_REPLACE); - - th->target = source; // where it came from - - - th->Vel = source->Vec3To(dest).Resized(th->Speed); - - double dist = 0; - - while (dist < SAFE_SELF_MISDIST) - { - dist += th->Speed; - th->Move(th->Vel); - if (!CleanAhead (th, th->X(), th->Y(), cmd)) - break; - } - th->Destroy (); - return dist; -} - -DAngle DBot::FireRox (AActor *enemy, ticcmd_t *cmd) -{ - double dist; - AActor *actor; - double m; - - Level->BotInfo.SetBodyAt(Level, player->mo->PosPlusZ(player->mo->Height / 2) + player->mo->Vel.XY() * 5, 2); - - actor = Level->BotInfo.body2; - - dist = actor->Distance2D (enemy); - if (dist < SAFE_SELF_MISDIST) - return 0.; - //Predict. - m = ((dist+1) / GetDefaultByName("Rocket")->Speed); - - Level->BotInfo.SetBodyAt(Level, DVector3((enemy->Pos() + enemy->Vel * (m + 2)), ONFLOORZ), 1); - - //try the predicted location - if (P_CheckSight (actor, Level->BotInfo.body1, SF_IGNOREVISIBILITY)) //See the predicted location, so give a test missile - { - FCheckPosition tm; - if (Level->BotInfo.SafeCheckPosition (player->mo, actor->X(), actor->Y(), tm)) - { - if (Level->BotInfo.FakeFire (actor, Level->BotInfo.body1, cmd) >= SAFE_SELF_MISDIST) - { - return actor->AngleTo(Level->BotInfo.body1); - } - } - } - //Try fire straight. - if (P_CheckSight (actor, enemy, 0)) - { - if (Level->BotInfo.FakeFire (player->mo, enemy, cmd) >= SAFE_SELF_MISDIST) - { - return player->mo->AngleTo(enemy); - } - } - return 0.; -} - -// [RH] We absolutely do not want to pick things up here. The bot code is -// executed apart from all the other simulation code, so we don't want it -// creating side-effects during gameplay. -bool FCajunMaster::SafeCheckPosition (AActor *actor, double x, double y, FCheckPosition &tm) -{ - ActorFlags savedFlags = actor->flags; - actor->flags &= ~MF_PICKUP; - bool res = P_CheckPosition (actor, DVector2(x, y), tm); - actor->flags = savedFlags; - return res; -} void FCajunMaster::StartTravel () { diff --git a/src/playsim/bots/b_game.cpp b/src/playsim/bots/b_game.cpp index a43c83ce03..f490ef8eeb 100644 --- a/src/playsim/bots/b_game.cpp +++ b/src/playsim/bots/b_game.cpp @@ -476,11 +476,11 @@ void FCajunMaster::ForgetBots () #if defined _WIN32 || defined __APPLE__ -FString M_GetCajunPath(const char* botfilename) +FString M_GetBotPath(const char* botfilename) { FString path; - path << progdir << "zcajun/" << botfilename; + path << progdir << botfilename; if (!FileExists(path)) { path = ""; @@ -490,7 +490,7 @@ FString M_GetCajunPath(const char* botfilename) #else -FString M_GetCajunPath(const char* botfilename) +FString M_GetBotPath(const char* botfilename) { FString path; @@ -519,7 +519,7 @@ bool FCajunMaster::LoadBots () int loaded_bots = 0; ForgetBots (); - tmp = M_GetCajunPath(BOTFILENAME); + tmp = M_GetBotPath(BOTFILENAME); if (tmp.IsEmpty()) { DPrintf (DMSG_ERROR, "No " BOTFILENAME ", so no bots\n"); diff --git a/src/playsim/bots/b_move.cpp b/src/playsim/bots/b_move.cpp deleted file mode 100644 index a22867af14..0000000000 --- a/src/playsim/bots/b_move.cpp +++ /dev/null @@ -1,390 +0,0 @@ -/* -** -** -**--------------------------------------------------------------------------- -** Copyright 1999 Martin Colberg -** Copyright 1999-2016 Randy Heit -** Copyright 2005-2016 Christoph Oelckers -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions -** are met: -** -** 1. Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** 2. Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** 3. The name of the author may not be used to endorse or promote products -** derived from this software without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -**--------------------------------------------------------------------------- -** -*/ -/******************************** -* B_Think.c * -* Description: * -* Movement/Roaming code for * -* the bot's * -*********************************/ - -#include "doomdef.h" -#include "doomstat.h" -#include "p_local.h" -#include "b_bot.h" -#include "g_game.h" -#include "p_lnspec.h" -#include "a_keys.h" -#include "d_event.h" -#include "p_enemy.h" -#include "d_player.h" -#include "p_spec.h" -#include "p_checkposition.h" -#include "actorinlines.h" - -static FRandom pr_botopendoor ("BotOpenDoor"); -static FRandom pr_bottrywalk ("BotTryWalk"); -static FRandom pr_botnewchasedir ("BotNewChaseDir"); - -// borrow some tables from p_enemy.cpp -extern dirtype_t opposite[9]; -extern dirtype_t diags[4]; - -//Called while the bot moves after its dest mobj -//which can be a weapon/enemy/item whatever. -void DBot::Roam (ticcmd_t *cmd) -{ - - if (Reachable(dest)) - { // Straight towards it. - Angle = player->mo->AngleTo(dest); - } - else if (player->mo->movedir < 8) // turn towards movement direction if not there yet - { - // no point doing this with floating point angles... - unsigned angle = Angle.BAMs() & (unsigned)(7 << 29); - int delta = angle - (player->mo->movedir << 29); - - if (delta > 0) - Angle -= 45; - else if (delta < 0) - Angle += 45; - } - - // chase towards destination. - if (--player->mo->movecount < 0 || !Move (cmd)) - { - NewChaseDir (cmd); - } -} - -bool DBot::Move (ticcmd_t *cmd) -{ - double tryx, tryy; - bool try_ok; - int good; - - if (player->mo->movedir >= DI_NODIR) - { - player->mo->movedir = DI_NODIR; // make sure it's valid. - return false; - } - - tryx = player->mo->X() + 8*xspeed[player->mo->movedir]; - tryy = player->mo->Y() + 8*yspeed[player->mo->movedir]; - - try_ok = Level->BotInfo.CleanAhead (player->mo, tryx, tryy, cmd); - - if (!try_ok) //Anything blocking that could be opened etc.. - { - if (!spechit.Size ()) - return false; - - player->mo->movedir = DI_NODIR; - - good = 0; - spechit_t spechit1; - line_t *ld; - - while (spechit.Pop (spechit1)) - { - ld = spechit1.line; - bool tryit = true; - - if (ld->special == Door_LockedRaise && !P_CheckKeys (player->mo, ld->args[3], false)) - tryit = false; - else if (ld->special == Generic_Door && !P_CheckKeys (player->mo, ld->args[4], false)) - tryit = false; - - if (tryit && - (P_TestActivateLine (ld, player->mo, 0, SPAC_Use) || - P_TestActivateLine (ld, player->mo, 0, SPAC_Push))) - { - good |= ld == player->mo->BlockingLine ? 1 : 2; - } - } - if (good && ((pr_botopendoor() >= 203) ^ (good & 1))) - { - cmd->ucmd.buttons |= BT_USE; - cmd->ucmd.forwardmove = FORWARDRUN; - return true; - } - else - return false; - } - else //Move forward. - cmd->ucmd.forwardmove = FORWARDRUN; - - return true; -} - -bool DBot::TryWalk (ticcmd_t *cmd) -{ - if (!Move (cmd)) - return false; - - player->mo->movecount = pr_bottrywalk() & 60; - return true; -} - -void DBot::NewChaseDir (ticcmd_t *cmd) -{ - dirtype_t d[3]; - - int tdir; - dirtype_t olddir; - - dirtype_t turnaround; - - if (!dest) - { -#ifndef BOT_RELEASE_COMPILE - Printf ("Bot tried move without destination\n"); -#endif - return; - } - - olddir = (dirtype_t)player->mo->movedir; - turnaround = opposite[olddir]; - - DVector2 delta = player->mo->Vec2To(dest); - - if (delta.X > 10) - d[1] = DI_EAST; - else if (delta.X < -10) - d[1] = DI_WEST; - else - d[1] = DI_NODIR; - - if (delta.Y < -10) - d[2] = DI_SOUTH; - else if (delta.Y > 10) - d[2] = DI_NORTH; - else - d[2] = DI_NODIR; - - // try direct route - if (d[1] != DI_NODIR && d[2] != DI_NODIR) - { - player->mo->movedir = diags[((delta.Y < 0) << 1) + (delta.X > 0)]; - if (player->mo->movedir != turnaround && TryWalk(cmd)) - return; - } - - // try other directions - if (pr_botnewchasedir() > 200 - || fabs(delta.Y) > fabs(delta.X)) - { - tdir = d[1]; - d[1] = d[2]; - d[2] = (dirtype_t)tdir; - } - - if (d[1]==turnaround) - d[1]=DI_NODIR; - if (d[2]==turnaround) - d[2]=DI_NODIR; - - if (d[1]!=DI_NODIR) - { - player->mo->movedir = d[1]; - if (TryWalk (cmd)) - return; - } - - if (d[2]!=DI_NODIR) - { - player->mo->movedir = d[2]; - - if (TryWalk(cmd)) - return; - } - - // there is no direct path to the player, - // so pick another direction. - if (olddir!=DI_NODIR) - { - player->mo->movedir = olddir; - - if (TryWalk(cmd)) - return; - } - - // randomly determine direction of search - if (pr_botnewchasedir()&1) - { - for ( tdir=DI_EAST; - tdir<=DI_SOUTHEAST; - tdir++ ) - { - if (tdir!=turnaround) - { - player->mo->movedir = tdir; - - if (TryWalk(cmd)) - return; - } - } - } - else - { - for ( tdir=DI_SOUTHEAST; - tdir != (DI_EAST-1); - tdir-- ) - { - if (tdir!=turnaround) - { - player->mo->movedir = tdir; - - if (TryWalk(cmd)) - return; - } - } - } - - if (turnaround != DI_NODIR) - { - player->mo->movedir = turnaround; - if (TryWalk(cmd)) - return; - } - - player->mo->movedir = DI_NODIR; // can not move -} - - -// -// B_CleanAhead -// Check if a place is ok to move towards. -// This is also a traverse function for -// bots pre-rocket fire (preventing suicide) -// -bool FCajunMaster::CleanAhead (AActor *thing, double x, double y, ticcmd_t *cmd) -{ - FCheckPosition tm; - - if (!SafeCheckPosition (thing, x, y, tm)) - return false; // solid wall or thing - - if (!(thing->flags & MF_NOCLIP) ) - { - if (tm.ceilingz - tm.floorz < thing->Height) - return false; // doesn't fit - - double maxmove = MAXMOVEHEIGHT; - if (!(thing->flags&MF_MISSILE)) - { - if(tm.floorz > (thing->Sector->floorplane.ZatPoint(x, y)+maxmove)) //Too high wall - return false; - - //Jumpable - if(tm.floorz > (thing->Sector->floorplane.ZatPoint(x, y)+thing->MaxStepHeight)) - cmd->ucmd.buttons |= BT_JUMP; - - - if ( !(thing->flags & MF_TELEPORT) && - tm.ceilingz < thing->Top()) - return false; // mobj must lower itself to fit - - // jump out of water -// if((thing->eflags & (MF_UNDERWATER|MF_TOUCHWATER))==(MF_UNDERWATER|MF_TOUCHWATER)) -// maxstep=37; - - if ( !(thing->flags & MF_TELEPORT) && - (tm.floorz - thing->Z() > thing->MaxStepHeight) ) - return false; // too big a step up - - - if ( !(thing->flags&(MF_DROPOFF|MF_FLOAT)) - && tm.floorz - tm.dropoffz > thing->MaxDropOffHeight ) - return false; // don't stand over a dropoff - - } - } - return true; -} - -#define OKAYRANGE (5) //counts *2, when angle is in range, turning is not executed. -#define MAXTURN (15) //Max degrees turned in one tic. Lower is smother but may cause the bot not getting where it should = crash -#define TURNSENS 3 //Higher is smoother but slower turn. - -void DBot::TurnToAng () -{ - double maxturn = MAXTURN; - - if (player->ReadyWeapon != NULL) - { - if (GetBotInfo(player->ReadyWeapon).flags & BIF_BOT_EXPLOSIVE) - { - if (t_roam && !missile) - { //Keep angle that where when shot where decided. - return; - } - } - - - if(enemy) - if(!dest) //happens when running after item in combat situations, or normal, prevents weak turns - if(GetBotInfo(player->ReadyWeapon).projectileType == NULL && GetBotInfo(player->ReadyWeapon).MoveCombatDist > 0) - if(Check_LOS(enemy, SHOOTFOV+5)) - maxturn = 3; - } - - DAngle distance = deltaangle(player->mo->Angles.Yaw, Angle); - - if (fabs (distance) < OKAYRANGE && !enemy) - return; - - distance /= TURNSENS; - if (fabs (distance) > maxturn) - distance = distance < 0 ? -maxturn : maxturn; - - player->mo->Angles.Yaw += distance; -} - -void DBot::Pitch (AActor *target) -{ - double aim; - double diff; - - diff = target->Z() - player->mo->Z(); - aim = g_atan(diff / player->mo->Distance2D(target)); - player->mo->Angles.Pitch = DAngle::ToDegrees(aim); -} - -//Checks if a sector is dangerous. -bool FCajunMaster::IsDangerous (sector_t *sec) -{ - return sec->damageamount > 0; -} diff --git a/src/playsim/bots/b_think.cpp b/src/playsim/bots/b_think.cpp deleted file mode 100644 index 0d9f266474..0000000000 --- a/src/playsim/bots/b_think.cpp +++ /dev/null @@ -1,439 +0,0 @@ -/* -** -** -**--------------------------------------------------------------------------- -** Copyright 1999 Martin Colberg -** Copyright 1999-2016 Randy Heit -** Copyright 2005-2016 Christoph Oelckers -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions -** are met: -** -** 1. Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** 2. Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** 3. The name of the author may not be used to endorse or promote products -** derived from this software without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -**--------------------------------------------------------------------------- -** -*/ -/******************************** -* B_Think.c * -* Description: * -* Functions for the different * -* states that the bot * -* uses. These functions are * -* the main AI * -* * -*********************************/ - -#include "doomdef.h" -#include "doomstat.h" -#include "p_local.h" -#include "b_bot.h" -#include "g_game.h" -#include "d_net.h" -#include "d_event.h" -#include "d_player.h" -#include "actorinlines.h" - -static FRandom pr_botmove ("BotMove"); - -//This function is called each tic for each bot, -//so this is what the bot does. -void DBot::Think () -{ - ticcmd_t *cmd = &netcmds[player - players][((gametic + 1)/ticdup)%BACKUPTICS]; - - memset (cmd, 0, sizeof(*cmd)); - - if (enemy && enemy->health <= 0) - enemy = nullptr; - - if (player->mo->health > 0) //Still alive - { - if (teamplay || !deathmatch) - mate = Choose_Mate (); - - AActor *actor = player->mo; - DAngle oldyaw = actor->Angles.Yaw; - DAngle oldpitch = actor->Angles.Pitch; - - Set_enemy (); - ThinkForMove (cmd); - TurnToAng (); - - cmd->ucmd.yaw = (short)((actor->Angles.Yaw - oldyaw).Degrees * (65536 / 360.f)) / ticdup; - cmd->ucmd.pitch = (short)((oldpitch - actor->Angles.Pitch).Degrees * (65536 / 360.f)); - if (cmd->ucmd.pitch == -32768) - cmd->ucmd.pitch = -32767; - cmd->ucmd.pitch /= ticdup; - actor->Angles.Yaw = oldyaw + DAngle(cmd->ucmd.yaw * ticdup * (360 / 65536.f)); - actor->Angles.Pitch = oldpitch - DAngle(cmd->ucmd.pitch * ticdup * (360 / 65536.f)); - } - - if (t_active) t_active--; - if (t_strafe) t_strafe--; - if (t_react) t_react--; - if (t_fight) t_fight--; - if (t_rocket) t_rocket--; - if (t_roam) t_roam--; - - //Respawn ticker - if (t_respawn) - { - t_respawn--; - } - else if (player->mo->health <= 0) - { // Time to respawn - cmd->ucmd.buttons |= BT_USE; - } -} - -#define THINKDISTSQ (50000.*50000./(65536.*65536.)) -//how the bot moves. -//MAIN movement function. -void DBot::ThinkForMove (ticcmd_t *cmd) -{ - double dist; - bool stuck; - int r; - - stuck = false; - dist = dest ? player->mo->Distance2D(dest) : 0; - - if (missile && - (!missile->Vel.X || !missile->Vel.Y || !Check_LOS(missile, SHOOTFOV*3/2))) - { - sleft = !sleft; - missile = nullptr; //Probably ended its travel. - } - -#if 0 // this has always been broken and without any reference it cannot be fixed. - if (player->mo->Angles.Pitch > 0) - player->mo->Angles.Pitch -= 80; - else if (player->mo->Angles.Pitch <= -60) - player->mo->Angles.Pitch += 80; -#endif - - //HOW TO MOVE: - if (missile && (player->mo->Distance2D(missile)mo->AngleTo(missile); - cmd->ucmd.sidemove = sleft ? -SIDERUN : SIDERUN; - cmd->ucmd.forwardmove = -FORWARDRUN; //Back IS best. - - if ((player->mo->Pos() - old).LengthSquared() < THINKDISTSQ - && t_strafe<=0) - { - t_strafe = 5; - sleft = !sleft; - } - - //If able to see enemy while avoiding missile, still fire at enemy. - if (enemy && Check_LOS (enemy, SHOOTFOV)) - Dofire (cmd); //Order bot to fire current weapon - } - else if (enemy && P_CheckSight (player->mo, enemy, 0)) //Fight! - { - Pitch (enemy); - - //Check if it's more important to get an item than fight. - if (dest && (dest->flags&MF_SPECIAL)) //Must be an item, that is close enough. - { -#define is(x) dest->IsKindOf (PClass::FindClass (#x)) - if ( - ( - (player->mo->health < skill.isp && - (is (Medikit) || - is (Stimpack) || - is (Soulsphere) || - is (Megasphere) || - is (CrystalVial) - ) - ) || ( - is (Invulnerability) || - is (Invisibility) || - is (Megasphere) - ) || - dist < (GETINCOMBAT/4) || - (GetBotInfo(player->ReadyWeapon).MoveCombatDist == 0) - ) - && (dist < GETINCOMBAT || (GetBotInfo(player->ReadyWeapon).MoveCombatDist == 0)) - && Reachable (dest)) -#undef is - { - goto roam; //Pick it up, no matter the situation. All bonuses are nice close up. - } - } - - dest = nullptr; //To let bot turn right - - if (GetBotInfo(player->ReadyWeapon).MoveCombatDist == 0) - player->mo->flags &= ~MF_DROPOFF; //Don't jump off any ledges when fighting. - - if (!(enemy->flags3 & MF3_ISMONSTER)) - t_fight = AFTERTICS; - - if (t_strafe <= 0 && - ((player->mo->Pos() - old).LengthSquared() < THINKDISTSQ - || ((pr_botmove()%30)==10)) - ) - { - stuck = true; - t_strafe = 5; - sleft = !sleft; - } - - Angle = player->mo->AngleTo(enemy); - - if (player->ReadyWeapon == NULL || - player->mo->Distance2D(enemy) > - GetBotInfo(player->ReadyWeapon).MoveCombatDist) - { - // If a monster, use lower speed (just for cooler apperance while strafing down doomed monster) - cmd->ucmd.forwardmove = (enemy->flags3 & MF3_ISMONSTER) ? FORWARDWALK : FORWARDRUN; - } - else if (!stuck) //Too close, so move away. - { - // If a monster, use lower speed (just for cooler apperance while strafing down doomed monster) - cmd->ucmd.forwardmove = (enemy->flags3 & MF3_ISMONSTER) ? -FORWARDWALK : -FORWARDRUN; - } - - //Strafing. - if (enemy->flags3 & MF3_ISMONSTER) //It's just a monster so take it down cool. - { - cmd->ucmd.sidemove = sleft ? -SIDEWALK : SIDEWALK; - } - else - { - cmd->ucmd.sidemove = sleft ? -SIDERUN : SIDERUN; - } - Dofire (cmd); //Order bot to fire current weapon - } - else if (mate && !enemy && (!dest || dest==mate)) //Follow mate move. - { - double matedist; - - Pitch (mate); - - if (!Reachable (mate)) - { - if (mate == dest && pr_botmove.Random() < 32) - { // [RH] If the mate is the dest, pick a new dest sometimes - dest = nullptr; - } - goto roam; - } - - Angle = player->mo->AngleTo(mate); - - matedist = player->mo->Distance2D(mate); - if (matedist > (FRIEND_DIST*2)) - cmd->ucmd.forwardmove = FORWARDRUN; - else if (matedist > FRIEND_DIST) - cmd->ucmd.forwardmove = FORWARDWALK; //Walk, when starting to get close. - else if (matedist < FRIEND_DIST-(FRIEND_DIST/3)) //Got too close, so move away. - cmd->ucmd.forwardmove = -FORWARDWALK; - } - else //Roam after something. - { - first_shot = true; - - ///// - roam: - ///// - if (enemy && Check_LOS (enemy, SHOOTFOV*3/2)) //If able to see enemy while avoiding missile , still fire at it. - Dofire (cmd); //Order bot to fire current weapon - - if (dest && !(dest->flags&MF_SPECIAL) && dest->health < 0) - { //Roaming after something dead. - dest = nullptr; - } - - if (dest == NULL) - { - if (t_fight && enemy) //Enemy/bot has jumped around corner. So what to do? - { - if (enemy->player) - { - if (((enemy->player->ReadyWeapon != NULL && GetBotInfo(enemy->player->ReadyWeapon).flags & BIF_BOT_EXPLOSIVE) || - (pr_botmove()%100)>skill.isp) && (GetBotInfo(player->ReadyWeapon).MoveCombatDist != 0)) - dest = enemy;//Dont let enemy kill the bot by supressive fire. So charge enemy. - else //hide while t_fight, but keep view at enemy. - Angle = player->mo->AngleTo(enemy); - } //Just a monster, so kill it. - else - dest = enemy; - - //VerifFavoritWeapon(player); //Dont know why here.., but it must be here, i know the reason, but not why at this spot, uh. - } - else //Choose a distant target. to get things going. - { - r = pr_botmove(); - if (r < 128) - { - auto it = player->mo->Level->GetThinkerIterator(NAME_Inventory, MAX_STATNUM+1, Level->BotInfo.firstthing); - auto item = it.Next(); - - if (item != NULL || (item = it.Next()) != NULL) - { - r &= 63; // Only scan up to 64 entries at a time - while (r) - { - --r; - item = it.Next(); - } - if (item == NULL) - { - item = it.Next(); - } - Level->BotInfo.firstthing = item; - dest = item; - } - } - else if (mate && (r < 179 || P_CheckSight(player->mo, mate))) - { - dest = mate; - } - else if ((playeringame[(r&(MAXPLAYERS-1))]) && players[(r&(MAXPLAYERS-1))].mo->health > 0) - { - dest = players[(r&(MAXPLAYERS-1))].mo; - } - } - - if (dest) - { - t_roam = MAXROAM; - } - } - if (dest) - { //Bot has a target so roam after it. - Roam (cmd); - } - - } //End of movement main part. - - if (!t_roam && dest) - { - prev = dest; - dest = nullptr; - } - - if (t_fight<(AFTERTICS/2)) - player->mo->flags |= MF_DROPOFF; - - old = player->mo->Pos(); -} - -int P_GetRealMaxHealth(AActor *actor, int max); - -//BOT_WhatToGet -// -//Determines if the bot will roam after an item or not. -void DBot::WhatToGet (AActor *item) -{ - if ((item->renderflags & RF_INVISIBLE) //Under respawn and away. - || item == prev) - { - return; - } - int weapgiveammo = (alwaysapplydmflags || deathmatch) && !(dmflags & DF_WEAPONS_STAY); - - if (item->IsKindOf(NAME_Weapon)) - { - // FIXME - auto heldWeapon = player->mo->FindInventory(item->GetClass()); - if (heldWeapon != NULL) - { - if (!weapgiveammo) - return; - auto ammo1 = heldWeapon->PointerVar(NAME_Ammo1); - auto ammo2 = heldWeapon->PointerVar(NAME_Ammo2); - if ((ammo1 == NULL || ammo1->IntVar(NAME_Amount) >= ammo1->IntVar(NAME_MaxAmount)) && - (ammo2 == NULL || ammo2->IntVar(NAME_Amount) >= ammo2->IntVar(NAME_MaxAmount))) - { - return; - } - } - } - else if (item->IsKindOf (PClass::FindActor(NAME_Ammo))) - { - auto ac = PClass::FindActor(NAME_Ammo); - auto parent = item->GetClass(); - while (parent->ParentClass != ac) parent = static_cast(parent->ParentClass); - AActor *holdingammo = player->mo->FindInventory(parent); - if (holdingammo != NULL && holdingammo->IntVar(NAME_Amount) >= holdingammo->IntVar(NAME_MaxAmount)) - { - return; - } - } - else if (item->GetClass()->TypeName == NAME_Megasphere || item->IsKindOf(NAME_Health)) - { - // do the test with the health item that's actually given. - AActor* const testItem = item->GetClass()->TypeName == NAME_Megasphere - ? GetDefaultByName(NAME_MegasphereHealth) - : item; - if (nullptr != testItem) - { - const int maxhealth = P_GetRealMaxHealth(player->mo, testItem->IntVar(NAME_MaxAmount)); - if (player->mo->health >= maxhealth) - return; - } - } - - if ((dest == NULL || - !(dest->flags & MF_SPECIAL)/* || - !Reachable (dest)*/)/* && - Reachable (item)*/) // Calling Reachable slows this down tremendously - { - prev = dest; - dest = item; - t_roam = MAXROAM; - } -} - -void DBot::Set_enemy () -{ - AActor *oldenemy; - - if (enemy - && enemy->health > 0 - && P_CheckSight (player->mo, enemy)) - { - oldenemy = enemy; - } - else - { - oldenemy = NULL; - } - - // [RH] Don't even bother looking for a different enemy if this is not deathmatch - // and we already have an existing enemy. - if (deathmatch || !enemy) - { - allround = !!enemy; - enemy = Find_enemy(); - if (!enemy) - enemy = oldenemy; //Try go for last (it will be NULL if there wasn't anyone) - } - //Verify that that enemy is really something alive that bot can kill. - if (enemy && ((enemy->health < 0 || !(enemy->flags&MF_SHOOTABLE)) || player->mo->IsFriend(enemy))) - enemy = nullptr; -} diff --git a/src/playsim/p_map.cpp b/src/playsim/p_map.cpp index 9ba6dd8e1d..dd17e3101b 100644 --- a/src/playsim/p_map.cpp +++ b/src/playsim/p_map.cpp @@ -2374,21 +2374,6 @@ bool P_TryMove(AActor *thing, const DVector2 &pos, thing->flags6 &= ~MF6_INTRYMOVE; return false; } - - //Added by MC: To prevent bot from getting into dangerous sectors. - if (thing->player && thing->player->Bot != NULL && thing->flags & MF_SHOOTABLE) - { - if (tm.sector != thing->Sector - && thing->Level->BotInfo.IsDangerous(tm.sector)) - { - thing->player->Bot->prev = thing->player->Bot->dest; - thing->player->Bot->dest = nullptr; - thing->Vel.X = thing->Vel.Y = 0; - thing->SetZ(oldz); - thing->flags6 &= ~MF6_INTRYMOVE; - return false; - } - } } // [RH] Check status of eyes against fake floor/ceiling in case diff --git a/src/playsim/p_mobj.cpp b/src/playsim/p_mobj.cpp index c889c4e7b6..c8daf7a6be 100644 --- a/src/playsim/p_mobj.cpp +++ b/src/playsim/p_mobj.cpp @@ -3760,12 +3760,6 @@ void AActor::Tick () } } - if (Level->BotInfo.botnum && !demoplayback && - ((flags & (MF_SPECIAL|MF_MISSILE)) || (flags3 & MF3_ISMONSTER))) - { - Level->BotInfo.BotTick(this); - } - // [RH] Consider carrying sectors here DVector2 cumm(0, 0); diff --git a/wadsrc_tdbots/CMakeLists.txt b/wadsrc_tdbots/CMakeLists.txt new file mode 100644 index 0000000000..cce19cce48 --- /dev/null +++ b/wadsrc_tdbots/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required( VERSION 2.8.7 ) + +add_pk3(gzdoom-tdbots.pk3 ${CMAKE_CURRENT_SOURCE_DIR}/static) diff --git a/wadsrc_tdbots/static/BOTINFO.zandro b/wadsrc_tdbots/static/BOTINFO.zandro new file mode 100644 index 0000000000..4a46fdea66 --- /dev/null +++ b/wadsrc_tdbots/static/BOTINFO.zandro @@ -0,0 +1,322 @@ +//These bots don't do anything at all, and that's exactly what i want! +clearbots + +{ + name = "Mact Man" + color = "00 00 00" + gender = "male" + class = "random" + chatfrequency = 0 + revealed = true + script = "" +} + +{ + name = "Slayer" + color = "ff ff ff" + gender = "male" + class = "random" + chatfrequency = 0 + revealed = true + script = "" +} + +{ + name = "Larry" + color = "50 50 60" + gender = "male" + class = "random" + chatfrequency = 0 + revealed = true + script = "" +} + +{ + name = "Karen" + color = "00 00 ff" + gender = "female" + class = "random" + chatfrequency = 0 + revealed = true + script = "" +} + +{ + name = "Joel" + color = "40 cf 00" + gender = "male" + class = "random" + chatfrequency = 0 + revealed = true + script = "" +} + +{ + name = "Gustavo" + color = "ff ff ff" + gender = "male" + class = "random" + chatfrequency = 0 + revealed = true + script = "" +} + +{ + name = "James" + color = "b0 b0 b0" + gender = "male" + class = "random" + chatfrequency = 0 + revealed = true + script = "" +} + +{ + name = "Tyler" + color = "ff ff ff" + gender = "male" + class = "random" + chatfrequency = 0 + revealed = true + script = "" +} + +{ + name = "Thomas" + color = "ff ff 00" + gender = "male" + class = "random" + chatfrequency = 0 + revealed = true + script = "" +} + +{ + name = "Kyle" + color = "ff af 3f" + gender = "male" + class = "random" + chatfrequency = 0 + revealed = true + script = "" +} + +{ + name = "Louis" + color = "40 cf 00" + gender = "male" + class = "random" + chatfrequency = 0 + revealed = true + script = "" +} + +{ + name = "Robert" + color = "50 50 60" + gender = "male" + class = "random" + chatfrequency = 0 + revealed = true + script = "" +} + +{ + name = "Jean Romero" + color = "8f 00 00" + gender = "male" + class = "random" + chatfrequency = 0 + revealed = true + script = "" +} + +{ + name = "Steve" + color = "b0 b0 b0" + gender = "male" + class = "random" + chatfrequency = 0 + revealed = true + script = "" +} + +{ + name = "Gaz" + color = "00 00 7f" + gender = "male" + class = "random" + chatfrequency = 0 + revealed = true + script = "" +} + +{ + name = "Josh" + color = "8f 00 00" + gender = "male" + class = "random" + chatfrequency = 0 + revealed = true + script = "" +} + +{ + name = "Alex" + color = "bf 00 00" + gender = "male" + class = "random" + chatfrequency = 0 + revealed = true + script = "" +} + +{ + name = "Patrick" + color = "ff 00 00" + gender = "male" + class = "random" + chatfrequency = 0 + revealed = true + script = "" +} + +{ + name = "Sarah" + color = "ff 00 00" + gender = "female" + class = "random" + chatfrequency = 0 + revealed = true + script = "" +} + +{ + name = "Sgt. Nigel" + color = "b0 b0 b0" + gender = "male" + class = "random" + chatfrequency = 0 + revealed = true + script = "" +} + +{ + name = "Flynn" + color = "00 ff 00" + gender = "male" + class = "random" + chatfrequency = 0 + revealed = true + script = "" +} + +{ + name = "Richard" + color = "00 ff 00" + gender = "male" + class = "random" + chatfrequency = 0 + revealed = true + script = "" +} + +{ + name = "Carl" + color = "ff ff 00" + gender = "male" + class = "random" + chatfrequency = 0 + revealed = true + script = "" +} + +{ + name = "Illucia" + color = "ff ff 00" + gender = "female" + class = "random" + chatfrequency = 0 + revealed = true + script = "" +} + +{ + name = "Nitroactive" + color = "00 ff 00" + gender = "male" + class = "random" + chatfrequency = 0 + revealed = true + script = "" +} + +{ + name = "Javier" + color = "00 00 ff" + gender = "male" + class = "random" + chatfrequency = 0 + revealed = true + script = "" +} + +{ + name = "Procyon" + color = "ff ff 00" + gender = "male" + class = "random" + chatfrequency = 0 + revealed = true + script = "" +} + +{ + name = "Seenas" + color = "ff 00 00" + gender = "male" + class = "random" + chatfrequency = 0 + revealed = true + script = "" +} + +{ + name = "Oscar" + color = "bf 8f 5f" + gender = "male" + class = "random" + chatfrequency = 0 + revealed = true + script = "" +} + +{ + name = "Synas" + color = "00 b0 ff" + gender = "male" + class = "random" + chatfrequency = 0 + revealed = true + script = "" +} + +{ + name = "Linguica" + color = "ff 00 00" + gender = "male" + class = "random" + chatfrequency = 0 + revealed = true + script = "" +} + +{ + name = "TheLegend27" + color = "8f 01 ff" + gender = "male" + class = "random" + chatfrequency = 0 + revealed = true + script = "" +} \ No newline at end of file diff --git a/wadsrc_tdbots/static/CHANGELOG.txt b/wadsrc_tdbots/static/CHANGELOG.txt new file mode 100644 index 0000000000..e1b4075a30 --- /dev/null +++ b/wadsrc_tdbots/static/CHANGELOG.txt @@ -0,0 +1,29 @@ +This file was made to help me not mess changelogs up as i'm writing down things +here as i make them. + +v27 THE WE'RE ONE UPDATE + +-Merged both the GZDoom and Zandronum version into one, so now Zandronum + players can play using the normal version. + (The bots still work much better in Zandronum vs GZDoom, please use it + if you can) + +-Bots will no longer shoot neither friendly monsters nor players in co-op, and + they also won't shoot players in their own team (in team-based modes). + Yes, for real this time, and it works in Zandronum too. + +-Bots can now follow players in Zandronum too, and they'll pick the closest + player to follow online. + +-Bots now switch between weapons they have in vanilla. + +-tdbots_usenodes no longer needs to be enabled for following the player, or + learning the map. + +-Slightly cleaned up the TDBot_Main script (less duplicate code). + +-Removed the ability for bots to seek items. The only place where this was + even remotely useful, was in bot-only matches, other than that the + map learning renders it obsolete, and it stops bots from eating up your + ammo in co-op. This change was done as it was the only feature left that + the Zandronum version didn't have, and it wasn't worth keeping the split. \ No newline at end of file diff --git a/wadsrc_tdbots/static/CVARINFO b/wadsrc_tdbots/static/CVARINFO new file mode 100644 index 0000000000..b5b1322f57 --- /dev/null +++ b/wadsrc_tdbots/static/CVARINFO @@ -0,0 +1,25 @@ +//Cosmetic options +server bool tdbots_chat = true; +server bool tdbots_roamchat = true; + +//Difficulty options +server bool tdbots_easymode = false; +server bool tdbots_buff = false; +server bool tdbots_lessfov = false; +server int tdbots_reactiontime = 0; +server int tdbots_weaponize = 0; + +//Other options +server noarchive bool tdbots_enable = true; +server noarchive bool tdbots_playerbot = false; +server bool tdbots_follow = true; +server bool tdbots_usenodes = true; +server bool tdbots_allowteleport = false; +server int tdbots_teleportdelaytime = 30; +server bool tdbots_learnfromplayer = true; + +//Gameplay modifiers +server noarchive bool tdbots_rocketarena = false; + +//Info CVARs (Not intended to be modified by players) +server noarchive int tdbots_currentgame = -1; \ No newline at end of file diff --git a/wadsrc_tdbots/static/DECORATE.dec b/wadsrc_tdbots/static/DECORATE.dec new file mode 100644 index 0000000000..a770a25cf0 --- /dev/null +++ b/wadsrc_tdbots/static/DECORATE.dec @@ -0,0 +1,482 @@ +//TDBots: The fast-performing bots +// +//(C) 2019 Moises Aguirre / Koneoi / TDRR +// +//Licensed under the MIT license + +#include "TDBots/PATHNODING.dec" //Waypointing and such things +#include "TDBots/NODESTUDIO.dec" + +#include "TDBots/DOOMWEAPONS.dec" //Doom weapons +#include "TDBots/HTICWEAPONS.dec" //Heretic weapons +#include "TDBots/HEXNWEAPONS.dec" //Hexen weapons +#include "TDBots/STRFWEAPONS.dec" //Strife weapons +#include "TDBots/CHEXWEAPONS.dec" //Chex weapons + +//Remember to also check the constants in PATHNODING.dec + +//Bot_MoveSpeed got moved to ACS, make sure to look for it there. + +//Bot button presses, BotAltAttack is unused in the main games +Actor BotAttack : Inventory {} +Actor BotAltAttack : Inventory {} + +//Bot state tokens +Actor BotSeekingItem : Inventory {} +Actor BotPrecisionMode : Inventory {} + +//Weapon flags +Actor BotExplosiveWeapon : Inventory {} +Actor BotMeleeWeapon : Inventory {} +Actor BotForceInacc : Inventory {} //Simulate A_Refire bullet spread +Actor BotCloseRange : Inventory {} //Bot stays close to target but outside of + //melee range. Useful for Shotguns. + +//CVAR option tokens +Actor TDBots_NavNodes : Inventory {} +Actor TDBots_LessFOV : Inventory {} +Actor TDBots_FollowNodeZan : Inventory {} + +//Bot info tokens +Actor TDBots_Success : Inventory {} +ACtor TDBots_IsFriend : Inventory {} +Actor TDBots_IsBot : Inventory {} //So pathnodes are only used by bots + +//Timers +Actor BotAttentionTimer : Inventory {Inventory.MaxAmount 40} //Item attention +Actor BotAttentionCooldown : Inventory {Inventory.MaxAmount 48} //Time to look for items again + +//Unused +Actor TDBot_WeaponFiring : Inventory {} +Actor TDBots_BotFound : Inventory {} + +//Base actor for all bot functions, given by ACS script TDBots_BotThink +Actor BotFunc : CustomInventory +{ + Inventory.PickupMessage "" + Inventory.MaxAmount 1 + +Inventory.InterHubStrip + +Inventory.AutoActivate +} + + +Actor BotFunc_Strafe : BotFunc +{ + states + { + Use: + TNT1 A 0 A_ChangeVelocity(0, Accuracy/2, momz, CVF_REPLACE|CVF_RELATIVE) + Stop + } +} + +Actor BotFunc_Strafe2 : BotFunc +{ + states + { + Use: + TNT1 A 0 A_ChangeVelocity(0,-Accuracy/2, momz, CVF_REPLACE|CVF_RELATIVE) + Stop + } +} + +//Resets the bot's target pointer so it doesn't circle around a dead bot +//Doesn't work sadly. +Actor BotFunc_Die : BotFunc +{ + states + { + Use: + TNT1 A 0 A_TransferPointer(AAPTR_DEFAULT, AAPTR_TARGET, AAPTR_NULL, AAPTR_TARGET, PTROP_NOSAFEGUARDS) + Stop + } +} + +Actor BotFunc_StartUp : BotFunc +{ + states + { + Use: + TNT1 A 0 A_GiveInventory("TDBots_IsBot", 1) + TNT1 A 0 ACS_NamedExecuteAlways("TDBots_BotThink", 0) //Init bot's thinking loop + Stop + } +} + +Actor BotFunc_Roam : BotFunc +{ + states + { + Use: + TNT1 A 0 A_ClearTarget + TNT1 A 0 A_SetPitch(0) + TNT1 A 0 A_JumpIfTargetInLOS("NormalChecks", 360) + TNT1 A 0 A_JumpIfInventory("BotAttentionTimer", 40, "Cooldown") + TNT1 A 0 A_JumpIfInventory("TDBots_NavNodes", 1, "SearchNodes") + TNT1 A 0 //A_CheckProximity("SeekItem", "Armor", 768, 1, CPXF_ANCESTOR | CPXF_SETTARGET | CPXF_CHECKSIGHT) + TNT1 A 0 //A_CheckProximity("SeekItem", "CustomInventory", 768, 1, CPXF_ANCESTOR | CPXF_SETTARGET | CPXF_CHECKSIGHT) + TNT1 A 0 //A_CheckProximity("SeekItem", "Ammo", 768, 1, CPXF_ANCESTOR | CPXF_SETTARGET | CPXF_CHECKSIGHT) + TNT1 A 0 //A_CheckProximity("SeekItem", "Weapon", 512, 1, CPXF_ANCESTOR | CPXF_SETTARGET | CPXF_CHECKSIGHT) + TNT1 A 0 A_Jump(256, "NormalChecks") + Stop + + SearchNodes: + //CheckProx("SeekItem", "Weapon", 128, 1, CPXF_ANCESTOR | CPXF_SETTARGET | CPXF_CHECKSIGHT) + //CheckProx("FollowNode", "TDBots_PathNode", 384, 1, CPXF_CLOSEST | CPXF_SETTARGET | CPXF_CHECKSIGHT) + TNT1 A 0 A_JumpIfInventory("TDBots_FollowNodeZan", 1, "FollowNode") + TNT1 A 0 A_Jump(256, "NormalChecks") + Stop + + Cooldown: + TNT1 A 0 A_GiveInventory("BotAttentionCooldown", 1) + TNT1 A 0 A_JumpIfInventory("BotAttentionCooldown", 48, "ResetTimer") + NormalChecks: + TNT1 A 0 A_CheckLOF("AvoidWall", CLOFF_SKIPOBSTACLES | CLOFF_JUMP_ON_MISS | CLOFF_SKIPTARGET | CLOFF_ALLOWNULL, 32, 0) + TNT1 A 0 A_Jump(32, "TurnLeft", "TurnRight", "TurnLeftStrong", "TurnRightStrong") //Low chance of a random turn + TNT1 A 0 A_Jump(256, "Proceed") //Set speed and look for targets + Stop + + Proceed: + TNT1 A 0 A_JumpIfInventory("BotPrecisionMode", 1, "ProceedPart2") + TNT1 A 0 A_Jump(56, "Strafe") + TNT1 A 0 A_Jump(256, "ProceedPart2") + Stop + ProceedPart2: + TNT1 A 0 A_JumpIf(momz != 0, "Null") + TNT1 A 0 A_ChangeVelocity(Accuracy/2, 0, momz, CVF_RELATIVE|CVF_REPLACE) + TNT1 A 0 ACS_NamedExecuteAlways("TDBots_UseItems",0,TRUE) + Stop + + AvoidWall: //Force turning if there's a wall nearby + TNT1 A 0 A_Jump(256, "TurnLeft", "TurnRight", "TurnLeftStrong", "TurnRightStrong") + Stop + + TurnLeft: + TNT1 A 0 A_SetAngle(angle-32) + TNT1 A 0 A_Jump(256, "Proceed") + Stop + + TurnRight: + TNT1 A 0 A_SetAngle(angle+32) + TNT1 A 0 A_Jump(256, "Proceed") + Stop + + TurnLeftStrong: + TNT1 A 0 A_SetAngle(angle-64) + TNT1 A 0 A_Jump(256, "Proceed") + Stop + + TurnRightStrong: + TNT1 A 0 A_SetAngle(angle+64) + TNT1 A 0 A_Jump(256, "Proceed") + Stop + + Strafe: + TNT1 A 0 A_Jump(128, 3) + TNT1 A 0 A_JumpIf(momz != 0, "Null") + TNT1 A 0 A_ChangeVelocity(Accuracy/2, -Accuracy/2, momz, CVF_RELATIVE|CVF_REPLACE) + TNT1 A 0 A_Jump(256, "StrafeEnd") + TNT1 A 0 A_JumpIf(momz != 0, "Null") + TNT1 A 0 A_ChangeVelocity(Accuracy/2, Accuracy/2, momz, CVF_RELATIVE|CVF_REPLACE) + TNT1 A 0 A_Jump(256, "StrafeEnd") + Stop + + StrafeEnd: + TNT1 A 0 A_RailWait + Stop + + SeekHealth: + TNT1 A 0 //A_CheckProximity("SeekItem", "Health", 768, 1, CPXF_ANCESTOR | CPXF_SETTARGET | CPXF_CHECKSIGHT) + TNT1 A 0 A_Jump(256, "NormalChecks") + Stop + + SeekItem: + TNT1 A 0 A_GiveInventory("BotAttentionTimer", 1) + FollowNode: + TNT1 A 0 A_JumpIfInventory("tdbots_lessfov", TRUE, "FollowNodeLessFOV") + TNT1 A 0 A_LookEx(0, 0, 0, 0, 360, "") + FollowNodeP2: + TNT1 A 0 A_FaceTarget + TNT1 A 0 A_CheckLOF("AvoidWall", CLOFF_SKIPOBSTACLES | CLOFF_JUMP_ON_MISS | CLOFF_SKIPTARGET | CLOFF_ALLOWNULL, 32, 0) + TNT1 A 0 A_Jump(256, "Proceed") + Stop + + FollowNodeLessFOV: + TNT1 A 0 A_LookEx(0, 0, 0, 0, 120, "") + TNT1 A 0 A_Jump(256, "FollowNodeP2") + Stop + + ResetTimer: + TNT1 A 0 A_TakeInventory("BotAttentionTimer", 40) + TNT1 A 0 A_TakeInventory("BotAttentionCooldown", 48) + TNT1 A 0 A_Jump(256, "NormalChecks") + Stop + } +} + +Actor BotFunc_LightRoam : BotFunc_Roam +{ + states + { + Use: + TNT1 A 0 A_SetPitch(0) + TNT1 A 0 A_JumpIfTargetInLOS("NormalChecks", 360) + TNT1 A 0 A_JumpIfInventory("BotAttentionTimer", 40, "Cooldown") + TNT1 A 0 A_JumpIfInventory("TDBots_NavNodes", 1, "SearchNodes") + TNT1 A 0 //A_CheckProximity("SeekItem", "Armor", 768, 1, CPXF_ANCESTOR | CPXF_SETTARGET | CPXF_CHECKSIGHT) + TNT1 A 0 //A_CheckProximity("SeekItem", "CustomInventory", 768, 1, CPXF_ANCESTOR | CPXF_SETTARGET | CPXF_CHECKSIGHT) + TNT1 A 0 //A_CheckProximity("SeekItem", "Ammo", 768, 1, CPXF_ANCESTOR | CPXF_SETTARGET | CPXF_CHECKSIGHT) + TNT1 A 0 //A_CheckProximity("SeekItem", "Weapon", 768, 1, CPXF_ANCESTOR | CPXF_SETTARGET | CPXF_CHECKSIGHT) + TNT1 A 0 A_Jump(256, "NormalChecks") + + Cooldown: + TNT1 A 0 A_GiveInventory("BotAttentionCooldown", 1) + TNT1 A 0 A_JumpIfInventory("BotAttentionCooldown", 48, "ResetTimer") + NormalChecks: + TNT1 A 0 A_ClearTarget + TNT1 A 0 A_CheckLOF("AvoidWall", CLOFF_SKIPOBSTACLES | CLOFF_JUMP_ON_MISS | CLOFF_SKIPTARGET | CLOFF_ALLOWNULL, 32, 0) + TNT1 A 0 A_Jump(256, "Proceed") //Set speed and look for targets + Stop + + SeekHealth: + TNT1 A 0 //A_CheckProximity("SeekItem", "Health", 768, 1, CPXF_ANCESTOR | CPXF_SETTARGET | CPXF_CHECKSIGHT) + TNT1 A 0 A_Jump(256, "NormalChecks") + Stop + } +} + +Actor BotFunc_AimDodge : BotFunc +{ + states + { + Use: + TNT1 A 0 A_JumpIf(momz != 0, "Finish") + TNT1 A 0 A_JumpIfInventory("BotPrecisionMode", 1, "Finish") + + TNT1 A 0 A_JumpIfInventory("BotMeleeWeapon", 1, "MeleeAttack") //Check if melee weapon flag set + TNT1 A 0 A_JumpIfInventory("BotExplosiveWeapon", 1, "MoveBackwards") //Check if explosive weapon flag set + TNT1 A 0 A_Jump(256, "Finish") + Stop + + DodgeMore: + TNT1 A 0 A_Jump(256, "DodgeLeft", "DodgeRight") + TNT1 A 0 A_Jump(256, "Finish") + Stop + + DodgeLeft: + TNT1 A 0 A_ChangeVelocity(0, -Accuracy/2, momz, CVF_RELATIVE|CVF_REPLACE) + TNT1 A 0 A_Jump(32, "MoveForward", "MoveBackwards") //Move further or closer from the target at random + TNT1 A 0 A_Jump(256, "Finish") + Stop + + DodgeRight: + TNT1 A 0 A_ChangeVelocity(0, Accuracy/2, momz, CVF_RELATIVE|CVF_REPLACE) + TNT1 A 0 A_Jump(32, "MoveForward", "MoveBackwards") //Move further or closer from the target at random + TNT1 A 0 A_Jump(256, "Finish") + Stop + + MoveForward: + TNT1 A 0 A_JumpIfCloser(72, "MoveBackwards") + TNT1 A 0 A_ChangeVelocity(Accuracy/2, momy, momz, CVF_RELATIVE|CVF_REPLACE) + TNT1 A 0 A_Jump(256, "Finish") + Stop + + MoveBackwards: + TNT1 A 0 A_ChangeVelocity(-Accuracy/2, momy, momz, CVF_RELATIVE|CVF_REPLACE) + TNT1 A 0 A_Jump(256, "Finish") + Stop + + MeleeAttack: + TNT1 A 0 A_FaceTarget + TNT1 A 0 A_ChangeVelocity(Accuracy/8, momy, momz, CVF_RELATIVE|CVF_REPLACE) + Stop + + Finish: + TNT1 A 0 A_FaceTarget + TNT1 A 0 A_CheckLOF("Attack", CLOFF_MUSTBESHOOTABLE | CLOFF_SKIPENEMY | CLOFF_SKIPNONHOSTILE) + TNT1 A 0 A_TakeInventory("BotAttack") + Stop + + Attack: + TNT1 A 0 ACS_NamedExecuteAlways("TDBots_BotEasyMode", 0) + Stop + + ExplosiveCheck: //Too close to target with explosive weapon? Back out! + TNT1 A 0 A_JumpIfCloser(200, "MoveBackwards") + TNT1 A 0 A_Jump(256, "Finish") + Stop + } +} + +Actor BotFunc_Aim : BotFunc +{ + states + { + Use: + TNT1 A 0 A_JumpIfInventory("BotPrecisionMode", 1, "Finish") + + TNT1 A 0 A_JumpIfInventory("BotMeleeWeapon", 1, "MeleeAttack") //Check if melee weapon flag set + TNT1 A 0 A_JumpIfInventory("BotExplosiveWeapon", 1, "MoveBackwards") //Check if explosive weapon flag set + //TNT1 A 0 A_JumpIfInventory("BotCloseRange", 1, "CloseRange") + TNT1 A 0 A_Jump(256, "Finish") + Stop + + CloseRange: + TNT1 A 0 A_JumpIfCloser(256, "MoveBackwards") + TNT1 A 0 A_Jump(256, "MeleeAttack") + Stop + + MoveBackwards: + TNT1 A 0 A_ChangeVelocity(-Accuracy/4, momy, momz, CVF_RELATIVE|CVF_REPLACE) + TNT1 A 0 A_Jump(256, "Finish") + Stop + + MeleeAttack: + TNT1 A 0 A_FaceTarget + TNT1 A 0 A_ChangeVelocity(Accuracy/4, momy, momz, CVF_RELATIVE|CVF_REPLACE) + TNT1 A 0 A_CheckLOF("Attack", CLOFF_MUSTBESHOOTABLE | CLOFF_SKIPENEMY | CLOFF_SKIPNONHOSTILE) + TNT1 A 0 A_TakeInventory("BotAttack") + Stop + + Finish: + TNT1 A 0 A_FaceTarget + TNT1 A 0 A_CheckLOF("Attack", CLOFF_MUSTBESHOOTABLE | CLOFF_SKIPENEMY | CLOFF_SKIPNONHOSTILE) + TNT1 A 0 A_TakeInventory("BotAttack") + Stop + + Attack: + TNT1 A 0 ACS_NamedExecuteAlways("TDBots_BotEasyMode", 0) + TNT1 A 0 A_GiveInventory("BotAttack") + Stop + } +} + +Actor BotFunc_CheckLOS : BotFunc +{ + states + { + Use: + TNT1 A 0 A_JumpIfInventory("tdbots_lessfov", TRUE, "LessFOVLook") + TNT1 A 0 A_LookEx(0, 0, 0, 0, 360, "") + UseP2: + TNT1 A 0 A_FaceTarget + TNT1 A 0 A_CheckLOF("CheckAttack", CLOFF_MUSTBESHOOTABLE) + Stop + + CheckAttack: + TNT1 A 0 A_JumpIf(CallACS("TDBots_IsAlly") == TRUE, "Null") + TNT1 A 0 A_Jump(256, "Attack") + Stop + + Attack: + TNT1 A 0 ACS_NamedExecuteAlways("TDBots_BotEasyMode", 0) + TNT1 A 0 A_GiveInventory("BotAttack", 1) + Stop + + LessFOVLook: + TNT1 A 0 A_LookEx(0, 0, 0, 0, 120, "") + TNT1 A 0 A_Jump(256, "UseP2") + Stop + } +} + +Actor BotFunc_FireStop : BotFunc +{ + states + { + Use: + TNT1 A 0 A_TakeInventory("BotAttack", 1) + Stop + } +} + +Actor BotFunc_BuffDoom : BotFunc +{ + states + { + Use: + TNT1 A 0 A_SpawnItemEx("Shell", 0, 0, 0, 0, 0, 0, 0, 0, 0, 987000) + TNT1 A 0 A_SpawnItemEx("Clip", 0, 0, 0, 0, 0, 0, 0, 0, 0, 987000) + TNT1 A 0 A_SpawnItemEx("RocketAmmo", 0, 0, 0, 0, 0, 0, 0, 0, 0, 987000) + TNT1 A 0 A_SpawnItemEx("Cell", 0, 0, 0, 0, 0, 0, 0, 0, 0, 987000) + TNT1 A 0 A_SpawnItemEx("HealthBonus", 0, 0, 0, 0, 0, 0, 0, 0, 0, 987000) + Stop + } +} + +Actor BotFunc_BuffHeretic : BotFunc +{ + states + { + Use: + TNT1 A 0 A_SpawnItemEx("CrossbowAmmo", 0, 0, 0, 0, 0, 0, 0, 0, 0, 987000) + TNT1 A 0 A_SpawnItemEx("GoldWandAmmo", 0, 0, 0, 0, 0, 0, 0, 0, 0, 987000) + TNT1 A 0 A_SpawnItemEx("SkullrodAmmo", 0, 0, 0, 0, 0, 0, 0, 0, 0, 987000) + TNT1 A 0 A_SpawnItemEx("PhoenixRodAmmo", 0, 0, 0, 0, 0, 0, 0, 0, 0, 987000) + TNT1 A 0 A_SpawnItemEx("BlasterAmmo", 0, 0, 0, 0, 0, 0, 0, 0, 0, 987000) + TNT1 A 0 A_SpawnItemEx("HealthBonus", 0, 0, 0, 0, 0, 0, 0, 0, 0, 987000) + Stop + } +} + +Actor BotFunc_BuffHexen : BotFunc +{ + states + { + Use: + TNT1 A 0 A_SpawnItemEx("Mana1", 0, 0, 0, 0, 0, 0, 0, 0, 0, 987000) + TNT1 A 0 A_SpawnItemEx("Mana2", 0, 0, 0, 0, 0, 0, 0, 0, 0, 987000) + TNT1 A 0 A_SpawnItemEx("HealthBonus", 0, 0, 0, 0, 0, 0, 0, 0, 0, 987000) + Stop + } +} + +Actor BotFunc_BuffStrife : BotFunc +{ + states + { + Use: + TNT1 A 0 A_SpawnItemEx("AmmoSatchel", 0, 0, 0, 0, 0, 0, 0, 0, 0, 987000) + Stop + } +} + +//Teleport call stuff +Actor TDBot_DoNotAllowTeleport : Inventory {} + +Actor TDBot_TeleportCall : CustomInventory +{ + -INVBAR + +INVENTORY.INTERHUBSTRIP + states + { + Use: + TNT1 A 0 A_JumpIfInventory("TDBot_DoNotAllowTeleport", 1, "UseButFail") + TNT1 A 0 A_JumpIf(ACS_NamedExecuteWithResult("TDBots_NeedSomeHelp") == TRUE, "SpawnSpot") + Fail + SpawnSpot: + TNT1 A 0 A_SpawnItem("Bot_TeleportSpot") + Fail + UseButFail: + TNT1 A 0 A_Print("You can't call a bot right now\nStill cooling down.") + Fail + } +} + +Actor Bot_TeleportSpot +{ + +THRUACTORS + -SHOOTABLE + Speed 30 + states + { + Spawn: + TNT1 AA 0 Thing_ChangeTID(0,16231) + TNT1 AAAAA 0 A_Wander + TNT1 A 1 A_Wander + TNT1 AAAAA 0 A_Wander + TNT1 A 1 A_Wander + TNT1 A -1 + Stop + } +} \ No newline at end of file diff --git a/wadsrc_tdbots/static/KEYCONF b/wadsrc_tdbots/static/KEYCONF new file mode 100644 index 0000000000..02fe7fcb3f --- /dev/null +++ b/wadsrc_tdbots/static/KEYCONF @@ -0,0 +1,19 @@ +//Emulates doom2.exe deathmatch as closely as possible +alias tdbots_oldschooldm "closemenu; dmflags 5570564; dmflags2 20971776; compatflags -1172751401; compatflags2 11" + +addkeysection "TDBots keys" TDBOTMENUKEYS +addmenukey "Open TDBots menu" "openmenu TDBots_Options" +addmenukey "Bot Teleport Call" "use TDBot_TeleportCall" + +addmenukey "Change node type" tdbots_changetype +alias tdbots_changetype "pukename tdbots_chnodetype" + +alias "tdbots_veryeasypreset" "tdbots_enable 1; tdbots_easymode 1; tdbots_reactiontime 70; tdbots_weaponize 0; tdbots_lessfov 1; tdbots_buff 0" +alias "tdbots_easypreset" "tdbots_enable 1; tdbots_easymode 1; tdbots_reactiontime 35; tdbots_weaponize 0; tdbots_lessfov 1; tdbots_buff 0" +alias "tdbots_mediumpreset" "tdbots_enable 1; tdbots_easymode 0; tdbots_reactiontime 35; tdbots_weaponize 0; tdbots_lessfov 1; tdbots_buff 0" +alias "tdbots_hardpreset" "tdbots_enable 1; tdbots_easymode 0; tdbots_reactiontime 16; tdbots_weaponize 0; tdbots_lessfov 0; tdbots_buff 0" +alias "tdbots_veryhardpreset" "tdbots_enable 1; tdbots_easymode 0; tdbots_reactiontime 0; tdbots_weaponize 0; tdbots_lessfov 0; tdbots_buff 0" +alias "tdbots_maxpreset" "tdbots_enable 1; tdbots_easymode 0; tdbots_reactiontime 0; tdbots_weaponize 6; tdbots_lessfov 0; tdbots_buff 1" + +alias "tdbots_startdmmap01" "closemenu; deathmatch 1; sv_weaponstay 1; sv_itemrespawn 1; sv_nomonsters 1; map map01" +alias "tdbots_startdmE1M1" "closemenu; deathmatch 1; sv_weaponstay 1; sv_itemrespawn 1; sv_nomonsters 1; map e1m1" \ No newline at end of file diff --git a/wadsrc_tdbots/static/KEYCONF.nstudio b/wadsrc_tdbots/static/KEYCONF.nstudio new file mode 100644 index 0000000000..9e4e438e39 --- /dev/null +++ b/wadsrc_tdbots/static/KEYCONF.nstudio @@ -0,0 +1 @@ +alias "NStudio_Start" "pukename tdbots_nodestudio_startup" \ No newline at end of file diff --git a/wadsrc_tdbots/static/LANGUAGE.pm b/wadsrc_tdbots/static/LANGUAGE.pm new file mode 100644 index 0000000000..71c52d8335 --- /dev/null +++ b/wadsrc_tdbots/static/LANGUAGE.pm @@ -0,0 +1,3 @@ +//Post Mortem stuff. +[enu default] +DUMB_BOTS = "TDBots do what Zandronum bots don't, period."; \ No newline at end of file diff --git a/wadsrc_tdbots/static/LICENSE.txt b/wadsrc_tdbots/static/LICENSE.txt new file mode 100644 index 0000000000..9261a74c95 --- /dev/null +++ b/wadsrc_tdbots/static/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019-2020 Moises Aguirre / Koneoi / TDRR + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/wadsrc_tdbots/static/LOADACS b/wadsrc_tdbots/static/LOADACS new file mode 100644 index 0000000000..20a46eac0b --- /dev/null +++ b/wadsrc_tdbots/static/LOADACS @@ -0,0 +1,2 @@ +NODESTUDIO +TDB_Main \ No newline at end of file diff --git a/wadsrc_tdbots/static/MENUDEF b/wadsrc_tdbots/static/MENUDEF new file mode 100644 index 0000000000..75decf6b35 --- /dev/null +++ b/wadsrc_tdbots/static/MENUDEF @@ -0,0 +1,103 @@ +optionmenu TDBOTS_OPTIONS +{ + Title "TDBots options" + StaticText "Bot and map Management options" + StaticText "" + Command "Add one bot", "addbot" + StaticText "" + Command "Remove all bots", "removebots" + StaticText "" + Command "Start deathmatch on MAP01", "tdbots_startdmmap01" + StaticText "" + Command "Start deathmatch on E1M1", "tdbots_startdmE1M1" + StaticText "" + Command "Start NodeStudio", "NStudio_Start" + StaticText "" + Command "Go to Next Map", "nextmap" + StaticText "" + StaticText "Gameplay modifiers" + StaticText "" + Option "Rocket Arena mode", "tdbots_rocketarena", "OnOff" + StaticText "Rocket Arena consists of every player starting",1 + StaticText "out with all weapons, full ammo, full health",1 + StaticText "and full armor. Works with most mods.",1 + StaticText "" + StaticText "Cosmetic bot options" + StaticText "" + Option "TDBots can chat", "tdbots_chat", "OnOff" + StaticText "" + Option "TDBots chat when idle", "tdbots_roamchat", "OnOff" + StaticText "" + StaticText "General bot options" + StaticText "" + Option "Enable TDBots AI", "tdbots_enable", "OnOff" + StaticText "When disabled, reverts back to ZDoom's ZCajun AI.",1 + StaticText "And they are terrible so i don't recommend doing so!",1 + StaticText "On Zandronum, this causes the bots to stand still.",1 + StaticText "" + Option "Learn the map from players", "tdbots_learnfromplayer", "OnOff" + StaticText "If no waypoints are found, the TDBots will instead",1 + StaticText "try to learn the map from your exploration of it.",1 + StaticText "May take up to 1 minute before the bots use the info.",1 + StaticText "" + Option "Use waypoints", "tdbots_usenodes", "OnOff" + StaticText "If enabled, makes the bots use the loaded waypoint file.",1 + StaticText "Only useful if you are using a waypoint pack with the bots,",1 + //StaticText "if not, leave this off or the bots won't search for items.",1 + StaticText "" + Option "Player is a TDBot", "tdbots_playerbot", "OnOff" + StaticText "If on, you will also move exactly like a TDBot does.",1 + StaticText "Type sv_forcerespawn 1 in the console so you respawn",1 + StaticText "automatically too.",1 + StaticText "" + Option "TDBots follow players", "tdbots_follow", "OnOff" + StaticText "When on, bots follow you on cooperative modes.",1 + StaticText "Automatically disabled for deathmatch.",1 + StaticText "" + Option "Allow teleport call", "tdbots_allowteleport", "OnOff" + StaticText "When enabled, you can press the teleport call key",1 + StaticText "to make a random bot currently in-game teleport to",1 + StaticText "your location.",1 + StaticText "" + Slider "Teleport cooldown time", "tdbots_teleportdelaytime", 5.0, 120.0, 1.0, 0 + StaticText "Amount of time in seconds until you can teleport",1 + StaticText "a bot to your location again.",1 + StaticText "" + StaticText "Bot difficulty options" + StaticText "" + StaticText "Most options will take effect once the" + StaticText "bot dies and then respawns." + StaticText "" + Option "Health and Ammo regeneration", "tdbots_buff", "OnOff" + StaticText "A fair warning: very overpowered. Don't put this on",1 + StaticText "for Deathmatch, or the bots will be very hard to kill",1 + StaticText "Instead, use it for Co-op so the bots don't take your ammo.",1 + StaticText "" + Option "Easy mode", "tdbots_easymode", "OnOff" + StaticText "Makes the bot's aim significantly worse, use it if",1 + StaticText "the bot keeps beating you too much on Deathmatch.",1 + StaticText "" + Slider "Reaction time", "tdbots_reactiontime", 0.0, 70.0, 1.0, 0 + StaticText "The higher the value, the more time the bots take to react.",1 + StaticText "35 equals one second, so 70 equals two seconds.",1 + StaticText "Don't set it very high or the bots may stop attacking suddenly.",1 + StaticText "" + Slider "Weapons the bot starts with", "tdbots_weaponize", 0.0, 6.0, 1.0, 0 + StaticText "The higher, the better weapons the bot spawns with.",1 + StaticText "In Hexen, try not to set this higher than 2, or the",1 + StaticText "TDBots will spawn with the best weapon!",1 + StaticText "" + Option "Reduced field of view", "tdbots_lessfov", "OnOff" + StaticText "When on, TDBots have a reduced fov of 120",1 + StaticText "Turn this on if you don't like bots being able",1 + StaticText "to see behind their back.",1 + StaticText "" + StaticText "TDBot difficulty presets:" + Command "Can i play, daddy?", "tdbots_veryeasypreset" + Command "I'm too young to die", "tdbots_easypreset" + Command "Hurt me plenty", "tdbots_mediumpreset" + Command "Bring it on!", "tdbots_hardpreset" + Command "Damn i'm good!", "tdbots_veryhardpreset" + Command "BFG SPAMMER", "tdbots_maxpreset" + StaticText "Note: BFG SPAMMER is a joke skill, and may not be fair at all",1 +} \ No newline at end of file diff --git a/wadsrc_tdbots/static/TO_MODDERS.txt b/wadsrc_tdbots/static/TO_MODDERS.txt new file mode 100644 index 0000000000..49a24b986d --- /dev/null +++ b/wadsrc_tdbots/static/TO_MODDERS.txt @@ -0,0 +1,227 @@ +If you are a modder and you want to add support for the TDBots into your mod, +look no further, as this file has all information you may need. + +First, the TDBots are more customizable than the ZCajun bots or the Skullbots. +So if your mod has special things you would need the bot to do, like reloading +or using special items like grenades, altfires in your weapons or special +behavior when carrying specific weapons you can do that without editing the bots +themselves thanks to the custom actions added in v17. + +These are quite similar to the usual hacks of making the bot do something at +random using ACS, but can be set to happen exactly when the bot starts doing +something, and don't necessarily require using ACS. + +What this guide covers: +-Editing weapons slightly to make them work with the bots + (No noticeable changes for the average player though). + +-Usage of weapon flags for special behavior with certain weapons. + +-Playerclass specific properties (Just the speed, because + everything else is done automatically). + +-Custom actions, like usage of alt fire and inventory items. + +-Making weapon and ammo lists for Rocket Arena mode. + +-Global flags, which alter TDBots behavior. + +=================================BASIC STUFF==================================== + +Let's get over the process to make the bots work with your mod in the most basic +way possible, which is adapting the weapons. +Thankfully, this process is easy, if a bit tedious sometimes, so take it slow +if your mod has many complex weapons. + +Copy-paste these actor definitions before any weapon: + +Actor BotAttack : Inventory {} +Actor BotAltAttack : Inventory {} +Actor BotExplosiveWeapon : Inventory {} +Actor BotMeleeWeapon : Inventory {} +Actor BotForceInacc : Inventory {} +Actor BotCloseRange : Inventory {} + +These aren't necessary but they fix warnings on startup when the TDBots +aren't loaded. + +You must add after every A_WeaponReady call (that doesn't stop the player from +firing of course) the following line of code: +TNT1 A 0 A_JumpIfInventory("BotAttack", TRUE, "Fire") + +And in every A_Refire call you should add something similar: +TNT1 A 0 A_JumpIfInventory("BotAttack", TRUE, "Fire") +Where "Fire" could also be "Hold" if such a state is defined in your weapon. +If the A_Refire call specifies another state (such as A_Refire("HoldP2") ) +You should change "Fire" to reflect that same state. + +Once this is done with all weapons, the bot should work. At this point, all +features of the TDBots that work with the IWADs should work with your mod just +the same. + +If you are going to add altfiring with custom actions, add this line right before +the "BotAttack" check but after the A_WeaponReady call: +TNT1 A 0 A_JumpIfInventory("BotAltAttack", TRUE, "AltFire") +All of the rules about A_Refire and this line of code still apply, except that +"Hold" is changed to "AltHold" instead. + +===============================USING WEAPON FLAGS=============================== + +There are also weapon flags, that allow the bot to act differently depending on +what weapon it is holding. + +Currently these flags are accepted by default: +BotExplosiveWeapon +BotMeleeWeapon +BotForceInacc + +To set these, do the following: + +In the Select state of the weapon in question, add a line like this: +TNT1 A 0 A_GiveInventory("NameOfFlag") + +And in the Deselect state, add a line like this: +TNT1 A 0 A_TakeInventory("NameOfFlag") + +Obviously, both should have the same flag name. BotExplosiveWeapon should only +be used if the bot can be damaged with the explosion of the weapon. + +BotMeleeWeapon should only be used if the weapon is very short ranged, like a +fist or something similar. It could also be used if the weapon has very drastic +damage falloff with distance, like Final Doomer's BTSX Super sonic blaster. + +BotForceInacc is for weapons that are inaccurate until first refire +(Like Doom's Pistol and Chaingun, Heretic's gold wand) so they are properly +inaccurate instead of pinpoint accurate in the bot's hands. If your weapon +fires multiple bullets with one A_FireBullets call (A shotgun, probably) or the +numbullets parameter on most A_FireBullets calls is -1, you don't need this. + +It doesn't matter if the actions get repeated again as part of the state loop, +as they only have an effect the first time they get executed. + + +===============================ADVANCED FEATURES================================ + +Now comes the advanced features i mentioned earlier. +For setting a class' speed, use the Accuracy property. The base Doomguy speed is +24, so change it to make it as close as your class' running speed as possible. +If you need help seeing the changes in an easy way, tdbots_playerbot 1 will make +your player character also work like a bot, which helps you immediately see any +changes in speed and stuff quickly and easily. + +If the Accuracy property is 0 or not defined, the TDBots will automatically set +it to base Doomguy speed, as in Doom, Hexen, Heretic and Strife. + +I also mentioned custom actions for the bots, which can be done in the following +way: + +Define a new actor, named like one of these, inheriting from CustomInventory: +TDBots_Custom_RoamLoop1 or TDBots_Custom_RoamLoop2 +These are if the bot is doing it's basic roaming. These will be executed every +4 tics if the bot is not attacking and just running around. + +TDBots_Custom_AttackLoop1 or TDBots_Custom_AttackLoop2 +These are executed every time the bot is attacking and not strafing at the same +time. It is guaranteed to execute when the bots start attacking, and has a +chance of being executed again if the bot isn't strafing. + +If any of these are defined, the TDBot's code will detect it and place it in +the regular bot action loop. Obviously, it's going to crash and burn if it's not +a CustomInventory item (It could also be Inventory, but that's useless). + +============================CODING THE CUSTOM ACTIONS=========================== + +It's a very simple to use kind of thing. As an example, let's make a bot use +Heretic's tome of power. I'm going to avoid using ACS for this, but you could +just use UseInventory("ArtiTomeOfPower"); in an ACS script +and call that randomly. + +(note: As of v18, the TDBots already use Heretic items automatically. So + it might be a better idea to replace the "A_GiveInventory" call with + something different so you can tell if it's actually working) + +This actor must have +INVENTORY.AUTOACTIVATE or else it won't work at all. + +Actor TDBots_Custom_AttackLoop1 : CustomInventory +{ + +INVENTORY.AUTOACTIVATE + states + { + Use: //Check if the bot has the tome of power. + TNT1 A 0 A_JumpIfInventory("ArtiTomeOfPower", TRUE, "UseTomeOfPower") + Stop + UseTomeOfPower: //It has it, so let's roll the dice and see if we use it. + TNT1 A 0 A_Jump(128, "ActivateTome") + Stop + ActivateTome: + TNT1 A 0 A_GiveInventory("TOP_ForceActive", 1) + TNT1 A 0 A_TakeInventory("ArtiTomeOfPower", 1)//Take the real tome away. + Stop + } +} + +And the TOP_ForceActive item is a tome of power that activates immediately when +it enters your inventory. So we can activate the item from DECORATE without using +ACS. +Here's the code: + +Actor TOP_ForceActive : ArtiTomeOfPower {+INVENTORY.AUTOACTIVATE} + +Now, with this code in place, the bot will now use Heretic's tome of power, if +it's carrying it in it's inventory, it's attacking, and if the dice roll says +that it should. Pretty cool right? You can easily define many other things +like this using the custom actions. + +You can use most actions you can use in a weapon, like A_SpawnItemEx, A_Jump and +it's variants among many others. All actions that are exclusive to weapons are +A_ZoomFactor and A_SetCrosshair, but those don't matter for bots anyways. + +You can even use stuff like A_FireShotgun directly in these actions. + + +==============================ROCKET ARENA MODE================================= + +Rocket Arena mode will work fine even if you don't make lists for it. However, +if you make a weapon and ammo list, you can control which weapons are given +to the players and which ones aren't, and bots can pick one random weapon +at start instead of always using the same at first. + +If you define these, making both the weapon and ammo lists are required. One +won't work without the other. + +Now, to make one, just create a LANGUAGE file, optionally give it the .tdbots +extension if you already have one, and put the following: + +[enu default] + +TDBOTS_RA_WEAPONS = "weapon;weapon;weapon"; +TDBOTS_RA_AMMO = "ammo;ammo;ammo"; + +Where "weapon" is a weapon actor, and "ammo" is an ammo or inventory actor. +Of course, you aren't limited to just 3, you can add as many weapon actor names +as your mod has, as long as you separate every actor name with a ; character. + +Ammo can also contain any inventory items you want the player to start out +with in Rocket Arena. Make sure to give health and armor here, since they +aren't given by default if a custom weapon list is detected! + +NOTE: When adding the final weapon name, remember to NOT place a ; character +at the end inside the quotes, because this may cause undefined behavior! + +And don't put in the starting weapons (Example: If your player starts out with +a pistol and fist, you shouldn't put those weapons in the weapon list). + + +=================================GLOBAL FLAGS=================================== + +Global flags serve to change certain TDBots behavior to fix various bugs with +certain mod tricks. Global flags of different types could be added in +the future though. + +Currently, the only global flag is TDBots_NoAnimation. +If an actor with this name is defined, the TDBots won't simulate animations. +Use this if walking animations don't look right in your mod, or if the death +animations play twice. + +If you have any questions about this or need some more help, contact me +in the ZDoom forums or Zandronum forums, where my user name is TDRR. \ No newline at end of file diff --git a/wadsrc_tdbots/static/acs/NODESTUDIO.o b/wadsrc_tdbots/static/acs/NODESTUDIO.o new file mode 100644 index 0000000000000000000000000000000000000000..247c38930253ae1ab4971a419021a2f102a97750 GIT binary patch literal 4092 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 I_VnNX1vr3Kh5!Hn literal 0 HcmV?d00001 diff --git a/wadsrc_tdbots/static/scripts/NODESTUDIO.acs b/wadsrc_tdbots/static/scripts/NODESTUDIO.acs new file mode 100644 index 0000000000..59063b153a --- /dev/null +++ b/wadsrc_tdbots/static/scripts/NODESTUDIO.acs @@ -0,0 +1,288 @@ +//TDBots: Node Studio +// +//(C) 2019 Moises Aguirre "TDRR" +// +//Licensed under the MIT license + +#library "NODESTUDIO" +#include "zcommon.acs" + +str IfSomethingBreaksThisAppears = "lol TDRR fix this you moron"; + +//============================================================================== +// +// Start of Node Studio +// +//============================================================================== + +//The nodelist is assembled on the fly into this value, and saved to a CVAR +//whenever needed. +str NodeList = ""; +str LastListEntry = ""; +int LastNodeTID; +int NodeAmount; +bool NodeListVersion = 0; +int NodeType; + +#define NODE_NORMAL 0 +#define NODE_JUMP 1 +#define NODE_PRECISE 2 + +//Save node to the nodelist, based on TID of the node. +function void SaveToNodeList (int TID) +{ + str NewString; + int X = GetActorX(TID); + int Y = GetActorY(TID); + int Z = GetActorZ(TID); + + if(NodeListVersion == 0) + { + NewString = StrParam(i:X/65536,s:";",i:Y/65536,s:";",i:Z/65536,s:";"); + } + else + { + NewString = StrParam(i:X/65536,s:";",i:Y/65536,s:";",i:Z/65536,s:";",i:NodeType,s:";"); + } + + NodeAmount++; + str stringtosave = StrParam(s:NodeList, s:NewString); + NodeList = StringToSave; + LastListEntry = NewString; + LastNodeTID = TID; +} + +//Removes last node placed, and removes it from the nodelist. +function void DeleteLastNode (void) +{ + if(LastNodeTID == 0) {return;} + str NodeListTemp; + int LastListLength = StrLen(LastListEntry); + int ListLength = StrLen(NodeList); + if(ListLength == 0) {return;} + if(LastListLength == 0) {return;} + Thing_Remove(LastNodeTID); + NodeListTemp = StrLeft(NodeList, ListLength-LastListLength); + NodeList = NodeListTemp; + NodeAmount--; + LastNodeTID = 0; +} + +Script "TDBots_NodeStudio_VersionChoice" (void) +{ + SetHUDSize(640, 480, TRUE); + SetFont("BIGFONT"); + + HUDMessage(s:"Press the use key to DELETE all your player stats!", + s:"Press any other key to KEEP all your player stats!"; + HUDMSG_PLAIN, 13046, CR_YELLOW, 320.0, 240.0, 0); + + delay(16); + + while(GetPlayerInput(-1, INPUT_BUTTONS) == 0) + { + delay(1); + } + + if(GetPlayerInput(-1, INPUT_BUTTONS) == BT_USE) + { + int pnum = PlayerNumber(); + SetUserCVAR(pnum, "vd_player_mapsbeat", 0); + SetUserCVAR(pnum, "vd_player_secrets", 0); + SetUserCVAR(pnum, "vd_player_kills", 0); + SetUserCVAR(pnum, "vd_player_maxkills", 0); + SetUserCVAR(pnum, "vd_player_sandboxkills", 0); + SetUserCVAR(pnum, "vd_player_sandboxbeat", 0); + } + + HUDMessage(s:"";HUDMSG_PLAIN, 13046, CR_UNTRANSLATED, 0, 0, 0); +} + +Script "tdbots_chnodetype" (void) +{ + NodeType++; + if(NodeType > NODE_PRECISE) {NodeType = NODE_NORMAL;} + if(NodelistVersion != 0) + switch(NodeType) + { + case NODE_NORMAL: + Print(s:"Node type: Normal (purple)"); + break; + + case NODE_JUMP: + Print(s:"Node type: Jump (green)"); + break; + + case NODE_PRECISE: + Print(s:"Node type: Precision (red)"); + break; + } +} + +Script "TDBots_NodeStudio_Startup" (void) +{ + if(PlayerCount() > 1) + {Print(s:"Node studio can be only used offline!"); terminate;} + if(GetCVAR("sv_freelook") == 1) + {Print(s:"Node studio requires freelook!"); terminate;} + + SetHUDSize(640, 480, TRUE); + SetFont("BIGFONT"); + + HUDMessage(s:"Press the use key to use version 1\n", + s:"Press any other key to use version 0\n", + s:"v1 allows use of jump and precision nodes\n", + s:"while v0 only has the basic node type"; + HUDMSG_PLAIN, 13076, CR_YELLOW, 320.0, 240.0, 0); + + delay(16); + + while(GetPlayerInput(-1, INPUT_BUTTONS) == 0) + { + delay(1); + } + + if(GetPlayerInput(-1, INPUT_BUTTONS) == BT_USE) + { + NodeListVersion = 1; + } + else + { + NodeListVersion = 0; + } + + HUDMessage(s:"";HUDMSG_PLAIN, 13076, CR_UNTRANSLATED, 0, 0, 0); + + Print(s:"Welcome to node studio.\nPress Weapon Reload for help with controls"); + ClearInventory(); Thing_Destroy(0); + ACS_NamedExecuteAlways("TDBots_NodeStudio_InputLoop",0); + SetPlayerProperty(1, TRUE, PROP_FLY); + SetPlayerProperty(1, 2, PROP_INVULNERABILITY); + SetActorFlag(0, "PICKUP", FALSE); + GiveInventory("TDBots_NodeStudio_Editor", 1); +} + +Script "TDBots_NodeStudio_InputLoop" (void) +{ + int buttons; + while(TRUE) + { + buttons = GetPlayerInput(-1, INPUT_BUTTONS); + + if(buttons & BT_RELOAD) {GiveInventory("TDB_NS_Reload",1);} + else {TakeInventory("TDB_NS_Reload",1);} + + if(buttons & BT_ZOOM) {GiveInventory("TDB_NS_Zoom",1);} + else {TakeInventory("TDB_NS_Zoom",1);} + + delay(1); + } +} + +Script "TDBots_NodeStudio_PlaceNode" (void) +{ + if(NodeAmount == 512) {Print(s:"Limit of 512 nodes reached."); terminate;} + int NodeX = GetActorX(0); + int NodeY = GetActorY(0); + int NodeZ = GetActorZ(0); + int TID = UniqueTID(); + /*PrintBold(s:"NodeX = ", i:NodeX); + PrintBold(s:"NodeY = ", i:NodeY); + PrintBold(s:"NodeZ = ", i:NodeZ); + PrintBold(s:"TID = ", i:TID);*/ + if(NodeListVersion == 0) + { + SpawnForced("TDBots_NodeStudio_FakeNode", NodeX, NodeY, NodeZ, TID); + } + else + { + switch(NodeType) + { + case NODE_NORMAL: + SpawnForced("TDBots_NodeStudio_FakeNode", NodeX, NodeY, NodeZ, TID); + break; + + case NODE_JUMP: + SpawnForced("TDBots_NodeStudio_FakeJumpNode", NodeX, NodeY, NodeZ, TID); + break; + + case NODE_PRECISE: + SpawnForced("TDBots_NodeStudio_FakePreciseNode", NodeX, NodeY, NodeZ, TID); + break; + } + } + SaveToNodeList(TID); +} + +Script "TDBots_NodeStudio_Undo" (void) {DeleteLastNode();} + +Script "TDBots_NodeStudio_Help" (void) +{ + Print(s:"Instructions have been printed to the console."); + + Log(s:"You will move in the direction you look to,"); + Log(s:"Including Up and Down. A Mouse is recommended"); + Log(s:"For easy operation of Node Studio."); + Log(s:""); + Log(s:"Attack/Fire = Place Node on crosshair"); + Log(s:"Alt Attack = Undo last placed node"); + Log(s:"Weapon Zoom = Save Nodelist"); + Log(s:"Weapon Ready = These Instructions"); + Log(s:"Change Node Type = Change node type (v1 only)"); + Log(s:"The Change node type key is not bound by default, go bind it!"); +} + +Script "TDBots_NodeStudio_Save" (void) +{ + int MapLmp = StrParam(n:PRINTNAME_LEVEL); + str cvartocheck; + if(NodeListVersion == 0) + { + cvartocheck = StrParam(s:MapLmp, s:"Nodes"); + } + else + { + cvartocheck = StrParam(s:MapLmp, s:"NodesNew"); + } + str LastListCharacter; + int NodeListLength = StrLen(NodeList); + str NodeListToSave; + //Print(s:"Saving to .ini file..."); + + NodeListToSave = NodeList; + + //Chop off the last ; on the list, because it can cause parsing errors. + if(NodeListLength > 0) + { + LastListCharacter = StrRight(NodeList, 1); + if(StrCmp(LastListCharacter, ";") == 0) + {NodeListToSave = StrLeft(NodeList, NodeListLength-1);} + } + + //SetCVARString(cvartocheck, NodeList); + Print(s:"The nodelist has been printed to the console.\nAlong with some instructions."); + Log(s:"===START OF HELP==="); + Log(s:"If you don't have a logfile active,"); + Log(s:"type logfile nodeoutput.txt in the console"); + Log(s:"and press this button again."); + Log(s:""); + Log(s:"Then, a text file called nodeoutput should be"); + Log(s:"In your ZDoom directory, so open it with"); + Log(s:"Notepad++, copy the line between the"); + Log(s:"COPY THIS markers, and paste it in another text"); + Log(s:"file, and name it whatever you want without spaces."); + Log(s:""); + Log(s:"Finally, to load it from ZDoom, type in the console"); + Log(s:"exec nameoffile.txt"); + Log(s:"====END OF HELP===="); + Log(s:"===COPY NEXT LINE==="); + Log(s:"set \"", + s:cvartocheck, s:"\" \"", s:NodeListToSave, s:"\""); + Log(s:"===COPY PREVIOUS LINE=="); +} + +//============================================================================== +// +// End of Node Studio +// +//============================================================================== \ No newline at end of file diff --git a/wadsrc_tdbots/static/scripts/TDB_Chat.acs b/wadsrc_tdbots/static/scripts/TDB_Chat.acs new file mode 100644 index 0000000000..2e74b84fa8 --- /dev/null +++ b/wadsrc_tdbots/static/scripts/TDB_Chat.acs @@ -0,0 +1,173 @@ +//Bot talking arrays, you should customize them to fit your mod/game. + +#DEFINE MAXMSGS_ENTER 55 +str BotSpeak_Enter[MAXMSGS_ENTER] = +{ + "Time to rack up some frags!", + "Another fight? Oh well.", + "Ready for heated combat!", + "One battle, hundreds of frags!", + "What, wanna lose again? Okay!", + "I'm putting all my effort in this fight.", + "Hey, want to watch your death counter go up?", + "Only the best will win, and that's me.", + "Got plans for you all, more pain!", + "Look at the time, It's time to kill!", + "h3y l00z3rs t1m3 2 g3t p0wn3d", + "Hey this isn't OpenArena!", + "Hola, no se hablar ingles.", + "We all know what happens, i win, you lose.", + "I am ready to roll!", + "Ohoho, this'll be good!", + "Ah, the site of one of my gest kills.", + "Back in my old stomping ground.", + "Let's get started.", + "Now, THIS is my kind of place!", + "Reporting for duty.", + "Here comes a new challenger!", + "I never thought I'd see this place again.", + "It's pay-back time!", + "Hi there", + "Hello", + "I bet you missed me", + "G'day", + "Yo! i'm in the house!", + "Wassup", + "Run in fear, i'm here!", + "Let's get it on!", + "Crap. Losers in here again!", + "Yessss! Fresh victims!", + "I'm back!", + "Showtime!", + "Let's rock and roll", + "Great! Easy frags ahead.", + "It's party time!", + "Let's kick it", + "Time for some fireworks", + "I'm here. There will be no survivors!", + "Guess its time to mow the grass", + "I am so ready for this", + "It's time for 'Bowling for Morons'", + "Fresh meat!", + "Howdy!", + "Let's get it ON!", + "Let's do it!", + "No Prisoners!", + "It's time for me to lay down the law here.", + "Hey, little kitty-cat, I have a treat for you.", + "Time to settle some scores!", + "Make this a good day, cuz it'll be your last.", + "I'm gonna kick your sorry butt into the next county." +}; + +#DEFINE MAXMSGS_ENTERCOOP 12 +str BotSpeak_EnterCoop[MAXMSGS_ENTERCOOP+1] = +{ + "Let's get them.", + "Lock and load!", + "I hear some monsters, let's kill them!", + "I wonder if this map has any keys?", + "This place makes me feel nervous.", + "Time to complete this operation.", + "I heard this place is really dangerous.", + "Okay, let's go, but don't steal all the ammo.", + "I am ready to roll!", + "Ohoho, this'll be good!", + "Let's get started.", + "I never thought I'd see this place again." +}; + +#DEFINE MAXMSGS_ROAM 29 +str BotSpeak_Roam[MAXMSGS_ROAM+1] = +{ + "Stop hiding, i can smell fear!", + "Come out, i'm coming for you.", + "What's the matter, scared?", + "One, two, three, ready or not, here i come!", + "This map is pretty nice.", + "This battle has been quite lenghty, huh?", + "All this fighting is getting me hungry.", + "Could you please stop camping? Thanks.", + "Come out and play!", + "Come on, I hate waiting.", + "...Okay, come on, maybe just burn a little something?", + "Party time! Need some fireworks?", + "Come on, show yourself.", + "A single death can change everything.", + "What's an aimbot?", + "The world could always use more heroes!", + "I don't like this standing around.", + "Make every second count!", + "Come on, i don't have all day", + "Lot of memories of this place. They weren't all bad.", + "Rocket jump? That sounds dangerous.", + "You're going to come with me, dead or alive.", + "Don't just stand around, do something.", + "Hm. This place is to my liking.", + "Come over here!", + "Staying out of trouble?", + "You might be fast, but you ain't faster than a bullet.", + "So many targets, so little time.", + "Old soldiers are hard to kill." +}; + +#DEFINE MAXMSGS_DEATH 30 +str BotSpeak_Die[MAXMSGS_DEATH] = +{ + "ARGH, YOU WILL PAY FOR THAT!", + "Alright, no more letting you win!", + "Nice shot, have you been practicing?", + "Dang, i better get some frags!", + "That was a lucky shot.", + "You fooled with the wrong person!", + "Beginner luck, but that's it!", + "Now you will see what i'm made of!", + "If at first you don't succeed... Blow it up again!", + "You won't get rid of me that easily.", + "Think you can do better than me?", + "Well that just happened.", + "Back to work!", + "That could have gone better.", + "This fight is not over yet!", + "Don't get me angry.", + "It's just a scratch.", + "Knock me down, and I'll keep getting back up.", + "A speedy recovery.", + "Where'd you learn to shoot like that?", + "Repetition teaches the smart.", + "Heh ... I had the safety on ...", + "I'm taking off the kid gloves now!", + "Okay, now I start fighting for real!", + "Enjoy that one. Your luck is about to run out", + "Now the match gets serious.", + "This was tragic", + "I will accept this as yet another learning experience", + "Ouch. That hurt", + "There will be some serious payback for this" +}; + +function void TDBots_Chat (int type) +{ + if(GetCVAR("tdbots_chat") == FALSE) + {return;} + + if(type == MSG_ENTER) + {Log(s:"\c*", n:0, s:"\c*: ", s:BotSpeak_Enter[random(0,MAXMSGS_ENTER-1)]);} + + else if(type == MSG_ENTERCOOP) + {Log(s:"\c*", n:0, s:"\c*: ", s:BotSpeak_EnterCoop[random(0,MAXMSGS_ENTERCOOP-1)]);} + + else if(type == MSG_ROAM) + { + if(GetCVAR("tdbots_roamchat") == FALSE) {return;} + Log(s:"\c*", n:0, s:"\c*: ", s:BotSpeak_Roam[random(0,MAXMSGS_ROAM-1)]); + } + + else if(type == MSG_DEATH) + {Log(s:"\c*", n:0, s:"\c*: ", s:BotSpeak_Die[random(0,MAXMSGS_DEATH-1)]);} + + else + {Log(s:"\c*", n:0, s:"\cg: Unknown message type ", i:type);} + + AmbientSound("misc/chat", 127); +} \ No newline at end of file diff --git a/wadsrc_tdbots/static/scripts/TDB_Defn.acs b/wadsrc_tdbots/static/scripts/TDB_Defn.acs new file mode 100644 index 0000000000..10d76bed52 --- /dev/null +++ b/wadsrc_tdbots/static/scripts/TDB_Defn.acs @@ -0,0 +1,33 @@ +int CurrentGame; +bool VanillaDoom = FALSE; + +//stupid ACS why can't you be normal with strings +str DummyWorkaroundString = "lol get bugged"; + +#DEFINE MAX_PLAYERS 64 + +#DEFINE BOTSPEED 30 + +#DEFINE GAME_DOOM 0 //Also the default for modded games +#DEFINE GAME_HERETIC 1 +#DEFINE GAME_HEXEN 2 +#DEFINE GAME_STRIFE 3 + +#DEFINE GAME_COOP 0 +#DEFINE GAME_DM 1 + +#DEFINE AXIS_X 0 +#DEFINE AXIS_Y 1 +#DEFINE AXIS_Z 2 +#DEFINE NODETYPE 3 + +#define NODE_NORMAL 0 +#define NODE_JUMP 1 +#define NODE_PRECISE 2 + +#DEFINE MSG_ENTER 0 +#DEFINE MSG_ENTERCOOP 1 +#DEFINE MSG_ROAM 2 +#DEFINE MSG_DEATH 3 + +#DEFINE TELTID 16231 \ No newline at end of file diff --git a/wadsrc_tdbots/static/scripts/TDB_Inv.acs b/wadsrc_tdbots/static/scripts/TDB_Inv.acs new file mode 100644 index 0000000000..871ff7c4ed --- /dev/null +++ b/wadsrc_tdbots/static/scripts/TDB_Inv.acs @@ -0,0 +1,48 @@ +//If there's a custom action in the current mod loaded, one or more of these +//should be true. + +bool TDB_Custom_Roam1; +bool TDB_Custom_Roam2; + +bool TDB_Custom_Attk1; +bool TDB_Custom_Attk2; + +function void TDB_GiveItem (str item) +{ + Spawn(item, GetActorX(0), GetActorY(0), GetActorZ(0), 987005); +} + +function void TDB_RA_Give (str item) +{ + int x = GetActorX(0); + int y = GetActorY(0); + int z = GetActorZ(0); + + Spawn(item, x, y, z, 987005); + Spawn(item, x, y, z, 987005); + Spawn(item, x, y, z, 987005); + Spawn(item, x, y, z, 987005); + Spawn(item, x, y, z, 987005); +} + +function void TDB_MaxGive (str item) +{ + GiveInventory(item, 0x7FFFFFFF); +} + +function void Codepointer (str actionfunction) +{ + GiveInventory(actionfunction, 1); + SetPlayerProperty(0,1,4); //Reset the frozen property every codepointer +} + +//To check for custom thinking modules +function bool TDB_ActorExists (str actor) +{ + if(SpawnForced(actor,0,0,0,15423,0)) + { + Thing_Remove(15423); + return TRUE; + } + return FALSE; +} \ No newline at end of file diff --git a/wadsrc_tdbots/static/scripts/TDB_Main.acs b/wadsrc_tdbots/static/scripts/TDB_Main.acs new file mode 100644 index 0000000000..1f916d5c9c --- /dev/null +++ b/wadsrc_tdbots/static/scripts/TDB_Main.acs @@ -0,0 +1,330 @@ +//TDBots: The fast-performing bots +// +//(C) 2021 Moises Aguirre "TDRR" +// +//Licensed under the MIT license + +#library "TDB_Main" +#include "zcommon.acs" + +#include "TDB_Defn.acs" + +#include "TDB_Chat.acs" +#include "TDB_Zan.acs" +#include "TDB_Inv.acs" +#include "TDB_RA.acs" +#include "TDB_Misc.acs" + +#include "TDB_Node.acs" + +//Internal variables, i recommend you don't change these. +bool LightRoam = TRUE; + +int Gamemode; + +bool BotNeeded; +bool OtherBotAssisted; + +Script "TDBots_TeleportBotGiver" ENTER +{ + if(GameType() == GAME_NET_DEATHMATCH) {terminate;} + if(PlayerIsBot(PlayerNumber()) == FALSE) + { + if(GetCVAR("tdbots_allowteleport") == TRUE) + {GiveInventory("TDBot_TeleportCall", 1);} + } +} + +Script "TDBots_TeleportBotGiver2" RESPAWN +{ + TakeInventory("TDBot_DoNotAllowTeleport", 1); + ACS_NamedExecuteAlways("TDBots_TeleportBotGiver", 0); +} + +function bool TDB_AnyBotConnected(void) +{ + for (int i = 0; i < 64; i++) + { + if (PlayerIsBot(i)) + {return TRUE;} + } + return FALSE; +} + +Script "TDBots_NeedSomeHelp" (void) +{ + int delayamount = GetCVAR("tdbots_teleportdelaytime")*35; + if(delayamount < 35*5) {delayamount = 35*5;} + else if(delayamount > 35*120) {delayamount = 35*120;} + else if(delayamount == 0) {delayamount = 35*30;} + if(BotNeeded == FALSE) + { + if(TDB_AnyBotConnected() == TRUE) + { + BotNeeded = TRUE; + ACS_NamedExecuteAlways("TDBots_BotNeededWait",0); + SetResultValue(TRUE); + GiveInventory("TDBot_DoNotAllowTeleport", 1); + delay(delayamount); + TakeInventory("TDBot_DoNotAllowTeleport", 1); + terminate; + } + else + { + Print(s:"No bot is currently connected."); + SetResultValue(FALSE); + } + } + else + { + Print(s:"Can't call a bot at the same time\nas another player!"); + SetResultValue(FALSE); + } +} + +Script "TDBots_BotNeededWait" (void) +{ + delay(35*5); + BotNeeded = FALSE; +} + +Script "TDBots_IsAlly" (void) +{ + //will be 255 in Co-op, so bots should still avoid shooting players as + //they'll have the same teamid. + int team = GetPlayerInfo(PlayerNumber(), PLAYERINFO_TEAM); + + //if regular deathmach, nothing to do here, shoot every living thing on sight + if( (Gametype() == GAME_NET_DEATHMATCH) && (!GetCVAR("teamplay"))) + { + SetResultValue(FALSE); + terminate; + } + + SetActivatorToTarget(0); + + if(PlayerNumber() >= 0) //is player + { + SetResultValue( (team == GetPlayerInfo(PlayerNumber(), PLAYERINFO_TEAM)) ); + terminate; + } + else + { + SetResultValue(CheckFlag(0, "FRIENDLY")); + terminate; + } + SetResultValue(FALSE); //just in case. +} + +Script "TDBots_TeleportBot" (void) +{ + int ImWhoAssisted; + delay(random(0,16)); + while(GetActorProperty(0, APROP_HEALTH) > 0) + { + delay(60); + if(BotNeeded == TRUE) + { + if(OtherBotAssisted == FALSE || ImWhoAssisted == TRUE) + { + OtherBotAssisted = TRUE; + ImWhoAssisted = TRUE; + if(SetActorPosition(0,GetActorX(TELTID), GetActorY(TELTID), GetActorZ(TELTID), TRUE)) + { + delay(1); + Thing_Remove(TELTID); + BotNeeded = FALSE; + OtherBotAssisted = FALSE; + ImWhoAssisted = FALSE; + } + else + { + delay(1); + SpawnForced("Bot_TeleportSpot", GetActorX(TELTID), GetActorY(TELTID), GetActorZ(TELTID), TELTID); + Thing_Remove(TELTID); + } + } + } + } +} + +Script "TDBots_WalkNoding" (void) +{ + if(PlayerIsBot(PlayerNumber()) || GetCVAR("tdbots_playerbot")) {terminate;} + if(GetCVAR("sv_forcerespawn") == FALSE) + {ACS_NamedExecuteAlways("TDBots_ZandronumCommands",0);} + if(Gametype() > GAME_NET_COOPERATIVE) {terminate;} //Can't follow in competitive gamemodes + if(!GetCVAR("tdbots_follow")) {terminate;} + + int TID; + + while(GetActorProperty(0, APROP_HEALTH) > 0) + { + for(int i = 0; i < MAX_FOLLOW_NODES; i++) + { + TID = UniqueTID(); + + delay(16); + SpawnForced("TDBots_TempNode", GetActorX(0), GetActorY(0), GetActorZ(0), TID); + + PlayerFollowTID[PlayerNumber()][i] = TID; + PlayerLocation[PlayerNumber()][0] = GetActorX(0); + PlayerLocation[PlayerNumber()][1] = GetActorY(0); + PlayerLocation[PlayerNumber()][2] = GetActorZ(0); + } + } +} + +Script "TDBots_BotAnimation" (void) +{ + int tics; + while(TDBotDead() == FALSE) + { + if(tics % 4 == 0) //if 16 tics have passed + { + if(CheckInventory("BotAttack") == FALSE) {AnimateTDBot("See");} + } + delay(4); + if(TDBotDead() == TRUE) {terminate;} + tics++; + } +} + +Script "TDBots_BotThink" (void) +{ + int firetime; + int tics; + + if(TDBotDead() == TRUE) {terminate;} + + if(TDB_Custom_Roam1 == TRUE) {Codepointer("TDBots_Custom_RoamLoop1");} + if(TDB_Custom_Roam2 == TRUE) {Codepointer("TDBots_Custom_RoamLoop2");} + + if(LightRoam == TRUE) + {Codepointer("BotFunc_LightRoam"); LightRoam = FALSE;} + else + {Codepointer("BotFunc_Roam"); LightRoam = TRUE;} + Delay(3); + + TDB_FollowNode(); + + if(TDBotDead() == TRUE) {terminate;} + if(random(0, 256) == 1) {TDBots_Chat(MSG_ROAM);} + + Codepointer("BotFunc_CheckLOS"); delay(1); + + TDB_RandomWeapon(); + + for(firetime = 0; firetime < random(26, 52); firetime++) + { + if(CheckInventory("BotAttack") == TRUE) + { + if(firetime == 0) + { + if(GetCVAR("tdbots_reactiontime") <= 70) + { + if(GetCVAR("tdbots_reactiontime") >= 0) + {Delay(GetCVAR("tdbots_reactiontime"));} + else {Delay(1);} + } + else + {Delay(70);} + } + + if(TDB_Custom_Attk1 == TRUE) {Codepointer("TDBots_Custom_AttackLoop1");} + Codepointer("BotFunc_AimDodge"); + ACS_NamedExecuteAlways("TDBots_UseItems", 0, FALSE); + if(TDB_Custom_Attk2 == TRUE) {Codepointer("TDBots_Custom_AttackLoop2");} + AnimateTDBot("Missile"); + + if(random(0,256) <= 128) + { + Codepointer("BotFunc_Strafe"); + Codepointer("BotFunc_Aim"); + if(TDB_IsZandronum() == TRUE) + { + for(tics = 0; tics <= 16; tics++) + { + if(tics % 2 == 0) + {if(TDBotDead() == TRUE) {terminate;}} + + if(tics % 4 == 0) + {Codepointer("BotFunc_Strafe");} + + Codepointer("BotFunc_Aim"); delay(2); + } + } + else + { + for(tics = 0; tics <= 32; tics++) + { + if(tics % 4 == 0) + {if(TDBotDead() == TRUE) {terminate;}} + + if(tics % 8 == 0) + {Codepointer("BotFunc_Strafe");} + + Codepointer("BotFunc_Aim"); delay(1); + } + } + } + else if(random(0,256) <= 168) + { + Codepointer("BotFunc_Strafe2"); + Codepointer("BotFunc_Aim"); + if(TDB_IsZandronum() == TRUE) + { + for(tics = 0; tics <= 16; tics++) + { + if(tics % 2 == 0) + {if(TDBotDead() == TRUE) {terminate;}} + + if(tics % 4 == 0) + {Codepointer("BotFunc_Strafe2");} + + Codepointer("BotFunc_Aim"); delay(2); + } + } + else + { + for(tics = 0; tics <= 32; tics++) + { + if(tics % 4 == 0) + {if(TDBotDead() == TRUE) {terminate;}} + + if(tics % 8 == 0) + {Codepointer("BotFunc_Strafe2");} + + Codepointer("BotFunc_Aim"); delay(1); + } + } + } + } + else + { + Codepointer("BotFunc_Roam"); + Delay(4); + break; + } + Delay(1); + if(TDBotDead() == TRUE) {terminate;} + } + firetime = 0; + Codepointer("BotFunc_FireStop"); + if(GetActorProperty(0, APROP_HEALTH) > 0) {restart;} +} + +Script "TDBots_BotDeath" DEATH +{ + TakeInventory("TDBot_DoNotAllowTeleport",9999); + if(GetCVAR("sv_forcerespawn") == FALSE) + {ACS_NamedExecuteAlways("TDBots_ZandronumCommands",0);} + if(PlayerIsBot(PlayerNumber()) == TRUE || GetCVAR("tdbots_playerbot") == TRUE) + { + if(GetCVAR("tdbots_enable") == TRUE || PlayerIsBot(PlayerNumber()) == FALSE) + { + if(random(0, 100) < 12) + {TDBots_Chat(MSG_DEATH);} + } + } +} \ No newline at end of file diff --git a/wadsrc_tdbots/static/scripts/TDB_Misc.acs b/wadsrc_tdbots/static/scripts/TDB_Misc.acs new file mode 100644 index 0000000000..44fe530727 --- /dev/null +++ b/wadsrc_tdbots/static/scripts/TDB_Misc.acs @@ -0,0 +1,417 @@ +bool BotAnim = TRUE; + +function void TDBots_SwapWeapons (void) +{ + if(CheckInventory("Pistol")) + { + TakeInventory("Fist", 1); + TakeInventory("Pistol", 1); + GiveInventory("BotFist", 1); + GiveInventory("BotPistol", 1); + + SetWeapon("BotPistol"); + + CurrentGame = GAME_DOOM; + VanillaDoom = TRUE; + } + else if(CheckInventory("GoldWand")) + { + TakeInventory("Staff", 1); + TakeInventory("GoldWand", 1); + GiveInventory("BotStaff", 1); + GiveInventory("BotGoldWand", 1); + + SetWeapon("BotGoldWand"); + + CurrentGame = GAME_HERETIC; + } + else if(CheckInventory("CWeapMace")) + { + TakeInventory("CWeapMace", 1); + GiveInventory("BotCWeapMace", 1); + + SetWeapon("BotCWeapMace"); + + CurrentGame = GAME_HEXEN; + } + else if(CheckInventory("FWeapFist")) + { + TakeInventory("FWeapFist", 1); + GiveInventory("BotFWeapFist", 1); + + SetWeapon("BotFWeapFist"); + + CurrentGame = GAME_HEXEN; + } + else if(CheckInventory("MWeapWand")) + { + TakeInventory("MWeapWand", 1); + GiveInventory("BotMWeapWand", 1); + + SetWeapon("BotMWeapWand"); + + CurrentGame = GAME_HEXEN; + } + else if(CheckInventory("PunchDagger")) + { + TakeInventory("PunchDagger", 1); + GiveInventory("BotPunchDagger", 1); + + SetWeapon("BotPunchDagger"); + + CurrentGame = GAME_STRIFE; + } + else if(CheckInventory("MiniZorcher")) + { + TakeInventory("MiniZorcher", 1); + TakeInventory("Bootspoon", 1); + GiveInventory("BotBootspoon", 1); + GiveInventory("BotMiniZorcher", 1); + + SetWeapon("BotMiniZorcher"); + + CurrentGame = GAME_DOOM; //For all intents and purposes, Chex is Doom. + } + + str CGVAR = GetCVAR("tdbots_currentgame"); + + if(CGVAR != -1) + { + CurrentGame = CGVAR; + } + else + { + SetCVAR("tdbots_currentgame", CurrentGame); + } +} + +function void TDBots_Weaponize (void) +{ + if(GetCVAR("tdbots_rocketarena") == TRUE) {return;} + if(GetCVAR("tdbots_weaponize") > 5) + { + if(random(0,4) == 1) + { + if(CurrentGame == GAME_DOOM) {TDB_GiveItem("BFG9000");} + else if(CurrentGame == GAME_HERETIC) {TDB_GiveItem("Mace");} + else if(CurrentGame == GAME_STRIFE) {TDB_GiveItem("StrifeGrenadeLauncher");} + } + } + if(GetCVAR("tdbots_weaponize") > 4) + { + if(random(0,4) == 1) + { + //No SSG equivalent in Heretic so only Doom here. + if(CurrentGame == GAME_DOOM) {TDB_GiveItem("SuperShotgun");} + else if(CurrentGame == GAME_STRIFE) {TDB_GiveItem("Mauler");} + } + } + if(GetCVAR("tdbots_weaponize") > 3) + { + if(random(0,3) == 1) + { + if(CurrentGame == GAME_DOOM) {TDB_GiveItem("PlasmaRifle");} + else if(CurrentGame == GAME_HERETIC) {TDB_GiveItem("SkullRod");} + else if(CurrentGame == GAME_STRIFE) {TDB_GiveItem("MiniMissileLauncher");} + } + } + if(GetCVAR("tdbots_weaponize") > 2) + { + if(random(0,3) == 1) + { + if(CurrentGame == GAME_DOOM) {TDB_GiveItem("RocketLauncher");} + else if(CurrentGame == GAME_HERETIC) {TDB_GiveItem("PhoenixRod");} + else if(CurrentGame == GAME_STRIFE) {TDB_GiveItem("AssaultGun");} + else if(CurrentGame == GAME_HEXEN) + {TDB_GiveItem("CWeapWraithverge"); TDB_GiveItem("FWeapQuietus"); TDB_GiveItem("MWeapBloodscourge");} + } + } + if(GetCVAR("tdbots_weaponize") > 1) + { + if(random(0,3) == 1) + { + if(CurrentGame == GAME_DOOM) {TDB_GiveItem("Chaingun");} + else if(CurrentGame == GAME_HERETIC) {TDB_GiveItem("Blaster");} + else if(CurrentGame == GAME_STRIFE) {TDB_GiveItem("AssaultGun");} + else if(CurrentGame == GAME_HEXEN) + {TDB_GiveItem("CWeapFlame"); TDB_GiveItem("FWeapHammer"); TDB_GiveItem("MWeapLighting");} + } + } + if(GetCVAR("tdbots_weaponize") > 0) + { + if(CurrentGame == GAME_DOOM) {TDB_GiveItem("Shotgun");} + else if(CurrentGame == GAME_HERETIC) {TDB_GiveItem("Crossbow");} + else if(CurrentGame == GAME_HEXEN) + {TDB_GiveItem("CWeapStaff"); TDB_GiveItem("FWeapAxe"); TDB_GiveItem("MWeapFrost");} + else if(CurrentGame == GAME_STRIFE) {TDB_GiveItem("StrifeCrossbow");} + } +} + +function void TDBots_Setup (bool respawning) +{ + if(PlayerIsBot(PlayerNumber()) == TRUE || GetCVAR("tdbots_playerbot") == TRUE) + { + if( (GetCVAR("tdbots_enable") == TRUE) || (PlayerIsBot(PlayerNumber()) == FALSE) ) + { + if(GetCVAR("tdbots_lessfov") == TRUE) {GiveInventory("tdbots_lessfov",1);} + if(BotAnim == TRUE) {ACS_NamedExecuteAlways("TDBots_BotAnimation", 0);} + if(GetCVAR("tdbots_buff") == TRUE) {ACS_NamedExecuteAlways("TDBots_BotBuff",0);} + Codepointer("BotFunc_StartUp"); + + if(Respawning == FALSE) + { + if(Gametype() <= GAME_NET_COOPERATIVE) + {TDBots_Chat(MSG_ENTERCOOP);} + else + {TDBots_Chat(MSG_ENTER);} + } + + ACS_NamedExecuteAlways("TDBots_TeleportBot",0); + + if( (GetCVAR("tdbots_usenodes")) || (GetCVAR("tdbots_learnfromplayer")) ) + { + GiveInventory("TDBots_NavNodes", 1); + } + + if(GetActorProperty(0, APROP_ACCURACY) == 0) + {SetActorProperty(0, APROP_ACCURACY, BOTSPEED);} + + //Magic value to make the bots not move + else if(GetActorProperty(0, APROP_ACCURACY) == -1) + {SetActorProperty(0, APROP_ACCURACY, BOTSPEED);} + } + } +} + +str TDB_Weapons[27] = +{ + "BotPistol", + "BotShotgun", + "BotSuperShotgun", + "BotChaingun", + "BotRocketLauncher", + "BotPlasmaRifle", + "BotBFG9000", + + "BotGoldWand", + "BotMace", + "BotSkullRod", + "BotPhoenixRod", + "BotBlaster", + "BotCrossbow", + + "BotStrifeGrenadeLauncher", + "BotMauler", + "BotMiniMissileLauncher", + "BotAssaultGun", + "BotStrifeCrossbow", + + "BotCWeapWraithverge", + "BotFWeapQuietus", + "BotMWeapBloodscourge", + "BotCWeapFlame", + "BotFWeapHammer", + "BotMWeapLighting", + "BotCWeapStaff", + "BotFWeapAxe", + "BotMWeapFrost" +}; + +function void TDB_RandomWeapon (void) +{ + if(random(0,100) > 2) {return;} + switch(CurrentGame) + { + case GAME_DOOM: + if(!VanillaDoom) {return;} + SetWeapon( TDB_Weapons[ random(1,6) ] ); + break; + + case GAME_HERETIC: + SetWeapon( TDB_Weapons[ random(8,12) ] ); + break; + + case GAME_STRIFE: + SetWeapon( TDB_Weapons[ random(14,17) ] ); + break; + + case GAME_HEXEN: + SetWeapon( TDB_Weapons[ random(19,26) ] ); + break; + } +} + +Script "TDBots_BotSetup" ENTER +{ + if(Gametype() <= GAME_NET_COOPERATIVE) + { + ACS_NamedExecuteAlways("TDBots_WalkNoding", 0); + } + + TDBots_SwapWeapons(); + delay(1); + TDBots_Setup(FALSE); + delay(2); + TDBots_Weaponize(); + TDBots_RA_Equip(); + delay(4); + Thing_Remove(987005); +} + +Script "TDBots_BotSetup_Respawn" RESPAWN +{ + if(Gametype() <= GAME_NET_COOPERATIVE) + { + ACS_NamedExecuteAlways("TDBots_WalkNoding", 0); + } + + TDBots_SwapWeapons(); + delay(1); + TDBots_Setup(TRUE); + delay(2); + TDBots_Weaponize(); + TDBots_RA_Equip(); + delay(4); + Thing_Remove(987005); +} + +//Gives the bots ammunition and armor every five seconds, to help them a bit +Script "TDBots_BotBuff" (void) +{ + delay(35 * 5); + if(GetCVAR("tdbots_buff") == 1) + { + while(GetActorProperty(0, APROP_HEALTH) > 0) + { + if(CurrentGame == GAME_DOOM) {Codepointer("BotFunc_BuffDoom");} + if(CurrentGame == GAME_HERETIC) {Codepointer("BotFunc_BuffHeretic");} + if(CurrentGame == GAME_HEXEN) {Codepointer("BotFunc_BuffHexen");} + if(CurrentGame == GAME_STRIFE) {Codepointer("BotFunc_BuffStrife");} + delay(1); + Thing_Remove(987000); + delay(35 * 5); + } + } + else {terminate;} +} + +Script "TDBots_BotEasyMode" (void) +{ + if( GetCvar("tdbots_easymode") || CheckInventory("BotForceInacc") ) + { + SetActorAngle(0, GetActorAngle(0) + random(-2048, 2048)); + } +} + +Script "TDBots_UseItems" (int Roam) +{ + int Rand; + if(Roam == TRUE) //Called from roaming state? + { + if(CurrentGame == GAME_HERETIC || GAME_HEXEN) + { + Rand = Random(0,8); + + if(Rand == 0) + {UseInventory("ArtiHealth");} + else if(Rand == 1) + {UseInventory("ArtiSuperHealth");} + else if(Rand == 2) + {UseInventory("ArtiFly");} + else if(Rand == 3) + { + UseInventory("ArtiInvulnerability"); //Heretic's + UseInventory("ArtiInvulnerability2");//Hexen's + } + else if(Rand == 4) + {UseInventory("ArtiTeleport");} + //No torch, since obviously bots don't need it + } + } + else //No, so we assume it's been called from the attack state. + { + if(CurrentGame == GAME_HERETIC) + { + Rand = Random(0,4); + + if(Rand == 0) + {UseInventory("ArtiEgg");} + else if(Rand == 1) + {UseInventory("ArtiTomeOfPower");} + else if(Rand == 2) + {UseInventory("ArtiInvisibility");} + else if(Rand == 3) + {UseInventory("ArtiTimeBomb");} + else if(Rand == 4) + {UseInventory("ArtiPork");} + } + else if(CurrentGame == GAME_HEXEN) + { + Rand = Random(0,6); + + if(Rand == 0) + {UseInventory("ArtiEgg");} + else if(Rand == 1) + {UseInventory("ArtiDarkServant");} + else if(Rand == 2) + {UseInventory("ArtiTeleportOther");} + else if(Rand == 3) + {UseInventory("ArtiBoostArmor");} + else if(Rand == 4) + {UseInventory("ArtiBlastRadius");} + else if(Rand == 5) + {UseInventory("ArtiPork");} + else if(Rand == 6) + { + UseInventory("ArtiPoisonBag"); //Doesn't look like a bag but ok. + UseInventory("ArtiPoisonBag1"); + UseInventory("ArtiPoisonBag2"); + UseInventory("ArtiPoisonBag3"); + } + } + } +} + +Script "TDBots_CheckForCustomModules" OPEN +{ + //Roaming + if(TDB_ActorExists("TDBots_Custom_RoamLoop1")) + {TDB_Custom_Roam1 = TRUE;} + + if(TDB_ActorExists("TDBots_Custom_RoamLoop2")) + {TDB_Custom_Roam2 = TRUE;} + + //Attacking + if(TDB_ActorExists("TDBots_Custom_AttackLoop1")) + {TDB_Custom_Attk1 = TRUE;} + + if(TDB_ActorExists("TDBots_Custom_AttackLoop2")) + {TDB_Custom_Attk2 = TRUE;} + + //Global flags + if(TDB_ActorExists("TDBots_NoAnimation")) + {BotAnim = FALSE;} +} + +function void AnimateTDBot (str state) +{ + if(BotAnim == FALSE) {return;} + SetActorState(0, state, 1); + SetPlayerProperty(0,1,4); +} + +function bool TDBotDead (void) +{ + bool BotIsDead; + if(GetActorProperty(0, APROP_HEALTH) <= 0) + { + Codepointer("BotFunc_Die"); + SetActorState(0, "Death", 1); + BotIsDead = TRUE; + } + else + { + BotIsDead = FALSE; + } + return BotIsDead; +} \ No newline at end of file diff --git a/wadsrc_tdbots/static/scripts/TDB_Node.acs b/wadsrc_tdbots/static/scripts/TDB_Node.acs new file mode 100644 index 0000000000..4e88236335 --- /dev/null +++ b/wadsrc_tdbots/static/scripts/TDB_Node.acs @@ -0,0 +1,478 @@ +//Nodelist parsing and loading. +#DEFINE MAX_NODES 512 +#DEFINE MAX_PROPS 4 +#DEFINE MAX_LEARN_TIME_P1 35*30 +#DEFINE MAX_LEARN_TIME_P2 35*60 +#DEFINE MAX_LEARN_TIME_P3 35*90 +#DEFINE MAX_LEARN_TIME 35*120 //2 nice minutes max +str NodePropStr[MAX_NODES][MAX_PROPS]; //Raw strings, have to be converted. + +int NodeTID[MAX_NODES]; + +//Player-generated nodes, then transfered to NodeTID once finished. +int PlayNodeTID[MAX_NODES]; +int CurrPNTID; //Keep track of current index on above array + +//Player following-related stuff. +#DEFINE MAX_FOLLOW_NODES 16 +int PlayerFollowTID[MAX_PLAYERS][MAX_FOLLOW_NODES]; +int PlayerLocation[MAX_PLAYERS][3]; + +//Time since map start +int TSMS = -1; + +int AmountOfNodes; + +//Note: commented log lines are just for debugging, you can uncomment them +//if you need to. +function void ProcessNodelist (str string, bool version) +{ + //Don't bother doing anything if it's just empty + if(strcmp(string, "") == 0) {return;} + + //Length of the string passed + int stringlength = strlen(string); + + //StrMid parameters, to copy a full actor name to the StartWeapons array + int strmidstart; + int strmidend; + + //Keeps track of current character index so i can pass it to strmidstart + //even after restarting it + int strmidstart2; + + //Actor name and currently stored character + int currentarrayindex; + str storedchar; + + int curraxis; + for(int currchar = 0; currchar <= stringlength; currchar++) + { + storedchar = StrMid(string, currchar, 1);//GetChar didn't work dunno why + + if(version == 0) + { + if(curraxis > AXIS_Z) {curraxis = AXIS_X;} + } + else + { + if(curraxis > NODETYPE) {curraxis = AXIS_X;} + } + + //If a separator is detected, switch to next actor name and clear the + //current string saved in memory. + if(currchar == stringlength) + { + amountofnodes = currentarrayindex; + //log(s:strmid(string, strmidstart, strmidend)); + NodePropStr[currentarrayindex][curraxis] = strmid(string, strmidstart, strmidend); + if(version == 0) + { + NodePropStr[currentarrayindex][NODETYPE] = ""; + currentarrayindex++; + } + return; + } + if(StrCmp(storedchar, ";") == 0) + { + //log(s:strmid(string, strmidstart, strmidend)); + NodePropStr[currentarrayindex][curraxis] = strmid(string, strmidstart, strmidend); + curraxis++; + if( (version == 0) && (curraxis > AXIS_Z) ) + { + NodePropStr[currentarrayindex][NODETYPE] = ""; + currentarrayindex++; + } + else if( (version == 1) && (curraxis > NODETYPE) ) + { + currentarrayindex++; + } + + strmidstart = strmidstart2+1; //needs to be AFTER the separator + strmidstart2++; + strmidend = 0; + //log(s: "strmidstart = ",i:strmidstart); + } + //If not, just continue storing the actor name. + else + { + strmidend++; + strmidstart2++; + //log(s: "strmidend = ",i:strmidend); + } + } +} + +//I have to parse the values TWICE because you can't use strings as integers. +//Thanks, ACS limitations! + +//And more importantly, thanks to Empyre who gave me an example on how to +//join numbers. I'm so stupid i didn't imagine just 4 * 10 + 7 would +//give me 47 :P +function int StrToInt (str string) +{ + //Don't bother doing anything if it's just empty + if(strcmp(string, "") == 0) {return FALSE;} + + //Length of the string passed + int stringlength = strlen(string); + + //Actor name and currently stored character + int currentarrayindex; + + str storedchar; + str storedcharcpy; + + int returnvalue; + + str firstchar = StrLeft(string, 1); + for(int currchar = 0; currchar <= stringlength; currchar++) + { + storedchar = StrMid(string, currchar, 1);//GetChar didn't work dunno why + storedcharcpy = storedchar; + + if(Strcmp(storedcharcpy, "0") == 0) + {returnvalue = returnvalue*10;} + + else if(Strcmp(storedcharcpy, "1") == 0) + {returnvalue = returnvalue*10+1;} + + else if(Strcmp(storedcharcpy, "2") == 0) + {returnvalue = returnvalue*10+2;} + + else if(Strcmp(storedcharcpy, "3") == 0) + {returnvalue = returnvalue*10+3;} + + else if(Strcmp(storedcharcpy, "4") == 0) + {returnvalue = returnvalue*10+4;} + + else if(Strcmp(storedcharcpy, "5") == 0) + {returnvalue = returnvalue*10+5;} + + else if(Strcmp(storedcharcpy, "6") == 0) + {returnvalue = returnvalue*10+6;} + + else if(Strcmp(storedcharcpy, "7") == 0) + {returnvalue = returnvalue*10+7;} + + else if(Strcmp(storedcharcpy, "8") == 0) + {returnvalue = returnvalue*10+8;} + + else if(Strcmp(storedcharcpy, "9") == 0) + {returnvalue = returnvalue*10+9;} + + currentarrayindex++; + } + if(Strcmp(firstchar, "-") == 0) + {returnvalue = -returnvalue;} + return returnvalue; +} + +//Function from ACSUtils, licensed under the MIT License. +//Copyright (c) 2016-2017 By Alexander Korshun and the ACSUtils contributors. +//Originally called ActorDistance2D. +function int Distance(int tid1, int tid2) +{ + return VectorLength(GetActorX(tid2) - GetActorX(tid1), + GetActorY(tid2) - GetActorY(tid1)); +} + +function void TDB_FollowNode (void) +{ + //Log(s:"Starting up TDB_FollowNode..."); + + TakeInventory("TDBots_FollowNodeZan", 1); + + if(Gametype() <= GAME_NET_COOPERATIVE) + { + TDB_FollowPlayer(); + return; + } + + if( (!GetCVAR("tdbots_usenodes")) && (!GetCVAR("tdbots_learnfromplayer")) ) {return;} + if(CheckInventory("BotAttack")) {return;} + + //Log(s:"TDB_FollowNode started up!"); + + int dist,node,newdist,retnode; + + dist = 384.0; + for(int i_ = 0; i_ <= MAX_NODES; i_++) + { + node = NodeTID[i_]; + if(node == 0) {break;} + newdist = Distance(0, node); + + if( (newdist < dist) && CheckSight(0, node, CSF_NOFAKEFLOORS)) + { + dist = newdist; + retnode = node; + } + } + SetPointer(AAPTR_TARGET, retnode, AAPTR_DEFAULT, PTROP_NOSAFEGUARDS|PTROP_UNSAFEMASTER); //return retnode; + GiveInventory("TDBots_FollowNodeZan", 1); + //Log(s:"Hating node ", i:retnode); + return; +} + +function void TDB_FollowPlayer (void) +{ + //Log(s:"Starting up TDB_FollowNode..."); + + TakeInventory("TDBots_FollowNodeZan", 1); + + if(!GetCVAR("tdbots_follow")) {return;} + if(CheckInventory("BotAttack")) {return;} + + //Log(s:"TDB_FollowNode started up!"); + + int dist,node,newdist,retnode; + int pnum; + + dist = 2048.0; + if(IsNetworkGame()) //if not network game then pnum will always be 0 + { + for(int i_ = 0; i_ < MAX_PLAYERS; i_++) + { + if(PlayerLocation[i_][0] + PlayerLocation[i_][1] != 0) + { + newdist = VectorLength(PlayerLocation[i_][0] - GetActorX(0), + PlayerLocation[i_][1] - GetActorY(0)); + + if(newdist < dist) + { + dist = newdist; + pnum = i_; + } + } + } + } + + dist = 768.0; + for(int i = 0; i < MAX_FOLLOW_NODES; i++) + { + node = PlayerFollowTID[pnum][i]; + if(node == 0) {break;} + + if(ThingCount(0, node) > 0) + { + newdist = Distance(0, node); + + if( (newdist < dist) && CheckSight(0, node, CSF_NOFAKEFLOORS)) + { + dist = newdist; + retnode = node; + } + } + } + SetPointer(AAPTR_TARGET, retnode, AAPTR_DEFAULT, PTROP_NOSAFEGUARDS|PTROP_UNSAFEMASTER); //return retnode; + GiveInventory("TDBots_FollowNodeZan", 1); + //Log(s:"Hating node ", i:retnode); + return; +} + +function bool TDB_PlayerNoding (void) +{ + if(CurrPNTID > MAX_NODES) {return FALSE;} + + int node,newdist,retnode; + int dist = 160.0; + + for(int i_ = 0; i_ <= MAX_NODES; i_++) + { + node = PlayNodeTID[i_]; + if(node == 0) {break;} + newdist = Distance(0, node); + + if( (newdist < dist) && CheckSight(0, node, CSF_NOFAKEFLOORS) ) + { + return FALSE; + } + } + + int TID = UniqueTID(); + SpawnForced("TDBots_PathNode",GetActorX(0),GetActorY(0),GetActorZ(0),TID); + PlayNodeTID[CurrPNTID] = TID; + CurrPNTID++; + return TRUE; +} + +function void TDB_CopyNodeTIDs (void) +{ + if(NodeTID[0] != 0) {return;} + int node; + for(int i; i <= MAX_NODES; i++) + { + node = PlayNodeTID[i]; + if(node == 0) {break;} + NodeTID[i] = node; + } +} + +Script "TDBots_Jump" (int force) //force param for wall jumps and such in Vault +{ + if( (GetActorVelZ(0) == 0) || (force == TRUE) ) + { + int JumpZ = GetActorProperty(0, APROP_JUMPZ); + SetActorVelocity(0, 0, 0, JumpZ, TRUE, FALSE); + } +} + +//I couldn't figure out how to put this into the main script without it looking +//exceptionally awful, so separated it into a function. + +//And oh boy does this code look messy! +bool NodelistVersion; +function str GetNodelist (bool silent) +{ + int MapLmp = StrParam(n:PRINTNAME_LEVEL); + str cvartocheck = StrParam(s:MapLmp, s:"NodesNew"); + str StrToProcess = GetCVARString(cvartocheck); + + if( (StrToProcess == 0) || (strlen(StrToProcess) == 0) ) + { + cvartocheck = StrParam(s:MapLmp, s:"Nodes"); + StrToProcess = GetCVARString(cvartocheck); + if( (StrToProcess == 0) || (strlen(StrToProcess) == 0) ) + { + cvartocheck = StrParam(s:MapLmp, s:"NodesNew"); + StrToProcess = StrParam(l:cvartocheck); + + //It's a weird check but after a lot of fiddling around i figured out + //that this is how the L operator on StrParam works. + //If you don't get it, basically the L operator will just return + //the exact same LANGUAGE string identifier you gave it if that + //identifier is undefined. + if(strcmp(StrToProcess, cvartocheck) == 0) + { + cvartocheck = StrParam(s:MapLmp, s:"Nodes"); + StrToProcess = StrParam(l:cvartocheck); + + if(strcmp(StrToProcess, cvartocheck) == 0) + { + return ""; + } + else + { + NodelistVersion = 0; + if(!silent) + Log(s:"Map-defined nodes found, loading..."); + } + } + else {NodelistVersion = 1; Log(s:"Map-defined nodes found, loading...");} + } + else + { + NodelistVersion = 0; + if(!silent) + Log(s:"Nodes found, loading..."); + } + } + else + { + NodelistVersion = 1; + if(!silent) + Log(s:"Nodes found, loading..."); + } + return StrToProcess; +} + +Script "TDBots_SetTarget" (int tid) +{ + SetPointer(AAPTR_TARGET, tid, AAPTR_DEFAULT, PTROP_NOSAFEGUARDS|PTROP_UNSAFEMASTER); +} + +Script "TDBots_PlayerNoding" ENTER +{ + if( (!PlayerIsBot(PlayerNumber())) && (StrLen(GetNodelist(TRUE)) == 0) && (GetCVAR("tdbots_learnfromplayer")) && (Gametype() > GAME_NET_COOPERATIVE) ) + { + while( (TSMS <= MAX_LEARN_TIME) && (GetActorProperty(0, APROP_HEALTH) > 0)) + { + delay(16); + TDB_PlayerNoding(); + } + } +} + +Script "TDBots_PlayerNoding2" RESPAWN +{ACS_NamedExecuteAlways("TDBots_PlayerNoding",0);} + +Script "TDBots_LoadNodes" OPEN +{ + if(GetCVAR("TDBots_UseNodes") == FALSE) {terminate;} + + ProcessNodelist(GetNodelist(FALSE), NodelistVersion); + delay(1); + + int TID,CurrArrayIndex; + while(currarrayindex <= amountofnodes) + { + if(NodePropStr[CurrArrayIndex][AXIS_X]) + { + TID = UniqueTID(); + + /*log(s:NodePropStr[CurrArrayIndex][AXIS_X],s:" ", + s:NodePropStr[CurrArrayIndex][AXIS_Y],s:" ", + s:NodePropStr[CurrArrayIndex][AXIS_Z],s:" ", + s:NodePropStr[CurrArrayIndex][NODETYPE]); + + log(i:StrToInt(NodePropStr[CurrArrayIndex][AXIS_X]),s:" ", + i:StrToInt(NodePropStr[CurrArrayIndex][AXIS_Y]),s:" ", + i:StrToInt(NodePropStr[CurrArrayIndex][AXIS_Z]),s:" ", + i:StrToInt(NodePropStr[CurrArrayIndex][NODETYPE]));*/ + + switch(StrToInt(NodePropStr[CurrArrayIndex][NODETYPE])) + { + case NODE_NORMAL: + SpawnForced("TDBots_PathNode", + StrToInt(NodePropStr[CurrArrayIndex][AXIS_X])*65536, + StrToInt(NodePropStr[CurrArrayIndex][AXIS_Y])*65536, + StrToInt(NodePropStr[CurrArrayIndex][AXIS_Z])*65536, TID); + break; + + case NODE_JUMP: + SpawnForced("TDBots_JumpNode", + StrToInt(NodePropStr[CurrArrayIndex][AXIS_X])*65536, + StrToInt(NodePropStr[CurrArrayIndex][AXIS_Y])*65536, + StrToInt(NodePropStr[CurrArrayIndex][AXIS_Z])*65536, TID); + break; + + case NODE_PRECISE: + SpawnForced("TDBots_PrecisionNode", + StrToInt(NodePropStr[CurrArrayIndex][AXIS_X])*65536, + StrToInt(NodePropStr[CurrArrayIndex][AXIS_Y])*65536, + StrToInt(NodePropStr[CurrArrayIndex][AXIS_Z])*65536, TID); + break; + + default: + log(s:"Unknown node type ", s:NodePropStr[CurrArrayIndex][NODETYPE]); + } + + NodeTID[CurrArrayIndex] = TID; + + CurrArrayIndex++; + delay(1); + } + else + { + break; + } + } + + if(currarrayindex != 0) + { + Log(i: currarrayindex, s:" Nodes fully loaded."); + Log(s:"Nodelist version: ",i: NodelistVersion); + } + else if(GetCVAR("tdbots_learnfromplayer")) + { + while(TSMS <= MAX_LEARN_TIME) + { + delay(1); + TSMS++; + if( (TSMS == MAX_LEARN_TIME_P1) || (TSMS == MAX_LEARN_TIME_P2 ) || (TSMS == MAX_LEARN_TIME_P3) ) + TDB_CopyNodeTIDs(); + } + TDB_CopyNodeTIDs(); + } +} \ No newline at end of file diff --git a/wadsrc_tdbots/static/scripts/TDB_RA.acs b/wadsrc_tdbots/static/scripts/TDB_RA.acs new file mode 100644 index 0000000000..40c8857ddb --- /dev/null +++ b/wadsrc_tdbots/static/scripts/TDB_RA.acs @@ -0,0 +1,287 @@ +#DEFINE MAX_CWEAP 128 +#DEFINE MAX_CAMMO 256 + +int TDB_TotalCWeap; +str TDB_CWeapons[MAX_CWEAP] = {""}; +str TDB_CAmmo[MAX_CAMMO] = {""}; + +function void TDBots_RA_Equip (void) +{ + bool CWLDetected = FALSE; + + if(GetCVAR("tdbots_rocketarena") == FALSE) {return;} + + //Custom weapon list detected, use it. + if(StrLen(TDB_CWeapons[0]) > 0) + { + for(int w; StrLen(TDB_CWeapons[w]) > 0; w++) + { + GiveInventory(TDB_CWeapons[w],1); + } + + if(StrLen(TDB_CAmmo[0]) > 0) + { + for(int a; StrLen(TDB_CAmmo[a]) > 0; a++) + { + TDB_MaxGive(TDB_CAmmo[a]); + } + CWLDetected = TRUE; + } + + SetWeapon(TDB_CWeapons[random(0,TDB_TotalCWeap)]); + } + + else if(CurrentGame == GAME_DOOM) + { + if(VanillaDoom) + { + GiveInventory("BotBFG9000",1); + GiveInventory("BotSuperShotgun",1); + GiveInventory("BotPlasmaRifle",1); + GiveInventory("BotRocketLauncher",1); + GiveInventory("BotChaingun",1); + GiveInventory("BotShotgun",1); + + if(!PlayerIsBot(PlayerNumber())) + { + GiveInventory("BotChainsaw",1); + } + + GiveInventory("Backpack",1); //To max ammo capacity + + //This max outs all ammo + TDB_MaxGive("Clip"); + TDB_MaxGive("Shell"); + TDB_MaxGive("Cell"); + TDB_MaxGive("RocketAmmo"); + + GiveInventory("MegaSphere",1); + } + else //10 backpacks are given, with 5 of each weapon to maximize ammo + { + TDB_RA_Give("Backpack"); + TDB_RA_Give("Backpack"); + + TDB_RA_Give("BFG9000"); + TDB_RA_Give("SuperShotgun"); + TDB_RA_Give("PlasmaRifle"); + TDB_RA_Give("RocketLauncher"); + TDB_RA_Give("Chaingun"); + TDB_RA_Give("Shotgun"); + + TDB_GiveItem("MegaSphere"); + + if(!PlayerIsBot(PlayerNumber())) {TDB_GiveItem("Chainsaw");} + } + } + + else if(CurrentGame == GAME_HERETIC) + { + GiveInventory("BotMace",1); + GiveInventory("BotSkullRod",1); + GiveInventory("BotPhoenixRod",1); + GiveInventory("BotBlaster",1); + GiveInventory("BotCrossbow",1); + + GiveInventory("BagOfHolding",1); + + TDB_MaxGive("GoldWandAmmo"); + TDB_MaxGive("CrossbowAmmo"); + TDB_MaxGive("MaceAmmo"); + TDB_MaxGive("BlasterAmmo"); + TDB_MaxGive("SkullRodAmmo"); + + if(!PlayerIsBot(PlayerNumber())) {GiveInventory("BotGauntlets",1);} + + GiveInventory("SoulSphere",1); + GiveInventory("EnchantedShield",1); + } + + else if(CurrentGame == GAME_STRIFE) + { + GiveInventory("BotStrifeGrenadeLauncher",1); + GiveInventory("BotMauler",1); + GiveInventory("BotMiniMissileLauncher",1); + GiveInventory("BotAssaultGun",1); + GiveInventory("BotStrifeCrossbow",1); + + GiveInventory("AmmoSatchel",1); + + TDB_MaxGive("HEGrenadeRounds"); + TDB_MaxGive("PhosphorusGrenadeRounds"); + TDB_MaxGive("ClipOfBullets"); + TDB_MaxGive("MiniMissiles"); + TDB_MaxGive("EnergyPod"); + TDB_MaxGive("PoisonBolts"); + TDB_MaxGive("ElectricBolts"); + + GiveInventory("SoulSphere",1); + GiveInventory("MetalArmor",1); + } + + else if(CurrentGame == GAME_HEXEN) + { + GiveInventory("BotCWeapWraithverge",1); + GiveInventory("BotFWeapQuietus",1); + GiveInventory("BotMWeapBloodscourge",1); + + GiveInventory("BotCWeapFlame",1); + GiveInventory("BotFWeapHammer",1); + GiveInventory("BotMWeapLightning",1); + + GiveInventory("BotCWeapStaff",1); + GiveInventory("BotFWeapAxe",1); + GiveInventory("BotMWeapFrost",1); + + TDB_MaxGive("Mana1"); + TDB_MaxGive("Mana2"); + + GiveInventory("SoulSphere",1); + GiveInventory("AmuletOfWarding",1); + } + + SetActorVelocity(0,0.01,0,0,1,0); + + if(!CWLDetected) + { + TDB_RandomWeapon(); + } +} + +//Note: commented log lines are just for debugging, you can uncomment them +//if you need to. +function void TDB_RA_ParseWeaponList (str string) +{ + //Don't bother doing anything if it's just empty + if(strcmp(string, "") == 0) {return;} + + //Length of the string passed + int stringlength = strlen(string); + + //StrMid parameters, to copy a full actor name to the StartWeapons array + int strmidstart; + int strmidend; + + //Keeps track of current character index so i can pass it to strmidstart + //even after restarting it + int strmidstart2; + + //Actor name and currently stored character + int currentarrayindex; + str storedchar; + + //Processing item amount instead of item name? + bool procamount; + for(int currchar = 0; currchar <= stringlength; currchar++) + { + storedchar = StrMid(string, currchar, 1);//GetChar didn't work dunno why + + //Last character? Save this last character and stop. + if(currchar == stringlength) + { + TDB_CWeapons[currentarrayindex] = strmid(string, strmidstart, strmidend); + TDB_TotalCWeap = currentarrayindex; + return; + } + + //Hack to work around a very strange bug where an extra character would + //be added depending on whatever the first call of StrCmp contained. + StrCmp(storedchar, "", 1); + + //If a separator is detected, switch to next actor name and clear the + //current string saved in memory. + if(StrCmp(storedchar, ";") == 0) + { + //log(s:"separator found"); + TDB_CWeapons[currentarrayindex] = strmid(string, strmidstart, strmidend); + + procamount = FALSE; + strmidstart = strmidstart2+1; //needs to be AFTER the separator + strmidstart2++; + strmidend = 0; + //log(s: "strmidstart = ",i:strmidstart); + currentarrayindex++; + } + + //If not, just continue storing the actor name. + else + { + strmidend++; + strmidstart2++; + //log(s: "strmidend = ",i:strmidend); + } + } +} + +//Removed comments since this is basically the same code as above +function void TDB_RA_ParseAmmoList (str string) +{ + if(strcmp(string, "") == 0) {return;} + + int stringlength = strlen(string); + + int strmidstart; + int strmidend; + + int strmidstart2; + + int currentarrayindex; + str storedchar; + + bool procamount; + for(int currchar = 0; currchar <= stringlength; currchar++) + { + storedchar = StrMid(string, currchar, 1); + + if(currchar == stringlength) + { + TDB_CAmmo[currentarrayindex] = strmid(string, strmidstart, strmidend); + currentarrayindex++; + return; + } + + StrCmp(storedchar, "", 1); + + if(StrCmp(storedchar, ";") == 0) + { + //log(s:"separator found"); + TDB_CAmmo[currentarrayindex] = strmid(string, strmidstart, strmidend); + + procamount = FALSE; + strmidstart = strmidstart2+1; + strmidstart2++; + strmidend = 0; + currentarrayindex++; + } + + else + { + strmidend++; + strmidstart2++; + } + } +} + +Script "TDB_RA_WeaponListInit" OPEN +{ + if(GetCVAR("tdbots_rocketarena") == FALSE) {terminate;} + + str WeaponListName = "TDBOTS_RA_WEAPONS"; + str WeaponList = StrParam(l:WeaponListName); + + if(strcmp(WeaponList, WeaponListName) == 0) {terminate;} + + delay(1); + + TDB_RA_ParseWeaponList(WeaponList); + + + str AmmoListName = "TDBOTS_RA_AMMO"; + str AmmoList = StrParam(l:AmmoListName); + + if(strcmp(AmmoList, AmmoListName) == 0) {terminate;} + + delay(1); + + TDB_RA_ParseAmmoList(AmmoList); +} \ No newline at end of file diff --git a/wadsrc_tdbots/static/scripts/TDB_Zan.acs b/wadsrc_tdbots/static/scripts/TDB_Zan.acs new file mode 100644 index 0000000000..e96282ec3e --- /dev/null +++ b/wadsrc_tdbots/static/scripts/TDB_Zan.acs @@ -0,0 +1,25 @@ +//Because GetPlayerAccountName doesn't quite work with else statements, i had to +//do this. +function bool TDB_IsZandronum (void) +{ + if(GetPlayerAccountName(0) != 0) + return TRUE; + return FALSE; +} + +Script "TDBots_ZandronumCommands" OPEN +{ + if(TDB_IsZandronum() == TRUE) + { + consolecommand("bot_allowchat 0"); + + if(GetCVAR("alwaysapplydmflags") == FALSE) + {consolecommand("alwaysapplydmflags 1");} + + if(GetCVAR("bot_allowchat") == TRUE) + {consolecommand("bot_allowchat 0");} + + if(GetCVAR("sv_forcerespawn") == FALSE) + {consolecommand("sv_forcerespawn 1");} + } +} \ No newline at end of file diff --git a/wadsrc_tdbots/static/sprites/TBNDA0.png b/wadsrc_tdbots/static/sprites/TBNDA0.png new file mode 100644 index 0000000000000000000000000000000000000000..250feeaf4996b42dc5bc7d99ffdb42f4310de42b GIT binary patch literal 135 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzj`SkOBp@vSlDKJfO9x0>dAc}; zL>zv5X(2CzAp?`cxA=JzThv5M;gwxN+{?q=; TGM=WbuRsExu6{1-oD!MEal| k5uN