- Renamed FBot to DBot and made it inherit from DThinker.

- Fixed: Bots added by players other than the net arbitrator did not have their skill set.
This commit is contained in:
ChillyDoom 2014-10-14 19:57:11 +01:00
parent 83d84eaae9
commit db323643f8
24 changed files with 355 additions and 326 deletions

View file

@ -1,7 +1,7 @@
// Cajun bot console commands. // Cajun bot
// //
// [RH] Moved out of d_netcmd.c (in Cajun source), because they don't really // [RH] Moved console commands out of d_netcmd.c (in Cajun source), because
// belong there. // they don't really belong there.
#include "c_cvars.h" #include "c_cvars.h"
#include "c_dispatch.h" #include "c_dispatch.h"
@ -14,6 +14,71 @@
#include "d_net.h" #include "d_net.h"
#include "farchive.h" #include "farchive.h"
IMPLEMENT_POINTY_CLASS(DBot)
DECLARE_POINTER(dest)
DECLARE_POINTER(prev)
DECLARE_POINTER(enemy)
DECLARE_POINTER(missile)
DECLARE_POINTER(mate)
DECLARE_POINTER(last_mate)
END_POINTERS
DBot::DBot ()
{
savedyaw = 0;
savedpitch = 0;
angle = 0;
dest = NULL;
prev = NULL;
enemy = NULL;
missile = NULL;
mate = NULL;
last_mate = NULL;
memset(&skill, 0, sizeof(skill));
t_active = 0;
t_respawn = 0;
t_strafe = 0;
t_react = 0;
t_fight = 0;
t_roam = 0;
t_rocket = 0;
first_shot = true;
sleft = false;
allround = false;
increase = false;
oldx = 0;
oldy = 0;
}
void DBot::Serialize (FArchive &arc)
{
Super::Serialize (arc);
arc << savedyaw
<< savedpitch
<< angle
<< dest
<< prev
<< enemy
<< missile
<< mate
<< last_mate
<< skill
<< t_active
<< t_respawn
<< t_strafe
<< t_react
<< t_fight
<< t_roam
<< t_rocket
<< first_shot
<< sleft
<< allround
<< increase
<< oldx
<< oldy;
}
CVAR (Int, bot_next_color, 11, 0) CVAR (Int, bot_next_color, 11, 0)
CVAR (Bool, bot_observer, false, 0) CVAR (Bool, bot_observer, false, 0)

View file

