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 0000000000..247c389302 Binary files /dev/null and b/wadsrc_tdbots/static/acs/NODESTUDIO.o differ diff --git a/wadsrc_tdbots/static/acs/TDB_Main.o b/wadsrc_tdbots/static/acs/TDB_Main.o new file mode 100644 index 0000000000..8764116b02 Binary files /dev/null and b/wadsrc_tdbots/static/acs/TDB_Main.o differ 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 0000000000..250feeaf49 Binary files /dev/null and b/wadsrc_tdbots/static/sprites/TBNDA0.png differ diff --git a/wadsrc_tdbots/static/sprites/TBNDB0.png b/wadsrc_tdbots/static/sprites/TBNDB0.png new file mode 100644 index 0000000000..b4c4d20bc8 Binary files /dev/null and b/wadsrc_tdbots/static/sprites/TBNDB0.png differ diff --git a/wadsrc_tdbots/static/sprites/TBNDC0.png b/wadsrc_tdbots/static/sprites/TBNDC0.png new file mode 100644 index 0000000000..f3511e4c79 Binary files /dev/null and b/wadsrc_tdbots/static/sprites/TBNDC0.png differ diff --git a/wadsrc_tdbots/static/sprites/TBNDD0.png b/wadsrc_tdbots/static/sprites/TBNDD0.png new file mode 100644 index 0000000000..0312e539e9 Binary files /dev/null and b/wadsrc_tdbots/static/sprites/TBNDD0.png differ diff --git a/wadsrc_tdbots/static/sprites/TBNDZ0.png b/wadsrc_tdbots/static/sprites/TBNDZ0.png new file mode 100644 index 0000000000..69d26e5118 Binary files /dev/null and b/wadsrc_tdbots/static/sprites/TBNDZ0.png differ diff --git a/wadsrc_tdbots/static/tdbots/CHEXWEAPONS.dec b/wadsrc_tdbots/static/tdbots/CHEXWEAPONS.dec new file mode 100644 index 0000000000..576677717f --- /dev/null +++ b/wadsrc_tdbots/static/tdbots/CHEXWEAPONS.dec @@ -0,0 +1,111 @@ +actor BotBootspoon : BotFist replaces Bootspoon +{ + game Chex + obituary "$OB_MPSPOON" + Tag "$TAG_SPOON" +} + +actor BotSuperBootspork : BotChainsaw replaces SuperBootspork +{ + game Chex + obituary "$OB_MPBOOTSPORK" + Inventory.PickupMessage "$GOTSUPERBOOTSPORK" + Tag "$TAG_SPORK" +} + +actor BotMiniZorcher : BotPistol replaces MiniZorcher +{ + game Chex + obituary "$OP_MPZORCH" + inventory.pickupmessage "$GOTMINIZORCHER" + Tag "$TAG_MINIZORCHER" + states + { + Spawn: + stop + } +} + +actor BotLargeZorcher : BotShotgun replaces LargeZorcher +{ + game Chex + obituary "$OP_MPZORCH" + inventory.pickupmessage "$GOTLARGEZORCHER" + Tag "$TAG_LARGEZORCHER" +} + +actor BotSuperLargeZorcher : BotSuperShotgun replaces SuperLargeZorcher +{ + game Chex + obituary "$OB_MPMEGAZORCH" + inventory.pickupmessage "$GOTSUPERLARGEZORCHER" + Tag "$TAG_SUPERLARGEZORCHER" +} + +actor BotRapidZorcher : BotChaingun replaces RapidZorcher +{ + game Chex + obituary "$OB_MPRAPIDZORCH" + inventory.pickupmessage "$GOTRAPIDZORCHER" + Tag "$TAG_RAPIDZORCHER" +} + +actor BotZorchPropulsor : BotRocketLauncher replaces ZorchPropulsor +{ + game Chex + obituary "" + inventory.pickupmessage "$GOTZORCHPROPULSOR" + Tag "$TAG_ZORCHPROPULSOR" + States + { + Fire: + MISG B 8 A_GunFlash + MISG B 12 A_FireCustomMissile("PropulsorMissile") + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MISG B 0 A_ReFire + TNT1 A 0 A_Jump(256, "Ready") + Goto Ready + } +} + +actor BotPhasingZorcher : BotPlasmaRifle replaces PhasingZorcher +{ + game Chex + obituary "" + inventory.pickupmessage "$GOTPHASINGZORCHER" + Tag "$TAG_PHASINGZORCHER" + states + { + Fire: + PLSG A 0 A_GunFlash + PLSG A 3 A_FireCustomMissile("PhaseZorchMissile") + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + PLSG B 20 A_ReFire + Goto Ready + Flash: + PLSF A 0 A_Jump(128, "Flash2") + PLSF A 3 Bright A_Light1 + Goto LightDone + Flash2: + PLSF B 3 Bright A_Light1 + Goto LightDone + } +} + +actor BotLAZDevice : BotBFG9000 replaces LAZDevice +{ + game Chex + obituary "" + inventory.pickupmessage "$GOTLAZDEVICE" + Tag "$TAG_LAZDEVICE" + states + { + Fire: + BFGG A 20 A_BFGsound + BFGG B 10 A_GunFlash + BFGG B 10 A_FireCustomMissile("LAZBall") + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + BFGG B 20 A_ReFire + Goto Ready + } +} \ No newline at end of file diff --git a/wadsrc_tdbots/static/tdbots/DOOMWEAPONS.dec b/wadsrc_tdbots/static/tdbots/DOOMWEAPONS.dec new file mode 100644 index 0000000000..9186fbf10d --- /dev/null +++ b/wadsrc_tdbots/static/tdbots/DOOMWEAPONS.dec @@ -0,0 +1,468 @@ +// -------------------------------------------------------------------------- +// +// Fist +// +// -------------------------------------------------------------------------- + +ACTOR BotFist : Weapon +{ + Game Doom + Weapon.SlotNumber 1 + Weapon.SelectionOrder 3700 + Weapon.Kickback 100 + Obituary "$OB_MPFIST" + Tag "$TAG_FIST" + +WEAPON.WIMPY_WEAPON + +WEAPON.MELEEWEAPON + States + { + Ready: + PUNG A 1 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Deselect: + TNT1 A 0 A_TakeInventory("BotMeleeWeapon") + PUNG A 1 A_Lower + Loop + Select: + TNT1 A 0 A_GiveInventory("BotMeleeWeapon") + PUNG A 1 A_Raise + Loop + Fire: + PUNG B 4 + PUNG C 4 A_Punch + PUNG D 5 + PUNG C 4 + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + PUNG B 5 A_ReFire + Goto Ready + } +} + + +// -------------------------------------------------------------------------- +// +// Pistol +// +// -------------------------------------------------------------------------- + +ACTOR BotPistol : DoomWeapon replaces Pistol +{ + Game Doom + Weapon.SlotNumber 2 + Weapon.SelectionOrder 1900 + Weapon.AmmoUse 1 + Weapon.AmmoGive 0 + Weapon.AmmoType "Clip" + Obituary "$OB_MPPISTOL" + +WEAPON.WIMPY_WEAPON + Inventory.Pickupmessage "$PICKUP_PISTOL_DROPPED" + Tag "$TAG_PISTOL" + States + { + Ready: + PISG A 1 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Deselect: + TNT1 A 0 A_TakeInventory("BotForceInacc") + PISG A 1 A_Lower + Loop + Select: + TNT1 A 0 A_GiveInventory("BotForceInacc") + PISG A 1 A_Raise + Loop + Fire: + PISG A 4 + PISG B 6 A_FirePistol + PISG C 4 + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "BotRefire") + PISG B 5 A_ReFire + Goto Ready + BotRefire: + TNT1 A 0 A_JumpIfNoAmmo("Deselect") + Goto Fire + Flash: + PISF A 7 Bright A_Light1 + Goto LightDone + PISF A 7 Bright A_Light1 + Goto LightDone + Spawn: + PIST A -1 + Stop + } +} + +// -------------------------------------------------------------------------- +// +// Chainsaw +// +// -------------------------------------------------------------------------- + +ACTOR BotChainsaw : Weapon replaces Chainsaw +{ + Game Doom + SpawnID 32 + Weapon.SlotNumber 1 + Weapon.Kickback 0 + Weapon.SelectionOrder 2200 + Weapon.UpSound "weapons/sawup" + Weapon.ReadySound "weapons/sawidle" + Inventory.PickupMessage "$GOTCHAINSAW" + Obituary "$OB_MPCHAINSAW" + Tag "$TAG_CHAINSAW" + +WEAPON.MELEEWEAPON + States + { + Ready: + SAWG C 4 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + SAWG D 4 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Deselect: + TNT1 A 0 A_TakeInventory("BotMeleeWeapon") + SAWG C 1 A_Lower + Loop + Select: + TNT1 A 0 A_GiveInventory("BotMeleeWeapon") + SAWG C 1 A_Raise + Loop + Fire: + SAWG AB 4 A_Saw + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + SAWG B 0 A_ReFire + Goto Ready + Spawn: + CSAW A -1 + Stop + } +} + + +// -------------------------------------------------------------------------- +// +// Shotgun +// +// -------------------------------------------------------------------------- + +ACTOR BotShotgun : DoomWeapon replaces Shotgun +{ + Game Doom + SpawnID 27 + Weapon.SlotNumber 3 + Weapon.SelectionOrder 1300 + Weapon.AmmoUse 1 + Weapon.AmmoGive 8 + Weapon.AmmoType "Shell" + Inventory.PickupMessage "$GOTSHOTGUN" + Obituary "$OB_MPSHOTGUN" + Tag "$TAG_SHOTGUN" + States + { + Ready: + SHTG A 1 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Deselect: + TNT1 A 0 A_TakeInventory("BotCloseRange") + SHTG A 1 A_Lower + Loop + Select: + TNT1 A 0 A_GiveInventory("BotCloseRange") + SHTG A 1 A_Raise + Loop + Fire: + SHTG A 3 + SHTG A 7 A_FireShotgun + SHTG BC 5 + SHTG D 4 + SHTG CB 5 + SHTG A 3 + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "BotReFire") + SHTG A 7 A_ReFire + Goto Ready + BotRefire: + TNT1 A 0 A_JumpIfNoAmmo("Deselect") + Goto Fire + Flash: + SHTF A 4 Bright A_Light1 + SHTF B 3 Bright A_Light2 + Goto LightDone + Spawn: + SHOT A -1 + Stop + } +} + +// -------------------------------------------------------------------------- +// +// Shotgun +// +// -------------------------------------------------------------------------- + +ACTOR BotSuperShotgun : DoomWeapon replaces SuperShotgun +{ + Game Doom + SpawnID 33 + Weapon.SlotNumber 3 + Weapon.SelectionOrder 400 + Weapon.AmmoUse 2 + Weapon.AmmoGive 8 + Weapon.AmmoType "Shell" + Inventory.PickupMessage "$GOTSHOTGUN2" + Obituary "$OB_MPSSHOTGUN" + Tag "$TAG_SUPERSHOTGUN" + States + { + Ready: + SHT2 A 1 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Deselect: + TNT1 A 0 A_TakeInventory("BotCloseRange") + SHT2 A 1 A_Lower + Loop + Select: + TNT1 A 0 A_GiveInventory("BotCloseRange") + SHT2 A 1 A_Raise + Loop + Fire: + SHT2 A 3 + SHT2 A 7 A_FireShotgun2 + SHT2 B 7 + SHT2 C 7 A_CheckReload + SHT2 D 7 A_OpenShotgun2 + SHT2 E 7 + SHT2 F 7 A_LoadShotgun2 + SHT2 G 6 + SHT2 H 6 A_CloseShotgun2 + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "BotRefire") + SHT2 A 5 A_ReFire + Goto Ready + // unused states + SHT2 B 7 + SHT2 A 3 + Goto Deselect + BotRefire: + TNT1 A 0 A_JumpIfNoAmmo("Deselect") + Goto Fire + Flash: + SHT2 I 4 Bright A_Light1 + SHT2 J 3 Bright A_Light2 + Goto LightDone + Spawn: + SGN2 A -1 + Stop + } +} + +// -------------------------------------------------------------------------- +// +// Chaingun +// +// -------------------------------------------------------------------------- + +ACTOR BotChaingun : DoomWeapon replaces Chaingun +{ + Game Doom + SpawnID 28 + Weapon.SlotNumber 4 + Weapon.SelectionOrder 700 + Weapon.AmmoUse 1 + Weapon.AmmoGive 20 + Weapon.AmmoType "Clip" + Inventory.PickupMessage "$GOTCHAINGUN" + Obituary "$OB_MPCHAINGUN" + Tag "$TAG_CHAINGUN" + States + { + Ready: + CHGG A 1 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Deselect: + TNT1 A 0 A_TakeInventory("BotForceInacc") + CHGG A 1 A_Lower + Loop + Select: + TNT1 A 0 A_GiveInventory("BotForceInacc") + CHGG A 1 A_Raise + Loop + Fire: + CHGG AB 4 A_FireCGun + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "BotRefire") + CHGG B 0 A_ReFire + Goto Ready + BotRefire: + TNT1 A 0 A_JumpIfNoAmmo("Deselect") + Goto Fire + Flash: + CHGF A 5 Bright A_Light1 + Goto LightDone + CHGF B 5 Bright A_Light2 + Goto LightDone + Spawn: + MGUN A -1 + Stop + } +} + +// -------------------------------------------------------------------------- +// +// Rocket launcher +// +// -------------------------------------------------------------------------- + +ACTOR BotRocketLauncher : DoomWeapon replaces RocketLauncher +{ + Game Doom + SpawnID 29 + Weapon.SlotNumber 5 + Weapon.SelectionOrder 2500 + Weapon.AmmoUse 1 + Weapon.AmmoGive 2 + Weapon.AmmoType "RocketAmmo" + +WEAPON.NOAUTOFIRE + Inventory.PickupMessage "$GOTLAUNCHER" + Tag "$TAG_ROCKETLAUNCHER" + States + { + Ready: + MISG A 1 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Deselect: + TNT1 A 0 A_TakeInventory("BotExplosiveWeapon") + MISG A 1 A_Lower + Loop + Select: + TNT1 A 0 A_GiveInventory("BotExplosiveWeapon") + MISG A 1 A_Raise + Loop + Fire: + MISG B 8 A_GunFlash + MISG B 12 A_FireMissile + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "BotRefire") + MISG B 0 A_ReFire + Goto Ready + BotRefire: + TNT1 A 0 A_JumpIfNoAmmo("Deselect") + Goto Fire + Flash: + MISF A 3 Bright A_Light1 + MISF B 4 Bright + MISF CD 4 Bright A_Light2 + Goto LightDone + Spawn: + LAUN A -1 + Stop + } +} + +// -------------------------------------------------------------------------- +// +// Plasma rifle +// +// -------------------------------------------------------------------------- + +ACTOR BotPlasmaRifle : DoomWeapon replaces PlasmaRifle +{ + Game Doom + SpawnID 30 + Weapon.SlotNumber 6 + Weapon.SelectionOrder 100 + Weapon.AmmoUse 1 + Weapon.AmmoGive 40 + Weapon.AmmoType "Cell" + Inventory.PickupMessage "$GOTPLASMA" + Tag "$TAG_PLASMARIFLE" + States + { + Ready: + PLSG A 1 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Deselect: + TNT1 A 0 A_TakeInventory("BotForceInacc") + PLSG A 1 A_Lower + Loop + Select: + TNT1 A 0 A_GiveInventory("BotForceInacc") + PLSG A 1 A_Raise + Loop + Fire: + PLSG A 3 A_FirePlasma + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "BotRefire") + PLSG B 20 A_ReFire + Goto Ready + BotRefire: + TNT1 A 0 A_JumpIfNoAmmo("Deselect") + Goto Fire + Flash: + PLSF A 4 Bright A_Light1 + Goto LightDone + PLSF B 4 Bright A_Light1 + Goto LightDone + Spawn: + PLAS A -1 + Stop + } +} + +// -------------------------------------------------------------------------- +// +// BFG 9000 +// +// -------------------------------------------------------------------------- + +ACTOR BotBFG9000 : DoomWeapon replaces BFG9000 +{ + Game Doom + Height 20 + SpawnID 31 + Weapon.SlotNumber 7 + Weapon.SelectionOrder 2800 + Weapon.AmmoUse 40 + Weapon.AmmoGive 40 + Weapon.AmmoType "Cell" + +WEAPON.NOAUTOFIRE + Inventory.PickupMessage "$GOTBFG9000" + Tag "$TAG_BFG9000" + States + { + Ready: + BFGG A 1 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Deselect: + BFGG A 1 A_Lower + Loop + Select: + BFGG A 1 A_Raise + Loop + Fire: + BFGG A 20 A_BFGsound + BFGG B 10 A_GunFlash + BFGG B 10 A_FireBFG + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "BotRefire") + BFGG B 20 A_ReFire + Goto Ready + BotRefire: + TNT1 A 0 A_JumpIfNoAmmo("Deselect") + Goto Fire + Flash: + BFGF A 11 Bright A_Light1 + BFGF B 6 Bright A_Light2 + Goto LightDone + Spawn: + BFUG A -1 + Stop + OldFire: + BFGG A 10 A_BFGsound + BFGG BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB 1 A_FireOldBFG + BFGG B 0 A_Light0 + BFGG B 20 A_ReFire + Goto Ready + } +} \ No newline at end of file diff --git a/wadsrc_tdbots/static/tdbots/HEXNWEAPONS.dec b/wadsrc_tdbots/static/tdbots/HEXNWEAPONS.dec new file mode 100644 index 0000000000..600f104d02 --- /dev/null +++ b/wadsrc_tdbots/static/tdbots/HEXNWEAPONS.dec @@ -0,0 +1,464 @@ +//============================================================================== +// +// The Cleric's Weapons +// +//============================================================================== + +ACTOR BotCWeapMace : CWeapMace replaces CWeapMace +{ + Weapon.Slotnumber 1 + States + { + Select: + TNT1 A 0 A_GiveInventory("BotMeleeWeapon") + Goto Super::Select + Deselect: + TNT1 A 0 A_TakeInventory("BotMeleeWeapon") + Goto Super::Deselect + Ready: + CMCE A 1 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Fire: + CMCE B 2 Offset (60, 20) + CMCE B 1 Offset (30, 33) + CMCE B 2 Offset (8, 45) + CMCE C 1 Offset (8, 45) + CMCE D 1 Offset (8, 45) + CMCE E 1 Offset (8, 45) + CMCE E 1 Offset (-11, 58) A_CMaceAttack + CMCE F 1 Offset (8, 45) + CMCE F 2 Offset (-8, 74) + CMCE F 1 Offset (-20, 96) + CMCE F 8 Offset (-33, 160) + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CMCE A 2 Offset (8, 75) A_ReFire + CMCE A 1 Offset (8, 65) + CMCE A 2 Offset (8, 60) + CMCE A 1 Offset (8, 55) + CMCE A 2 Offset (8, 50) + CMCE A 1 Offset (8, 45) + Goto Ready + } +} + +ACTOR BotCWeapFlame : CWeapFlame replaces CWeapFlame +{ + Weapon.Slotnumber 3 + States + { + Ready: + CFLM A 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CFLM A 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CFLM A 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CFLM A 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CFLM B 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CFLM B 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CFLM B 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CFLM B 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CFLM C 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CFLM C 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CFLM C 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CFLM C 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Fire: + CFLM A 2 Offset (0, 40) + CFLM D 2 Offset (0, 50) + CFLM D 2 Offset (0, 36) + CFLM E 4 Bright + CFLM F 4 Bright A_CFlameAttack + CFLM E 4 Bright + CFLM G 2 Offset (0, 40) + CFLM G 2 + Goto Ready + } +} + +//Extremely unlikely the bot will ever get this, but for the sake of completion +ACTOR BotCWeapWraithverge : CWeapWraithverge replaces CWeapWraithverge +{ + Weapon.Slotnumber 4 + States + { + Ready: + CHLY A 1 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Fire: + CHLY AB 1 Bright Offset (0, 40) + CHLY CD 2 Bright Offset (0, 43) + CHLY E 2 Bright Offset (0, 45) + CHLY F 6 Bright Offset (0, 48) A_CHolyAttack + CHLY GG 2 Bright Offset (0, 40) A_CHolyPalette + CHLY G 2 Offset (0, 36) A_CHolyPalette + Goto Ready + } +} + +ACTOR BotCWeapStaff : CWeapStaff replaces CWeapStaff +{ + Weapon.Slotnumber 2 + States + { + Ready: + CSSF C 4 + CSSF B 3 A_CStaffInitBlink + CSSF A 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CSSF A 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CSSF A 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CSSF A 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CSSF A 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CSSF A 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CSSF A 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CSSF A 1 A_CStaffCheckBlink + Goto Ready + 2 + Fire: + CSSF A 1 Offset (0, 45) A_CStaffCheck + CSSF J 1 Offset (0, 50) A_CStaffAttack + CSSF J 2 Offset (0, 50) + CSSF J 2 Offset (0, 45) + CSSF A 2 Offset (0, 40) + CSSF A 2 Offset (0, 36) + Goto Ready + 2 + Blink: + CSSF B 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CCSF B 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CCSF B 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CSSF C 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CSSF C 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CSSF C 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CSSF C 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CSSF C 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CSSF B 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CSSF B 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CSSF B 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Goto Ready + 2 + Drain: + CSSF K 10 Offset (0, 36) + Goto Ready + 2 + } +} + +//============================================================================== +// +//The Fighter's weapons +// +//============================================================================== + +ACTOR BotFWeapFist : FWeapFist replaces FWeapFist +{ + Weapon.Slotnumber 1 + States + { + Select: + TNT1 A 0 A_GiveInventory("BotMeleeWeapon") + Goto Super::Select + Deselect: + TNT1 A 0 A_TakeInventory("BotMeleeWeapon") + Goto Super::Deselect + Ready: + FPCH A 1 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Fire: + FPCH B 5 Offset (5, 40) + FPCH C 4 Offset (5, 40) + FPCH D 4 Offset (5, 40) A_FPunchAttack + FPCH C 4 Offset (5, 40) + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + FPCH B 5 Offset (5, 40) A_ReFire + Goto Ready + Fire2: + FPCH DE 4 Offset (5, 40) + FPCH E 1 Offset (15, 50) + FPCH E 1 Offset (25, 60) + FPCH E 1 Offset (35, 70) + FPCH E 1 Offset (45, 80) + FPCH E 1 Offset (55, 90) + FPCH E 1 Offset (65, 90) + FPCH E 10 Offset (0, 150) + Goto Ready + } +} + +ACTOR BotFWeapHammer : FWeapHammer replaces FWeapHammer +{ + Weapon.Slotnumber 3 + States + { + Deselect: + TNT1 A 0 A_TakeInventory("BotCloseRange") + FHMR A 1 A_Lower + Loop + Select: + TNT1 A 0 A_GiveInventory("BotCloseRange") + FHMR A 1 A_Raise + Loop + Ready: + FHMR A 1 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Fire: + FHMR B 6 Offset (5, 0) + FHMR C 3 Offset (5, 0) A_FHammerAttack + FHMR D 3 Offset (5, 0) + FHMR E 2 Offset (5, 0) + FHMR E 10 Offset (5, 150) A_FHammerThrow + FHMR A 1 Offset (0, 60) + FHMR A 1 Offset (0, 55) + FHMR A 1 Offset (0, 50) + FHMR A 1 Offset (0, 45) + FHMR A 1 Offset (0, 40) + FHMR A 1 Offset (0, 35) + FHMR A 1 + Goto Ready + } +} + +ACTOR BotFWeapAxe : FWeapAxe replaces FWeapAxe +{ + Weapon.Slotnumber 2 + States + { + Select: + TNT1 A 0 A_GiveInventory("BotMeleeWeapon") + Goto Super::Select + Deselect: + TNT1 A 0 A_TakeInventory("BotMeleeWeapon") + Goto Super::Deselect + Ready: + FAXE A 1 A_FAxeCheckReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Fire: + FAXE B 4 Offset (15, 32) A_FAxeCheckAtk + FAXE C 3 Offset (15, 32) + FAXE D 2 Offset (15, 32) + FAXE D 1 Offset (-5, 70) A_FAxeAttack + FAXE D 2 Offset (-25, 90) + FAXE E 1 Offset (15, 32) + FAXE E 2 Offset (10, 54) + FAXE E 7 Offset (10, 150) + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + FAXE A 1 Offset (0, 60) A_ReFire + FAXE A 1 Offset (0, 52) + FAXE A 1 Offset (0, 44) + FAXE A 1 Offset (0, 36) + FAXE A 1 + Goto Ready + ReadyGlow: + FAXE L 1 A_FAxeCheckReadyG TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "FireGlow") + FAXE L 1 A_FAxeCheckReadyG TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "FireGlow") + FAXE L 1 A_FAxeCheckReadyG TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "FireGlow") + FAXE M 1 A_FAxeCheckReadyG TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "FireGlow") + FAXE M 1 A_FAxeCheckReadyG TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "FireGlow") + FAXE M 1 A_FAxeCheckReadyG TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "FireGlow") + Loop + FireGlow: + FAXE N 4 Offset (15, 32) + FAXE O 3 Offset (15, 32) + FAXE P 2 Offset (15, 32) + FAXE P 1 Offset (-5, 70) A_FAxeAttack + FAXE P 2 Offset (-25, 90) + FAXE Q 1 Offset (15, 32) + FAXE Q 2 Offset (10, 54) + FAXE Q 7 Offset (10, 150) + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "FireGlow") + FAXE A 1 Offset (0, 60) A_ReFire + FAXE A 1 Offset (0, 52) + FAXE A 1 Offset (0, 44) + FAXE A 1 Offset (0, 36) + FAXE A 1 + Goto ReadyGlow + } +} + +ACTOR BotFWeapQuietus : FWeapQuietus replaces FWeapQuietus +{ + Weapon.Slotnumber 4 + States + { + Deselect: + TNT1 A 0 A_TakeInventory("BotCloseRange") + FSRD A 1 A_Lower + Loop + Select: + TNT1 A 0 A_GiveInventory("BotCloseRange") + FSRD A 1 A_Raise + Loop + Ready: + FSRD A 1 Bright A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + FSRD A 1 Bright A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + FSRD A 1 Bright A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + FSRD A 1 Bright A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + FSRD B 1 Bright A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + FSRD B 1 Bright A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + FSRD B 1 Bright A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + FSRD B 1 Bright A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + FSRD C 1 Bright A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + FSRD C 1 Bright A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + FSRD C 1 Bright A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + FSRD C 1 Bright A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Fire: + FSRD DE 3 Bright Offset (5, 36) + FSRD F 2 Bright Offset (5, 36) + FSRD G 3 Bright Offset (5, 36) A_FSwordAttack + FSRD H 2 Bright Offset (5, 36) + FSRD I 2 Bright Offset (5, 36) + FSRD I 10 Bright Offset (5, 150) + FSRD A 1 Bright Offset (5, 60) + FSRD B 1 Bright Offset (5, 55) + FSRD C 1 Bright Offset (5, 50) + FSRD A 1 Bright Offset (5, 45) + FSRD B 1 Bright Offset (5, 40) + Goto Ready + } +} + +//============================================================================== +// +//The Mage's weapons +// +//============================================================================== + +ACTOR BotMWeapWand : MWeapWand replaces MWeapWand +{ + Weapon.Slotnumber 1 + States + { + Ready: + MWND A 1 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Fire: + MWND A 6 + MWND B 6 Bright Offset (0, 48) A_FireCustomMissile ("MageWandMissile") + MWND A 3 Offset (0, 40) + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MWND A 3 Offset (0, 36) A_ReFire + Goto Ready + } +} + +ACTOR BotMWeapFrost : MWeapFrost replaces MWeapFrost +{ + Weapon.Slotnumber 2 + States + { + Ready: + CONE A 1 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Fire: + CONE B 3 + CONE C 4 + Hold: + CONE D 3 + CONE E 5 + CONE F 3 A_FireConePL1 + CONE G 3 + CONE A 9 + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Hold") + CONE A 10 A_ReFire + Goto Ready + } +} + +ACTOR BotMWeapLightning : MWeapLightning replaces MWeapLightning +{ + Weapon.Slotnumber 3 + States + { + Ready: + MLNG A 1 Bright A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MLNG A 1 Bright A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MLNG A 1 Bright A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MLNG A 1 Bright A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MLNG A 1 Bright A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MLNG A 1 Bright A_LightningReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MLNG B 1 Bright A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MLNG B 1 Bright A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MLNG B 1 Bright A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MLNG B 1 Bright A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MLNG B 1 Bright A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MLNG B 1 Bright A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MLNG C 1 Bright A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MLNG C 1 Bright A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MLNG C 1 Bright A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MLNG C 1 Bright A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MLNG C 1 Bright A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MLNG C 1 Bright A_LightningReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MLNG B 1 Bright A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MLNG B 1 Bright A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MLNG B 1 Bright A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MLNG B 1 Bright A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MLNG B 1 Bright A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MLNG B 1 Bright A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Fire: + MLNG DE 3 Bright + MLNG F 4 Bright A_MLightningAttack + MLNG G 4 Bright + MLNG HI 3 Bright + MLNG I 6 Bright Offset (0, 199) + MLNG C 2 Bright Offset (0, 55) + MLNG B 2 Bright Offset (0, 50) + MLNG B 2 Bright Offset (0, 45) + MLNG B 2 Bright Offset (0, 40) + Goto Ready + } +} + +ACTOR BotMWeapBloodscourge : MWeapBloodscourge replaces MWeapBloodscourge +{ + Weapon.Slotnumber 4 + States + { + Ready: + MSTF A 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MSTF A 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MSTF A 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MSTF A 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MSTF A 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MSTF A 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MSTF B 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MSTF B 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MSTF B 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MSTF B 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MSTF B 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MSTF B 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MSTF C 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MSTF C 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MSTF C 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MSTF C 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MSTF C 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MSTF C 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MSTF D 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MSTF D 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MSTF D 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MSTF D 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MSTF D 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MSTF D 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MSTF E 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MSTF E 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MSTF E 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MSTF E 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MSTF E 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MSTF E 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MSTF F 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MSTF F 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MSTF F 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MSTF F 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MSTF F 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Fire: + MSTF G 4 Offset (0, 40) + MSTF H 4 Bright Offset (0, 48) A_MStaffAttack + MSTF H 2 Bright Offset (0, 48) A_MStaffPalette + MSTF II 2 Offset (0, 48) A_MStaffPalette + MSTF I 1 Offset (0, 40) + MSTF J 5 Offset (0, 36) + Goto Ready + } +} \ No newline at end of file diff --git a/wadsrc_tdbots/static/tdbots/HTICWEAPONS.dec b/wadsrc_tdbots/static/tdbots/HTICWEAPONS.dec new file mode 100644 index 0000000000..2d4ec03d25 --- /dev/null +++ b/wadsrc_tdbots/static/tdbots/HTICWEAPONS.dec @@ -0,0 +1,564 @@ +// -------------------------------------------------------------------------- +// +// Staff +// +// -------------------------------------------------------------------------- + +ACTOR BotStaff : Staff replaces Staff +{ + Weapon.SlotNumber 1 + Weapon.SisterWeapon "BotStaffPowered" + States + { + Select: + TNT1 A 0 A_GiveInventory("BotMeleeWeapon") + Goto Super::Select + Deselect: + TNT1 A 0 A_TakeInventory("BotMeleeWeapon") + Goto Super::Deselect + Ready: + STFF A 1 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Fire: + STFF B 6 + STFF C 8 A_StaffAttack(random[StaffAttack](5, 20), "StaffPuff") + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + STFF B 8 A_ReFire + Goto Ready + } +} + +ACTOR BotStaffPowered : StaffPowered replaces StaffPowered +{ + Weapon.SlotNumber 1 + Weapon.sisterweapon "BotStaff" + States + { + Select: + TNT1 A 0 A_GiveInventory("BotMeleeWeapon") + Goto Super::Select + Deselect: + TNT1 A 0 A_TakeInventory("BotMeleeWeapon") + Goto Super::Deselect + Ready: + STFF D 4 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + STFF E 4 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + STFF F 4 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Fire: + STFF G 6 + STFF H 8 A_StaffAttack(random[StaffAttack](18, 81), "StaffPuff2") + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + STFF G 8 A_ReFire + Goto Ready + } +} + +// -------------------------------------------------------------------------- +// +// Gold wand +// +// -------------------------------------------------------------------------- + +ACTOR BotGoldWand : GoldWand replaces GoldWand +{ + Weapon.SlotNumber 2 + Weapon.AmmoGive 0 + Weapon.SisterWeapon "BotGoldWandPowered" + States + { + Ready: + GWND A 1 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Deselect: + TNT1 A 0 A_TakeInventory("BotForceInacc") + GWND A 1 A_Lower + Loop + Select: + TNT1 A 0 A_GiveInventory("BotForceInacc") + GWND A 1 A_Raise + Loop + Fire: + GWND B 3 + GWND C 5 A_FireGoldWandPL1 + GWND D 3 + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "BotRefire") + GWND D 0 A_ReFire + Goto Ready + BotRefire: + TNT1 A 0 A_JumpIfNoAmmo("Deselect") + Goto Fire + } +} + +ACTOR BotGoldWandPowered : GoldWandPowered replaces GoldWandPowered +{ + Weapon.SlotNumber 2 + Weapon.SisterWeapon "BotGoldWand" + States + { + Ready: + GWND A 1 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Fire: + GWND B 3 + GWND C 4 A_FireGoldWandPL2 + GWND D 3 + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "BotRefire") + GWND D 0 A_ReFire + Goto Ready + BotRefire: + TNT1 A 0 A_JumpIfNoAmmo("Deselect") + Goto Fire + } +} + +// -------------------------------------------------------------------------- +// +// Crossbow +// +// -------------------------------------------------------------------------- + +ACTOR BotCrossbow : Crossbow replaces Crossbow +{ + Weapon.SlotNumber 3 + Weapon.SisterWeapon "BotCrossbowPowered" + States + { + Spawn: + WBOW A -1 + Stop + Ready: + CRBW A 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CRBW A 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CRBW A 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CRBW A 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CRBW A 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CRBW A 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CRBW B 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CRBW B 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CRBW B 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CRBW B 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CRBW B 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CRBW B 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CRBW C 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CRBW C 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CRBW C 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CRBW C 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CRBW C 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CRBW C 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Deselect: + TNT1 A 0 A_TakeInventory("BotCloseRange") + CRBW A 1 A_Lower + Loop + Select: + TNT1 A 0 A_GiveInventory("BotCloseRange") + CRBW A 1 A_Raise + Loop + Fire: + CRBW D 6 A_FireCrossbowPL1 + CRBW EFGH 3 + CRBW AB 4 + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "BotRefire") + CRBW C 5 A_ReFire + Goto Ready + BotRefire: + TNT1 A 0 A_JumpIfNoAmmo("Deselect") + Goto Fire + } +} + + +ACTOR BotCrossbowPowered : CrossbowPowered replaces CrossbowPowered +{ + Weapon.SlotNumber 3 + Weapon.SisterWeapon "BotCrossbow" + States + { + Ready: + CRBW A 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CRBW A 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CRBW A 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CRBW A 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CRBW A 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CRBW A 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CRBW B 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CRBW B 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CRBW B 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CRBW B 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CRBW B 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CRBW B 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CRBW C 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CRBW C 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CRBW C 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CRBW C 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CRBW C 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + CRBW C 1 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Fire: + CRBW D 5 A_FireCrossbowPL2 + CRBW E 3 + CRBW F 2 + CRBW G 3 + CRBW H 2 + CRBW A 3 + CRBW B 3 + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "BotRefire") + CRBW C 4 A_ReFire + Goto Ready + BotRefire: + TNT1 A 0 A_JumpIfNoAmmo("Deselect") + Goto Fire + } +} + +// -------------------------------------------------------------------------- +// +// Gauntlets +// +// -------------------------------------------------------------------------- + +ACTOR BotGauntlets : Gauntlets replaces Gauntlets +{ + Weapon.SlotNumber 1 + Weapon.SisterWeapon "BotGauntletsPowered" + States + { + Spawn: + WGNT A -1 + Stop + Ready: + GAUN A 1 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Deselect: + TNT1 A 0 A_TakeInventory("BotMeleeWeapon") + GAUN A 1 A_Lower + Loop + Select: + TNT1 A 0 A_GiveInventory("BotMeleeWeapon") + GAUN A 1 A_Raise + Loop + Fire: + GAUN B 4 A_PlayWeaponSound("weapons/gauntletsuse") + GAUN C 4 + Hold: + GAUN DEF 4 BRIGHT A_GauntletAttack(0) + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "BotRefire") + GAUN C 4 A_ReFire + GAUN B 4 A_Light0 + Goto Ready + BotRefire: + TNT1 A 0 A_JumpIfNoAmmo("Deselect") + Goto Hold + } +} + + +ACTOR BotGauntletsPowered : GauntletsPowered replaces GauntletsPowered +{ + Weapon.SlotNumber 1 + Weapon.SisterWeapon "BotGauntlets" + States + { + Ready: + GAUN G 4 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + GAUN H 4 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + GAUN I 4 A_WeaponReady TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Deselect: + TNT1 A 0 A_TakeInventory("BotMeleeWeapon") + GAUN G 1 A_Lower + Loop + Select: + TNT1 A 0 A_GiveInventory("BotMeleeWeapon") + GAUN G 1 A_Raise + Loop + Fire: + GAUN J 4 A_PlayWeaponSound("weapons/gauntletsuse") + GAUN K 4 + Hold: + GAUN LMN 4 BRIGHT A_GauntletAttack(1) + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "BotRefire") + GAUN K 4 A_ReFire + GAUN J 4 A_Light0 + Goto Ready + BotRefire: + TNT1 A 0 A_JumpIfNoAmmo("Deselect") + Goto Hold + } +} + +// -------------------------------------------------------------------------- +// +// The mace itself +// +// -------------------------------------------------------------------------- + +ACTOR BotMace : Mace replaces Mace +{ + Weapon.SlotNumber 7 + Weapon.SisterWeapon "BotMacePowered" + States + { + Spawn: + WMCE A -1 + Stop + Ready: + MACE A 1 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Deselect: + TNT1 A 0 A_TakeInventory("BotCloseRange") + MACE A 1 A_Lower + Loop + Select: + TNT1 A 0 A_GiveInventory("BotCloseRange") + MACE A 1 A_Raise + Loop + Fire: + MACE B 4 + Hold: + MACE CDEF 3 A_FireMacePL1 + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "BotRefire") + MACE C 4 A_ReFire + MACE DEFB 4 + Goto Ready + BotRefire: + TNT1 A 0 A_JumpIfNoAmmo("Deselect") + Goto Hold + } +} + +ACTOR BotMacePowered : MacePowered replaces MacePowered +{ + Weapon.SlotNumber 7 + Weapon.SisterWeapon "BotMace" + States + { + Deselect: + TNT1 A 0 A_TakeInventory("BotCloseRange") + MACE A 1 A_Lower + Loop + Select: + TNT1 A 0 A_GiveInventory("BotCloseRange") + MACE A 1 A_Raise + Loop + Ready: + MACE A 1 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Fire: + Hold: + MACE B 4 + MACE D 4 A_FireMacePL2 + MACE B 4 + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "BotRefire") + MACE A 8 A_ReFire + Goto Ready + BotRefire: + TNT1 A 0 A_JumpIfNoAmmo("Deselect") + Goto Hold + } +} + +// -------------------------------------------------------------------------- +// +// Blaster +// +// -------------------------------------------------------------------------- + +ACTOR BotBlaster : Blaster replaces Blaster +{ + Weapon.SlotNumber 4 + Weapon.SisterWeapon "BotBlasterPowered" + States + { + Spawn: + WBLS A -1 + Stop + Ready: + BLSR A 1 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Deselect: + TNT1 A 0 A_TakeInventory("BotForceInacc") + BLSR A 1 A_Lower + Loop + Select: + TNT1 A 0 A_GiveInventory("BotForceInacc") + BLSR A 1 A_Raise + Loop + Fire: + BLSR BC 3 + Hold: + BLSR D 2 A_FireBlasterPL1 + BLSR CB 2 + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "BotRefire") + BLSR A 0 A_ReFire + Goto Ready + BotRefire: + TNT1 A 0 A_JumpIfNoAmmo("Deselect") + Goto Hold + } +} + +ACTOR BotBlasterPowered : BlasterPowered replaces BlasterPowered +{ + Weapon.SlotNumber 4 + Weapon.SisterWeapon "BotBlaster" + States + { + Ready: + BLSR A 1 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Fire: + BLSR BC 0 + Hold: + BLSR D 3 A_FireCustomMissile("BlasterFX1") + BLSR CB 4 + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "BotRefire") + BLSR A 0 A_ReFire + Goto Ready + BotRefire: + TNT1 A 0 A_JumpIfNoAmmo("Deselect") + Goto Hold + } +} + +// -------------------------------------------------------------------------- +// +// Skull (horn) rod +// +// -------------------------------------------------------------------------- + +ACTOR BotSkullRod : SkullRod replaces SkullRod +{ + Weapon.SlotNumber 5 + Weapon.SisterWeapon "BotSkullRodPowered" + States + { + Spawn: + WSKL A -1 + Stop + Ready: + HROD A 1 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Deselect: + TNT1 A 0 A_TakeInventory("BotForceInacc") + HROD A 1 A_Lower + Loop + Select: + TNT1 A 0 A_GiveInventory("BotForceInacc") + HROD A 1 A_Raise + Loop + Fire: + HROD AB 4 A_FireSkullRodPL1 + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "BotRefire") + HROD B 0 A_ReFire + Goto Ready + BotRefire: + TNT1 A 0 A_JumpIfNoAmmo("Deselect") + Goto Fire + } +} + +ACTOR BotSkullRodPowered : SkullRodPowered replaces SkullRodPowered +{ + Weapon.SlotNumber 5 + Weapon.SisterWeapon "BotSkullRod" + States + { + Ready: + HROD A 1 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Fire: + HROD C 2 + HROD D 3 + HROD E 2 + HROD F 3 + HROD G 4 A_FireSkullRodPL2 + HROD F 2 + HROD E 3 + HROD D 2 + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "BotRefire") + HROD C 2 A_ReFire + Goto Ready + BotRefire: + TNT1 A 0 A_JumpIfNoAmmo("Deselect") + Goto Fire + } +} + +// -------------------------------------------------------------------------- +// +// Phoenix rod +// +// -------------------------------------------------------------------------- + +ACTOR BotPhoenixRod : PhoenixRod replaces PhoenixRod +{ + Weapon.SlotNumber 6 + Weapon.Sisterweapon "BotPhoenixRodPowered" + States + { + Spawn: + WPHX A -1 + Stop + Ready: + PHNX A 1 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Deselect: + PHNX A 1 A_Lower + Loop + Select: + PHNX A 1 A_Raise + Loop + Fire: + PHNX B 5 + PHNX C 7 A_FirePhoenixPL1 + PHNX DB 4 + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "BotRefire") + PHNX B 0 A_ReFire + Goto Ready + BotRefire: + TNT1 A 0 A_JumpIfNoAmmo("Deselect") + Goto Fire + } +} + +ACTOR BotPhoenixRodPowered : PhoenixRodPowered replaces PhoenixRodPowered +{ + Weapon.SlotNumber 6 + Weapon.SisterWeapon "BotPhoenixRod" + States + { + Ready: + PHNX A 1 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Fire: + PHNX B 3 A_InitPhoenixPL2 + Hold: + PHNX C 1 A_FirePhoenixPL2 + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "BotRefire") + PHNX B 4 A_ReFire + Powerdown: + PHNX B 4 A_ShutdownPhoenixPL2 + Goto Ready + BotRefire: + TNT1 A 0 A_JumpIfNoAmmo("Deselect") + Goto Hold + } +} diff --git a/wadsrc_tdbots/static/tdbots/NODESTUDIO.dec b/wadsrc_tdbots/static/tdbots/NODESTUDIO.dec new file mode 100644 index 0000000000..f039f9b6ca --- /dev/null +++ b/wadsrc_tdbots/static/tdbots/NODESTUDIO.dec @@ -0,0 +1,115 @@ +Actor TDBots_NodeStudio_Editor : Weapon +{ + +WEAPON.NOAUTOFIRE + +WEAPON.NOLMS + +WEAPON.CHEATNOTWEAPON + const int FLAGS = FBF_NORANDOMPUFFZ|FBF_NORANDOM|FBF_NOFLASH; + states + { + Select: + TNT1 AA 0 A_Raise + TNT1 A 1 A_Raise + Loop + Deselect: + TNT1 A 1 A_Lower + Loop + Ready: + TNT1 A 1 A_FireBullets(0,0,1,0,"TDBots_NodeStudio_Marker",FLAGS) + TNT1 A 0 A_WeaponReady(WRF_NOSWITCH) + TNT1 A 0 A_JumpIfInventory("TDB_NS_Reload",1,"Reload") + TNT1 A 0 A_JumpIfInventory("TDB_NS_Zoom",1,"Zoom") + Loop + Zoom: + TNT1 A 1 ACS_NamedExecuteAlways("TDBots_NodeStudio_Save",0) + TNT1 A 0 A_JumpIfInventory("TDB_NS_Zoom",1,"ZR_Done") + Goto Ready + Reload: + TNT1 A 1 ACS_NamedExecuteAlways("TDBots_NodeStudio_Help",0) + TNT1 A 0 A_JumpIfInventory("TDB_NS_Reload",1,"ZR_Done") + Goto Ready + Fire: + TNT1 A 0 + TNT1 A 1 A_FireBullets(0,0,1,0,"TDBots_NodeStudio_Node",FLAGS) + TNT1 A 0 A_Refire("Done") + Goto Ready + AltFire: + TNT1 A 1 ACS_NamedExecuteAlways("TDBots_NodeStudio_Undo",0) + TNT1 A 0 A_Refire("Done") + Goto Ready + Done: + TNT1 A 1 A_FireBullets(0,0,1,0,"TDBots_NodeStudio_Marker",FLAGS) + TNT1 A 0 A_Refire("Done") + Goto Ready + ZR_Done: + TNT1 A 1 A_FireBullets(0,0,1,0,"TDBots_NodeStudio_Marker",FLAGS) + TNT1 A 0 A_JumpIfInventory("TDB_NS_Reload",1,"ZR_Done") + TNT1 A 0 A_JumpIfInventory("TDB_NS_Zoom",1,"ZR_Done") + Goto Ready + } +} + +Actor TDB_NS_Reload : Inventory {} +Actor TDB_NS_Zoom : Inventory {} + +Actor TDBots_NodeStudio_Marker : BulletPuff +{ + +FORCEXYBILLBOARD + +CLIENTSIDEONLY + +PUFFONACTORS + -RANDOMIZE + states + { + Spawn: + Melee: + TBND A 2 BRIGHT + Stop + } +} + +Actor TDBots_NodeStudio_Node : TDBots_NodeStudio_Marker +{ + -CLIENTSIDEONLY + states + { + Spawn: + Melee: + TNT1 A 0 + TNT1 A 0 ACS_NamedExecuteAlways("TDBots_NodeStudio_PlaceNode",0) + TBND A 2 BRIGHT + Stop + } +} + +Actor TDBots_NodeStudio_FakeNode +{ + Scale 3.0 + states + { + Spawn: + TNT1 AA 0 A_Stop + TBND B -1 BRIGHT + Stop + } +} + +Actor TDBots_NodeStudio_FakeJumpNode : TDBots_NodeStudio_FakeNode +{ + states + { + Spawn: + TNT1 AA 0 A_Stop + TBND C -1 BRIGHT + Stop + } +} + +Actor TDBots_NodeStudio_FakePreciseNode : TDBots_NodeStudio_FakeNode +{ + states + { + Spawn: + TNT1 AA 0 A_Stop + TBND D -1 BRIGHT + Stop + } +} \ No newline at end of file diff --git a/wadsrc_tdbots/static/tdbots/PATHNODING.dec b/wadsrc_tdbots/static/tdbots/PATHNODING.dec new file mode 100644 index 0000000000..2cde43a4fd --- /dev/null +++ b/wadsrc_tdbots/static/tdbots/PATHNODING.dec @@ -0,0 +1,177 @@ +ACTOR TDBots_IDoExist : Inventory {} + +/*============================================================================== + + Normal Path Node, does nothing special to the bot following it + +==============================================================================*/ +ACTOR TDBots_PathNode : CustomInventory 2401 +{ + +INVISIBLE + + +INVENTORY.NOSCREENFLASH + +INVENTORY.NEVERRESPAWN + Inventory.MaxAmount 99999 + Inventory.PickupMessage "" + Inventory.PickupSound "" + Scale 3.0 + states + { + Spawn: + "####" "#" 0 NoDelay A_GiveInventory("TDBots_IDoExist") + "####" "#" 0 A_SpawnItemEx("TDBots_NodeRespawn",0,0,0,0,0,0,0,SXF_NOCHECKPOSITION,0,tid) + TBND B -1 + Stop + Pickup: + TNT1 A 0 A_TakeInventory("BotPrecisionMode") + TNT1 A 0 A_JumpIfInventory("TDBots_IsBot", 1, "DoPickup") + Fail + DoPickup: + TNT1 A 0 A_RailWait + Stop + } +} + +ACTOR TDBots_NodeRespawn +{ + +INVISIBLE + + Scale 4.0 + +NOBLOCKMAP + +NOCLIP + +ISMONSTER + -SHOOTABLE + -SOLID + + var int user_ntid; + states + { + Spawn: + TNT1 A 0 NoDelay A_SetUserVar("user_ntid", tid) + TNT1 A 0 Thing_ChangeTID(0,0) + TNT1 A 0 ACS_NamedExecuteWithResult("TDBots_SetTarget", user_ntid) + Idle: + TNT1 A 1 + TNT1 A 0 A_JumpIfInventory("TDBots_IDoExist", 1, "Idle", AAPTR_TARGET) + RespawnNode: + TBND Z 0 + "####" "#####" 35 + TNT1 A 0 A_SpawnItemEx("TDBots_PathNode",0,0,0,0,0,0,0,SXF_NOCHECKPOSITION,0,user_ntid) + Stop + } +} + +/*============================================================================== + + Jump Node, makes the bot jump in the direction it was moving + when touching it. + +==============================================================================*/ +ACTOR TDBots_JumpNode : TDBots_PathNode 2402 +{ + states + { + Spawn: + "####" "#" 0 NoDelay A_GiveInventory("TDBots_IDoExist") + "####" "#" 0 A_SpawnItemEx("TDBots_JNodeRespawn",0,0,0,0,0,0,0,SXF_NOCHECKPOSITION,0,tid) + TBND C -1 + Stop + Pickup: + TNT1 A 0 A_TakeInventory("BotPrecisionMode") + TNT1 A 0 A_JumpIfInventory("TDBots_IsBot", 1, "DoPickup") + Fail + DoPickup: + TNT1 A 0 ACS_NamedExecuteAlways("TDBots_Jump") + TNT1 A 0 A_Jump(256, "DoPickup2") + Stop + DoPickup2: + TNT1 A 0 A_RailWait + Stop + + } +} + +ACTOR TDBots_JNodeRespawn : TDBots_NodeRespawn +{ + states + { + Spawn: + TNT1 A 0 NoDelay A_SetUserVar("user_ntid", tid) + TNT1 A 0 Thing_ChangeTID(0,0) + TNT1 A 0 ACS_NamedExecuteWithResult("TDBots_SetTarget", user_ntid) + Idle: + TNT1 A 1 + TNT1 A 0 A_JumpIfInventory("TDBots_IDoExist", 1, "Idle", AAPTR_TARGET) + RespawnNode: + TBND Z 0 + "####" "#####" 35 + TNT1 A 0 A_SpawnItemEx("TDBots_JumpNode",0,0,0,0,0,0,0,SXF_NOCHECKPOSITION,0,user_ntid) + Stop + } +} + +/*============================================================================== + + Precision Node, once touched, makes the bot completely stop + dodging attacks when in attacking state, to avoid falling down + accidentally. + +==============================================================================*/ +ACTOR TDBots_PrecisionNode : TDBots_PathNode +{ + states + { + Spawn: + "####" "#" 0 NoDelay A_GiveInventory("TDBots_IDoExist") + "####" "#" 0 A_SpawnItemEx("TDBots_PNodeRespawn",0,0,0,0,0,0,0,SXF_SETMASTER|SXF_NOCHECKPOSITION,0,tid) + TBND D -1 + Stop + Pickup: + TNT1 A 0 A_GiveInventory("BotPrecisionMode") + TNT1 A 0 A_JumpIfInventory("TDBots_IsBot", 1, "DoPickup") + Fail + DoPickup: + TNT1 A 0 A_RailWait + Stop + } +} + +ACTOR TDBots_PNodeRespawn : TDBots_NodeRespawn +{ + states + { + Spawn: + TNT1 A 0 NoDelay A_SetUserVar("user_ntid", tid) + TNT1 A 0 Thing_ChangeTID(0,0) + TNT1 A 0 ACS_NamedExecuteWithResult("TDBots_SetTarget", user_ntid) + Idle: + TNT1 A 1 + TNT1 A 0 A_JumpIfInventory("TDBots_IDoExist", 1, "Idle", AAPTR_TARGET) + RespawnNode: + TBND Z 0 + "####" "#####" 35 + TNT1 A 0 A_SpawnItemEx("TDBots_PrecisionNode",0,0,0,0,0,0,0,SXF_NOCHECKPOSITION,0,user_ntid) + Stop + } +} + +ACTOR TDBots_TempNode : TDBots_PathNode +{ + states + { + Spawn: + TNT1 AAAAA 35 + Stop + } +} + +ACTOR TDBots_AutoNoder +{ + +NOINTERACTION + +INVISIBLE + states + { + Spawn: + Goto Null + } +} \ No newline at end of file diff --git a/wadsrc_tdbots/static/tdbots/STRFWEAPONS.dec b/wadsrc_tdbots/static/tdbots/STRFWEAPONS.dec new file mode 100644 index 0000000000..c0f5ba036f --- /dev/null +++ b/wadsrc_tdbots/static/tdbots/STRFWEAPONS.dec @@ -0,0 +1,327 @@ +//NOTE: The sigil weapon is not included, because it relies on hardcoded +//behavior. To be more clear, hardcoded behavior i don't understand and thus +//can't recreate reliably. + +// -------------------------------------------------------------------------- +// +// Dagger +// +// -------------------------------------------------------------------------- + +ACTOR BotPunchDagger : PunchDagger replaces PunchDagger +{ + Weapon.SlotNumber 1 + States + { + Ready: + PNCH A 1 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Deselect: + TNT1 A 0 A_TakeInventory("BotMeleeWeapon") + PNCH A 1 A_Lower + Loop + Select: + TNT1 A 0 A_GiveInventory("BotMeleeWeapon") + PNCH A 1 A_Raise + Loop + Fire: + PNCH B 4 + PNCH C 4 A_JabDagger + PNCH D 5 + PNCH C 4 + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + PNCH B 5 A_ReFire + Goto Ready + } + +} + +// -------------------------------------------------------------------------- +// +// Strife's crossbow +// +// -------------------------------------------------------------------------- + +ACTOR BotStrifeCrossbow : StrifeCrossbow replaces StrifeCrossbow +{ + Weapon.SlotNumber 2 + Weapon.SisterWeapon "BotStrifeCrossbow2" + states + { + Ready: + XBOW A 0 A_ShowElectricFlash + Ready2: + XBOW A 1 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Fire: + XBOW A 3 A_ClearFlash + XBOW B 6 A_FireArrow("ElectricBolt") + XBOW C 4 + XBOW D 6 + XBOW E 3 + XBOW F 5 + XBOW G 0 A_ShowElectricFlash + XBOW G 5 A_CheckReload + Goto Ready+1 + } +} + + +ACTOR BotStrifeCrossbow2 : StrifeCrossbow2 replaces StrifeCrossbow2 +{ + Weapon.SlotNumber 2 + Weapon.SisterWeapon "BotStrifeCrossbow" + States + { + Ready: + XBOW H 1 A_WeaponReady + Loop + Fire: + XBOW H 3 + XBOW B 6 A_FireArrow("PoisonBolt") + XBOW C 4 + XBOW D 6 + XBOW E 3 + XBOW I 5 + XBOW J 5 A_CheckReload + Goto Ready + Flash: + Stop + } +} + +// -------------------------------------------------------------------------- +// +// Assault gun +// +// -------------------------------------------------------------------------- + +actor BotAssaultGun : AssaultGun replaces AssaultGun +{ + Weapon.SlotNumber 3 + States + { + Ready: + RIFG A 1 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Fire: + RIFF AB 3 A_FireAssaultGun + RIFG D 3 A_FireAssaultGun + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + RIFG C 0 A_ReFire + RIFG B 2 A_Light0 + Goto Ready + } +} + + +// -------------------------------------------------------------------------- +// +// Assault Gun (standing variant) +// +// -------------------------------------------------------------------------- + +ACTOR BotAssaultGunStanding : AssaultGunStanding replaces AssaultGunStanding {} + +// -------------------------------------------------------------------------- +// +// Mini-Missile Launcher +// +// -------------------------------------------------------------------------- + +ACTOR BotMiniMissileLauncher : MiniMissileLauncher replaces MiniMissileLauncher +{ + Weapon.SlotNumber 4 + States + { + Ready: + MMIS A 1 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Deselect: + TNT1 A 0 A_TakeInventory("BotExplosiveWeapon") + MMIS A 1 A_Lower + Loop + Select: + TNT1 A 0 A_GiveInventory("BotExplosiveWeapon") + MMIS A 1 A_Raise + Loop + Fire: + MMIS A 4 A_FireMiniMissile + MMIS B 4 A_Light1 + MMIS C 5 Bright + MMIS D 2 Bright A_Light2 + MMIS E 2 Bright + MMIS F 2 Bright A_Light0 + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MMIS F 0 A_ReFire + Goto Ready + } +} + +// -------------------------------------------------------------------------- +// +// Flame thrower +// +// -------------------------------------------------------------------------- + +ACTOR BotFlameThrower : FlameThrower replaces FlameThrower +{ + Weapon.SlotNumber 6 + States + { + Ready: + FLMT A 3 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + FLMT B 3 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Fire: + FLMF A 2 A_FireFlamer + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + FLMF B 3 A_ReFire + Goto Ready + } +} + +// Mauler ------------------------------------------------------------------- +// The scatter version + +ACTOR BotMauler : Mauler replaces Mauler +{ + Weapon.SlotNumber 7 + Weapon.SisterWeapon "BotMauler2" + States + { + Ready: + MAUL F 6 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MAUL G 6 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MAUL H 6 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MAUL A 6 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Fire: + BLSF A 5 Bright A_FireMauler1 + MAUL B 3 Bright A_Light1 + MAUL C 2 A_Light2 + MAUL DE 2 + MAUL A 7 A_Light0 + MAUL H 7 + MAUL G 7 A_CheckReload + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Goto Ready + } +} + + +// -------------------------------------------------------------------------- +// +// Mauler (torpedo version) +// +// -------------------------------------------------------------------------- + +ACTOR BotMauler2 : Mauler2 replaces Mauler2 +{ + Weapon.SlotNumber 7 + Weapon.SisterWeapon "BotMauler" + States + { + Ready: + MAUL I 7 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MAUL J 7 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MAUL K 7 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MAUL L 7 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Fire: + MAUL I 20 A_FireMauler2Pre + MAUL J 10 A_Light1 + BLSF A 10 Bright A_FireMauler2 + MAUL B 10 Bright A_Light2 + MAUL C 2 + MAUL D 2 A_Light0 + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + MAUL E 2 A_ReFire + Goto Ready + } +} + +// -------------------------------------------------------------------------- +// +// HE-Grenade Launcher +// +// -------------------------------------------------------------------------- + +ACTOR BotStrifeGrenadeLauncher : StrifeGrenadeLauncher replaces StrifeGrenadeLauncher +{ + Weapon.SlotNumber 5 + Weapon.SisterWeapon "BotStrifeGrenadeLauncher2" + States + { + Ready: + GREN A 1 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Deselect: + TNT1 A 0 A_TakeInventory("BotExplosiveWeapon") + GREN A 1 A_Lower + Loop + Select: + TNT1 A 0 A_GiveInventory("BotExplosiveWeapon") + GREN A 1 A_Raise + Loop + Fire: + GREN A 5 A_FireGrenade("HEGrenade", -90, "Flash") + GREN B 10 + GREN A 5 A_FireGrenade("HEGrenade", 90, "Flash2") + GREN C 10 + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + GREN A 0 A_ReFire + Goto Ready + } + +} + +// -------------------------------------------------------------------------- +// +// White phosphorus Grenade Launcher +// +// -------------------------------------------------------------------------- + +ACTOR BotStrifeGrenadeLauncher2 : StrifeGrenadeLauncher2 replaces StrifeGrenadeLauncher2 +{ + Weapon.SlotNumber 5 + Weapon.SisterWeapon "BotStrifeGrenadeLauncher" + States + { + Ready: + GREN D 1 A_WeaponReady + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + Loop + Deselect: + TNT1 A 0 A_TakeInventory("BotExplosiveWeapon") + GREN D 1 A_Lower + Loop + Select: + TNT1 A 0 A_GiveInventory("BotExplosiveWeapon") + GREN D 1 A_Raise + Loop + Fire: + GREN D 5 A_FireGrenade("PhosphorousGrenade", -90, "Flash") + GREN E 10 + GREN D 5 A_FireGrenade("PhosphorousGrenade", 90, "Flash2") + GREN F 10 + TNT1 A 0 A_JumpIfInventory("BotAttack", 1, "Fire") + GREN A 0 A_ReFire + Goto Ready + } +} \ No newline at end of file