diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 38cc45189..5073f2310 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,6 +7,7 @@ endif( COMMAND cmake_policy ) include( CheckCXXSourceCompiles ) include( CheckFunctionExists ) include( CheckCXXCompilerFlag ) +include( CheckLibraryExists ) include( FindPkgConfig ) include( FindOpenGL ) diff --git a/src/b_bot.cpp b/src/b_bot.cpp index 26c8f99bd..969d57a1b 100644 --- a/src/b_bot.cpp +++ b/src/b_bot.cpp @@ -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 -// belong there. +// [RH] Moved console commands out of d_netcmd.c (in Cajun source), because +// they don't really belong there. #include "c_cvars.h" #include "c_dispatch.h" @@ -14,6 +14,76 @@ #include "d_net.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 () +{ + Clear (); +} + +void DBot::Clear () +{ + 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 (Bool, bot_observer, false, 0) @@ -58,6 +128,11 @@ void FCajunMaster::ClearPlayer (int i, bool keepTeam) bot->inuse = false; bot->lastteam = keepTeam ? players[i].userinfo.GetTeam() : TEAM_NONE; } + if (players[i].Bot != NULL) + { + players[i].Bot->Destroy (); + players[i].Bot = NULL; + } players[i].~player_t(); ::new(&players[i]) player_t; players[i].userinfo.Reset(); @@ -66,6 +141,12 @@ void FCajunMaster::ClearPlayer (int i, bool keepTeam) CCMD (removebots) { + if (!players[consoleplayer].settings_controller) + { + Printf ("Only setting controllers can remove bots\n"); + return; + } + Net_WriteByte (DEM_KILLBOTS); } diff --git a/src/b_bot.h b/src/b_bot.h index d178a85fa..51fe64129 100644 --- a/src/b_bot.h +++ b/src/b_bot.h @@ -85,12 +85,12 @@ public: void Main (int buf); void Init (); void End(); - void CleanBotstuff (player_t *p); bool SpawnBot (const char *name, int color = NOCOLOR); bool LoadBots (); void ForgetBots (); - void DoAddBot (int bnum, char *info); + void DoAddBot (BYTE **stream); void RemoveAllBots (bool fromlist); + void DestroyAllBots (); //(B_Func.c) bool Check_LOS (AActor *mobj1, AActor *mobj2, angle_t vangle); @@ -109,7 +109,6 @@ public: bool IsDangerous (sector_t *sec); TArray 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 changefreeze:1; //Game wants to change freeze mode. int botnum; @@ -126,6 +125,7 @@ private: //(B_Func.c) bool Reachable (AActor *actor, AActor *target); void Dofire (AActor *actor, ticcmd_t *cmd); + bool IsLeader (player_t *player); AActor *Choose_Mate (AActor *bot); AActor *Find_enemy (AActor *bot); void SetBodyAt (fixed_t x, fixed_t y, fixed_t z, int hostnum); @@ -145,6 +145,56 @@ protected: bool observer; //Consoleplayer is observer. }; +class DBot : public DObject +{ + DECLARE_CLASS(DBot,DObject) + HAS_OBJECT_POINTERS +public: + DBot (); + + void Clear (); + void Serialize (FArchive &arc); + + angle_t savedyaw; + int savedpitch; + + angle_t angle; // The wanted angle that the bot try to get every tic. + // (used to get a smooth view movement) + TObjPtr dest; // Move Destination. + TObjPtr prev; // Previous move destination. + + + TObjPtr enemy; // The dead meat. + TObjPtr missile; // A threatening missile that needs to be avoided. + TObjPtr mate; // Friend (used for grouping in teamplay or coop). + TObjPtr last_mate; // If bots mate disappeared (not if died) that mate is + // pointed to by this. Allows bot to roam to it if + // necessary. + + //Skills + struct botskill_t skill; + + //Tickers + int t_active; // Open door, lower lift stuff, door must open and + // lift must go down before bot does anything + // radical like try a stuckmove + int t_respawn; + int t_strafe; + int t_react; + int t_fight; + int t_roam; + int t_rocket; + + //Misc booleans + bool first_shot; // Used for reaction skill. + bool sleft; // If false, strafe is right. + bool allround; + bool increase; + + fixed_t oldx; + fixed_t oldy; +}; + //Externs extern FCajunMaster bglobal; diff --git a/src/b_func.cpp b/src/b_func.cpp index 2703a1933..0303de8c6 100644 --- a/src/b_func.cpp +++ b/src/b_func.cpp @@ -140,8 +140,7 @@ void FCajunMaster::Dofire (AActor *actor, ticcmd_t *cmd) fixed_t dist; angle_t an; int m; - static bool inc[MAXPLAYERS]; - AActor *enemy = actor->player->enemy; + AActor *enemy = actor->player->Bot->enemy; if (!enemy || !(enemy->flags & MF_SHOOTABLE) || enemy->health <= 0) return; @@ -149,20 +148,20 @@ void FCajunMaster::Dofire (AActor *actor, ticcmd_t *cmd) if (actor->player->ReadyWeapon == NULL) return; - if (actor->player->damagecount > actor->player->skill.isp) + if (actor->player->damagecount > actor->player->Bot->skill.isp) { - actor->player->first_shot = true; + actor->player->Bot->first_shot = true; return; } //Reaction skill thing. - if (actor->player->first_shot && + if (actor->player->Bot->first_shot && !(actor->player->ReadyWeapon->WeaponFlags & WIF_BOT_REACTION_SKILL_THING)) { - actor->player->t_react = (100-actor->player->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->first_shot = false; - if (actor->player->t_react) + actor->player->Bot->first_shot = false; + if (actor->player->Bot->t_react) return; //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) { //MAKEME: This should be smarter. - if ((pr_botdofire()%200)<=actor->player->skill.reaction) - if(Check_LOS(actor, actor->player->enemy, SHOOTFOV)) + if ((pr_botdofire()%200)<=actor->player->Bot->skill.reaction) + if(Check_LOS(actor, actor->player->Bot->enemy, SHOOTFOV)) no_fire = false; } else if (actor->player->ReadyWeapon->ProjectileType != NULL) @@ -211,11 +210,11 @@ void FCajunMaster::Dofire (AActor *actor, ticcmd_t *cmd) an = FireRox (actor, enemy, cmd); if(an) { - actor->player->angle = an; + actor->player->Bot->angle = an; //have to be somewhat precise. to avoid suicide. - if (abs (actor->player->angle - actor->angle) < 12*ANGLE_1) + if (abs (actor->player->Bot->angle - actor->angle) < 12*ANGLE_1) { - actor->player->t_rocket = 9; + actor->player->Bot->t_rocket = 9; no_fire = false; } } @@ -225,14 +224,14 @@ shootmissile: dist = P_AproxDistance (actor->x - enemy->x, actor->y - enemy->y); m = dist / GetDefaultByType (actor->player->ReadyWeapon->ProjectileType)->Speed; SetBodyAt (enemy->x + enemy->velx*m*2, enemy->y + enemy->vely*m*2, enemy->z, 1); - actor->player->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)) no_fire = false; } else { //Other weapons, mostly instant hit stuff. - actor->player->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; if (enemy->flags & MF_SHADOW) aiming_penalty += (pr_botdofire()%25)+10; @@ -240,7 +239,7 @@ shootmissile: aiming_penalty += pr_botdofire()%40;//Dark if (actor->player->damagecount) aiming_penalty += actor->player->damagecount; //Blood in face makes it hard to aim - aiming_value = actor->player->skill.aiming - aiming_penalty; + aiming_value = actor->player->Bot->skill.aiming - aiming_penalty; if (aiming_value <= 0) aiming_value = 1; m = ((SHOOTFOV/2)-(aiming_value*SHOOTFOV/200)); //Higher skill is more accurate @@ -249,15 +248,15 @@ shootmissile: if (m) { - if (inc[actor->player - players]) - actor->player->angle += m; + if (actor->player->Bot->increase) + actor->player->Bot->angle += m; else - actor->player->angle -= m; + actor->player->Bot->angle -= m; } - if (abs (actor->player->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))) @@ -271,6 +270,19 @@ shootmissile: //actor->angle = R_PointToAngle2(actor->x, actor->y, actor->player->enemy->x, actor->player->enemy->y); } +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; +} //This function is called every //tick (for each bot) to set @@ -278,43 +290,25 @@ shootmissile: AActor *FCajunMaster::Choose_Mate (AActor *bot) { int count; - int count2; fixed_t closest_dist, test; AActor *target; AActor *observer; - bool p_leader[MAXPLAYERS]; //is mate alive? - if (bot->player->mate) + if (bot->player->Bot->mate) { - if (bot->player->mate->health <= 0) - bot->player->mate = NULL; + if (bot->player->Bot->mate->health <= 0) + bot->player->Bot->mate = NULL; else - bot->player->last_mate = bot->player->mate; + bot->player->Bot->last_mate = bot->player->Bot->mate; } - if (bot->player->mate) //Still is.. - return bot->player->mate; + if (bot->player->Bot->mate) //Still is.. + return bot->player->Bot->mate; //Check old_mates status. - if (bot->player->last_mate) - if (bot->player->last_mate->health <= 0) - bot->player->last_mate = NULL; - - for (count = 0; count < MAXPLAYERS; count++) - { - if (!playeringame[count]) - continue; - p_leader[count] = false; - for (count2 = 0; count2 < MAXPLAYERS; count2++) - { - if (players[count].isbot - && players[count2].mate == players[count].mo) - { - p_leader[count] = true; - break; - } - } - } + if (bot->player->Bot->last_mate) + if (bot->player->Bot->last_mate->health <= 0) + bot->player->Bot->last_mate = NULL; target = NULL; closest_dist = FIXED_MAX; @@ -335,9 +329,8 @@ AActor *FCajunMaster::Choose_Mate (AActor *bot) && client->mo->health > 0 && client->mo != observer && ((bot->health/2) <= client->mo->health || !deathmatch) - && !p_leader[count]) //taken? + && !IsLeader(client)) //taken? { - if (P_CheckSight (bot, client->mo, SF_IGNOREVISIBILITY)) { test = P_AproxDistance (client->mo->x - bot->x, @@ -386,11 +379,11 @@ AActor *FCajunMaster::Find_enemy (AActor *bot) } //Note: It's hard to ambush a bot who is not alone - if (bot->player->allround || bot->player->mate) + if (bot->player->Bot->allround || bot->player->Bot->mate) vangle = ANGLE_MAX; else vangle = ENEMY_SCAN_FOV; - bot->player->allround = false; + bot->player->Bot->allround = false; target = NULL; closest_dist = FIXED_MAX; diff --git a/src/b_game.cpp b/src/b_game.cpp index f3878b8bb..807ec2b46 100644 --- a/src/b_game.cpp +++ b/src/b_game.cpp @@ -118,7 +118,7 @@ void FCajunMaster::Main (int buf) BotThinkCycles.Clock(); for (i = 0; i < MAXPLAYERS; i++) { - if (playeringame[i] && players[i].mo && !freeze && players[i].isbot) + if (playeringame[i] && players[i].mo && !freeze && players[i].Bot != NULL) Think (players[i].mo, &netcmds[i][buf]); } BotThinkCycles.Unclock(); @@ -172,16 +172,9 @@ void FCajunMaster::Init () body1 = NULL; body2 = NULL; - //Remove all bots upon each level start, they'll get spawned instead. for (i = 0; i < MAXPLAYERS; i++) { waitingforspawn[i] = false; - if (playeringame[i] && players[i].isbot) - { - CleanBotstuff (&players[i]); - players[i].isbot = false; - botingame[i] = false; - } } if (ctf && teamplay == false) @@ -214,13 +207,12 @@ void FCajunMaster::End () getspawned.Clear(); for (i = 0; i < MAXPLAYERS; i++) { - if (playeringame[i] && players[i].isbot) + if (playeringame[i] && players[i].Bot != NULL) { if (deathmatch) { getspawned.Push(players[i].userinfo.GetName()); } - CleanBotstuff (&players[i]); } } if (deathmatch) @@ -335,8 +327,10 @@ bool FCajunMaster::SpawnBot (const char *name, int color) } Net_WriteString (concat); } - - players[playernumber].skill = thebot->skill; + Net_WriteByte(thebot->skill.aiming); + Net_WriteByte(thebot->skill.perfection); + Net_WriteByte(thebot->skill.reaction); + Net_WriteByte(thebot->skill.isp); thebot->inuse = true; @@ -346,10 +340,21 @@ bool FCajunMaster::SpawnBot (const char *name, int color) 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; + botskill_t skill; + skill.aiming = ReadByte (stream); + skill.perfection = ReadByte (stream); + skill.reaction = ReadByte (stream); + skill.isp = ReadByte (stream); + D_ReadUserInfoStrings (bnum, &infob, false); + + delete[] info; + if (!deathmatch && playerstarts[bnum].type == 0) { Printf ("%s tried to join, but there was no player %d start\n", @@ -363,11 +368,12 @@ void FCajunMaster::DoAddBot (int bnum, char *info) else { multiplayer = true; //Prevents cheating and so on; emulates real netgame (almost). - players[bnum].isbot = true; + players[bnum].Bot = new DBot; + GC::WriteBarrier (players[bnum].Bot); + players[bnum].Bot->skill = skill; playeringame[bnum] = true; players[bnum].mo = NULL; players[bnum].playerstate = PST_ENTER; - botingame[bnum] = true; if (teamplay) Printf ("%s joined the %s team\n", players[bnum].userinfo.GetName(), Teams[players[bnum].userinfo.GetTeam()].GetName()); @@ -389,13 +395,13 @@ void FCajunMaster::RemoveAllBots (bool fromlist) 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 // look through his own eyes instead. 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) { @@ -421,26 +427,16 @@ void FCajunMaster::RemoveAllBots (bool fromlist) 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) +void FCajunMaster::DestroyAllBots () { - p->angle = ANG45; - p->dest = NULL; - p->enemy = NULL; //The dead meat. - p->missile = NULL; //A threatening missile that needs to be avoided. - p->mate = NULL; //Friend (used for grouping in templay or coop. - p->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->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->t_respawn = 0; - p->t_strafe = 0; - p->t_react = 0; - //Misc bools - p->isbot = true; //Important. - p->first_shot = true; //Used for reaction skill. - p->sleft = false; //If false, strafe is right. - p->allround = false; + for (int i = 0; i < MAXPLAYERS; ++i) + { + if (players[i].Bot != NULL) + { + players[i].Bot->Destroy (); + players[i].Bot = NULL; + } + } } diff --git a/src/b_move.cpp b/src/b_move.cpp index 0345bccfb..f020cf4fc 100644 --- a/src/b_move.cpp +++ b/src/b_move.cpp @@ -33,19 +33,19 @@ void FCajunMaster::Roam (AActor *actor, ticcmd_t *cmd) { int delta; - if (Reachable(actor, actor->player->dest)) + if (Reachable(actor, actor->player->Bot->dest)) { // Straight towards it. - actor->player->angle = R_PointToAngle2(actor->x, actor->y, actor->player->dest->x, actor->player->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 { - actor->player->angle &= (angle_t)(7<<29); - delta = actor->player->angle - (actor->movedir << 29); + actor->player->Bot->angle &= (angle_t)(7<<29); + delta = actor->player->Bot->angle - (actor->movedir << 29); if (delta > 0) - actor->player->angle -= ANG45; + actor->player->Bot->angle -= ANG45; else if (delta < 0) - actor->player->angle += ANG45; + actor->player->Bot->angle += ANG45; } // chase towards destination. @@ -134,7 +134,7 @@ void FCajunMaster::NewChaseDir (AActor *actor, ticcmd_t *cmd) dirtype_t turnaround; - if (!actor->player->dest) + if (!actor->player->Bot->dest) { #ifndef BOT_RELEASE_COMPILE Printf ("Bot tried move without destination\n"); @@ -145,8 +145,8 @@ void FCajunMaster::NewChaseDir (AActor *actor, ticcmd_t *cmd) olddir = (dirtype_t)actor->movedir; turnaround = opposite[olddir]; - deltax = actor->player->dest->x - actor->x; - deltay = actor->player->dest->y - actor->y; + deltax = actor->player->Bot->dest->x - actor->x; + deltay = actor->player->Bot->dest->y - actor->y; if (deltax > 10*FRACUNIT) d[1] = DI_EAST; @@ -315,23 +315,23 @@ void FCajunMaster::TurnToAng (AActor *actor) { if (actor->player->ReadyWeapon->WeaponFlags & WIF_BOT_EXPLOSIVE) { - if (actor->player->t_roam && !actor->player->missile) + if (actor->player->Bot->t_roam && !actor->player->Bot->missile) { //Keep angle that where when shot where decided. return; } } - if(actor->player->enemy) - if(!actor->player->dest) //happens when running after item in combat situations, or normal, prevents weak turns + 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->ReadyWeapon->ProjectileType == NULL && !(actor->player->ReadyWeapon->WeaponFlags & WIF_MELEEWEAPON)) - if(Check_LOS(actor, actor->player->enemy, SHOOTFOV+5*ANGLE_1)) + if(Check_LOS(actor, actor->player->Bot->enemy, SHOOTFOV+5*ANGLE_1)) maxturn = 3; } - int distance = actor->player->angle - actor->angle; + int distance = actor->player->Bot->angle - actor->angle; - if (abs (distance) < OKAYRANGE && !actor->player->enemy) + if (abs (distance) < OKAYRANGE && !actor->player->Bot->enemy) return; distance /= TURNSENS; diff --git a/src/b_think.cpp b/src/b_think.cpp index cc7f087e6..e5ee8775c 100644 --- a/src/b_think.cpp +++ b/src/b_think.cpp @@ -28,13 +28,13 @@ void FCajunMaster::Think (AActor *actor, ticcmd_t *cmd) { memset (cmd, 0, sizeof(*cmd)); - if (actor->player->enemy && actor->player->enemy->health <= 0) - actor->player->enemy = NULL; + if (actor->player->Bot->enemy && actor->player->Bot->enemy->health <= 0) + actor->player->Bot->enemy = NULL; if (actor->health > 0) //Still alive { if (teamplay || !deathmatch) - actor->player->mate = Choose_Mate (actor); + actor->player->Bot->mate = Choose_Mate (actor); angle_t oldyaw = actor->angle; int oldpitch = actor->pitch; @@ -52,17 +52,17 @@ void FCajunMaster::Think (AActor *actor, ticcmd_t *cmd) actor->pitch = oldpitch - (cmd->ucmd.pitch << 16) * ticdup; } - if (actor->player->t_active) actor->player->t_active--; - if (actor->player->t_strafe) actor->player->t_strafe--; - if (actor->player->t_react) actor->player->t_react--; - if (actor->player->t_fight) actor->player->t_fight--; - if (actor->player->t_rocket) actor->player->t_rocket--; - if (actor->player->t_roam) actor->player->t_roam--; + 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_react) actor->player->Bot->t_react--; + 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_roam) actor->player->Bot->t_roam--; //Respawn ticker - if (actor->player->t_respawn) + if (actor->player->Bot->t_respawn) { - actor->player->t_respawn--; + actor->player->Bot->t_respawn--; } else if (actor->health <= 0) { // Time to respawn @@ -80,17 +80,17 @@ void FCajunMaster::ThinkForMove (AActor *actor, ticcmd_t *cmd) int r; b = actor->player; - if (!b->isbot) + if (b->Bot == NULL) return; stuck = false; - dist = b->dest ? P_AproxDistance(actor->x-b->dest->x, actor->y-b->dest->y) : 0; + dist = b->Bot->dest ? P_AproxDistance(actor->x-b->Bot->dest->x, actor->y-b->Bot->dest->y) : 0; - if (b->missile && - ((!b->missile->velx || !b->missile->vely) || !Check_LOS(actor, b->missile, SHOOTFOV*3/2))) + if (b->Bot->missile && + ((!b->Bot->missile->velx || !b->Bot->missile->vely) || !Check_LOS(actor, b->Bot->missile, SHOOTFOV*3/2))) { - b->sleft = !b->sleft; - b->missile = NULL; //Probably ended its travel. + b->Bot->sleft = !b->Bot->sleft; + b->Bot->missile = NULL; //Probably ended its travel. } if (actor->pitch > 0) @@ -99,35 +99,35 @@ void FCajunMaster::ThinkForMove (AActor *actor, ticcmd_t *cmd) actor->pitch += 80; //HOW TO MOVE: - if (b->missile && (P_AproxDistance(actor->x-b->missile->x, actor->y-b->missile->y)Bot->missile && (P_AproxDistance(actor->x-b->Bot->missile->x, actor->y-b->Bot->missile->y)missile); - actor->player->angle = R_PointToAngle2(actor->x, actor->y, b->missile->x, b->missile->y); - cmd->ucmd.sidemove = b->sleft ? -SIDERUN : SIDERUN; + Pitch (actor, b->Bot->missile); + 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.forwardmove = -FORWARDRUN; //Back IS best. - if ((P_AproxDistance(actor->x-b->oldx, actor->y-b->oldy)<50000) - && b->t_strafe<=0) + if ((P_AproxDistance(actor->x-b->Bot->oldx, actor->y-b->Bot->oldy)<50000) + && b->Bot->t_strafe<=0) { - b->t_strafe = 5; - b->sleft = !b->sleft; + b->Bot->t_strafe = 5; + b->Bot->sleft = !b->Bot->sleft; } //If able to see enemy while avoiding missile, still fire at enemy. - if (b->enemy && Check_LOS (actor, b->enemy, SHOOTFOV)) + if (b->Bot->enemy && Check_LOS (actor, b->Bot->enemy, SHOOTFOV)) Dofire (actor, cmd); //Order bot to fire current weapon } - else if (b->enemy && P_CheckSight (actor, b->enemy, 0)) //Fight! + else if (b->Bot->enemy && P_CheckSight (actor, b->Bot->enemy, 0)) //Fight! { - Pitch (actor, b->enemy); + Pitch (actor, b->Bot->enemy); //Check if it's more important to get an item than fight. - if (b->dest && (b->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->dest->IsKindOf (PClass::FindClass (#x)) +#define is(x) b->Bot->dest->IsKindOf (PClass::FindClass (#x)) if ( ( - (actor->health < b->skill.isp && + (actor->health < b->Bot->skill.isp && (is (Medikit) || is (Stimpack) || is (Soulsphere) || @@ -143,75 +143,75 @@ void FCajunMaster::ThinkForMove (AActor *actor, ticcmd_t *cmd) (b->ReadyWeapon == NULL || b->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON) ) && (dist < GETINCOMBAT || (b->ReadyWeapon == NULL || b->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON)) - && Reachable (actor, b->dest)) + && Reachable (actor, b->Bot->dest)) #undef is { goto roam; //Pick it up, no matter the situation. All bonuses are nice close up. } } - b->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)) actor->flags &= ~MF_DROPOFF; //Don't jump off any ledges when fighting. - if (!(b->enemy->flags3 & MF3_ISMONSTER)) - b->t_fight = AFTERTICS; + if (!(b->Bot->enemy->flags3 & MF3_ISMONSTER)) + b->Bot->t_fight = AFTERTICS; - if (b->t_strafe <= 0 && - (P_AproxDistance(actor->x-b->oldx, actor->y-b->oldy)<50000 + if (b->Bot->t_strafe <= 0 && + (P_AproxDistance(actor->x-b->Bot->oldx, actor->y-b->Bot->oldy)<50000 || ((pr_botmove()%30)==10)) ) { stuck = true; - b->t_strafe = 5; - b->sleft = !b->sleft; + b->Bot->t_strafe = 5; + b->Bot->sleft = !b->Bot->sleft; } - b->angle = R_PointToAngle2(actor->x, actor->y, b->enemy->x, b->enemy->y); + b->Bot->angle = R_PointToAngle2(actor->x, actor->y, b->Bot->enemy->x, b->Bot->enemy->y); if (b->ReadyWeapon == NULL || - P_AproxDistance(actor->x-b->enemy->x, actor->y-b->enemy->y) > + P_AproxDistance(actor->x-b->Bot->enemy->x, actor->y-b->Bot->enemy->y) > b->ReadyWeapon->MoveCombatDist) { // If a monster, use lower speed (just for cooler apperance while strafing down doomed monster) - cmd->ucmd.forwardmove = (b->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. { // If a monster, use lower speed (just for cooler apperance while strafing down doomed monster) - cmd->ucmd.forwardmove = (b->enemy->flags3 & MF3_ISMONSTER) ? -FORWARDWALK : -FORWARDRUN; + cmd->ucmd.forwardmove = (b->Bot->enemy->flags3 & MF3_ISMONSTER) ? -FORWARDWALK : -FORWARDRUN; } //Strafing. - if (b->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->sleft ? -SIDEWALK : SIDEWALK; + cmd->ucmd.sidemove = b->Bot->sleft ? -SIDEWALK : SIDEWALK; } else { - cmd->ucmd.sidemove = b->sleft ? -SIDERUN : SIDERUN; + cmd->ucmd.sidemove = b->Bot->sleft ? -SIDERUN : SIDERUN; } Dofire (actor, cmd); //Order bot to fire current weapon } - else if (b->mate && !b->enemy && (!b->dest || b->dest==b->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; - Pitch (actor, b->mate); + Pitch (actor, b->Bot->mate); - if (!Reachable (actor, b->mate)) + if (!Reachable (actor, b->Bot->mate)) { - if (b->mate == b->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 - b->dest = NULL; + b->Bot->dest = NULL; } goto roam; } - actor->player->angle = R_PointToAngle2(actor->x, actor->y, b->mate->x, b->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->mate->x, actor->y - b->mate->y); + matedist = P_AproxDistance(actor->x - b->Bot->mate->x, actor->y - b->Bot->mate->y); if (matedist > (FRIEND_DIST*2)) cmd->ucmd.forwardmove = FORWARDRUN; else if (matedist > FRIEND_DIST) @@ -221,33 +221,33 @@ void FCajunMaster::ThinkForMove (AActor *actor, ticcmd_t *cmd) } else //Roam after something. { - b->first_shot = true; + b->Bot->first_shot = true; ///// roam: ///// - if (b->enemy && Check_LOS (actor, b->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 - if (b->dest && !(b->dest->flags&MF_SPECIAL) && b->dest->health < 0) + if (b->Bot->dest && !(b->Bot->dest->flags&MF_SPECIAL) && b->Bot->dest->health < 0) { //Roaming after something dead. - b->dest = NULL; + b->Bot->dest = NULL; } - if (b->dest == NULL) + if (b->Bot->dest == NULL) { - if (b->t_fight && b->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->enemy->player) + if (b->Bot->enemy->player) { - if (((b->enemy->player->ReadyWeapon != NULL && b->enemy->player->ReadyWeapon->WeaponFlags & WIF_BOT_EXPLOSIVE) || - (pr_botmove()%100)>b->skill.isp) && b->ReadyWeapon != NULL && !(b->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON)) - b->dest = b->enemy;//Dont let enemy kill the bot by supressive fire. So charge enemy. + 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)) + 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. - b->angle = R_PointToAngle2(actor->x, actor->y, b->enemy->x, b->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. else - b->dest = b->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. } @@ -272,42 +272,42 @@ void FCajunMaster::ThinkForMove (AActor *actor, ticcmd_t *cmd) item = it.Next(); } firstthing = item; - b->dest = item; + b->Bot->dest = item; } } - else if (b->mate && (r < 179 || P_CheckSight(actor, b->mate))) + else if (b->Bot->mate && (r < 179 || P_CheckSight(actor, b->Bot->mate))) { - b->dest = b->mate; + b->Bot->dest = b->Bot->mate; } else if ((playeringame[(r&(MAXPLAYERS-1))]) && players[(r&(MAXPLAYERS-1))].mo->health > 0) { - b->dest = players[(r&(MAXPLAYERS-1))].mo; + b->Bot->dest = players[(r&(MAXPLAYERS-1))].mo; } } - if (b->dest) + if (b->Bot->dest) { - b->t_roam = MAXROAM; + b->Bot->t_roam = MAXROAM; } } - if (b->dest) + if (b->Bot->dest) { //Bot has a target so roam after it. Roam (actor, cmd); } } //End of movement main part. - if (!b->t_roam && b->dest) + if (!b->Bot->t_roam && b->Bot->dest) { - b->prev = b->dest; - b->dest = NULL; + b->Bot->prev = b->Bot->dest; + b->Bot->dest = NULL; } - if (b->t_fight<(AFTERTICS/2)) + if (b->Bot->t_fight<(AFTERTICS/2)) actor->flags |= MF_DROPOFF; - b->oldx = actor->x; - b->oldy = actor->y; + b->Bot->oldx = actor->x; + b->Bot->oldy = actor->y; } //BOT_WhatToGet @@ -324,7 +324,7 @@ void FCajunMaster::WhatToGet (AActor *actor, AActor *item) #define typeis(x) item->IsKindOf (PClass::FindClass (#x)) if ((item->renderflags & RF_INVISIBLE) //Under respawn and away. - || item == b->prev) + || item == b->Bot->prev) { return; } @@ -366,21 +366,21 @@ void FCajunMaster::WhatToGet (AActor *actor, AActor *item) else if (item->IsKindOf (RUNTIME_CLASS(AHealth)) && actor->health >= deh.MaxHealth /*MAXHEALTH*/) return; - if ((b->dest == NULL || - !(b->dest->flags & MF_SPECIAL)/* || + if ((b->Bot->dest == NULL || + !(b->Bot->dest->flags & MF_SPECIAL)/* || !Reachable (actor, b->dest)*/)/* && Reachable (actor, item)*/) // Calling Reachable slows this down tremendously { - b->prev = b->dest; - b->dest = item; - b->t_roam = MAXROAM; + b->Bot->prev = b->Bot->dest; + b->Bot->dest = item; + b->Bot->t_roam = MAXROAM; } } void FCajunMaster::Set_enemy (AActor *actor) { AActor *oldenemy; - AActor **enemy = &actor->player->enemy; + AActor **enemy = &actor->player->Bot->enemy; if (*enemy && (*enemy)->health > 0 @@ -397,7 +397,7 @@ void FCajunMaster::Set_enemy (AActor *actor) // and we already have an existing enemy. if (deathmatch || !*enemy) { - actor->player->allround = !!*enemy; + actor->player->Bot->allround = !!*enemy; *enemy = NULL; *enemy = Find_enemy(actor); if (!*enemy) diff --git a/src/d_main.cpp b/src/d_main.cpp index a24586bc4..030387118 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -978,19 +978,19 @@ void D_DoomLoop () int i; for (i = 0; i < MAXPLAYERS; i++) { - if (playeringame[i] && players[i].isbot && players[i].mo) + if (playeringame[i] && players[i].Bot != NULL && players[i].mo) { - players[i].savedyaw = players[i].mo->angle; - players[i].savedpitch = players[i].mo->pitch; + players[i].Bot->savedyaw = players[i].mo->angle; + players[i].Bot->savedpitch = players[i].mo->pitch; } } bglobal.Main (maketic%BACKUPTICS); for (i = 0; i < MAXPLAYERS; i++) { - if (playeringame[i] && players[i].isbot && players[i].mo) + if (playeringame[i] && players[i].Bot != NULL && players[i].mo) { - players[i].mo->angle = players[i].savedyaw; - players[i].mo->pitch = players[i].savedpitch; + players[i].mo->angle = players[i].Bot->savedyaw; + players[i].mo->pitch = players[i].Bot->savedpitch; } } if (advancedemo) diff --git a/src/d_net.cpp b/src/d_net.cpp index c06d628f9..89dc36d69 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -697,7 +697,7 @@ void PlayerIsGone (int netnode, int netconsole) // Pick a new network arbitrator for (int i = 0; i < MAXPLAYERS; i++) { - if (i != netconsole && playeringame[i] && !players[i].isbot) + if (i != netconsole && playeringame[i] && players[i].Bot == NULL) { Net_Arbitrator = i; players[i].settings_controller = true; @@ -902,7 +902,7 @@ void GetPackets (void) for (i = 0; i < numplayers; ++i) { - int node = !players[playerbytes[i]].isbot ? + int node = (players[playerbytes[i]].Bot == NULL) ? nodeforplayer[playerbytes[i]] : netnode; SkipTicCmd (&start, nettics[node] - realstart); @@ -918,7 +918,7 @@ void GetPackets (void) // packet. for (i = 0; i < numplayers; ++i) { - if (!players[playerbytes[i]].isbot) + if (players[playerbytes[i]].Bot == NULL) { nettics[nodeforplayer[playerbytes[i]]] = realend; } @@ -935,10 +935,10 @@ void AdjustBots (int gameticdiv) // be in even when gametic lags behind maketic. for (int i = 0; i < MAXPLAYERS; i++) { - if (playeringame[i] && players[i].isbot && players[i].mo) + if (playeringame[i] && players[i].Bot != NULL && players[i].mo) { - players[i].savedyaw = players[i].mo->angle; - players[i].savedpitch = players[i].mo->pitch; + players[i].Bot->savedyaw = players[i].mo->angle; + players[i].Bot->savedpitch = players[i].mo->pitch; for (int j = gameticdiv; j < maketic/ticdup; j++) { 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++) { - if (playeringame[i] && players[i].isbot && players[i].mo) + if (playeringame[i] && players[i].Bot != NULL && players[i].mo) { - players[i].mo->angle = players[i].savedyaw; - players[i].mo->pitch = players[i].savedpitch; + players[i].mo->angle = players[i].Bot->savedyaw; + players[i].mo->pitch = players[i].Bot->savedpitch; } } } @@ -1127,7 +1127,7 @@ void NetUpdate (void) { if (playeringame[j]) { - if (players[j].isbot || NetMode == NET_PacketServer) + if (players[j].Bot != NULL || NetMode == NET_PacketServer) { count++; } @@ -1269,7 +1269,7 @@ void NetUpdate (void) { if (playeringame[j] && j != playerfornode[i] && j != consoleplayer) { - if (players[j].isbot || NetMode == NET_PacketServer) + if (players[j].Bot != NULL || NetMode == NET_PacketServer) { playerbytes[l++] = j; netbuffer[k++] = j; @@ -1308,9 +1308,8 @@ void NetUpdate (void) } else if (i != 0) { - if (players[playerbytes[l]].isbot) + if (players[playerbytes[l]].Bot != NULL) { - WriteWord (0, &cmddata); // fake consistancy word } else @@ -2251,10 +2250,7 @@ void Net_DoCommand (int type, BYTE **stream, int player) break; case DEM_ADDBOT: - { - BYTE num = ReadByte (stream); - bglobal.DoAddBot (num, s = ReadString (stream)); - } + bglobal.DoAddBot (stream); break; case DEM_KILLBOTS: @@ -2710,10 +2706,13 @@ void Net_SkipCommand (int type, BYTE **stream) switch (type) { case DEM_SAY: - case DEM_ADDBOT: skip = strlen ((char *)(*stream + 1)) + 2; break; + case DEM_ADDBOT: + skip = strlen ((char *)(*stream + 1)) + 6; + break; + case DEM_GIVECHEAT: case DEM_TAKECHEAT: skip = strlen ((char *)(*stream)) + 3; @@ -2875,7 +2874,7 @@ static void Network_Controller (int playernum, bool add) return; } - if (players[playernum].isbot) + if (players[playernum].Bot != NULL) { Printf ("Bots cannot be added to the controller list.\n"); return; diff --git a/src/d_player.h b/src/d_player.h index 5affd6913..ceb1ff894 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -442,47 +442,10 @@ public: FName LastDamageType; // [RH] For damage-specific pain and death sounds //Added by MC: - angle_t savedyaw; - int savedpitch; - - angle_t angle; // The wanted angle that the bot try to get every tic. - // (used to get a smoth view movement) - TObjPtr dest; // Move Destination. - TObjPtr prev; // Previous move destination. - - - TObjPtr enemy; // The dead meat. - TObjPtr missile; // A threatening missile that needs to be avoided. - TObjPtr mate; // Friend (used for grouping in teamplay or coop). - TObjPtr last_mate; // If bots mate disappeared (not if died) that mate is - // pointed to by this. Allows bot to roam to it if - // necessary. + TObjPtr Bot; bool settings_controller; // Player can control game settings. - //Skills - struct botskill_t skill; - - //Tickers - int t_active; // Open door, lower lift stuff, door must open and - // lift must go down before bot does anything - // radical like try a stuckmove - int t_respawn; - int t_strafe; - int t_react; - int t_fight; - int t_roam; - int t_rocket; - - //Misc booleans - bool isbot; - bool first_shot; // Used for reaction skill. - bool sleft; // If false, strafe is right. - bool allround; - - fixed_t oldx; - fixed_t oldy; - float BlendR; // [RH] Final blending values float BlendG; float BlendB; diff --git a/src/d_protocol.h b/src/d_protocol.h index 028ad1606..0d11c7760 100644 --- a/src/d_protocol.h +++ b/src/d_protocol.h @@ -112,7 +112,7 @@ enum EDemoCommand DEM_DROPPLAYER, // 13 Not implemented, takes a byte DEM_CHANGEMAP, // 14 Name of map to change to 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_INVUSEALL, // 18 Use every item (panic!) DEM_INVUSE, // 19 4 bytes: ID of item to use diff --git a/src/g_game.cpp b/src/g_game.cpp index 95b0f587d..a58753b86 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -875,7 +875,7 @@ static void ChangeSpy (int changespy) pnum &= MAXPLAYERS-1; if (playeringame[pnum] && (!checkTeam || players[pnum].mo->IsTeammate (players[consoleplayer].mo) || - (bot_allowspy && players[pnum].isbot))) + (bot_allowspy && players[pnum].Bot != NULL))) { break; } @@ -1156,7 +1156,7 @@ void G_Ticker () Printf ("%s is turbo!\n", players[i].userinfo.GetName()); } - if (netgame && !players[i].isbot && !demoplayback && (gametic%ticdup) == 0) + if (netgame && players[i].Bot == NULL && !demoplayback && (gametic%ticdup) == 0) { //players[i].inconsistant = 0; if (gametic > BACKUPTICS*ticdup && consistancy[i][buf] != cmd->consistancy) @@ -1338,10 +1338,10 @@ void G_PlayerReborn (int player) int chasecam; BYTE currclass; userinfo_t userinfo; // [RH] Save userinfo - botskill_t b_skill; //Added by MC: APlayerPawn *actor; const PClass *cls; FString log; + DBot *Bot; //Added by MC: p = &players[player]; @@ -1351,12 +1351,12 @@ void G_PlayerReborn (int player) itemcount = p->itemcount; secretcount = p->secretcount; currclass = p->CurrentPlayerClass; - b_skill = p->skill; //Added by MC: userinfo.TransferFrom(p->userinfo); actor = p->mo; cls = p->cls; log = p->LogText; chasecam = p->cheats & CF_CHASECAM; + Bot = p->Bot; //Added by MC: // Reset player structure to its defaults p->~player_t(); @@ -1373,8 +1373,7 @@ void G_PlayerReborn (int player) p->cls = cls; p->LogText = log; p->cheats |= chasecam; - - p->skill = b_skill; //Added by MC: + p->Bot = Bot; //Added by MC: p->oldbuttons = ~0, p->attackdown = true; p->usedown = true; // don't do anything immediately p->original_oldbuttons = ~0; @@ -1386,11 +1385,13 @@ void G_PlayerReborn (int player) p->ReadyWeapon = p->PendingWeapon; } - //Added by MC: Init bot structure. - if (bglobal.botingame[player]) - bglobal.CleanBotstuff (p); - else - p->isbot = false; + //Added by MC: Init bot structure. + if (p->Bot != NULL) + { + botskill_t skill = p->Bot->skill; + p->Bot->Clear (); + p->Bot->skill = skill; + } } // diff --git a/src/g_shared/a_pickups.cpp b/src/g_shared/a_pickups.cpp index 429cb649b..a16a1316f 100644 --- a/src/g_shared/a_pickups.cpp +++ b/src/g_shared/a_pickups.cpp @@ -1024,8 +1024,8 @@ void AInventory::Touch (AActor *toucher) //Added by MC: Check if item taken was the roam destination of any bot for (int i = 0; i < MAXPLAYERS; i++) { - if (playeringame[i] && this == players[i].dest) - players[i].dest = NULL; + if (playeringame[i] && players[i].Bot != NULL && this == players[i].Bot->dest) + players[i].Bot->dest = NULL; } } diff --git a/src/i_net.cpp b/src/i_net.cpp index 6fdcb52ba..188d2871e 100644 --- a/src/i_net.cpp +++ b/src/i_net.cpp @@ -135,8 +135,8 @@ struct PreGamePacket }; struct { - u_long address; - u_short port; + DWORD address; + WORD port; BYTE player; BYTE pad; } machines[MAXNETNODES]; @@ -660,6 +660,12 @@ void HostGame (int i) numplayers = 2; } + if (numplayers > MAXNETNODES) + { + I_FatalError("You cannot host a game with %d players. The limit is currently %d.", numplayers, MAXNETNODES); + return; + } + if (numplayers == 1) { // Special case: Only 1 player, so don't bother starting the network netgame = false; diff --git a/src/p_acs.cpp b/src/p_acs.cpp index bc4cbdb75..e48e97e86 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -4228,7 +4228,7 @@ int DLevelScript::DoClassifyActor(int tid) { classify |= ACTOR_VOODOODOLL; } - if (actor->player->isbot) + if (actor->player->Bot != NULL) { classify |= ACTOR_BOT; } @@ -5619,15 +5619,27 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) wallMask = args[6]; } + bool forceTID = 0; + if (argCount >= 8) + { + if (args[7] != 0) + forceTID = 1; + } + AActor* pickedActor = P_LinePickActor(actor, args[1] << 16, args[3], args[2] << 16, actorMask, wallMask); if (pickedActor == NULL) { return 0; } - pickedActor->RemoveFromHash(); - pickedActor->tid = args[4]; - pickedActor->AddToHash(); - + if (!(forceTID) && (args[4] == 0) && (pickedActor->tid == 0)) + return 0; + + if ((pickedActor->tid == 0) || (forceTID)) + { + pickedActor->RemoveFromHash(); + pickedActor->tid = args[4]; + pickedActor->AddToHash(); + } return 1; } break; @@ -8632,7 +8644,7 @@ scriptwait: } else { - STACK(1) = players[STACK(1)].isbot; + STACK(1) = (players[STACK(1)].Bot != NULL); } break; diff --git a/src/p_doors.cpp b/src/p_doors.cpp index 7c6952d7f..844f23af9 100644 --- a/src/p_doors.cpp +++ b/src/p_doors.cpp @@ -460,7 +460,7 @@ bool EV_DoDoor (DDoor::EVlDoor type, line_t *line, AActor *thing, // run into them (otherwise opening them would be // a real pain). { - if (!thing->player || thing->player->isbot) + if (!thing->player || thing->player->Bot != NULL) return false; // JDC: bad guys never close doors //Added by MC: Neither do bots. diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index c3be908ea..6aa1a28f6 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -100,10 +100,10 @@ void P_TouchSpecialThing (AActor *special, AActor *toucher) return; //Added by MC: Finished with this destination. - if (toucher->player != NULL && toucher->player->isbot && special == toucher->player->dest) + if (toucher->player != NULL && toucher->player->Bot != NULL && special == toucher->player->Bot->dest) { - toucher->player->prev = toucher->player->dest; - toucher->player->dest = NULL; + toucher->player->Bot->prev = toucher->player->Bot->dest; + toucher->player->Bot->dest = NULL; } special->Touch (toucher); @@ -593,7 +593,7 @@ void AActor::Die (AActor *source, AActor *inflictor, int dmgflags) // even those caused by other monsters players[0].killcount++; } - + if (player) { // [RH] Death messages @@ -608,17 +608,17 @@ void AActor::Die (AActor *source, AActor *inflictor, int dmgflags) //Added by MC: Respawn bots if (bglobal.botnum && consoleplayer == Net_Arbitrator && !demoplayback) { - if (player->isbot) - player->t_respawn = (pr_botrespawn()%15)+((bglobal.botnum-1)*2)+TICRATE+1; + if (player->Bot != NULL) + player->Bot->t_respawn = (pr_botrespawn()%15)+((bglobal.botnum-1)*2)+TICRATE+1; //Added by MC: Discard enemies. for (int i = 0; i < MAXPLAYERS; i++) { - if (players[i].isbot && this == players[i].enemy) + if (players[i].Bot != NULL && this == players[i].Bot->enemy) { - if (players[i].dest == players[i].enemy) - players[i].dest = NULL; - players[i].enemy = NULL; + if (players[i].Bot->dest == players[i].Bot->enemy) + players[i].Bot->dest = NULL; + players[i].Bot->enemy = NULL; } } @@ -1193,11 +1193,10 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, // if (player) { - - //Added by MC: Lets bots look allround for enemies if they survive an ambush. - if (player->isbot) + //Added by MC: Lets bots look allround for enemies if they survive an ambush. + if (player->Bot != NULL) { - player->allround = true; + player->Bot->allround = true; } // end of game hell hack @@ -1711,7 +1710,6 @@ void P_PoisonDamage (player_t *player, AActor *source, int damage, P_SetMobjState(target, target->info->painstate); } */ - return; } diff --git a/src/p_map.cpp b/src/p_map.cpp index f0087e220..e7c477955 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -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. - if (thing->player && thing->player->isbot && thing->flags & MF_SHOOTABLE) + if (thing->player && thing->player->Bot != NULL && thing->flags & MF_SHOOTABLE) { if (tm.sector != thing->Sector && bglobal.IsDangerous(tm.sector)) { - thing->player->prev = thing->player->dest; - thing->player->dest = NULL; + thing->player->Bot->prev = thing->player->Bot->dest; + thing->player->Bot->dest = NULL; thing->velx = 0; thing->vely = 0; thing->z = oldz; diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 3e06561a9..a67b96dfb 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -3116,7 +3116,7 @@ void AActor::Tick () special2++; } //Added by MC: Freeze mode. - if (bglobal.freeze && !(player && !player->isbot)) + if (bglobal.freeze && !(player && player->Bot == NULL)) { return; } @@ -3237,18 +3237,18 @@ void AActor::Tick () bglobal.m_Thinking = true; for (i = 0; i < MAXPLAYERS; i++) { - if (!playeringame[i] || !players[i].isbot) + if (!playeringame[i] || players[i].Bot == NULL) continue; if (flags3 & MF3_ISMONSTER) { if (health > 0 - && !players[i].enemy + && !players[i].Bot->enemy && player ? !IsTeammate (players[i].mo) : true && P_AproxDistance (players[i].mo->x-x, players[i].mo->y-y) < MAX_MONSTER_TARGET_DIST && P_CheckSight (players[i].mo, this, SF_SEEPASTBLOCKEVERYTHING)) { //Probably a monster, so go kill it. - players[i].enemy = this; + players[i].Bot->enemy = this; } } else if (flags & MF_SPECIAL) @@ -3260,10 +3260,10 @@ void AActor::Tick () } else if (flags & MF_MISSILE) { - if (!players[i].missile && (flags3 & MF3_WARNBOT)) + if (!players[i].Bot->missile && (flags3 & MF3_WARNBOT)) { //warn for incoming missiles. if (target != players[i].mo && bglobal.Check_LOS (players[i].mo, this, ANGLE_90)) - players[i].missile = this; + players[i].Bot->missile = this; } } } diff --git a/src/p_pspr.cpp b/src/p_pspr.cpp index e477d9751..6e699a41b 100644 --- a/src/p_pspr.cpp +++ b/src/p_pspr.cpp @@ -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 // when they were observers... - if (!player->isbot && bot_observer) + if (player->Bot == NULL && bot_observer) { 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 // when they were observers... - if (!player->isbot && bot_observer) + if (player->Bot == NULL && bot_observer) { return; } @@ -298,7 +298,7 @@ void P_FireWeaponAlt (player_t *player, FState *state) void P_ReloadWeapon (player_t *player, FState *state) { AWeapon *weapon; - if (!player->isbot && bot_observer) + if (player->Bot == NULL && bot_observer) { return; } @@ -329,7 +329,7 @@ void P_ReloadWeapon (player_t *player, FState *state) void P_ZoomWeapon (player_t *player, FState *state) { AWeapon *weapon; - if (!player->isbot && bot_observer) + if (player->Bot == NULL && bot_observer) { return; } diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 834945f48..d8aa9876b 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -271,7 +271,7 @@ static void CopyPlayer (player_t *dst, player_t *src, const char *name) dst->cheats |= chasecam; - if (dst->isbot) + if (dst->Bot != NULL) { botinfo_t *thebot = bglobal.botinfo; 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; } bglobal.botnum++; - bglobal.botingame[dst - players] = true; dst->userinfo.TransferFrom(uibackup2); } else diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 6b5d9403f..53b288798 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -4175,6 +4175,7 @@ static void P_Shutdown () P_FreeLevelData (); P_FreeExtraLevelData (); ST_Clear(); + bglobal.DestroyAllBots (); } #if 0 diff --git a/src/p_tick.cpp b/src/p_tick.cpp index 07e153ffa..44fe88110 100644 --- a/src/p_tick.cpp +++ b/src/p_tick.cpp @@ -120,7 +120,7 @@ void P_Ticker (void) for (i = 0; iTick (); // [RH] moved this here diff --git a/src/p_user.cpp b/src/p_user.cpp index 53cd6789a..51105cd3d 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -293,28 +293,7 @@ player_t::player_t() respawn_time(0), camera(0), air_finished(0), - savedyaw(0), - savedpitch(0), - angle(0), - dest(0), - prev(0), - enemy(0), - missile(0), - mate(0), - last_mate(0), - t_active(0), - t_respawn(0), - t_strafe(0), - t_react(0), - t_fight(0), - t_roam(0), - t_rocket(0), - isbot(0), - first_shot(0), - sleft(0), - allround(0), - oldx(0), - oldy(0), + Bot(0), BlendR(0), BlendG(0), BlendB(0), @@ -333,7 +312,6 @@ player_t::player_t() memset (&cmd, 0, sizeof(cmd)); memset (frags, 0, sizeof(frags)); memset (psprites, 0, sizeof(psprites)); - memset (&skill, 0, sizeof(skill)); } player_t &player_t::operator=(const player_t &p) @@ -401,30 +379,8 @@ player_t &player_t::operator=(const player_t &p) camera = p.camera; air_finished = p.air_finished; LastDamageType = p.LastDamageType; - savedyaw = p.savedyaw; - savedpitch = p.savedpitch; - angle = p.angle; - dest = p.dest; - prev = p.prev; - enemy = p.enemy; - missile = p.missile; - mate = p.mate; - last_mate = p.last_mate; + Bot = p.Bot; settings_controller = p.settings_controller; - skill = p.skill; - t_active = p.t_active; - t_respawn = p.t_respawn; - t_strafe = p.t_strafe; - t_react = p.t_react; - t_fight = p.t_fight; - t_roam = p.t_roam; - t_rocket = p.t_rocket; - isbot = p.isbot; - first_shot = p.first_shot; - sleft = p.sleft; - allround = p.allround; - oldx = p.oldx; - oldy = p.oldy; BlendR = p.BlendR; BlendG = p.BlendG; BlendB = p.BlendB; @@ -466,12 +422,7 @@ size_t player_t::FixPointers (const DObject *old, DObject *rep) if (*&poisoner == old) poisoner = replacement, changed++; if (*&attacker == old) attacker = replacement, changed++; if (*&camera == old) camera = replacement, changed++; - if (*&dest == old) dest = replacement, changed++; - if (*&prev == old) prev = replacement, changed++; - if (*&enemy == old) enemy = replacement, changed++; - if (*&missile == old) missile = replacement, changed++; - if (*&mate == old) mate = replacement, changed++; - if (*&last_mate == old) last_mate = replacement, changed++; + if (*&Bot == old) Bot = static_cast(rep), changed++; if (ReadyWeapon == old) ReadyWeapon = static_cast(rep), changed++; if (PendingWeapon == old) PendingWeapon = static_cast(rep), changed++; if (*&PremorphWeapon == old) PremorphWeapon = static_cast(rep), changed++; @@ -486,12 +437,7 @@ size_t player_t::PropagateMark() GC::Mark(poisoner); GC::Mark(attacker); GC::Mark(camera); - GC::Mark(dest); - GC::Mark(prev); - GC::Mark(enemy); - GC::Mark(missile); - GC::Mark(mate); - GC::Mark(last_mate); + GC::Mark(Bot); GC::Mark(ReadyWeapon); GC::Mark(ConversationNPC); GC::Mark(ConversationPC); @@ -740,10 +686,10 @@ void APlayerPawn::SetupWeaponSlots() // 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. if (player - players == consoleplayer || - (player->isbot && consoleplayer == Net_Arbitrator)) + (player->Bot != NULL && consoleplayer == Net_Arbitrator)) { FWeaponSlots local_slots(player->weapons); - if (player->isbot) + if (player->Bot != NULL) { // Bots only need weapons from KEYCONF, not INI modifications. P_PlaybackKeyConfWeapons(&local_slots); } @@ -2175,7 +2121,7 @@ void P_DeathThink (player_t *player) if ((player->cmd.ucmd.buttons & BT_USE || ((multiplayer || alwaysapplydmflags) && (dmflags & DF_FORCE_RESPAWN))) && !(dmflags2 & DF2_NO_RESPAWN)) { - if (level.time >= player->respawn_time || ((player->cmd.ucmd.buttons & BT_USE) && !player->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->playerstate = (multiplayer || (level.flags2 & LEVEL2_ALLOWRESPAWN)) ? PST_REBORN : PST_ENTER; @@ -3007,9 +2953,17 @@ void player_t::Serialize (FArchive &arc) << respawn_time << air_finished << turnticks - << oldbuttons - << isbot - << BlendR + << oldbuttons; + bool IsBot; + if (SaveVersion >= 4514) + { + arc << Bot; + } + else + { + arc << IsBot; + } + arc << BlendR << BlendG << BlendB << BlendA; @@ -3092,32 +3046,31 @@ void player_t::Serialize (FArchive &arc) onground = (mo->z <= mo->floorz) || (mo->flags2 & MF2_ONMOBJ) || (mo->BounceFlags & BOUNCE_MBF) || (cheats & CF_NOCLIP2); } - if (isbot) + if (SaveVersion < 4514 && IsBot) { - arc << 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 - << oldx - << oldy; - } - else - { - dest = prev = enemy = missile = mate = last_mate = NULL; + Bot = new DBot; + GC::WriteBarrier (Bot); + + arc << Bot->angle + << Bot->dest + << Bot->prev + << Bot->enemy + << Bot->missile + << Bot->mate + << Bot->last_mate + << Bot->skill + << Bot->t_active + << Bot->t_respawn + << Bot->t_strafe + << Bot->t_react + << Bot->t_fight + << Bot->t_roam + << Bot->t_rocket + << Bot->first_shot + << Bot->sleft + << Bot->allround + << Bot->oldx + << Bot->oldy; } if (arc.IsLoading ()) { diff --git a/src/version.h b/src/version.h index e6e372d4c..8f6d85168 100644 --- a/src/version.h +++ b/src/version.h @@ -76,7 +76,7 @@ const char *GetVersionString(); // Use 4500 as the base git save version, since it's higher than the // SVN revision ever got. -#define SAVEVER 4513 +#define SAVEVER 4514 #define SAVEVERSTRINGIFY2(x) #x #define SAVEVERSTRINGIFY(x) SAVEVERSTRINGIFY2(x) diff --git a/src/wi_stuff.cpp b/src/wi_stuff.cpp index 2ab93f090..02492f272 100644 --- a/src/wi_stuff.cpp +++ b/src/wi_stuff.cpp @@ -1330,7 +1330,7 @@ void WI_updateDeathmatchStats () for (i = 0; i < MAXPLAYERS; i++) { // If the player is in the game and not ready, stop checking - if (playeringame[i] && !players[i].isbot && !playerready[i]) + if (playeringame[i] && players[i].Bot == NULL && !playerready[i]) break; } @@ -1429,7 +1429,7 @@ void WI_drawDeathmatchStats () 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); - if (playerready[pnum] || player->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); color = (EColorRange)HU_GetRowColor(player, pnum == consoleplayer); @@ -1638,7 +1638,7 @@ void WI_updateNetgameStats () for (i = 0; i < MAXPLAYERS; i++) { // If the player is in the game and not ready, stop checking - if (playeringame[i] && !players[i].isbot && !playerready[i]) + if (playeringame[i] && players[i].Bot == NULL && !playerready[i]) break; } @@ -1735,7 +1735,7 @@ void WI_drawNetgameStats () 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); - if (playerready[i] || player->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); color = (EColorRange)HU_GetRowColor(player, i == consoleplayer); @@ -2010,7 +2010,7 @@ void WI_checkForAccelerate(void) { if ((player->cmd.ucmd.buttons ^ player->oldbuttons) && ((players[i].cmd.ucmd.buttons & players[i].oldbuttons) - == players[i].oldbuttons) && !player->isbot) + == players[i].oldbuttons) && player->Bot == NULL) { acceleratestage = 1; playerready[i] = true;