@ -85,11 +85,10 @@ public:
void Main (int buf); void Main (int buf);
void Init (); void Init ();
void End(); void End();
void CleanBotstuff (player_t *p);
bool SpawnBot (const char *name, int color = NOCOLOR); bool SpawnBot (const char *name, int color = NOCOLOR);
bool LoadBots (); bool LoadBots ();
void ForgetBots (); void ForgetBots ();
void DoAddBot (int bnum, char *info); void DoAddBot (BYTE **stream);
void RemoveAllBots (bool fromlist); void RemoveAllBots (bool fromlist);
//(B_Func.c) //(B_Func.c)
@ -109,7 +108,6 @@ public:
bool IsDangerous (sector_t *sec); bool IsDangerous (sector_t *sec);
TArray<FString> getspawned; //Array of bots (their names) which should be spawned when starting a game. TArray<FString> getspawned; //Array of bots (their names) which should be spawned when starting a game.
bool botingame[MAXPLAYERS];
BYTE freeze:1; //Game in freeze mode. BYTE freeze:1; //Game in freeze mode.
BYTE changefreeze:1; //Game wants to change freeze mode. BYTE changefreeze:1; //Game wants to change freeze mode.
int botnum; int botnum;
@ -145,14 +143,20 @@ protected:
bool observer; //Consoleplayer is observer. bool observer; //Consoleplayer is observer.
}; };
class FBot class DBot : public DThinker
{ {
DECLARE_CLASS(DBot,DThinker)
HAS_OBJECT_POINTERS
public: public:
DBot ();
void Serialize (FArchive &arc);
angle_t savedyaw; angle_t savedyaw;
int savedpitch; int savedpitch;
angle_t angle; // The wanted angle that the bot try to get every tic. angle_t angle; // The wanted angle that the bot try to get every tic.
// (used to get a smoth view movement) // (used to get a smooth view movement)
TObjPtr<AActor> dest; // Move Destination. TObjPtr<AActor> dest; // Move Destination.
TObjPtr<AActor> prev; // Previous move destination. TObjPtr<AActor> prev; // Previous move destination.
@ -179,10 +183,10 @@ public:
int t_rocket; int t_rocket;
//Misc booleans //Misc booleans
bool isbot;
bool first_shot; // Used for reaction skill. bool first_shot; // Used for reaction skill.
bool sleft; // If false, strafe is right. bool sleft; // If false, strafe is right.
bool allround; bool allround;
bool increase;
fixed_t oldx; fixed_t oldx;
fixed_t oldy; fixed_t oldy;

View file

@ -140,8 +140,7 @@ void FCajunMaster::Dofire (AActor *actor, ticcmd_t *cmd)
fixed_t dist; fixed_t dist;
angle_t an; angle_t an;
int m; int m;
static bool inc[MAXPLAYERS]; AActor *enemy = actor->player->Bot->enemy;
AActor *enemy = actor->player->Bot.enemy;
if (!enemy || !(enemy->flags & MF_SHOOTABLE) || enemy->health <= 0) if (!enemy || !(enemy->flags & MF_SHOOTABLE) || enemy->health <= 0)
return; return;
@ -149,20 +148,20 @@ void FCajunMaster::Dofire (AActor *actor, ticcmd_t *cmd)
if (actor->player->ReadyWeapon == NULL) if (actor->player->ReadyWeapon == NULL)
return; return;
if (actor->player->damagecount > actor->player->Bot.skill.isp) if (actor->player->damagecount > actor->player->Bot->skill.isp)
{ {
actor->player->Bot.first_shot = true; actor->player->Bot->first_shot = true;
return; return;
} }
//Reaction skill thing. //Reaction skill thing.
if (actor->player->Bot.first_shot && if (actor->player->Bot->first_shot &&
!(actor->player->ReadyWeapon->WeaponFlags & WIF_BOT_REACTION_SKILL_THING)) !(actor->player->ReadyWeapon->WeaponFlags & WIF_BOT_REACTION_SKILL_THING))
{ {
actor->player->Bot.t_react = (100-actor->player->Bot.skill.reaction+1)/((pr_botdofire()%3)+3); actor->player->Bot->t_react = (100-actor->player->Bot->skill.reaction+1)/((pr_botdofire()%3)+3);
} }
actor->player->Bot.first_shot = false; actor->player->Bot->first_shot = false;
if (actor->player->Bot.t_react) if (actor->player->Bot->t_react)
return; return;
//MAKEME: Decrease the rocket suicides even more. //MAKEME: Decrease the rocket suicides even more.
@ -199,8 +198,8 @@ void FCajunMaster::Dofire (AActor *actor, ticcmd_t *cmd)
else if (actor->player->ReadyWeapon->WeaponFlags & WIF_BOT_BFG) else if (actor->player->ReadyWeapon->WeaponFlags & WIF_BOT_BFG)
{ {
//MAKEME: This should be smarter. //MAKEME: This should be smarter.
if ((pr_botdofire()%200)<=actor->player->Bot.skill.reaction) if ((pr_botdofire()%200)<=actor->player->Bot->skill.reaction)
if(Check_LOS(actor, actor->player->Bot.enemy, SHOOTFOV)) if(Check_LOS(actor, actor->player->Bot->enemy, SHOOTFOV))
no_fire = false; no_fire = false;
} }
else if (actor->player->ReadyWeapon->ProjectileType != NULL) else if (actor->player->ReadyWeapon->ProjectileType != NULL)
@ -211,11 +210,11 @@ void FCajunMaster::Dofire (AActor *actor, ticcmd_t *cmd)
an = FireRox (actor, enemy, cmd); an = FireRox (actor, enemy, cmd);
if(an) if(an)
{ {
actor->player->Bot.angle = an; actor->player->Bot->angle = an;
//have to be somewhat precise. to avoid suicide. //have to be somewhat precise. to avoid suicide.
if (abs (actor->player->Bot.angle - actor->angle) < 12*ANGLE_1) if (abs (actor->player->Bot->angle - actor->angle) < 12*ANGLE_1)
{ {
actor->player->Bot.t_rocket = 9; actor->player->Bot->t_rocket = 9;
no_fire = false; no_fire = false;
} }
} }
@ -225,14 +224,14 @@ shootmissile:
dist = P_AproxDistance (actor->x - enemy->x, actor->y - enemy->y); dist = P_AproxDistance (actor->x - enemy->x, actor->y - enemy->y);
m = dist / GetDefaultByType (actor->player->ReadyWeapon->ProjectileType)->Speed; m = dist / GetDefaultByType (actor->player->ReadyWeapon->ProjectileType)->Speed;
SetBodyAt (enemy->x + enemy->velx*m*2, enemy->y + enemy->vely*m*2, enemy->z, 1); SetBodyAt (enemy->x + enemy->velx*m*2, enemy->y + enemy->vely*m*2, enemy->z, 1);
actor->player->Bot.angle = R_PointToAngle2 (actor->x, actor->y, body1->x, body1->y); actor->player->Bot->angle = R_PointToAngle2 (actor->x, actor->y, body1->x, body1->y);
if (Check_LOS (actor, enemy, SHOOTFOV)) if (Check_LOS (actor, enemy, SHOOTFOV))
no_fire = false; no_fire = false;
} }
else else
{ {
//Other weapons, mostly instant hit stuff. //Other weapons, mostly instant hit stuff.
actor->player->Bot.angle = R_PointToAngle2 (actor->x, actor->y, enemy->x, enemy->y); actor->player->Bot->angle = R_PointToAngle2 (actor->x, actor->y, enemy->x, enemy->y);
aiming_penalty = 0; aiming_penalty = 0;
if (enemy->flags & MF_SHADOW) if (enemy->flags & MF_SHADOW)
aiming_penalty += (pr_botdofire()%25)+10; aiming_penalty += (pr_botdofire()%25)+10;
@ -240,7 +239,7 @@ shootmissile:
aiming_penalty += pr_botdofire()%40;//Dark aiming_penalty += pr_botdofire()%40;//Dark
if (actor->player->damagecount) if (actor->player->damagecount)
aiming_penalty += actor->player->damagecount; //Blood in face makes it hard to aim aiming_penalty += actor->player->damagecount; //Blood in face makes it hard to aim
aiming_value = actor->player->Bot.skill.aiming - aiming_penalty; aiming_value = actor->player->Bot->skill.aiming - aiming_penalty;
if (aiming_value <= 0) if (aiming_value <= 0)
aiming_value = 1; aiming_value = 1;
m = ((SHOOTFOV/2)-(aiming_value*SHOOTFOV/200)); //Higher skill is more accurate m = ((SHOOTFOV/2)-(aiming_value*SHOOTFOV/200)); //Higher skill is more accurate
@ -249,15 +248,15 @@ shootmissile:
if (m) if (m)
{ {
if (inc[actor->player - players]) if (actor->player->Bot->increase)
actor->player->Bot.angle += m; actor->player->Bot->angle += m;
else else
actor->player->Bot.angle -= m; actor->player->Bot->angle -= m;
} }
if (abs (actor->player->Bot.angle - actor->angle) < 4*ANGLE_1) if (abs (actor->player->Bot->angle - actor->angle) < 4*ANGLE_1)
{ {
inc[actor->player - players] = !inc[actor->player - players]; actor->player->Bot->increase = !actor->player->Bot->increase;
} }
if (Check_LOS (actor, enemy, (SHOOTFOV/2))) if (Check_LOS (actor, enemy, (SHOOTFOV/2)))
@ -285,20 +284,20 @@ AActor *FCajunMaster::Choose_Mate (AActor *bot)
bool p_leader[MAXPLAYERS]; bool p_leader[MAXPLAYERS];
//is mate alive? //is mate alive?
if (bot->player->Bot.mate) if (bot->player->Bot->mate)
{ {
if (bot->player->Bot.mate->health <= 0) if (bot->player->Bot->mate->health <= 0)
bot->player->Bot.mate = NULL; bot->player->Bot->mate = NULL;
else else
bot->player->Bot.last_mate = bot->player->Bot.mate; bot->player->Bot->last_mate = bot->player->Bot->mate;
} }
if (bot->player->Bot.mate) //Still is.. if (bot->player->Bot->mate) //Still is..
return bot->player->Bot.mate; return bot->player->Bot->mate;
//Check old_mates status. //Check old_mates status.
if (bot->player->Bot.last_mate) if (bot->player->Bot->last_mate)
if (bot->player->Bot.last_mate->health <= 0) if (bot->player->Bot->last_mate->health <= 0)
bot->player->Bot.last_mate = NULL; bot->player->Bot->last_mate = NULL;
for (count = 0; count < MAXPLAYERS; count++) for (count = 0; count < MAXPLAYERS; count++)
{ {
@ -307,8 +306,8 @@ AActor *FCajunMaster::Choose_Mate (AActor *bot)
p_leader[count] = false; p_leader[count] = false;
for (count2 = 0; count2 < MAXPLAYERS; count2++) for (count2 = 0; count2 < MAXPLAYERS; count2++)
{ {
if (players[count].Bot.isbot if (players[count2].Bot != NULL
&& players[count2].Bot.mate == players[count].mo) && players[count2].Bot->mate == players[count].mo)
{ {
p_leader[count] = true; p_leader[count] = true;
break; break;
@ -337,7 +336,6 @@ AActor *FCajunMaster::Choose_Mate (AActor *bot)
&& ((bot->health/2) <= client->mo->health || !deathmatch) && ((bot->health/2) <= client->mo->health || !deathmatch)
&& !p_leader[count]) //taken? && !p_leader[count]) //taken?
{ {
if (P_CheckSight (bot, client->mo, SF_IGNOREVISIBILITY)) if (P_CheckSight (bot, client->mo, SF_IGNOREVISIBILITY))
{ {
test = P_AproxDistance (client->mo->x - bot->x, test = P_AproxDistance (client->mo->x - bot->x,
@ -386,11 +384,11 @@ AActor *FCajunMaster::Find_enemy (AActor *bot)
} }
//Note: It's hard to ambush a bot who is not alone //Note: It's hard to ambush a bot who is not alone
if (bot->player->Bot.allround || bot->player->Bot.mate) if (bot->player->Bot->allround || bot->player->Bot->mate)
vangle = ANGLE_MAX; vangle = ANGLE_MAX;
else else
vangle = ENEMY_SCAN_FOV; vangle = ENEMY_SCAN_FOV;
bot->player->Bot.allround = false; bot->player->Bot->allround = false;
target = NULL; target = NULL;
closest_dist = FIXED_MAX; closest_dist = FIXED_MAX;

View file

@ -118,7 +118,7 @@ void FCajunMaster::Main (int buf)
BotThinkCycles.Clock(); BotThinkCycles.Clock();
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
{ {
if (playeringame[i] && players[i].mo && !freeze && players[i].Bot.isbot) if (playeringame[i] && players[i].mo && !freeze && players[i].Bot != NULL)
Think (players[i].mo, &netcmds[i][buf]); Think (players[i].mo, &netcmds[i][buf]);
} }
BotThinkCycles.Unclock(); BotThinkCycles.Unclock();
@ -172,16 +172,9 @@ void FCajunMaster::Init ()
body1 = NULL; body1 = NULL;
body2 = NULL; body2 = NULL;
//Remove all bots upon each level start, they'll get spawned instead.
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
{ {
waitingforspawn[i] = false; waitingforspawn[i] = false;
if (playeringame[i] && players[i].Bot.isbot)
{
CleanBotstuff (&players[i]);
players[i].Bot.isbot = false;
botingame[i] = false;
}
} }
if (ctf && teamplay == false) if (ctf && teamplay == false)
@ -214,13 +207,12 @@ void FCajunMaster::End ()
getspawned.Clear(); getspawned.Clear();
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
{ {
if (playeringame[i] && players[i].Bot.isbot) if (playeringame[i] && players[i].Bot != NULL)
{ {
if (deathmatch) if (deathmatch)
{ {
getspawned.Push(players[i].userinfo.GetName()); getspawned.Push(players[i].userinfo.GetName());
} }
CleanBotstuff (&players[i]);
} }
} }
if (deathmatch) if (deathmatch)
@ -335,8 +327,10 @@ bool FCajunMaster::SpawnBot (const char *name, int color)
} }
Net_WriteString (concat); Net_WriteString (concat);
} }
Net_WriteByte(thebot->skill.aiming);
players[playernumber].Bot.skill = thebot->skill; Net_WriteByte(thebot->skill.perfection);
Net_WriteByte(thebot->skill.reaction);
Net_WriteByte(thebot->skill.isp);
thebot->inuse = true; thebot->inuse = true;
@ -346,9 +340,17 @@ bool FCajunMaster::SpawnBot (const char *name, int color)
return true; return true;
} }
void FCajunMaster::DoAddBot (int bnum, char *info) void FCajunMaster::DoAddBot (BYTE **stream)
{ {
int bnum = ReadByte (stream);
char *info = ReadString (stream);
BYTE *infob = (BYTE *)info; BYTE *infob = (BYTE *)info;
botskill_t skill;
skill.aiming = ReadByte (stream);
skill.perfection = ReadByte (stream);
skill.reaction = ReadByte (stream);
skill.isp = ReadByte (stream);
D_ReadUserInfoStrings (bnum, &infob, false); D_ReadUserInfoStrings (bnum, &infob, false);
if (!deathmatch && playerstarts[bnum].type == 0) if (!deathmatch && playerstarts[bnum].type == 0)
{ {
@ -363,11 +365,11 @@ void FCajunMaster::DoAddBot (int bnum, char *info)
else else
{ {
multiplayer = true; //Prevents cheating and so on; emulates real netgame (almost). multiplayer = true; //Prevents cheating and so on; emulates real netgame (almost).
players[bnum].Bot.isbot = true; players[bnum].Bot = new DBot;
players[bnum].Bot->skill = skill;
playeringame[bnum] = true; playeringame[bnum] = true;
players[bnum].mo = NULL; players[bnum].mo = NULL;
players[bnum].playerstate = PST_ENTER; players[bnum].playerstate = PST_ENTER;
botingame[bnum] = true;
if (teamplay) if (teamplay)
Printf ("%s joined the %s team\n", players[bnum].userinfo.GetName(), Teams[players[bnum].userinfo.GetTeam()].GetName()); Printf ("%s joined the %s team\n", players[bnum].userinfo.GetName(), Teams[players[bnum].userinfo.GetTeam()].GetName());
@ -389,13 +391,13 @@ void FCajunMaster::RemoveAllBots (bool fromlist)
for (i = 0; i < MAXPLAYERS; ++i) for (i = 0; i < MAXPLAYERS; ++i)
{ {
if (playeringame[i] && botingame[i]) if (playeringame[i] && players[i].Bot != NULL)
{ {
// If a player is looking through this bot's eyes, make him // If a player is looking through this bot's eyes, make him
// look through his own eyes instead. // look through his own eyes instead.
for (j = 0; j < MAXPLAYERS; ++j) for (j = 0; j < MAXPLAYERS; ++j)
{ {
if (i != j && playeringame[j] && !botingame[j]) if (i != j && playeringame[j] && players[j].Bot == NULL)
{ {
if (players[j].camera == players[i].mo) if (players[j].camera == players[i].mo)
{ {
@ -421,28 +423,6 @@ void FCajunMaster::RemoveAllBots (bool fromlist)
botnum = 0; botnum = 0;
} }
//Clean the bot part of the player_t
//Used when bots are respawned or at level starts.
void FCajunMaster::CleanBotstuff (player_t *p)
{
p->Bot.angle = ANG45;
p->Bot.dest = NULL;
p->Bot.enemy = NULL; //The dead meat.
p->Bot.missile = NULL; //A threatening missile that needs to be avoided.
p->Bot.mate = NULL; //Friend (used for grouping in templay or coop.
p->Bot.last_mate = NULL; //If bot's mate dissapeared (not if died) that mate is pointed to by this. Allows bot to roam to it if necessary.
//Tickers
p->Bot.t_active = 0; //Open door, lower lift stuff, door must open and lift must go down before bot does anything radical like try a stuckmove
p->Bot.t_respawn = 0;
p->Bot.t_strafe = 0;
p->Bot.t_react = 0;
//Misc bools
p->Bot.isbot = true; //Important.
p->Bot.first_shot = true; //Used for reaction skill.
p->Bot.sleft = false; //If false, strafe is right.
p->Bot.allround = false;
}
//------------------ //------------------
//Reads data for bot from //Reads data for bot from

View file

@ -33,19 +33,19 @@ void FCajunMaster::Roam (AActor *actor, ticcmd_t *cmd)
{ {
int delta; int delta;
if (Reachable(actor, actor->player->Bot.dest)) if (Reachable(actor, actor->player->Bot->dest))
{ // Straight towards it. { // Straight towards it.
actor->player->Bot.angle = R_PointToAngle2(actor->x, actor->y, actor->player->Bot.dest->x, actor->player->Bot.dest->y); actor->player->Bot->angle = R_PointToAngle2(actor->x, actor->y, actor->player->Bot->dest->x, actor->player->Bot->dest->y);
} }
else if (actor->movedir < 8) // turn towards movement direction if not there yet else if (actor->movedir < 8) // turn towards movement direction if not there yet
{ {
actor->player->Bot.angle &= (angle_t)(7<<29); actor->player->Bot->angle &= (angle_t)(7<<29);
delta = actor->player->Bot.angle - (actor->movedir << 29); delta = actor->player->Bot->angle - (actor->movedir << 29);
if (delta > 0) if (delta > 0)
actor->player->Bot.angle -= ANG45; actor->player->Bot->angle -= ANG45;
else if (delta < 0) else if (delta < 0)
actor->player->Bot.angle += ANG45; actor->player->Bot->angle += ANG45;
} }
// chase towards destination. // chase towards destination.
@ -134,7 +134,7 @@ void FCajunMaster::NewChaseDir (AActor *actor, ticcmd_t *cmd)
dirtype_t turnaround; dirtype_t turnaround;
if (!actor->player->Bot.dest) if (!actor->player->Bot->dest)
{ {
#ifndef BOT_RELEASE_COMPILE #ifndef BOT_RELEASE_COMPILE
Printf ("Bot tried move without destination\n"); Printf ("Bot tried move without destination\n");
@ -145,8 +145,8 @@ void FCajunMaster::NewChaseDir (AActor *actor, ticcmd_t *cmd)
olddir = (dirtype_t)actor->movedir; olddir = (dirtype_t)actor->movedir;
turnaround = opposite[olddir]; turnaround = opposite[olddir];
deltax = actor->player->Bot.dest->x - actor->x; deltax = actor->player->Bot->dest->x - actor->x;
deltay = actor->player->Bot.dest->y - actor->y; deltay = actor->player->Bot->dest->y - actor->y;
if (deltax > 10*FRACUNIT) if (deltax > 10*FRACUNIT)
d[1] = DI_EAST; d[1] = DI_EAST;
@ -315,23 +315,23 @@ void FCajunMaster::TurnToAng (AActor *actor)
{ {
if (actor->player->ReadyWeapon->WeaponFlags & WIF_BOT_EXPLOSIVE) if (actor->player->ReadyWeapon->WeaponFlags & WIF_BOT_EXPLOSIVE)
{ {
if (actor->player->Bot.t_roam && !actor->player->Bot.missile) if (actor->player->Bot->t_roam && !actor->player->Bot->missile)
{ //Keep angle that where when shot where decided. { //Keep angle that where when shot where decided.
return; return;
} }
} }
if(actor->player->Bot.enemy) if(actor->player->Bot->enemy)
if(!actor->player->Bot.dest) //happens when running after item in combat situations, or normal, prevents weak turns if(!actor->player->Bot->dest) //happens when running after item in combat situations, or normal, prevents weak turns
if(actor->player->ReadyWeapon->ProjectileType == NULL && !(actor->player->ReadyWeapon->WeaponFlags & WIF_MELEEWEAPON)) if(actor->player->ReadyWeapon->ProjectileType == NULL && !(actor->player->ReadyWeapon->WeaponFlags & WIF_MELEEWEAPON))
if(Check_LOS(actor, actor->player->Bot.enemy, SHOOTFOV+5*ANGLE_1)) if(Check_LOS(actor, actor->player->Bot->enemy, SHOOTFOV+5*ANGLE_1))
maxturn = 3; maxturn = 3;
} }
int distance = actor->player->Bot.angle - actor->angle; int distance = actor->player->Bot->angle - actor->angle;
if (abs (distance) < OKAYRANGE && !actor->player->Bot.enemy) if (abs (distance) < OKAYRANGE && !actor->player->Bot->enemy)
return; return;
distance /= TURNSENS; distance /= TURNSENS;

View file

@ -28,13 +28,13 @@ void FCajunMaster::Think (AActor *actor, ticcmd_t *cmd)
{ {
memset (cmd, 0, sizeof(*cmd)); memset (cmd, 0, sizeof(*cmd));
if (actor->player->Bot.enemy && actor->player->Bot.enemy->health <= 0) if (actor->player->Bot->enemy && actor->player->Bot->enemy->health <= 0)
actor->player->Bot.enemy = NULL; actor->player->Bot->enemy = NULL;
if (actor->health > 0) //Still alive if (actor->health > 0) //Still alive
{ {
if (teamplay || !deathmatch) if (teamplay || !deathmatch)
actor->player->Bot.mate = Choose_Mate (actor); actor->player->Bot->mate = Choose_Mate (actor);
angle_t oldyaw = actor->angle; angle_t oldyaw = actor->angle;
int oldpitch = actor->pitch; int oldpitch = actor->pitch;
@ -52,17 +52,17 @@ void FCajunMaster::Think (AActor *actor, ticcmd_t *cmd)
actor->pitch = oldpitch - (cmd->ucmd.pitch << 16) * ticdup; actor->pitch = oldpitch - (cmd->ucmd.pitch << 16) * ticdup;
} }
if (actor->player->Bot.t_active) actor->player->Bot.t_active--; if (actor->player->Bot->t_active) actor->player->Bot->t_active--;
if (actor->player->Bot.t_strafe) actor->player->Bot.t_strafe--; if (actor->player->Bot->t_strafe) actor->player->Bot->t_strafe--;
if (actor->player->Bot.t_react) actor->player->Bot.t_react--; if (actor->player->Bot->t_react) actor->player->Bot->t_react--;
if (actor->player->Bot.t_fight) actor->player->Bot.t_fight--; if (actor->player->Bot->t_fight) actor->player->Bot->t_fight--;
if (actor->player->Bot.t_rocket) actor->player->Bot.t_rocket--; if (actor->player->Bot->t_rocket) actor->player->Bot->t_rocket--;
if (actor->player->Bot.t_roam) actor->player->Bot.t_roam--; if (actor->player->Bot->t_roam) actor->player->Bot->t_roam--;
//Respawn ticker //Respawn ticker
if (actor->player->Bot.t_respawn) if (actor->player->Bot->t_respawn)
{ {
actor->player->Bot.t_respawn--; actor->player->Bot->t_respawn--;
} }
else if (actor->health <= 0) else if (actor->health <= 0)
{ // Time to respawn { // Time to respawn
@ -80,17 +80,17 @@ void FCajunMaster::ThinkForMove (AActor *actor, ticcmd_t *cmd)
int r; int r;
b = actor->player; b = actor->player;
if (!b->Bot.isbot) if (b->Bot == NULL)
return; return;
stuck = false; stuck = false;
dist = b->Bot.dest ? P_AproxDistance(actor->x-b->Bot.dest->x, actor->y-b->Bot.dest->y) : 0; dist = b->Bot->dest ? P_AproxDistance(actor->x-b->Bot->dest->x, actor->y-b->Bot->dest->y) : 0;
if (b->Bot.missile && if (b->Bot->missile &&
((!b->Bot.missile->velx || !b->Bot.missile->vely) || !Check_LOS(actor, b->Bot.missile, SHOOTFOV*3/2))) ((!b->Bot->missile->velx || !b->Bot->missile->vely) || !Check_LOS(actor, b->Bot->missile, SHOOTFOV*3/2)))
{ {
b->Bot.sleft = !b->Bot.sleft; b->Bot->sleft = !b->Bot->sleft;
b->Bot.missile = NULL; //Probably ended its travel. b->Bot->missile = NULL; //Probably ended its travel.
} }
if (actor->pitch > 0) if (actor->pitch > 0)
@ -99,35 +99,35 @@ void FCajunMaster::ThinkForMove (AActor *actor, ticcmd_t *cmd)
actor->pitch += 80; actor->pitch += 80;
//HOW TO MOVE: //HOW TO MOVE:
if (b->Bot.missile && (P_AproxDistance(actor->x-b->Bot.missile->x, actor->y-b->Bot.missile->y)<AVOID_DIST)) //try avoid missile got from P_Mobj.c thinking part. if (b->Bot->missile && (P_AproxDistance(actor->x-b->Bot->missile->x, actor->y-b->Bot->missile->y)<AVOID_DIST)) //try avoid missile got from P_Mobj.c thinking part.
{ {
Pitch (actor, b->Bot.missile); Pitch (actor, b->Bot->missile);
actor->player->Bot.angle = R_PointToAngle2(actor->x, actor->y, b->Bot.missile->x, b->Bot.missile->y); actor->player->Bot->angle = R_PointToAngle2(actor->x, actor->y, b->Bot->missile->x, b->Bot->missile->y);
cmd->ucmd.sidemove = b->Bot.sleft ? -SIDERUN : SIDERUN; cmd->ucmd.sidemove = b->Bot->sleft ? -SIDERUN : SIDERUN;
cmd->ucmd.forwardmove = -FORWARDRUN; //Back IS best. cmd->ucmd.forwardmove = -FORWARDRUN; //Back IS best.
if ((P_AproxDistance(actor->x-b->Bot.oldx, actor->y-b->Bot.oldy)<50000) if ((P_AproxDistance(actor->x-b->Bot->oldx, actor->y-b->Bot->oldy)<50000)
&& b->Bot.t_strafe<=0) && b->Bot->t_strafe<=0)
{ {
b->Bot.t_strafe = 5; b->Bot->t_strafe = 5;
b->Bot.sleft = !b->Bot.sleft; b->Bot->sleft = !b->Bot->sleft;
} }
//If able to see enemy while avoiding missile, still fire at enemy. //If able to see enemy while avoiding missile, still fire at enemy.
if (b->Bot.enemy && Check_LOS (actor, b->Bot.enemy, SHOOTFOV)) if (b->Bot->enemy && Check_LOS (actor, b->Bot->enemy, SHOOTFOV))
Dofire (actor, cmd); //Order bot to fire current weapon Dofire (actor, cmd); //Order bot to fire current weapon
} }
else if (b->Bot.enemy && P_CheckSight (actor, b->Bot.enemy, 0)) //Fight! else if (b->Bot->enemy && P_CheckSight (actor, b->Bot->enemy, 0)) //Fight!
{ {
Pitch (actor, b->Bot.enemy); Pitch (actor, b->Bot->enemy);
//Check if it's more important to get an item than fight. //Check if it's more important to get an item than fight.
if (b->Bot.dest && (b->Bot.dest->flags&MF_SPECIAL)) //Must be an item, that is close enough. if (b->Bot->dest && (b->Bot->dest->flags&MF_SPECIAL)) //Must be an item, that is close enough.
{ {
#define is(x) b->Bot.dest->IsKindOf (PClass::FindClass (#x)) #define is(x) b->Bot->dest->IsKindOf (PClass::FindClass (#x))
if ( if (
( (
(actor->health < b->Bot.skill.isp && (actor->health < b->Bot->skill.isp &&
(is (Medikit) || (is (Medikit) ||
is (Stimpack) || is (Stimpack) ||
is (Soulsphere) || is (Soulsphere) ||
@ -143,75 +143,75 @@ void FCajunMaster::ThinkForMove (AActor *actor, ticcmd_t *cmd)
(b->ReadyWeapon == NULL || b->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON) (b->ReadyWeapon == NULL || b->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON)
) )
&& (dist < GETINCOMBAT || (b->ReadyWeapon == NULL || b->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON)) && (dist < GETINCOMBAT || (b->ReadyWeapon == NULL || b->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON))
&& Reachable (actor, b->Bot.dest)) && Reachable (actor, b->Bot->dest))
#undef is #undef is
{ {
goto roam; //Pick it up, no matter the situation. All bonuses are nice close up. goto roam; //Pick it up, no matter the situation. All bonuses are nice close up.
} }
} }
b->Bot.dest = NULL; //To let bot turn right b->Bot->dest = NULL; //To let bot turn right
if (b->ReadyWeapon != NULL && !(b->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON)) if (b->ReadyWeapon != NULL && !(b->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON))
actor->flags &= ~MF_DROPOFF; //Don't jump off any ledges when fighting. actor->flags &= ~MF_DROPOFF; //Don't jump off any ledges when fighting.
if (!(b->Bot.enemy->flags3 & MF3_ISMONSTER)) if (!(b->Bot->enemy->flags3 & MF3_ISMONSTER))
b->Bot.t_fight = AFTERTICS; b->Bot->t_fight = AFTERTICS;
if (b->Bot.t_strafe <= 0 && if (b->Bot->t_strafe <= 0 &&
(P_AproxDistance(actor->x-b->Bot.oldx, actor->y-b->Bot.oldy)<50000 (P_AproxDistance(actor->x-b->Bot->oldx, actor->y-b->Bot->oldy)<50000
|| ((pr_botmove()%30)==10)) || ((pr_botmove()%30)==10))
) )
{ {
stuck = true; stuck = true;
b->Bot.t_strafe = 5; b->Bot->t_strafe = 5;
b->Bot.sleft = !b->Bot.sleft; b->Bot->sleft = !b->Bot->sleft;
} }
b->Bot.angle = R_PointToAngle2(actor->x, actor->y, b->Bot.enemy->x, b->Bot.enemy->y); b->Bot->angle = R_PointToAngle2(actor->x, actor->y, b->Bot->enemy->x, b->Bot->enemy->y);
if (b->ReadyWeapon == NULL || if (b->ReadyWeapon == NULL ||
P_AproxDistance(actor->x-b->Bot.enemy->x, actor->y-b->Bot.enemy->y) > P_AproxDistance(actor->x-b->Bot->enemy->x, actor->y-b->Bot->enemy->y) >
b->ReadyWeapon->MoveCombatDist) b->ReadyWeapon->MoveCombatDist)
{ {
// If a monster, use lower speed (just for cooler apperance while strafing down doomed monster) // If a monster, use lower speed (just for cooler apperance while strafing down doomed monster)
cmd->ucmd.forwardmove = (b->Bot.enemy->flags3 & MF3_ISMONSTER) ? FORWARDWALK : FORWARDRUN; cmd->ucmd.forwardmove = (b->Bot->enemy->flags3 & MF3_ISMONSTER) ? FORWARDWALK : FORWARDRUN;
} }
else if (!stuck) //Too close, so move away. else if (!stuck) //Too close, so move away.
{ {
// If a monster, use lower speed (just for cooler apperance while strafing down doomed monster) // If a monster, use lower speed (just for cooler apperance while strafing down doomed monster)
cmd->ucmd.forwardmove = (b->Bot.enemy->flags3 & MF3_ISMONSTER) ? -FORWARDWALK : -FORWARDRUN; cmd->ucmd.forwardmove = (b->Bot->enemy->flags3 & MF3_ISMONSTER) ? -FORWARDWALK : -FORWARDRUN;
} }
//Strafing. //Strafing.
if (b->Bot.enemy->flags3 & MF3_ISMONSTER) //It's just a monster so take it down cool. if (b->Bot->enemy->flags3 & MF3_ISMONSTER) //It's just a monster so take it down cool.
{ {
cmd->ucmd.sidemove = b->Bot.sleft ? -SIDEWALK : SIDEWALK; cmd->ucmd.sidemove = b->Bot->sleft ? -SIDEWALK : SIDEWALK;
} }
else else
{ {
cmd->ucmd.sidemove = b->Bot.sleft ? -SIDERUN : SIDERUN; cmd->ucmd.sidemove = b->Bot->sleft ? -SIDERUN : SIDERUN;
} }
Dofire (actor, cmd); //Order bot to fire current weapon Dofire (actor, cmd); //Order bot to fire current weapon
} }
else if (b->Bot.mate && !b->Bot.enemy && (!b->Bot.dest || b->Bot.dest==b->Bot.mate)) //Follow mate move. else if (b->Bot->mate && !b->Bot->enemy && (!b->Bot->dest || b->Bot->dest==b->Bot->mate)) //Follow mate move.
{ {
fixed_t matedist; fixed_t matedist;
Pitch (actor, b->Bot.mate); Pitch (actor, b->Bot->mate);
if (!Reachable (actor, b->Bot.mate)) if (!Reachable (actor, b->Bot->mate))
{ {
if (b->Bot.mate == b->Bot.dest && pr_botmove.Random() < 32) if (b->Bot->mate == b->Bot->dest && pr_botmove.Random() < 32)
{ // [RH] If the mate is the dest, pick a new dest sometimes { // [RH] If the mate is the dest, pick a new dest sometimes
b->Bot.dest = NULL; b->Bot->dest = NULL;
} }
goto roam; goto roam;
} }
actor->player->Bot.angle = R_PointToAngle2(actor->x, actor->y, b->Bot.mate->x, b->Bot.mate->y); actor->player->Bot->angle = R_PointToAngle2(actor->x, actor->y, b->Bot->mate->x, b->Bot->mate->y);
matedist = P_AproxDistance(actor->x - b->Bot.mate->x, actor->y - b->Bot.mate->y); matedist = P_AproxDistance(actor->x - b->Bot->mate->x, actor->y - b->Bot->mate->y);
if (matedist > (FRIEND_DIST*2)) if (matedist > (FRIEND_DIST*2))
cmd->ucmd.forwardmove = FORWARDRUN; cmd->ucmd.forwardmove = FORWARDRUN;
else if (matedist > FRIEND_DIST) else if (matedist > FRIEND_DIST)
@ -221,33 +221,33 @@ void FCajunMaster::ThinkForMove (AActor *actor, ticcmd_t *cmd)
} }
else //Roam after something. else //Roam after something.
{ {
b->Bot.first_shot = true; b->Bot->first_shot = true;
///// /////
roam: roam:
///// /////
if (b->Bot.enemy && Check_LOS (actor, b->Bot.enemy, SHOOTFOV*3/2)) //If able to see enemy while avoiding missile , still fire at it. if (b->Bot->enemy && Check_LOS (actor, b->Bot->enemy, SHOOTFOV*3/2)) //If able to see enemy while avoiding missile , still fire at it.
Dofire (actor, cmd); //Order bot to fire current weapon Dofire (actor, cmd); //Order bot to fire current weapon
if (b->Bot.dest && !(b->Bot.dest->flags&MF_SPECIAL) && b->Bot.dest->health < 0) if (b->Bot->dest && !(b->Bot->dest->flags&MF_SPECIAL) && b->Bot->dest->health < 0)
{ //Roaming after something dead. { //Roaming after something dead.
b->Bot.dest = NULL; b->Bot->dest = NULL;
} }
if (b->Bot.dest == NULL) if (b->Bot->dest == NULL)
{ {
if (b->Bot.t_fight && b->Bot.enemy) //Enemy/bot has jumped around corner. So what to do? if (b->Bot->t_fight && b->Bot->enemy) //Enemy/bot has jumped around corner. So what to do?
{ {
if (b->Bot.enemy->player) if (b->Bot->enemy->player)
{ {
if (((b->Bot.enemy->player->ReadyWeapon != NULL && b->Bot.enemy->player->ReadyWeapon->WeaponFlags & WIF_BOT_EXPLOSIVE) || if (((b->Bot->enemy->player->ReadyWeapon != NULL && b->Bot->enemy->player->ReadyWeapon->WeaponFlags & WIF_BOT_EXPLOSIVE) ||
(pr_botmove()%100)>b->Bot.skill.isp) && b->ReadyWeapon != NULL && !(b->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON)) (pr_botmove()%100)>b->Bot->skill.isp) && b->ReadyWeapon != NULL && !(b->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON))
b->Bot.dest = b->Bot.enemy;//Dont let enemy kill the bot by supressive fire. So charge enemy. b->Bot->dest = b->Bot->enemy;//Dont let enemy kill the bot by supressive fire. So charge enemy.
else //hide while b->t_fight, but keep view at enemy. else //hide while b->t_fight, but keep view at enemy.
b->Bot.angle = R_PointToAngle2(actor->x, actor->y, b->Bot.enemy->x, b->Bot.enemy->y); b->Bot->angle = R_PointToAngle2(actor->x, actor->y, b->Bot->enemy->x, b->Bot->enemy->y);
} //Just a monster, so kill it. } //Just a monster, so kill it.
else else
b->Bot.dest = b->Bot.enemy; b->Bot->dest = b->Bot->enemy;
//VerifFavoritWeapon(actor->player); //Dont know why here.., but it must be here, i know the reason, but not why at this spot, uh. //VerifFavoritWeapon(actor->player); //Dont know why here.., but it must be here, i know the reason, but not why at this spot, uh.
} }
@ -272,42 +272,42 @@ void FCajunMaster::ThinkForMove (AActor *actor, ticcmd_t *cmd)
item = it.Next(); item = it.Next();
} }
firstthing = item; firstthing = item;
b->Bot.dest = item; b->Bot->dest = item;
} }
} }
else if (b->Bot.mate && (r < 179 || P_CheckSight(actor, b->Bot.mate))) else if (b->Bot->mate && (r < 179 || P_CheckSight(actor, b->Bot->mate)))
{ {
b->Bot.dest = b->Bot.mate; b->Bot->dest = b->Bot->mate;
} }
else if ((playeringame[(r&(MAXPLAYERS-1))]) && players[(r&(MAXPLAYERS-1))].mo->health > 0) else if ((playeringame[(r&(MAXPLAYERS-1))]) && players[(r&(MAXPLAYERS-1))].mo->health > 0)
{ {
b->Bot.dest = players[(r&(MAXPLAYERS-1))].mo; b->Bot->dest = players[(r&(MAXPLAYERS-1))].mo;
} }
} }
if (b->Bot.dest) if (b->Bot->dest)
{ {
b->Bot.t_roam = MAXROAM; b->Bot->t_roam = MAXROAM;
} }
} }
if (b->Bot.dest) if (b->Bot->dest)
{ //Bot has a target so roam after it. { //Bot has a target so roam after it.
Roam (actor, cmd); Roam (actor, cmd);
} }
} //End of movement main part. } //End of movement main part.
if (!b->Bot.t_roam && b->Bot.dest) if (!b->Bot->t_roam && b->Bot->dest)
{ {
b->Bot.prev = b->Bot.dest; b->Bot->prev = b->Bot->dest;
b->Bot.dest = NULL; b->Bot->dest = NULL;
} }
if (b->Bot.t_fight<(AFTERTICS/2)) if (b->Bot->t_fight<(AFTERTICS/2))
actor->flags |= MF_DROPOFF; actor->flags |= MF_DROPOFF;
b->Bot.oldx = actor->x; b->Bot->oldx = actor->x;
b->Bot.oldy = actor->y; b->Bot->oldy = actor->y;
} }
//BOT_WhatToGet //BOT_WhatToGet
@ -324,7 +324,7 @@ void FCajunMaster::WhatToGet (AActor *actor, AActor *item)
#define typeis(x) item->IsKindOf (PClass::FindClass (#x)) #define typeis(x) item->IsKindOf (PClass::FindClass (#x))
if ((item->renderflags & RF_INVISIBLE) //Under respawn and away. if ((item->renderflags & RF_INVISIBLE) //Under respawn and away.
|| item == b->Bot.prev) || item == b->Bot->prev)
{ {
return; return;
} }
@ -366,21 +366,21 @@ void FCajunMaster::WhatToGet (AActor *actor, AActor *item)
else if (item->IsKindOf (RUNTIME_CLASS(AHealth)) && actor->health >= deh.MaxHealth /*MAXHEALTH*/) else if (item->IsKindOf (RUNTIME_CLASS(AHealth)) && actor->health >= deh.MaxHealth /*MAXHEALTH*/)
return; return;
if ((b->Bot.dest == NULL || if ((b->Bot->dest == NULL ||
!(b->Bot.dest->flags & MF_SPECIAL)/* || !(b->Bot->dest->flags & MF_SPECIAL)/* ||
!Reachable (actor, b->dest)*/)/* && !Reachable (actor, b->dest)*/)/* &&
Reachable (actor, item)*/) // Calling Reachable slows this down tremendously Reachable (actor, item)*/) // Calling Reachable slows this down tremendously
{ {
b->Bot.prev = b->Bot.dest; b->Bot->prev = b->Bot->dest;
b->Bot.dest = item; b->Bot->dest = item;
b->Bot.t_roam = MAXROAM; b->Bot->t_roam = MAXROAM;
} }
} }
void FCajunMaster::Set_enemy (AActor *actor) void FCajunMaster::Set_enemy (AActor *actor)
{ {
AActor *oldenemy; AActor *oldenemy;
AActor **enemy = &actor->player->Bot.enemy; AActor **enemy = &actor->player->Bot->enemy;
if (*enemy if (*enemy
&& (*enemy)->health > 0 && (*enemy)->health > 0
@ -397,7 +397,7 @@ void FCajunMaster::Set_enemy (AActor *actor)
// and we already have an existing enemy. // and we already have an existing enemy.
if (deathmatch || !*enemy) if (deathmatch || !*enemy)
{ {
actor->player->Bot.allround = !!*enemy; actor->player->Bot->allround = !!*enemy;
*enemy = NULL; *enemy = NULL;
*enemy = Find_enemy(actor); *enemy = Find_enemy(actor);
if (!*enemy) if (!*enemy)

View file

@ -978,19 +978,19 @@ void D_DoomLoop ()
int i; int i;
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
{ {
if (playeringame[i] && players[i].Bot.isbot && players[i].mo) if (playeringame[i] && players[i].Bot != NULL && players[i].mo)
{ {
players[i].Bot.savedyaw = players[i].mo->angle; players[i].Bot->savedyaw = players[i].mo->angle;
players[i].Bot.savedpitch = players[i].mo->pitch; players[i].Bot->savedpitch = players[i].mo->pitch;
} }
} }
bglobal.Main (maketic%BACKUPTICS); bglobal.Main (maketic%BACKUPTICS);
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
{ {
if (playeringame[i] && players[i].Bot.isbot && players[i].mo) if (playeringame[i] && players[i].Bot != NULL && players[i].mo)
{ {
players[i].mo->angle = players[i].Bot.savedyaw; players[i].mo->angle = players[i].Bot->savedyaw;
players[i].mo->pitch = players[i].Bot.savedpitch; players[i].mo->pitch = players[i].Bot->savedpitch;
} }
} }
if (advancedemo) if (advancedemo)

View file

@ -697,7 +697,7 @@ void PlayerIsGone (int netnode, int netconsole)
// Pick a new network arbitrator // Pick a new network arbitrator
for (int i = 0; i < MAXPLAYERS; i++) for (int i = 0; i < MAXPLAYERS; i++)
{ {
if (i != netconsole && playeringame[i] && !players[i].Bot.isbot) if (i != netconsole && playeringame[i] && players[i].Bot == NULL)
{ {
Net_Arbitrator = i; Net_Arbitrator = i;
players[i].settings_controller = true; players[i].settings_controller = true;
@ -902,7 +902,7 @@ void GetPackets (void)
for (i = 0; i < numplayers; ++i) for (i = 0; i < numplayers; ++i)
{ {
int node = !players[playerbytes[i]].Bot.isbot ? int node = (players[playerbytes[i]].Bot == NULL) ?
nodeforplayer[playerbytes[i]] : netnode; nodeforplayer[playerbytes[i]] : netnode;
SkipTicCmd (&start, nettics[node] - realstart); SkipTicCmd (&start, nettics[node] - realstart);
@ -918,7 +918,7 @@ void GetPackets (void)
// packet. // packet.
for (i = 0; i < numplayers; ++i) for (i = 0; i < numplayers; ++i)
{ {
if (!players[playerbytes[i]].Bot.isbot) if (players[playerbytes[i]].Bot == NULL)
{ {
nettics[nodeforplayer[playerbytes[i]]] = realend; nettics[nodeforplayer[playerbytes[i]]] = realend;
} }
@ -935,10 +935,10 @@ void AdjustBots (int gameticdiv)
// be in even when gametic lags behind maketic. // be in even when gametic lags behind maketic.
for (int i = 0; i < MAXPLAYERS; i++) for (int i = 0; i < MAXPLAYERS; i++)
{ {
if (playeringame[i] && players[i].Bot.isbot && players[i].mo) if (playeringame[i] && players[i].Bot != NULL && players[i].mo)
{ {
players[i].Bot.savedyaw = players[i].mo->angle; players[i].Bot->savedyaw = players[i].mo->angle;
players[i].Bot.savedpitch = players[i].mo->pitch; players[i].Bot->savedpitch = players[i].mo->pitch;
for (int j = gameticdiv; j < maketic/ticdup; j++) for (int j = gameticdiv; j < maketic/ticdup; j++)
{ {
players[i].mo->angle += (netcmds[i][j%BACKUPTICS].ucmd.yaw << 16) * ticdup; players[i].mo->angle += (netcmds[i][j%BACKUPTICS].ucmd.yaw << 16) * ticdup;
@ -952,10 +952,10 @@ void UnadjustBots ()
{ {
for (int i = 0; i < MAXPLAYERS; i++) for (int i = 0; i < MAXPLAYERS; i++)
{ {
if (playeringame[i] && players[i].Bot.isbot && players[i].mo) if (playeringame[i] && players[i].Bot != NULL && players[i].mo)
{ {
players[i].mo->angle = players[i].Bot.savedyaw; players[i].mo->angle = players[i].Bot->savedyaw;
players[i].mo->pitch = players[i].Bot.savedpitch; players[i].mo->pitch = players[i].Bot->savedpitch;
} }
} }
} }
@ -1127,7 +1127,7 @@ void NetUpdate (void)
{ {
if (playeringame[j]) if (playeringame[j])
{ {
if (players[j].Bot.isbot || NetMode == NET_PacketServer) if (players[j].Bot != NULL || NetMode == NET_PacketServer)
{ {
count++; count++;
} }
@ -1269,7 +1269,7 @@ void NetUpdate (void)
{ {
if (playeringame[j] && j != playerfornode[i] && j != consoleplayer) if (playeringame[j] && j != playerfornode[i] && j != consoleplayer)
{ {
if (players[j].Bot.isbot || NetMode == NET_PacketServer) if (players[j].Bot != NULL || NetMode == NET_PacketServer)
{ {
playerbytes[l++] = j; playerbytes[l++] = j;
netbuffer[k++] = j; netbuffer[k++] = j;
@ -1308,7 +1308,7 @@ void NetUpdate (void)
} }
else if (i != 0) else if (i != 0)
{ {
if (players[playerbytes[l]].Bot.isbot) if (players[playerbytes[l]].Bot != NULL)
{ {
WriteWord (0, &cmddata); // fake consistancy word WriteWord (0, &cmddata); // fake consistancy word
} }
@ -2250,10 +2250,7 @@ void Net_DoCommand (int type, BYTE **stream, int player)
break; break;
case DEM_ADDBOT: case DEM_ADDBOT:
{ bglobal.DoAddBot (stream);
BYTE num = ReadByte (stream);
bglobal.DoAddBot (num, s = ReadString (stream));
}
break; break;
case DEM_KILLBOTS: case DEM_KILLBOTS:
@ -2709,10 +2706,13 @@ void Net_SkipCommand (int type, BYTE **stream)
switch (type) switch (type)
{ {
case DEM_SAY: case DEM_SAY:
case DEM_ADDBOT:
skip = strlen ((char *)(*stream + 1)) + 2; skip = strlen ((char *)(*stream + 1)) + 2;
break; break;
case DEM_ADDBOT:
skip = strlen ((char *)(*stream + 1)) + 6;
break;
case DEM_GIVECHEAT: case DEM_GIVECHEAT:
case DEM_TAKECHEAT: case DEM_TAKECHEAT:
skip = strlen ((char *)(*stream)) + 3; skip = strlen ((char *)(*stream)) + 3;
@ -2874,7 +2874,7 @@ static void Network_Controller (int playernum, bool add)
return; return;
} }
if (players[playernum].Bot.isbot) if (players[playernum].Bot != NULL)
{ {
Printf ("Bots cannot be added to the controller list.\n"); Printf ("Bots cannot be added to the controller list.\n");
return; return;

View file

@ -442,7 +442,7 @@ public:
FName LastDamageType; // [RH] For damage-specific pain and death sounds FName LastDamageType; // [RH] For damage-specific pain and death sounds
//Added by MC: //Added by MC:
FBot Bot; TObjPtr<DBot> Bot;
bool settings_controller; // Player can control game settings. bool settings_controller; // Player can control game settings.

View file

@ -112,7 +112,7 @@ enum EDemoCommand
DEM_DROPPLAYER, // 13 Not implemented, takes a byte DEM_DROPPLAYER, // 13 Not implemented, takes a byte
DEM_CHANGEMAP, // 14 Name of map to change to DEM_CHANGEMAP, // 14 Name of map to change to
DEM_SUICIDE, // 15 Player wants to die DEM_SUICIDE, // 15 Player wants to die
DEM_ADDBOT, // 16 Byte: player#, String: userinfo for bot DEM_ADDBOT, // 16 Byte: player#, String: userinfo for bot, 4 Bytes: skill (aiming, perfection, reaction, isp)
DEM_KILLBOTS, // 17 Remove all bots from the world DEM_KILLBOTS, // 17 Remove all bots from the world
DEM_INVUSEALL, // 18 Use every item (panic!) DEM_INVUSEALL, // 18 Use every item (panic!)
DEM_INVUSE, // 19 4 bytes: ID of item to use DEM_INVUSE, // 19 4 bytes: ID of item to use

View file

@ -875,7 +875,7 @@ static void ChangeSpy (int changespy)
pnum &= MAXPLAYERS-1; pnum &= MAXPLAYERS-1;
if (playeringame[pnum] && if (playeringame[pnum] &&
(!checkTeam || players[pnum].mo->IsTeammate (players[consoleplayer].mo) || (!checkTeam || players[pnum].mo->IsTeammate (players[consoleplayer].mo) ||
(bot_allowspy && players[pnum].Bot.isbot))) (bot_allowspy && players[pnum].Bot != NULL)))
{ {
break; break;
} }
@ -1156,7 +1156,7 @@ void G_Ticker ()
Printf ("%s is turbo!\n", players[i].userinfo.GetName()); Printf ("%s is turbo!\n", players[i].userinfo.GetName());
} }
if (netgame && !players[i].Bot.isbot && !demoplayback && (gametic%ticdup) == 0) if (netgame && players[i].Bot == NULL && !demoplayback && (gametic%ticdup) == 0)
{ {
//players[i].inconsistant = 0; //players[i].inconsistant = 0;
if (gametic > BACKUPTICS*ticdup && consistancy[i][buf] != cmd->consistancy) if (gametic > BACKUPTICS*ticdup && consistancy[i][buf] != cmd->consistancy)
@ -1338,10 +1338,10 @@ void G_PlayerReborn (int player)
int chasecam; int chasecam;
BYTE currclass; BYTE currclass;
userinfo_t userinfo; // [RH] Save userinfo userinfo_t userinfo; // [RH] Save userinfo
botskill_t b_skill; //Added by MC:
APlayerPawn *actor; APlayerPawn *actor;
const PClass *cls; const PClass *cls;
FString log; FString log;
DBot *OldBot; //Added by MC:
p = &players[player]; p = &players[player];
@ -1351,12 +1351,12 @@ void G_PlayerReborn (int player)
itemcount = p->itemcount; itemcount = p->itemcount;
secretcount = p->secretcount; secretcount = p->secretcount;
currclass = p->CurrentPlayerClass; currclass = p->CurrentPlayerClass;
b_skill = p->Bot.skill; //Added by MC:
userinfo.TransferFrom(p->userinfo); userinfo.TransferFrom(p->userinfo);
actor = p->mo; actor = p->mo;
cls = p->cls; cls = p->cls;
log = p->LogText; log = p->LogText;
chasecam = p->cheats & CF_CHASECAM; chasecam = p->cheats & CF_CHASECAM;
OldBot = p->Bot; //Added by MC:
// Reset player structure to its defaults // Reset player structure to its defaults
p->~player_t(); p->~player_t();
@ -1374,7 +1374,12 @@ void G_PlayerReborn (int player)
p->LogText = log; p->LogText = log;
p->cheats |= chasecam; p->cheats |= chasecam;
p->Bot.skill = b_skill; //Added by MC: //Added by MC: Init bot structure.
if (OldBot != NULL)
{
p->Bot = new DBot;
p->Bot->skill = OldBot->skill;
}
p->oldbuttons = ~0, p->attackdown = true; p->usedown = true; // don't do anything immediately p->oldbuttons = ~0, p->attackdown = true; p->usedown = true; // don't do anything immediately
p->original_oldbuttons = ~0; p->original_oldbuttons = ~0;
@ -1385,12 +1390,6 @@ void G_PlayerReborn (int player)
actor->GiveDefaultInventory (); actor->GiveDefaultInventory ();
p->ReadyWeapon = p->PendingWeapon; p->ReadyWeapon = p->PendingWeapon;
} }
//Added by MC: Init bot structure.
if (bglobal.botingame[player])
bglobal.CleanBotstuff (p);
else
p->Bot.isbot = false;
} }
// //

View file

@ -1110,6 +1110,11 @@ void G_StartTravel ()
P_DelSector_List (); P_DelSector_List ();
} }
} }
if (players[i].Bot != NULL)
{
players[i].Bot->ChangeStatNum (STAT_TRAVELLING);
}
} }
} }
} }
@ -1184,6 +1189,11 @@ void G_FinishTravel ()
pawn->SetState(pawn->SpawnState); pawn->SetState(pawn->SpawnState);
pawn->player->SendPitchLimits(); pawn->player->SendPitchLimits();
if (pawn->player->Bot != NULL)
{
pawn->player->Bot->ChangeStatNum (STAT_DEFAULT);
}
for (inv = pawn->Inventory; inv != NULL; inv = inv->Inventory) for (inv = pawn->Inventory; inv != NULL; inv = inv->Inventory)
{ {
inv->ChangeStatNum (STAT_INVENTORY); inv->ChangeStatNum (STAT_INVENTORY);

View file

@ -1024,8 +1024,8 @@ void AInventory::Touch (AActor *toucher)
//Added by MC: Check if item taken was the roam destination of any bot //Added by MC: Check if item taken was the roam destination of any bot
for (int i = 0; i < MAXPLAYERS; i++) for (int i = 0; i < MAXPLAYERS; i++)
{ {
if (playeringame[i] && this == players[i].Bot.dest) if (playeringame[i] && players[i].Bot != NULL && this == players[i].Bot->dest)
players[i].Bot.dest = NULL; players[i].Bot->dest = NULL;
} }
} }

View file

@ -4228,7 +4228,7 @@ int DLevelScript::DoClassifyActor(int tid)
{ {
classify |= ACTOR_VOODOODOLL; classify |= ACTOR_VOODOODOLL;
} }
if (actor->player->Bot.isbot) if (actor->player->Bot != NULL)
{ {
classify |= ACTOR_BOT; classify |= ACTOR_BOT;
} }
@ -8632,7 +8632,7 @@ scriptwait:
} }
else else
{ {
STACK(1) = players[STACK(1)].Bot.isbot; STACK(1) = (players[STACK(1)].Bot != NULL);
} }
break; break;

View file

@ -460,7 +460,7 @@ bool EV_DoDoor (DDoor::EVlDoor type, line_t *line, AActor *thing,
// run into them (otherwise opening them would be // run into them (otherwise opening them would be
// a real pain). // a real pain).
{ {
if (!thing->player || thing->player->Bot.isbot) if (!thing->player || thing->player->Bot != NULL)
return false; // JDC: bad guys never close doors return false; // JDC: bad guys never close doors
//Added by MC: Neither do bots. //Added by MC: Neither do bots.

View file

@ -100,10 +100,10 @@ void P_TouchSpecialThing (AActor *special, AActor *toucher)
return; return;
//Added by MC: Finished with this destination. //Added by MC: Finished with this destination.
if (toucher->player != NULL && toucher->player->Bot.isbot && special == toucher->player->Bot.dest) if (toucher->player != NULL && toucher->player->Bot != NULL && special == toucher->player->Bot->dest)
{ {
toucher->player->Bot.prev = toucher->player->Bot.dest; toucher->player->Bot->prev = toucher->player->Bot->dest;
toucher->player->Bot.dest = NULL; toucher->player->Bot->dest = NULL;
} }
special->Touch (toucher); special->Touch (toucher);
@ -608,17 +608,17 @@ void AActor::Die (AActor *source, AActor *inflictor, int dmgflags)
//Added by MC: Respawn bots //Added by MC: Respawn bots
if (bglobal.botnum && consoleplayer == Net_Arbitrator && !demoplayback) if (bglobal.botnum && consoleplayer == Net_Arbitrator && !demoplayback)
{ {
if (player->Bot.isbot) if (player->Bot != NULL)
player->Bot.t_respawn = (pr_botrespawn()%15)+((bglobal.botnum-1)*2)+TICRATE+1; player->Bot->t_respawn = (pr_botrespawn()%15)+((bglobal.botnum-1)*2)+TICRATE+1;
//Added by MC: Discard enemies. //Added by MC: Discard enemies.
for (int i = 0; i < MAXPLAYERS; i++) for (int i = 0; i < MAXPLAYERS; i++)
{ {
if (players[i].Bot.isbot && this == players[i].Bot.enemy) if (players[i].Bot != NULL && this == players[i].Bot->enemy)
{ {
if (players[i].Bot.dest == players[i].Bot.enemy) if (players[i].Bot->dest == players[i].Bot->enemy)
players[i].Bot.dest = NULL; players[i].Bot->dest = NULL;
players[i].Bot.enemy = NULL; players[i].Bot->enemy = NULL;
} }
} }
@ -1194,9 +1194,9 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage,
if (player) if (player)
{ {
//Added by MC: Lets bots look allround for enemies if they survive an ambush. //Added by MC: Lets bots look allround for enemies if they survive an ambush.
if (player->Bot.isbot) if (player->Bot != NULL)
{ {
player->Bot.allround = true; player->Bot->allround = true;
} }
// end of game hell hack // end of game hell hack

View file

@ -1919,13 +1919,13 @@ bool P_TryMove(AActor *thing, fixed_t x, fixed_t y,
} }
//Added by MC: To prevent bot from getting into dangerous sectors. //Added by MC: To prevent bot from getting into dangerous sectors.
if (thing->player && thing->player->Bot.isbot && thing->flags & MF_SHOOTABLE) if (thing->player && thing->player->Bot != NULL && thing->flags & MF_SHOOTABLE)
{ {
if (tm.sector != thing->Sector if (tm.sector != thing->Sector
&& bglobal.IsDangerous(tm.sector)) && bglobal.IsDangerous(tm.sector))
{ {
thing->player->Bot.prev = thing->player->Bot.dest; thing->player->Bot->prev = thing->player->Bot->dest;
thing->player->Bot.dest = NULL; thing->player->Bot->dest = NULL;
thing->velx = 0; thing->velx = 0;
thing->vely = 0; thing->vely = 0;
thing->z = oldz; thing->z = oldz;

View file

@ -3116,7 +3116,7 @@ void AActor::Tick ()
special2++; special2++;
} }
//Added by MC: Freeze mode. //Added by MC: Freeze mode.
if (bglobal.freeze && !(player && !player->Bot.isbot)) if (bglobal.freeze && !(player && player->Bot == NULL))
{ {
return; return;
} }
@ -3237,18 +3237,18 @@ void AActor::Tick ()
bglobal.m_Thinking = true; bglobal.m_Thinking = true;
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
{ {
if (!playeringame[i] || !players[i].Bot.isbot) if (!playeringame[i] || players[i].Bot == NULL)
continue; continue;
if (flags3 & MF3_ISMONSTER) if (flags3 & MF3_ISMONSTER)
{ {
if (health > 0 if (health > 0
&& !players[i].Bot.enemy && !players[i].Bot->enemy
&& player ? !IsTeammate (players[i].mo) : true && player ? !IsTeammate (players[i].mo) : true
&& P_AproxDistance (players[i].mo->x-x, players[i].mo->y-y) < MAX_MONSTER_TARGET_DIST && P_AproxDistance (players[i].mo->x-x, players[i].mo->y-y) < MAX_MONSTER_TARGET_DIST
&& P_CheckSight (players[i].mo, this, SF_SEEPASTBLOCKEVERYTHING)) && P_CheckSight (players[i].mo, this, SF_SEEPASTBLOCKEVERYTHING))
{ //Probably a monster, so go kill it. { //Probably a monster, so go kill it.
players[i].Bot.enemy = this; players[i].Bot->enemy = this;
} }
} }
else if (flags & MF_SPECIAL) else if (flags & MF_SPECIAL)
@ -3260,10 +3260,10 @@ void AActor::Tick ()
} }
else if (flags & MF_MISSILE) else if (flags & MF_MISSILE)
{ {
if (!players[i].Bot.missile && (flags3 & MF3_WARNBOT)) if (!players[i].Bot->missile && (flags3 & MF3_WARNBOT))
{ //warn for incoming missiles. { //warn for incoming missiles.
if (target != players[i].mo && bglobal.Check_LOS (players[i].mo, this, ANGLE_90)) if (target != players[i].mo && bglobal.Check_LOS (players[i].mo, this, ANGLE_90))
players[i].Bot.missile = this; players[i].Bot->missile = this;
} }
} }
} }

View file

@ -227,7 +227,7 @@ void P_FireWeapon (player_t *player, FState *state)
// [SO] 9/2/02: People were able to do an awful lot of damage // [SO] 9/2/02: People were able to do an awful lot of damage
// when they were observers... // when they were observers...
if (!player->Bot.isbot && bot_observer) if (player->Bot == NULL && bot_observer)
{ {
return; return;
} }
@ -263,7 +263,7 @@ void P_FireWeaponAlt (player_t *player, FState *state)
// [SO] 9/2/02: People were able to do an awful lot of damage // [SO] 9/2/02: People were able to do an awful lot of damage
// when they were observers... // when they were observers...
if (!player->Bot.isbot && bot_observer) if (player->Bot == NULL && bot_observer)
{ {
return; return;
} }
@ -298,7 +298,7 @@ void P_FireWeaponAlt (player_t *player, FState *state)
void P_ReloadWeapon (player_t *player, FState *state) void P_ReloadWeapon (player_t *player, FState *state)
{ {
AWeapon *weapon; AWeapon *weapon;
if (!player->Bot.isbot && bot_observer) if (player->Bot == NULL && bot_observer)
{ {
return; return;
} }
@ -329,7 +329,7 @@ void P_ReloadWeapon (player_t *player, FState *state)
void P_ZoomWeapon (player_t *player, FState *state) void P_ZoomWeapon (player_t *player, FState *state)
{ {
AWeapon *weapon; AWeapon *weapon;
if (!player->Bot.isbot && bot_observer) if (player->Bot == NULL && bot_observer)
{ {
return; return;
} }

View file

@ -271,7 +271,7 @@ static void CopyPlayer (player_t *dst, player_t *src, const char *name)
dst->cheats |= chasecam; dst->cheats |= chasecam;
if (dst->Bot.isbot) if (dst->Bot != NULL)
{ {
botinfo_t *thebot = bglobal.botinfo; botinfo_t *thebot = bglobal.botinfo;
while (thebot && stricmp (name, thebot->name)) while (thebot && stricmp (name, thebot->name))
@ -283,7 +283,6 @@ static void CopyPlayer (player_t *dst, player_t *src, const char *name)
thebot->inuse = true; thebot->inuse = true;
} }
bglobal.botnum++; bglobal.botnum++;
bglobal.botingame[dst - players] = true;
dst->userinfo.TransferFrom(uibackup2); dst->userinfo.TransferFrom(uibackup2);
} }
else else

View file

@ -120,7 +120,7 @@ void P_Ticker (void)
for (i = 0; i<MAXPLAYERS; i++) for (i = 0; i<MAXPLAYERS; i++)
if (playeringame[i] && if (playeringame[i] &&
/*Added by MC: Freeze mode.*/!(bglobal.freeze && players[i].Bot.isbot)) /*Added by MC: Freeze mode.*/!(bglobal.freeze && players[i].Bot != NULL))
P_PlayerThink (&players[i]); P_PlayerThink (&players[i]);
StatusBar->Tick (); // [RH] moved this here StatusBar->Tick (); // [RH] moved this here

View file

@ -293,6 +293,7 @@ player_t::player_t()
respawn_time(0), respawn_time(0),
camera(0), camera(0),
air_finished(0), air_finished(0),
Bot(0),
BlendR(0), BlendR(0),
BlendG(0), BlendG(0),
BlendB(0), BlendB(0),
@ -311,7 +312,6 @@ player_t::player_t()
memset (&cmd, 0, sizeof(cmd)); memset (&cmd, 0, sizeof(cmd));
memset (frags, 0, sizeof(frags)); memset (frags, 0, sizeof(frags));
memset (psprites, 0, sizeof(psprites)); memset (psprites, 0, sizeof(psprites));
memset (&Bot, 0, sizeof(Bot));
} }
player_t &player_t::operator=(const player_t &p) player_t &player_t::operator=(const player_t &p)
@ -379,29 +379,7 @@ player_t &player_t::operator=(const player_t &p)
camera = p.camera; camera = p.camera;
air_finished = p.air_finished; air_finished = p.air_finished;
LastDamageType = p.LastDamageType; LastDamageType = p.LastDamageType;
Bot.savedyaw = p.Bot.savedyaw; Bot = p.Bot;
Bot.savedpitch = p.Bot.savedpitch;
Bot.angle = p.Bot.angle;
Bot.dest = p.Bot.dest;
Bot.prev = p.Bot.prev;
Bot.enemy = p.Bot.enemy;
Bot.missile = p.Bot.missile;
Bot.mate = p.Bot.mate;
Bot.last_mate = p.Bot.last_mate;
Bot.skill = p.Bot.skill;
Bot.t_active = p.Bot.t_active;
Bot.t_respawn = p.Bot.t_respawn;
Bot.t_strafe = p.Bot.t_strafe;
Bot.t_react = p.Bot.t_react;
Bot.t_fight = p.Bot.t_fight;
Bot.t_roam = p.Bot.t_roam;
Bot.t_rocket = p.Bot.t_rocket;
Bot.isbot = p.Bot.isbot;
Bot.first_shot = p.Bot.first_shot;
Bot.sleft = p.Bot.sleft;
Bot.allround = p.Bot.allround;
Bot.oldx = p.Bot.oldx;
Bot.oldy = p.Bot.oldy;
settings_controller = p.settings_controller; settings_controller = p.settings_controller;
BlendR = p.BlendR; BlendR = p.BlendR;
BlendG = p.BlendG; BlendG = p.BlendG;
@ -444,12 +422,7 @@ size_t player_t::FixPointers (const DObject *old, DObject *rep)
if (*&poisoner == old) poisoner = replacement, changed++; if (*&poisoner == old) poisoner = replacement, changed++;
if (*&attacker == old) attacker = replacement, changed++; if (*&attacker == old) attacker = replacement, changed++;
if (*&camera == old) camera = replacement, changed++; if (*&camera == old) camera = replacement, changed++;
if (*&Bot.dest == old) Bot.dest = replacement, changed++; if (*&Bot == old) Bot = static_cast<DBot *>(rep), changed++;
if (*&Bot.prev == old) Bot.prev = replacement, changed++;
if (*&Bot.enemy == old) Bot.enemy = replacement, changed++;
if (*&Bot.missile == old) Bot.missile = replacement, changed++;
if (*&Bot.mate == old) Bot.mate = replacement, changed++;
if (*&Bot.last_mate == old) Bot.last_mate = replacement, changed++;
if (ReadyWeapon == old) ReadyWeapon = static_cast<AWeapon *>(rep), changed++; if (ReadyWeapon == old) ReadyWeapon = static_cast<AWeapon *>(rep), changed++;
if (PendingWeapon == old) PendingWeapon = static_cast<AWeapon *>(rep), changed++; if (PendingWeapon == old) PendingWeapon = static_cast<AWeapon *>(rep), changed++;
if (*&PremorphWeapon == old) PremorphWeapon = static_cast<AWeapon *>(rep), changed++; if (*&PremorphWeapon == old) PremorphWeapon = static_cast<AWeapon *>(rep), changed++;
@ -464,12 +437,7 @@ size_t player_t::PropagateMark()
GC::Mark(poisoner); GC::Mark(poisoner);
GC::Mark(attacker); GC::Mark(attacker);
GC::Mark(camera); GC::Mark(camera);
GC::Mark(Bot.dest); GC::Mark(Bot);
GC::Mark(Bot.prev);
GC::Mark(Bot.enemy);
GC::Mark(Bot.missile);
GC::Mark(Bot.mate);
GC::Mark(Bot.last_mate);
GC::Mark(ReadyWeapon); GC::Mark(ReadyWeapon);
GC::Mark(ConversationNPC); GC::Mark(ConversationNPC);
GC::Mark(ConversationPC); GC::Mark(ConversationPC);
@ -718,10 +686,10 @@ void APlayerPawn::SetupWeaponSlots()
// If we're the local player, then there's a bit more work to do. // If we're the local player, then there's a bit more work to do.
// This also applies if we're a bot and this is the net arbitrator. // This also applies if we're a bot and this is the net arbitrator.
if (player - players == consoleplayer || if (player - players == consoleplayer ||
(player->Bot.isbot && consoleplayer == Net_Arbitrator)) (player->Bot != NULL && consoleplayer == Net_Arbitrator))
{ {
FWeaponSlots local_slots(player->weapons); FWeaponSlots local_slots(player->weapons);
if (player->Bot.isbot) if (player->Bot != NULL)
{ // Bots only need weapons from KEYCONF, not INI modifications. { // Bots only need weapons from KEYCONF, not INI modifications.
P_PlaybackKeyConfWeapons(&local_slots); P_PlaybackKeyConfWeapons(&local_slots);
} }
@ -2153,7 +2121,7 @@ void P_DeathThink (player_t *player)
if ((player->cmd.ucmd.buttons & BT_USE || if ((player->cmd.ucmd.buttons & BT_USE ||
((multiplayer || alwaysapplydmflags) && (dmflags & DF_FORCE_RESPAWN))) && !(dmflags2 & DF2_NO_RESPAWN)) ((multiplayer || alwaysapplydmflags) && (dmflags & DF_FORCE_RESPAWN))) && !(dmflags2 & DF2_NO_RESPAWN))
{ {
if (level.time >= player->respawn_time || ((player->cmd.ucmd.buttons & BT_USE) && !player->Bot.isbot)) if (level.time >= player->respawn_time || ((player->cmd.ucmd.buttons & BT_USE) && player->Bot == NULL))
{ {
player->cls = NULL; // Force a new class if the player is using a random class player->cls = NULL; // Force a new class if the player is using a random class
player->playerstate = (multiplayer || (level.flags2 & LEVEL2_ALLOWRESPAWN)) ? PST_REBORN : PST_ENTER; player->playerstate = (multiplayer || (level.flags2 & LEVEL2_ALLOWRESPAWN)) ? PST_REBORN : PST_ENTER;
@ -2985,9 +2953,17 @@ void player_t::Serialize (FArchive &arc)
<< respawn_time << respawn_time
<< air_finished << air_finished
<< turnticks << turnticks
<< oldbuttons << oldbuttons;
<< Bot.isbot bool IsBot;
<< BlendR if (SaveVersion >= 4514)
{
arc << Bot;
}
else
{
arc << IsBot;
}
arc << BlendR
<< BlendG << BlendG
<< BlendB << BlendB
<< BlendA; << BlendA;
@ -3070,32 +3046,30 @@ void player_t::Serialize (FArchive &arc)
onground = (mo->z <= mo->floorz) || (mo->flags2 & MF2_ONMOBJ) || (mo->BounceFlags & BOUNCE_MBF) || (cheats & CF_NOCLIP2); onground = (mo->z <= mo->floorz) || (mo->flags2 & MF2_ONMOBJ) || (mo->BounceFlags & BOUNCE_MBF) || (cheats & CF_NOCLIP2);
} }
if (Bot.isbot) if (SaveVersion < 4514 && IsBot)
{ {
arc << Bot.angle Bot = new DBot;
<< Bot.dest
<< Bot.prev arc << Bot->angle
<< Bot.enemy << Bot->dest
<< Bot.missile << Bot->prev
<< Bot.mate << Bot->enemy
<< Bot.last_mate << Bot->missile
<< Bot.skill << Bot->mate
<< Bot.t_active << Bot->last_mate
<< Bot.t_respawn << Bot->skill
<< Bot.t_strafe << Bot->t_active
<< Bot.t_react << Bot->t_respawn
<< Bot.t_fight << Bot->t_strafe
<< Bot.t_roam << Bot->t_react
<< Bot.t_rocket << Bot->t_fight
<< Bot.first_shot << Bot->t_roam
<< Bot.sleft << Bot->t_rocket
<< Bot.allround << Bot->first_shot
<< Bot.oldx << Bot->sleft
<< Bot.oldy; << Bot->allround
} << Bot->oldx
else << Bot->oldy;
{
Bot.dest = Bot.prev = Bot.enemy = Bot.missile = Bot.mate = Bot.last_mate = NULL;
} }
if (arc.IsLoading ()) if (arc.IsLoading ())
{ {

View file

@ -76,7 +76,7 @@ const char *GetVersionString();
// Use 4500 as the base git save version, since it's higher than the // Use 4500 as the base git save version, since it's higher than the
// SVN revision ever got. // SVN revision ever got.
#define SAVEVER 4513 #define SAVEVER 4514
#define SAVEVERSTRINGIFY2(x) #x #define SAVEVERSTRINGIFY2(x) #x
#define SAVEVERSTRINGIFY(x) SAVEVERSTRINGIFY2(x) #define SAVEVERSTRINGIFY(x) SAVEVERSTRINGIFY2(x)

View file

@ -1330,7 +1330,7 @@ void WI_updateDeathmatchStats ()
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
{ {
// If the player is in the game and not ready, stop checking // If the player is in the game and not ready, stop checking
if (playeringame[i] && !players[i].Bot.isbot && !playerready[i]) if (playeringame[i] && players[i].Bot == NULL && !playerready[i])
break; break;
} }
@ -1429,7 +1429,7 @@ void WI_drawDeathmatchStats ()
clamp(int(g*255.f), 0, 255), clamp(int(g*255.f), 0, 255),
clamp(int(b*255.f), 0, 255)), 0.8f, x, y - ypadding, (deaths_x - x) + (8 * CleanXfac), lineheight); clamp(int(b*255.f), 0, 255)), 0.8f, x, y - ypadding, (deaths_x - x) + (8 * CleanXfac), lineheight);
if (playerready[pnum] || player->Bot.isbot) // Bots are automatically assumed ready, to prevent confusion if (playerready[pnum] || player->Bot != NULL) // Bots are automatically assumed ready, to prevent confusion
screen->DrawTexture(readyico, x - (readyico->GetWidth() * CleanXfac), y, DTA_CleanNoMove, true, TAG_DONE); screen->DrawTexture(readyico, x - (readyico->GetWidth() * CleanXfac), y, DTA_CleanNoMove, true, TAG_DONE);
color = (EColorRange)HU_GetRowColor(player, pnum == consoleplayer); color = (EColorRange)HU_GetRowColor(player, pnum == consoleplayer);
@ -1638,7 +1638,7 @@ void WI_updateNetgameStats ()
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
{ {
// If the player is in the game and not ready, stop checking // If the player is in the game and not ready, stop checking
if (playeringame[i] && !players[i].Bot.isbot && !playerready[i]) if (playeringame[i] && players[i].Bot == NULL && !playerready[i])
break; break;
} }
@ -1735,7 +1735,7 @@ void WI_drawNetgameStats ()
clamp(int(g*255.f), 0, 255), clamp(int(g*255.f), 0, 255),
clamp(int(b*255.f), 0, 255)), 0.8f, x, y - ypadding, (secret_x - x) + (8 * CleanXfac), lineheight); clamp(int(b*255.f), 0, 255)), 0.8f, x, y - ypadding, (secret_x - x) + (8 * CleanXfac), lineheight);
if (playerready[i] || player->Bot.isbot) // Bots are automatically assumed ready, to prevent confusion if (playerready[i] || player->Bot != NULL) // Bots are automatically assumed ready, to prevent confusion
screen->DrawTexture(readyico, x - (readyico->GetWidth() * CleanXfac), y, DTA_CleanNoMove, true, TAG_DONE); screen->DrawTexture(readyico, x - (readyico->GetWidth() * CleanXfac), y, DTA_CleanNoMove, true, TAG_DONE);
color = (EColorRange)HU_GetRowColor(player, i == consoleplayer); color = (EColorRange)HU_GetRowColor(player, i == consoleplayer);
@ -2010,7 +2010,7 @@ void WI_checkForAccelerate(void)
{ {
if ((player->cmd.ucmd.buttons ^ player->oldbuttons) && if ((player->cmd.ucmd.buttons ^ player->oldbuttons) &&
((players[i].cmd.ucmd.buttons & players[i].oldbuttons) ((players[i].cmd.ucmd.buttons & players[i].oldbuttons)
== players[i].oldbuttons) && !player->Bot.isbot) == players[i].oldbuttons) && player->Bot == NULL)
{ {
acceleratestage = 1; acceleratestage = 1;
playerready[i] = true; playerready[i] = true;