- add TDBots 2.7 to replace ZCajun

This commit is contained in:
Rachael Alexanderson 2021-01-26 14:30:26 -05:00
parent d36ca650e3
commit 6e73adff20
49 changed files with 5947 additions and 1489 deletions

View file

@ -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 )

381
bot-config/bots.cfg Normal file
View file

@ -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
}

View file

@ -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

View file

@ -13,6 +13,7 @@ struct FStartupInfo
int LoadLights = -1;
int LoadBrightmaps = -1;
int LoadWidescreen = -1;
int LoadBots = -1;
int modern = 0;
enum
{

View file

@ -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<FString> &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.

View file

@ -55,7 +55,6 @@
#include "texturemanager.h"
extern void LoadActors ();
extern void InitBotStuff();
extern void ClearStrifeTypes();
TArray<PClassActor *> 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);

View file

@ -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;i<countof(warnbotmissiles);i++)
{
AActor *a = GetDefaultByName (warnbotmissiles[i]);
if (a != nullptr)
{
a->flags3|=MF3_WARNBOT;
}
}
}

View file

@ -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<FString> 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);
};

View file

@ -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->lightlevel<WHATS_DARK/* && !(player->powers & 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 ()
{

View file

@ -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");

View file

@ -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;
}

View file

@ -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)<AVOID_DIST)) //try avoid missile got from P_Mobj.c thinking part.
{
Pitch (missile);
Angle = player->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<AActor>(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<AActor>(NAME_Ammo1);
auto ammo2 = heldWeapon->PointerVar<AActor>(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<PClassActor*>(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;
}

View file

@ -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

View file

@ -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);

View file

@ -0,0 +1,3 @@
cmake_minimum_required( VERSION 2.8.7 )
add_pk3(gzdoom-tdbots.pk3 ${CMAKE_CURRENT_SOURCE_DIR}/static)

View file

@ -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 = ""
}

View file

@ -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.

View file

@ -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;

View file

@ -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
}
}

View file

@ -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"

View file

@ -0,0 +1 @@
alias "NStudio_Start" "pukename tdbots_nodestudio_startup"

View file

@ -0,0 +1,3 @@
//Post Mortem stuff.
[enu default]
DUMB_BOTS = "TDBots do what Zandronum bots don't, period.";

View file

@ -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.

View file

@ -0,0 +1,2 @@
NODESTUDIO
TDB_Main

View file

@ -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
}

View file

@ -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.

Binary file not shown.

Binary file not shown.

View file

@ -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
//
//==============================================================================

View file

@ -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);
}

View file

@ -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

View file

@ -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;
}

View file

@ -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);}
}
}
}

View file

@ -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;
}

View file

@ -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();
}
}

View file

@ -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);
}

View file

@ -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");}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 B

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -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
}
}