diff --git a/src/actor.h b/src/actor.h index 9a3c6844a4..f1c733ab24 100644 --- a/src/actor.h +++ b/src/actor.h @@ -341,6 +341,9 @@ enum MF7_ALWAYSTELEFRAG = 0x00000004, // will unconditionally be telefragged when in the way. Overrides all other settings. MF7_HANDLENODELAY = 0x00000008, // respect NoDelay state flag MF7_WEAPONSPAWN = 0x00000010, // subject to DF_NO_COOP_WEAPON_SPAWN dmflag + MF7_HARMFRIENDS = 0x00000020, // is allowed to harm friendly monsters. + MF7_BUDDHA = 0x00000040, // Behaves just like the buddha cheat. + MF7_FOILBUDDHA = 0x00000080, // Similar to FOILINVUL, foils buddha mode. // --- mobj.renderflags --- @@ -713,6 +716,9 @@ public: // Transforms the actor into a finely-ground paste virtual bool Grind(bool items); + // Get this actor's team + int GetTeam(); + // Is the other actor on my team? bool IsTeammate (AActor *other); diff --git a/src/am_map.cpp b/src/am_map.cpp index 904593dfb7..29cd607937 100644 --- a/src/am_map.cpp +++ b/src/am_map.cpp @@ -908,8 +908,8 @@ void AM_StaticInit() if (gameinfo.mMapArrow.IsNotEmpty()) AM_ParseArrow(MapArrow, gameinfo.mMapArrow); if (gameinfo.mCheatMapArrow.IsNotEmpty()) AM_ParseArrow(CheatMapArrow, gameinfo.mCheatMapArrow); - AM_ParseArrow(CheatKey, "maparrows/key.txt"); - AM_ParseArrow(EasyKey, "maparrows/ravenkey.txt"); + AM_ParseArrow(CheatKey, gameinfo.mCheatKey); + AM_ParseArrow(EasyKey, gameinfo.mEasyKey); if (MapArrow.Size() == 0) I_FatalError("No automap arrow defined"); char namebuf[9]; diff --git a/src/b_bot.cpp b/src/b_bot.cpp index 26c8f99bd5..969d57a1bb 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 d178a85faf..51fe64129a 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 2703a19337..0303de8c6f 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 f3878b8bbf..807ec2b463 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 0345bccfb4..f020cf4fc0 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 cc7f087e66..e5ee8775c2 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/c_cmds.cpp b/src/c_cmds.cpp index 89005dbc78..3dd97434e2 100644 --- a/src/c_cmds.cpp +++ b/src/c_cmds.cpp @@ -124,6 +124,15 @@ CCMD (god) Net_WriteByte (CHT_GOD); } +CCMD(god2) +{ + if (CheckCheatmode()) + return; + + Net_WriteByte(DEM_GENERICCHEAT); + Net_WriteByte(CHT_GOD2); +} + CCMD (iddqd) { if (CheckCheatmode ()) @@ -142,6 +151,15 @@ CCMD (buddha) Net_WriteByte(CHT_BUDDHA); } +CCMD(buddha2) +{ + if (CheckCheatmode()) + return; + + Net_WriteByte(DEM_GENERICCHEAT); + Net_WriteByte(CHT_BUDDHA2); +} + CCMD (notarget) { if (CheckCheatmode ()) diff --git a/src/c_cvars.cpp b/src/c_cvars.cpp index 19187353f6..ce4e886323 100644 --- a/src/c_cvars.cpp +++ b/src/c_cvars.cpp @@ -500,9 +500,10 @@ UCVarValue FBaseCVar::FromString (const char *value, ECVarType type) goodv = false; break; default: - if (value[i] < '0' && value[i] > '9' && - value[i] < 'A' && value[i] > 'F' && - value[i] < 'a' && value[i] > 'f') + if (value[i] < '0' || + (value[i] > '9' && value[i] < 'A') || + (value[i] > 'F' && value[i] < 'a') || + value[i] > 'f') { goodv = false; } diff --git a/src/compatibility.cpp b/src/compatibility.cpp index 351c2672a9..7bd28e5d94 100644 --- a/src/compatibility.cpp +++ b/src/compatibility.cpp @@ -141,6 +141,7 @@ static FCompatOption Options[] = { "maskedmidtex", COMPATF_MASKEDMIDTEX, SLOT_COMPAT }, { "badangles", COMPATF2_BADANGLES, SLOT_COMPAT2 }, { "floormove", COMPATF2_FLOORMOVE, SLOT_COMPAT2 }, + { "soundcutoff", COMPATF2_SOUNDCUTOFF, SLOT_COMPAT2 }, { NULL, 0, 0 } }; diff --git a/src/d_main.cpp b/src/d_main.cpp index a24586bc4d..79c8990855 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -466,7 +466,7 @@ CUSTOM_CVAR (Int, dmflags2, 0, CVAR_SERVERINFO) } // Come out of chasecam mode if we're not allowed to use chasecam. - if (!(dmflags2 & DF2_CHASECAM) && !G_SkillProperty (SKILLP_DisableCheats) && !sv_cheats) + if (!(dmflags2 & DF2_CHASECAM) && CheckCheatmode(false)) { // Take us out of chasecam mode only. if (p->cheats & CF_CHASECAM) @@ -620,6 +620,7 @@ CVAR (Flag, compat_polyobj, compatflags, COMPATF_POLYOBJ); CVAR (Flag, compat_maskedmidtex, compatflags, COMPATF_MASKEDMIDTEX); CVAR (Flag, compat_badangles, compatflags2, COMPATF2_BADANGLES); CVAR (Flag, compat_floormove, compatflags2, COMPATF2_FLOORMOVE); +CVAR (Flag, compat_soundcutoff, compatflags2, COMPATF2_SOUNDCUTOFF); //========================================================================== // @@ -978,19 +979,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 c06d628f9d..89dc36d69e 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 5affd6913c..e5644e8cb7 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -205,6 +205,8 @@ typedef enum CF_DOUBLEFIRINGSPEED= 1 << 21, // Player owns a double firing speed artifact CF_EXTREMELYDEAD = 1 << 22, // [RH] Reliably let the status bar know about extreme deaths. CF_INFINITEAMMO = 1 << 23, // Player owns an infinite ammo artifact + CF_BUDDHA2 = 1 << 24, // [MC] Absolute buddha. No voodoo can kill it either. + CF_GODMODE2 = 1 << 25, // [MC] Absolute godmode. No voodoo can kill it either. CF_BUDDHA = 1 << 27, // [SP] Buddha mode - take damage, but don't die CF_NOCLIP2 = 1 << 30, // [RH] More Quake-like noclip } cheat_t; @@ -442,47 +444,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 028ad1606f..da54d3f2bb 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 @@ -219,7 +219,9 @@ enum ECheatCommand CHT_GIMMIEJ, CHT_GIMMIEZ, CHT_BUDDHA, - CHT_NOCLIP2 + CHT_NOCLIP2, + CHT_BUDDHA2, + CHT_GOD2 }; void StartChunk (int id, BYTE **stream); diff --git a/src/doomdef.h b/src/doomdef.h index 3e00975cfc..767f976618 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -339,6 +339,7 @@ enum COMPATF2_BADANGLES = 1 << 0, // It is impossible to face directly NSEW. COMPATF2_FLOORMOVE = 1 << 1, // Use the same floor motion behavior as Doom. + COMPATF2_SOUNDCUTOFF = 1 << 2, // Cut off sounds when an actor vanishes instead of making it owner-less }; // Emulate old bugs for select maps. These are not exposed by a cvar diff --git a/src/g_game.cpp b/src/g_game.cpp index 95b0f587d8..8419fc017c 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,18 +1351,19 @@ 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(); ::new(p) player_t; memcpy (p->frags, frags, sizeof(p->frags)); + p->health = actor->health; p->fragcount = fragcount; p->killcount = killcount; p->itemcount = itemcount; @@ -1373,8 +1374,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; @@ -1382,15 +1382,19 @@ void G_PlayerReborn (int player) if (gamestate != GS_TITLELEVEL) { + + // [GRB] Give inventory specified in DECORATE actor->GiveDefaultInventory (); 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_level.cpp b/src/g_level.cpp index d84a487768..525f673185 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -1183,6 +1183,15 @@ void G_FinishTravel () pawn->AddToHash (); pawn->SetState(pawn->SpawnState); pawn->player->SendPitchLimits(); + // Sync the FLY flags. + if (pawn->flags2 & MF2_FLY) + { + pawn->player->cheats |= CF_FLY; + } + else + { + pawn->player->cheats &= ~CF_FLY; + } for (inv = pawn->Inventory; inv != NULL; inv = inv->Inventory) { diff --git a/src/g_mapinfo.cpp b/src/g_mapinfo.cpp index 5a451dfaaa..b68408237a 100644 --- a/src/g_mapinfo.cpp +++ b/src/g_mapinfo.cpp @@ -1306,6 +1306,7 @@ MapFlagHandlers[] = { "compat_maskedmidtex", MITYPE_COMPATFLAG, COMPATF_MASKEDMIDTEX, 0 }, { "compat_badangles", MITYPE_COMPATFLAG, 0, COMPATF2_BADANGLES }, { "compat_floormove", MITYPE_COMPATFLAG, 0, COMPATF2_FLOORMOVE }, + { "compat_soundcutoff", MITYPE_COMPATFLAG, 0, COMPATF2_SOUNDCUTOFF }, { "cd_start_track", MITYPE_EATNEXT, 0, 0 }, { "cd_end1_track", MITYPE_EATNEXT, 0, 0 }, { "cd_end2_track", MITYPE_EATNEXT, 0, 0 }, diff --git a/src/g_shared/a_artifacts.cpp b/src/g_shared/a_artifacts.cpp index 8891c8674d..4a2d066cde 100644 --- a/src/g_shared/a_artifacts.cpp +++ b/src/g_shared/a_artifacts.cpp @@ -1296,6 +1296,18 @@ void APowerTargeter::InitEffect () PositionAccuracy (); } +bool APowerTargeter::HandlePickup(AInventory *item) +{ + if (Super::HandlePickup(item)) + { + InitEffect(); // reset the HUD sprites + return true; + } + return false; +} + + + void APowerTargeter::DoEffect () { Super::DoEffect (); @@ -1384,6 +1396,42 @@ void APowerFrightener::EndEffect () Owner->player->cheats &= ~CF_FRIGHTENING; } +// Buddha Powerup -------------------------------- + +IMPLEMENT_CLASS (APowerBuddha) + +//=========================================================================== +// +// APowerBuddha :: InitEffect +// +//=========================================================================== + +void APowerBuddha::InitEffect () +{ + Super::InitEffect(); + + if (Owner== NULL || Owner->player == NULL) + return; + + Owner->player->cheats |= CF_BUDDHA; +} + +//=========================================================================== +// +// APowerBuddha :: EndEffect +// +//=========================================================================== + +void APowerBuddha::EndEffect () +{ + Super::EndEffect(); + + if (Owner== NULL || Owner->player == NULL) + return; + + Owner->player->cheats &= ~CF_BUDDHA; +} + // Scanner powerup ---------------------------------------------------------- IMPLEMENT_CLASS (APowerScanner) diff --git a/src/g_shared/a_artifacts.h b/src/g_shared/a_artifacts.h index 0efd64ab8e..4e1823f5a0 100644 --- a/src/g_shared/a_artifacts.h +++ b/src/g_shared/a_artifacts.h @@ -173,6 +173,7 @@ protected: void EndEffect (); void PositionAccuracy (); void Travelled (); + bool HandlePickup(AInventory *item); }; class APowerFrightener : public APowerup @@ -183,6 +184,14 @@ protected: void EndEffect (); }; +class APowerBuddha : public APowerup +{ + DECLARE_CLASS (APowerBuddha, APowerup) +protected: + void InitEffect (); + void EndEffect (); +}; + class APowerTimeFreezer : public APowerup { DECLARE_CLASS( APowerTimeFreezer, APowerup ) diff --git a/src/g_shared/a_morph.cpp b/src/g_shared/a_morph.cpp index 2b3a6040d3..6014d6feee 100644 --- a/src/g_shared/a_morph.cpp +++ b/src/g_shared/a_morph.cpp @@ -12,6 +12,7 @@ #include "doomstat.h" #include "g_level.h" #include "farchive.h" +#include "p_enemy.h" static FRandom pr_morphmonst ("MorphMonster"); @@ -527,11 +528,11 @@ bool P_MorphedDeath(AActor *actor, AActor **morphed, int *morphedstyle, int *mor if (actor->GetClass()->IsDescendantOf(RUNTIME_CLASS(AMorphedMonster))) { AMorphedMonster *fakeme = static_cast(actor); + AActor *realme = fakeme->UnmorphedMe; if ((fakeme->UnmorphTime) && (fakeme->MorphStyle & MORPH_UNDOBYDEATH) && - (fakeme->UnmorphedMe)) + (realme)) { - AActor *realme = fakeme->UnmorphedMe; int realstyle = fakeme->MorphStyle; int realhealth = fakeme->health; if (P_UndoMonsterMorph(fakeme, !!(fakeme->MorphStyle & MORPH_UNDOBYDEATHFORCED))) @@ -542,6 +543,11 @@ bool P_MorphedDeath(AActor *actor, AActor **morphed, int *morphedstyle, int *mor return true; } } + if (realme->flags4 & MF4_BOSSDEATH) + { + realme->health = 0; // make sure that A_BossDeath considers it dead. + CALL_ACTION(A_BossDeath, realme); + } fakeme->flags3 |= MF3_STAYMORPHED; // moved here from AMorphedMonster::Die() return false; } diff --git a/src/g_shared/a_pickups.cpp b/src/g_shared/a_pickups.cpp index 429cb649b8..fc3e863a2c 100644 --- a/src/g_shared/a_pickups.cpp +++ b/src/g_shared/a_pickups.cpp @@ -499,7 +499,7 @@ bool AInventory::ShouldRespawn () { if ((ItemFlags & IF_BIGPOWERUP) && !(dmflags2 & DF2_RESPAWN_SUPER)) return false; if (ItemFlags & IF_NEVERRESPAWN) return false; - return !!(dmflags & DF_ITEMS_RESPAWN); + return !!((dmflags & DF_ITEMS_RESPAWN) || (ItemFlags & IF_ALWAYSRESPAWN)); } //=========================================================================== @@ -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/g_shared/a_pickups.h b/src/g_shared/a_pickups.h index 72548776a5..8616393e71 100644 --- a/src/g_shared/a_pickups.h +++ b/src/g_shared/a_pickups.h @@ -135,6 +135,7 @@ enum IF_NEVERRESPAWN = 1<<20, // Never, ever respawns IF_NOSCREENFLASH = 1<<21, // No pickup flash on the player's screen IF_TOSSED = 1<<22, // Was spawned by P_DropItem (i.e. as a monster drop) + IF_ALWAYSRESPAWN = 1<<23, // Always respawn, regardless of dmflag }; diff --git a/src/g_shared/sbar_mugshot.cpp b/src/g_shared/sbar_mugshot.cpp index 96dd072656..b8f419ef05 100644 --- a/src/g_shared/sbar_mugshot.cpp +++ b/src/g_shared/sbar_mugshot.cpp @@ -444,7 +444,7 @@ int FMugShot::UpdateState(player_t *player, StateFlags stateflags) if (bNormal) { bool good; - if ((player->cheats & CF_GODMODE) || (player->mo != NULL && player->mo->flags2 & MF2_INVULNERABLE)) + if ((player->cheats & CF_GODMODE) || (player->cheats & CF_GODMODE2) || (player->mo != NULL && player->mo->flags2 & MF2_INVULNERABLE)) { good = SetState((stateflags & ANIMATEDGODMODE) ? "godanimated" : "god"); } diff --git a/src/g_strife/a_alienspectres.cpp b/src/g_strife/a_alienspectres.cpp index 4626891bbe..2b9f499e9a 100644 --- a/src/g_strife/a_alienspectres.cpp +++ b/src/g_strife/a_alienspectres.cpp @@ -106,7 +106,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_AlienSpectreDeath) switch (self->GetClass()->TypeName) { case NAME_AlienSpectre1: - EV_DoFloor (DFloor::floorLowerToLowest, NULL, 999, FRACUNIT, 0, 0, 0, false); + EV_DoFloor (DFloor::floorLowerToLowest, NULL, 999, FRACUNIT, 0, -1, 0, false); log = 95; break; @@ -180,7 +180,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_AlienSpectreDeath) { // Another Sigil piece. Woohoo! log = 83; } - EV_DoFloor (DFloor::floorLowerToLowest, NULL, 666, FRACUNIT, 0, 0, 0, false); + EV_DoFloor (DFloor::floorLowerToLowest, NULL, 666, FRACUNIT, 0, -1, 0, false); break; default: diff --git a/src/g_strife/a_crusader.cpp b/src/g_strife/a_crusader.cpp index 6d34182f4f..7ef0b24a18 100644 --- a/src/g_strife/a_crusader.cpp +++ b/src/g_strife/a_crusader.cpp @@ -80,6 +80,6 @@ DEFINE_ACTION_FUNCTION(AActor, A_CrusaderDeath) { if (CheckBossDeath (self)) { - EV_DoFloor (DFloor::floorLowerToLowest, NULL, 667, FRACUNIT, 0, 0, 0, false); + EV_DoFloor (DFloor::floorLowerToLowest, NULL, 667, FRACUNIT, 0, -1, 0, false); } } diff --git a/src/g_strife/a_strifestuff.cpp b/src/g_strife/a_strifestuff.cpp index d51b618b70..95b0030a09 100644 --- a/src/g_strife/a_strifestuff.cpp +++ b/src/g_strife/a_strifestuff.cpp @@ -548,7 +548,7 @@ void APowerCoupling::Die (AActor *source, AActor *inflictor, int dmgflags) P_NoiseAlert (source, this); } EV_DoDoor (DDoor::doorClose, NULL, players[i].mo, 225, 2*FRACUNIT, 0, 0, 0); - EV_DoFloor (DFloor::floorLowerToHighest, NULL, 44, FRACUNIT, 0, 0, 0, false); + EV_DoFloor (DFloor::floorLowerToHighest, NULL, 44, FRACUNIT, 0, -1, 0, false); players[i].mo->GiveInventoryType (QuestItemClasses[5]); S_Sound (CHAN_VOICE, "svox/voc13", 1, ATTN_NORM); players[i].SetLogNumber (13); diff --git a/src/gi.cpp b/src/gi.cpp index 4e64dd4256..a17e97631c 100644 --- a/src/gi.cpp +++ b/src/gi.cpp @@ -290,6 +290,9 @@ void FMapInfoParser::ParseGameInfo() else gameinfo.mCheatMapArrow = ""; } // Insert valid keys here. + GAMEINFOKEY_STRING(mCheatKey, "cheatKey") + GAMEINFOKEY_STRING(mEasyKey, "easyKey") + GAMEINFOKEY_STRING(TitlePage, "titlePage") GAMEINFOKEY_STRING(TitlePage, "titlePage") GAMEINFOKEY_STRINGARRAY(creditPages, "addcreditPage", 8, false) GAMEINFOKEY_STRINGARRAY(creditPages, "CreditPage", 8, true) diff --git a/src/gi.h b/src/gi.h index bbfbe73ff6..d8d19a14bc 100644 --- a/src/gi.h +++ b/src/gi.h @@ -169,6 +169,7 @@ struct gameinfo_t int TextScreenY; FName DefaultEndSequence; FString mMapArrow, mCheatMapArrow; + FString mEasyKey, mCheatKey; FGIFont mStatscreenMapNameFont; FGIFont mStatscreenFinishedFont; FGIFont mStatscreenEnteringFont; diff --git a/src/i_net.cpp b/src/i_net.cpp index 6fdcb52ba9..188d2871e3 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/m_cheat.cpp b/src/m_cheat.cpp index 5d9d48bea6..e48b79980a 100644 --- a/src/m_cheat.cpp +++ b/src/m_cheat.cpp @@ -99,6 +99,23 @@ void cht_DoCheat (player_t *player, int cheat) msg = GStrings("TXT_BUDDHAOFF"); break; + case CHT_GOD2: + player->cheats ^= CF_GODMODE2; + if (player->cheats & CF_GODMODE2) + msg = GStrings("STSTR_DQD2ON"); + else + msg = GStrings("STSTR_DQD2OFF"); + ST_SetNeedRefresh(); + break; + + case CHT_BUDDHA2: + player->cheats ^= CF_BUDDHA2; + if (player->cheats & CF_BUDDHA2) + msg = GStrings("TXT_BUDDHA2ON"); + else + msg = GStrings("TXT_BUDDHA2OFF"); + break; + case CHT_NOCLIP: player->cheats ^= CF_NOCLIP; if (player->cheats & CF_NOCLIP) @@ -323,7 +340,6 @@ void cht_DoCheat (player_t *player, int cheat) player->mo->Translation = TRANSLATION(TRANSLATION_Players, BYTE(player-players)); } player->mo->DamageType = NAME_None; -// player->mo->GiveDefaultInventory(); if (player->ReadyWeapon != NULL) { P_SetPsprite(player, ps_weapon, player->ReadyWeapon->GetUpState()); diff --git a/src/p_3dfloors.cpp b/src/p_3dfloors.cpp index bdeb45d782..d13e462042 100644 --- a/src/p_3dfloors.cpp +++ b/src/p_3dfloors.cpp @@ -120,6 +120,7 @@ static void P_Add3DFloor(sector_t* sec, sector_t* sec2, line_t* master, int flag //Add the floor ffloor = new F3DFloor; + ffloor->top.copied = ffloor->bottom.copied = false; ffloor->top.model = ffloor->bottom.model = ffloor->model = sec2; ffloor->target = sec; ffloor->ceilingclip = ffloor->floorclip = NULL; @@ -420,6 +421,8 @@ void P_Recalculate3DFloors(sector_t * sector) F3DFloor * pick; unsigned pickindex; F3DFloor * clipped=NULL; + F3DFloor * solid=NULL; + fixed_t solid_bottom=0; fixed_t clipped_top; fixed_t clipped_bottom=0; fixed_t maxheight, minheight; @@ -477,6 +480,7 @@ void P_Recalculate3DFloors(sector_t * sector) } oldlist.Delete(pickindex); + fixed_t pick_bottom=pick->bottom.plane->ZatPoint(CenterSpot(sector)); if (pick->flags & FF_THISINSIDE) { @@ -486,10 +490,38 @@ void P_Recalculate3DFloors(sector_t * sector) } else if (pick->flags&(FF_SWIMMABLE|FF_TRANSLUCENT) && pick->flags&FF_EXISTS) { - clipped=pick; - clipped_top=height; - clipped_bottom=pick->bottom.plane->ZatPoint(CenterSpot(sector)); - ffloors.Push(pick); + // We must check if this nonsolid segment gets clipped from the top by another 3D floor + if (solid != NULL && solid_bottom < height) + { + ffloors.Push(pick); + if (solid_bottom < pick_bottom) + { + // this one is fully covered + pick->flags|=FF_CLIPPED; + pick->flags&=~FF_EXISTS; + } + else + { + F3DFloor * dyn=new F3DFloor; + *dyn=*pick; + pick->flags|=FF_CLIPPED; + pick->flags&=~FF_EXISTS; + dyn->flags|=FF_DYNAMIC; + dyn->top.copyPlane(&solid->bottom); + ffloors.Push(dyn); + + clipped = dyn; + clipped_top = solid_bottom; + clipped_bottom = pick_bottom; + } + } + else + { + clipped = pick; + clipped_top = height; + clipped_bottom = pick_bottom; + ffloors.Push(pick); + } } else if (clipped && clipped_bottomflags|=FF_CLIPPED; clipped->flags&=~FF_EXISTS; dyn->flags|=FF_DYNAMIC; - dyn->bottom=pick->top; + dyn->bottom.copyPlane(&pick->top); ffloors.Push(dyn); ffloors.Push(pick); - fixed_t pick_bottom=pick->bottom.plane->ZatPoint(CenterSpot(sector)); - if (pick_bottom<=clipped_bottom) { clipped=NULL; @@ -515,14 +545,25 @@ void P_Recalculate3DFloors(sector_t * sector) dyn=new F3DFloor; *dyn=*clipped; dyn->flags|=FF_DYNAMIC|FF_EXISTS; - dyn->top=pick->bottom; + dyn->top.copyPlane(&pick->bottom); ffloors.Push(dyn); + clipped = dyn; + clipped_top = pick_bottom; } + solid = pick; + solid_bottom = pick_bottom; } else { - clipped=NULL; + clipped = NULL; + if (solid == NULL || solid_bottom > pick_bottom) + { + // only if this one is lower + solid = pick; + solid_bottom = pick_bottom; + } ffloors.Push(pick); + } } @@ -910,3 +951,27 @@ int P_Find3DFloor(sector_t * sec, fixed_t x, fixed_t y, fixed_t z, bool above, b } #endif + +#include "c_dispatch.h" + + +CCMD (dump3df) +{ + if (argv.argc() > 1) + { + int sec = strtol(argv[1], NULL, 10); + sector_t *sector = §ors[sec]; + TArray & ffloors=sector->e->XFloor.ffloors; + + for (unsigned int i = 0; i < ffloors.Size(); i++) + { + fixed_t height=ffloors[i]->top.plane->ZatPoint(CenterSpot(sector)); + fixed_t bheight=ffloors[i]->bottom.plane->ZatPoint(CenterSpot(sector)); + + Printf("FFloor %d @ top = %f (model = %d), bottom = %f (model = %d), flags = %B, alpha = %d %s %s\n", + i, height / 65536., ffloors[i]->top.model->sectornum, + bheight / 65536., ffloors[i]->bottom.model->sectornum, + ffloors[i]->flags, ffloors[i]->alpha, (ffloors[i]->flags&FF_EXISTS)? "Exists":"", (ffloors[i]->flags&FF_DYNAMIC)? "Dynamic":""); + } + } +} diff --git a/src/p_3dfloors.h b/src/p_3dfloors.h index d0bca5e8fd..8d42560af0 100644 --- a/src/p_3dfloors.h +++ b/src/p_3dfloors.h @@ -77,6 +77,13 @@ struct F3DFloor sector_t * model; int isceiling; int vindex; + bool copied; + + void copyPlane(planeref * other) + { + *this = *other; + copied = true; + } }; planeref bottom; diff --git a/src/p_acs.cpp b/src/p_acs.cpp index bc4cbdb758..e48e97e86c 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 7c6952d7f2..844f23af96 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_enemy.cpp b/src/p_enemy.cpp index 00e2a3dc29..e51d601f3a 100644 --- a/src/p_enemy.cpp +++ b/src/p_enemy.cpp @@ -1592,7 +1592,7 @@ bool P_LookForPlayers (AActor *actor, INTBOOL allaround, FLookExParams *params) } #endif // [SP] If you don't see any enemies in deathmatch, look for players (but only when friend to a specific player.) - if (actor->FriendPlayer == 0 && (!teamplay || actor->DesignatedTeam == TEAM_NONE)) return result; + if (actor->FriendPlayer == 0 && (!teamplay || actor->GetTeam() == TEAM_NONE)) return result; if (result || !deathmatch) return true; @@ -3275,13 +3275,13 @@ DEFINE_ACTION_FUNCTION(AActor, A_BossDeath) { if (type == NAME_Fatso) { - EV_DoFloor (DFloor::floorLowerToLowest, NULL, 666, FRACUNIT, 0, 0, 0, false); + EV_DoFloor (DFloor::floorLowerToLowest, NULL, 666, FRACUNIT, 0, -1, 0, false); return; } if (type == NAME_Arachnotron) { - EV_DoFloor (DFloor::floorRaiseByTexture, NULL, 667, FRACUNIT, 0, 0, 0, false); + EV_DoFloor (DFloor::floorRaiseByTexture, NULL, 667, FRACUNIT, 0, -1, 0, false); return; } } @@ -3290,11 +3290,11 @@ DEFINE_ACTION_FUNCTION(AActor, A_BossDeath) switch (level.flags & LEVEL_SPECACTIONSMASK) { case LEVEL_SPECLOWERFLOOR: - EV_DoFloor (DFloor::floorLowerToLowest, NULL, 666, FRACUNIT, 0, 0, 0, false); + EV_DoFloor (DFloor::floorLowerToLowest, NULL, 666, FRACUNIT, 0, -1, 0, false); return; case LEVEL_SPECLOWERFLOORTOHIGHEST: - EV_DoFloor (DFloor::floorLowerToHighest, NULL, 666, FRACUNIT, 0, 0, 0, false); + EV_DoFloor (DFloor::floorLowerToHighest, NULL, 666, FRACUNIT, 0, -1, 0, false); return; case LEVEL_SPECOPENDOOR: diff --git a/src/p_floor.cpp b/src/p_floor.cpp index f630876195..9a5a6eaf82 100644 --- a/src/p_floor.cpp +++ b/src/p_floor.cpp @@ -320,7 +320,7 @@ manual_floor: rtn = true; floor = new DFloor (sec); floor->m_Type = floortype; - floor->m_Crush = -1; + floor->m_Crush = crush; floor->m_Hexencrush = hexencrush; floor->m_Speed = speed; floor->m_ResetCount = 0; // [RH] @@ -374,7 +374,6 @@ manual_floor: break; case DFloor::floorRaiseAndCrushDoom: - floor->m_Crush = crush; case DFloor::floorRaiseToLowestCeiling: floor->m_Direction = 1; newheight = sec->FindLowestCeilingSurrounding (&spot); @@ -406,7 +405,6 @@ manual_floor: break; case DFloor::floorRaiseAndCrush: - floor->m_Crush = crush; floor->m_Direction = 1; newheight = sec->FindLowestCeilingPoint (&spot) - 8*FRACUNIT; floor->m_FloorDestDist = sec->floorplane.PointToDist (spot, newheight); diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index c3be908ea8..2ac29e11cf 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; } } @@ -938,7 +938,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, FState * woundstate = NULL; PainChanceList * pc = NULL; bool justhit = false; - + if (target == NULL || !((target->flags & MF_SHOOTABLE) || (target->flags6 & MF6_VULNERABLE))) { // Shouldn't happen return -1; @@ -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 @@ -1210,8 +1209,10 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, if (!(flags & DMG_FORCED)) { // check the real player, not a voodoo doll here for invulnerability effects - if (damage < TELEFRAG_DAMAGE && ((player->mo->flags2 & MF2_INVULNERABLE) || - (player->cheats & CF_GODMODE))) + if ((damage < TELEFRAG_DAMAGE && ((player->mo->flags2 & MF2_INVULNERABLE) || + (player->cheats & CF_GODMODE))) || + (player->cheats & CF_GODMODE2) || (player->mo->flags5 & MF5_NODAMAGE)) + //Absolutely no hurting if NODAMAGE is involved. Same for GODMODE2. { // player is invulnerable, so don't hurt him return -1; } @@ -1219,8 +1220,13 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, if (!(flags & DMG_NO_ARMOR) && player->mo->Inventory != NULL) { int newdam = damage; - player->mo->Inventory->AbsorbDamage (damage, mod, newdam); - damage = newdam; + player->mo->Inventory->AbsorbDamage(damage, mod, newdam); + if (damage < TELEFRAG_DAMAGE) + { + // if we are telefragging don't let the damage value go below that magic value. Some further checks would fail otherwise. + damage = newdam; + } + if (damage <= 0) { // If MF6_FORCEPAIN is set, make the player enter the pain state. @@ -1233,7 +1239,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, } } - if (damage >= player->health + if (damage >= player->health && damage < TELEFRAG_DAMAGE && (G_SkillProperty(SKILLP_AutoUseHealth) || deathmatch) && !player->morphTics) { // Try to use some inventory health @@ -1255,9 +1261,8 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, // This does not save the player if damage >= TELEFRAG_DAMAGE, still need to // telefrag him right? ;) (Unfortunately the damage is "absorbed" by armor, // but telefragging should still do enough damage to kill the player) - if ((player->cheats & CF_BUDDHA) && damage < TELEFRAG_DAMAGE - // Ignore players that are already dead. - && player->playerstate != PST_DEAD) + // Ignore players that are already dead. + if (((player->cheats & CF_BUDDHA2) || ((player->cheats & CF_BUDDHA) && damage < TELEFRAG_DAMAGE)) && player->playerstate != PST_DEAD) { // If this is a voodoo doll we need to handle the real player as well. player->mo->health = target->health = player->health = 1; @@ -1317,43 +1322,52 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, if (target->health <= 0) - { // Death - target->special1 = damage; - - // use inflictor's death type if it got one. - if (inflictor && inflictor->DeathType != NAME_None) mod = inflictor->DeathType; - - // check for special fire damage or ice damage deaths - if (mod == NAME_Fire) + { + if ((target->flags7 & MF7_BUDDHA) && (damage < TELEFRAG_DAMAGE) && (!(inflictor->flags3 & MF7_FOILBUDDHA) && !(flags & DMG_FOILBUDDHA))) + { //Make sure FOILINVUL flags work here too for monsters. Or perhaps consider a FOILBUDDHA flag... + target->health = 1; + } + else { - if (player && !player->morphTics) - { // Check for flame death - if (!inflictor || - ((target->health > -50) && (damage > 25)) || - !(inflictor->flags5 & MF5_SPECIALFIREDAMAGE)) + + // Death + target->special1 = damage; + + // use inflictor's death type if it got one. + if (inflictor && inflictor->DeathType != NAME_None) mod = inflictor->DeathType; + + // check for special fire damage or ice damage deaths + if (mod == NAME_Fire) + { + if (player && !player->morphTics) + { // Check for flame death + if (!inflictor || + ((target->health > -50) && (damage > 25)) || + !(inflictor->flags5 & MF5_SPECIALFIREDAMAGE)) + { + target->DamageType = NAME_Fire; + } + } + else { target->DamageType = NAME_Fire; } } else { - target->DamageType = NAME_Fire; + target->DamageType = mod; } - } - else - { - target->DamageType = mod; - } - if (source && source->tracer && (source->flags5 & MF5_SUMMONEDMONSTER)) - { // Minotaur's kills go to his master - // Make sure still alive and not a pointer to fighter head - if (source->tracer->player && (source->tracer->player->mo == source->tracer)) - { - source = source->tracer; + if (source && source->tracer && (source->flags5 & MF5_SUMMONEDMONSTER)) + { // Minotaur's kills go to his master + // Make sure still alive and not a pointer to fighter head + if (source->tracer->player && (source->tracer->player->mo == source->tracer)) + { + source = source->tracer; + } } + target->Die (source, inflictor, flags); + return damage; } - target->Die (source, inflictor, flags); - return damage; } woundstate = target->FindState(NAME_Wound, mod); @@ -1673,7 +1687,7 @@ void P_PoisonDamage (player_t *player, AActor *source, int damage, target->health -= damage; if (target->health <= 0) { // Death - if (player->cheats & CF_BUDDHA) + if ((player->cheats & CF_BUDDHA && damage < TELEFRAG_DAMAGE) || (player->cheats & CF_BUDDHA2)) { // [SP] Save the player... player->health = target->health = 1; } @@ -1711,7 +1725,6 @@ void P_PoisonDamage (player_t *player, AActor *source, int damage, P_SetMobjState(target, target->info->painstate); } */ - return; } diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index 14bcd430cd..0d74b4d423 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -281,43 +281,43 @@ FUNC(LS_Generic_Door) FUNC(LS_Floor_LowerByValue) // Floor_LowerByValue (tag, speed, height) { - return EV_DoFloor (DFloor::floorLowerByValue, ln, arg0, SPEED(arg1), FRACUNIT*arg2, 0, 0, false); + return EV_DoFloor (DFloor::floorLowerByValue, ln, arg0, SPEED(arg1), FRACUNIT*arg2, -1, 0, false); } FUNC(LS_Floor_LowerToLowest) // Floor_LowerToLowest (tag, speed) { - return EV_DoFloor (DFloor::floorLowerToLowest, ln, arg0, SPEED(arg1), 0, 0, 0, false); + return EV_DoFloor (DFloor::floorLowerToLowest, ln, arg0, SPEED(arg1), 0, -1, 0, false); } FUNC(LS_Floor_LowerToHighest) // Floor_LowerToHighest (tag, speed, adjust, hereticlower) { - return EV_DoFloor (DFloor::floorLowerToHighest, ln, arg0, SPEED(arg1), (arg2-128)*FRACUNIT, 0, 0, false, arg3==1); + return EV_DoFloor (DFloor::floorLowerToHighest, ln, arg0, SPEED(arg1), (arg2-128)*FRACUNIT, -1, 0, false, arg3==1); } FUNC(LS_Floor_LowerToNearest) // Floor_LowerToNearest (tag, speed) { - return EV_DoFloor (DFloor::floorLowerToNearest, ln, arg0, SPEED(arg1), 0, 0, 0, false); + return EV_DoFloor (DFloor::floorLowerToNearest, ln, arg0, SPEED(arg1), 0, -1, 0, false); } FUNC(LS_Floor_RaiseByValue) // Floor_RaiseByValue (tag, speed, height) { - return EV_DoFloor (DFloor::floorRaiseByValue, ln, arg0, SPEED(arg1), FRACUNIT*arg2, 0, 0, false); + return EV_DoFloor (DFloor::floorRaiseByValue, ln, arg0, SPEED(arg1), FRACUNIT*arg2, -1, 0, false); } FUNC(LS_Floor_RaiseToHighest) // Floor_RaiseToHighest (tag, speed) { - return EV_DoFloor (DFloor::floorRaiseToHighest, ln, arg0, SPEED(arg1), 0, 0, 0, false); + return EV_DoFloor (DFloor::floorRaiseToHighest, ln, arg0, SPEED(arg1), 0, -1, 0, false); } FUNC(LS_Floor_RaiseToNearest) // Floor_RaiseToNearest (tag, speed) { - return EV_DoFloor (DFloor::floorRaiseToNearest, ln, arg0, SPEED(arg1), 0, 0, 0, false); + return EV_DoFloor (DFloor::floorRaiseToNearest, ln, arg0, SPEED(arg1), 0, -1, 0, false); } FUNC(LS_Floor_RaiseAndCrush) @@ -335,13 +335,13 @@ FUNC(LS_Floor_RaiseAndCrushDoom) FUNC(LS_Floor_RaiseByValueTimes8) // FLoor_RaiseByValueTimes8 (tag, speed, height) { - return EV_DoFloor (DFloor::floorRaiseByValue, ln, arg0, SPEED(arg1), FRACUNIT*arg2*8, 0, 0, false); + return EV_DoFloor (DFloor::floorRaiseByValue, ln, arg0, SPEED(arg1), FRACUNIT*arg2*8, -1, 0, false); } FUNC(LS_Floor_LowerByValueTimes8) // Floor_LowerByValueTimes8 (tag, speed, height) { - return EV_DoFloor (DFloor::floorLowerByValue, ln, arg0, SPEED(arg1), FRACUNIT*arg2*8, 0, 0, false); + return EV_DoFloor (DFloor::floorLowerByValue, ln, arg0, SPEED(arg1), FRACUNIT*arg2*8, -1, 0, false); } FUNC(LS_Floor_CrushStop) @@ -353,51 +353,51 @@ FUNC(LS_Floor_CrushStop) FUNC(LS_Floor_LowerInstant) // Floor_LowerInstant (tag, unused, height) { - return EV_DoFloor (DFloor::floorLowerInstant, ln, arg0, 0, arg2*FRACUNIT*8, 0, 0, false); + return EV_DoFloor (DFloor::floorLowerInstant, ln, arg0, 0, arg2*FRACUNIT*8, -1, 0, false); } FUNC(LS_Floor_RaiseInstant) // Floor_RaiseInstant (tag, unused, height) { - return EV_DoFloor (DFloor::floorRaiseInstant, ln, arg0, 0, arg2*FRACUNIT*8, 0, 0, false); + return EV_DoFloor (DFloor::floorRaiseInstant, ln, arg0, 0, arg2*FRACUNIT*8, -1, 0, false); } FUNC(LS_Floor_MoveToValueTimes8) // Floor_MoveToValueTimes8 (tag, speed, height, negative) { return EV_DoFloor (DFloor::floorMoveToValue, ln, arg0, SPEED(arg1), - arg2*FRACUNIT*8*(arg3?-1:1), 0, 0, false); + arg2*FRACUNIT*8*(arg3?-1:1), -1, 0, false); } FUNC(LS_Floor_MoveToValue) // Floor_MoveToValue (tag, speed, height, negative) { return EV_DoFloor (DFloor::floorMoveToValue, ln, arg0, SPEED(arg1), - arg2*FRACUNIT*(arg3?-1:1), 0, 0, false); + arg2*FRACUNIT*(arg3?-1:1), -1, 0, false); } FUNC(LS_Floor_RaiseToLowestCeiling) // Floor_RaiseToLowestCeiling (tag, speed) { - return EV_DoFloor (DFloor::floorRaiseToLowestCeiling, ln, arg0, SPEED(arg1), 0, 0, 0, false); + return EV_DoFloor (DFloor::floorRaiseToLowestCeiling, ln, arg0, SPEED(arg1), 0, -1, 0, false); } FUNC(LS_Floor_RaiseByTexture) // Floor_RaiseByTexture (tag, speed) { - return EV_DoFloor (DFloor::floorRaiseByTexture, ln, arg0, SPEED(arg1), 0, 0, 0, false); + return EV_DoFloor (DFloor::floorRaiseByTexture, ln, arg0, SPEED(arg1), 0, -1, 0, false); } FUNC(LS_Floor_RaiseByValueTxTy) // Floor_RaiseByValueTxTy (tag, speed, height) { - return EV_DoFloor (DFloor::floorRaiseAndChange, ln, arg0, SPEED(arg1), arg2*FRACUNIT, 0, 0, false); + return EV_DoFloor (DFloor::floorRaiseAndChange, ln, arg0, SPEED(arg1), arg2*FRACUNIT, -1, 0, false); } FUNC(LS_Floor_LowerToLowestTxTy) // Floor_LowerToLowestTxTy (tag, speed) { - return EV_DoFloor (DFloor::floorLowerAndChange, ln, arg0, SPEED(arg1), arg2*FRACUNIT, 0, 0, false); + return EV_DoFloor (DFloor::floorLowerAndChange, ln, arg0, SPEED(arg1), arg2*FRACUNIT, -1, 0, false); } FUNC(LS_Floor_Waggle) @@ -1764,7 +1764,7 @@ FUNC(LS_FloorAndCeiling_LowerRaise) // more or less unintuitive value for the fourth arg to trigger Boom's broken behavior if (arg3 != 1998 || !res) // (1998 for the year in which Boom was released... :P) { - res |= EV_DoFloor (DFloor::floorLowerToLowest, ln, arg0, SPEED(arg1), 0, 0, 0, false); + res |= EV_DoFloor (DFloor::floorLowerToLowest, ln, arg0, SPEED(arg1), 0, -1, 0, false); } return res; } diff --git a/src/p_local.h b/src/p_local.h index 2dc8c773eb..f7e4738456 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -559,6 +559,7 @@ enum EDmgFlags DMG_NO_FACTOR = 16, DMG_PLAYERATTACK = 32, DMG_FOILINVUL = 64, + DMG_FOILBUDDHA = 128, }; diff --git a/src/p_map.cpp b/src/p_map.cpp index f0087e2201..43b16a5064 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -198,7 +198,7 @@ void P_GetFloorCeilingZ(FCheckPosition &tmf, int flags) if (ff_top > tmf.floorz) { - if (ff_top <= tmf.z || (!(flags && FFCF_3DRESTRICT) && (tmf.thing != NULL && ff_bottom < tmf.z && ff_top < tmf.z + tmf.thing->MaxStepHeight))) + if (ff_top <= tmf.z || (!(flags & FFCF_3DRESTRICT) && (tmf.thing != NULL && ff_bottom < tmf.z && ff_top < tmf.z + tmf.thing->MaxStepHeight))) { tmf.dropoffz = tmf.floorz = ff_top; tmf.floorpic = *rover->top.texture; @@ -1111,24 +1111,27 @@ bool PIT_CheckThing(AActor *thing, FCheckPosition &tm) // cases where they are clearly supposed to do that if (thing->IsFriend(tm.thing->target)) { - // Friends never harm each other - return false; + // Friends never harm each other, unless the shooter has the HARMFRIENDS set. + if (!(thing->flags7 & MF7_HARMFRIENDS)) return false; } - if (thing->TIDtoHate != 0 && thing->TIDtoHate == tm.thing->target->TIDtoHate) + else { - // [RH] Don't hurt monsters that hate the same thing as you do - return false; - } - if (thing->GetSpecies() == tm.thing->target->GetSpecies() && !(thing->flags6 & MF6_DOHARMSPECIES)) - { - // Don't hurt same species or any relative - - // but only if the target isn't one's hostile. - if (!thing->IsHostile(tm.thing->target)) + if (thing->TIDtoHate != 0 && thing->TIDtoHate == tm.thing->target->TIDtoHate) { - // Allow hurting monsters the shooter hates. - if (thing->tid == 0 || tm.thing->target->TIDtoHate != thing->tid) + // [RH] Don't hurt monsters that hate the same thing as you do + return false; + } + if (thing->GetSpecies() == tm.thing->target->GetSpecies() && !(thing->flags6 & MF6_DOHARMSPECIES)) + { + // Don't hurt same species or any relative - + // but only if the target isn't one's hostile. + if (!thing->IsHostile(tm.thing->target)) { - return false; + // Allow hurting monsters the shooter hates. + if (thing->tid == 0 || tm.thing->target->TIDtoHate != thing->tid) + { + return false; + } } } } @@ -1919,13 +1922,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; @@ -2157,7 +2160,7 @@ bool P_CheckMove(AActor *thing, fixed_t x, fixed_t y) { // too big a step up return false; } - else if ((thing->flags & MF_MISSILE) && !(thing->flags6 && MF6_STEPMISSILE) && tm.floorz > newz) + else if ((thing->flags & MF_MISSILE) && !(thing->flags6 & MF6_STEPMISSILE) && tm.floorz > newz) { // [RH] Don't let normal missiles climb steps return false; } diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 3e06561a9d..465a4d7456 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -1199,13 +1199,7 @@ void P_ExplodeMissile (AActor *mo, line_t *line, AActor *target) if (nextstate == NULL) nextstate = mo->FindState(NAME_Death, NAME_Extreme); } if (nextstate == NULL) nextstate = mo->FindState(NAME_Death); - mo->SetState (nextstate); - if (mo->ObjectFlags & OF_EuthanizeMe) - { - return; - } - if (line != NULL && line->special == Line_Horizon && !(mo->flags3 & MF3_SKYEXPLODE)) { // [RH] Don't explode missiles on horizon lines. @@ -1280,8 +1274,17 @@ void P_ExplodeMissile (AActor *mo, line_t *line, AActor *target) } } - if (nextstate != NULL) + // play the sound before changing the state, so that AActor::Destroy can call S_RelinkSounds on it and the death state can override it. + if (mo->DeathSound) { + S_Sound (mo, CHAN_VOICE, mo->DeathSound, 1, + (mo->flags3 & MF3_FULLVOLDEATH) ? ATTN_NONE : ATTN_NORM); + } + + mo->SetState (nextstate); + if (!(mo->ObjectFlags & OF_EuthanizeMe)) + { + // The rest only applies if the missile actor still exists. // [RH] Change render style of exploding rockets if (mo->flags5 & MF5_DEHEXPLOSION) { @@ -1314,11 +1317,6 @@ void P_ExplodeMissile (AActor *mo, line_t *line, AActor *target) mo->flags &= ~MF_MISSILE; - if (mo->DeathSound) - { - S_Sound (mo, CHAN_VOICE, mo->DeathSound, 1, - (mo->flags3 & MF3_FULLVOLDEATH) ? ATTN_NONE : ATTN_NORM); - } } } @@ -3116,7 +3114,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 +3235,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 +3258,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; } } } @@ -5018,10 +5016,11 @@ void P_SpawnBlood (fixed_t x, fixed_t y, fixed_t z, angle_t dir, int damage, AAc cls = cls->ParentClass; } } + + statedone: + if (!(bloodtype <= 1)) th->renderflags |= RF_INVISIBLE; } -statedone: - if (!(bloodtype <= 1)) th->renderflags |= RF_INVISIBLE; if (bloodtype >= 1) P_DrawSplash2 (40, x, y, z, dir, 2, bloodcolor); } @@ -5857,22 +5856,41 @@ AActor *P_SpawnPlayerMissile (AActor *source, fixed_t x, fixed_t y, fixed_t z, return NULL; } +int AActor::GetTeam() +{ + if (player) + { + return player->userinfo.GetTeam(); + } + + int myTeam = DesignatedTeam; + + // Check for monsters that belong to a player on the team but aren't part of the team themselves. + if (myTeam == TEAM_NONE && FriendPlayer != 0) + { + myTeam = players[FriendPlayer - 1].userinfo.GetTeam(); + } + return myTeam; + +} + bool AActor::IsTeammate (AActor *other) { if (!other) + { return false; + } else if (!deathmatch && player && other->player) - return true; - int myTeam = DesignatedTeam; - int otherTeam = other->DesignatedTeam; - if (player) - myTeam = player->userinfo.GetTeam(); - if (other->player) - otherTeam = other->player->userinfo.GetTeam(); - if (teamplay && myTeam != TEAM_NONE && myTeam == otherTeam) { return true; } + else if (teamplay) + { + int myTeam = GetTeam(); + int otherTeam = other->GetTeam(); + + return (myTeam != TEAM_NONE && myTeam == otherTeam); + } return false; } diff --git a/src/p_pspr.cpp b/src/p_pspr.cpp index e477d97519..6e699a41b0 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 bf0da0cbe3..5e9ecbdba9 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 6b5d9403f0..53b2887986 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_states.cpp b/src/p_states.cpp index cfe455716a..3ba0ae14f3 100644 --- a/src/p_states.cpp +++ b/src/p_states.cpp @@ -39,6 +39,7 @@ #include "cmdlib.h" #include "i_system.h" #include "c_dispatch.h" +#include "v_text.h" #include "thingdef/thingdef.h" // Each state is owned by an actor. Actors can own any number of @@ -699,6 +700,10 @@ FState *FStateDefinitions::ResolveGotoLabel (AActor *actor, const PClass *mytype { I_Error ("Attempt to get invalid state %s from actor %s.", label, type->TypeName.GetChars()); } + else + { + Printf (TEXTCOLOR_RED "Attempt to get invalid state %s from actor %s.\n", label, type->TypeName.GetChars()); + } delete[] namestart; // free the allocated string buffer return state; } diff --git a/src/p_tick.cpp b/src/p_tick.cpp index 07e153ffa4..44fe881100 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_trace.cpp b/src/p_trace.cpp index d1b9f63ac8..26852269ab 100644 --- a/src/p_trace.cpp +++ b/src/p_trace.cpp @@ -366,7 +366,7 @@ bool FTraceInfo::TraceTraverse (int ptflags) Results->HitTexture = CurSector->GetTexture(sector_t::ceiling); } else if (entersector == NULL || - hitz <= bf || hitz >= bc || + hitz < bf || hitz > bc || in->d.line->flags & WallMask) { // hit the wall Results->HitType = TRACE_HitWall; diff --git a/src/p_user.cpp b/src/p_user.cpp index 53cd6789a9..045e619694 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); } @@ -1257,9 +1203,6 @@ void APlayerPawn::GiveDefaultInventory () { if (player == NULL) return; - // [GRB] Give inventory specified in DECORATE - player->health = GetDefault ()->health; - // HexenArmor must always be the first item in the inventory because // it provides player class based protection that should not affect // any other protection item. @@ -2175,7 +2118,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 +2950,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 +3043,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/s_sound.cpp b/src/s_sound.cpp index 035b71c74c..75bd33d471 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -1575,7 +1575,7 @@ void S_RelinkSound (AActor *from, AActor *to) { chan->Actor = to; } - else if (!(chan->ChanFlags & CHAN_LOOP)) + else if (!(chan->ChanFlags & CHAN_LOOP) && !(compatflags2 & COMPATF2_SOUNDCUTOFF)) { chan->Actor = NULL; chan->SourceType = SOURCE_Unattached; diff --git a/src/sound/music_midistream.cpp b/src/sound/music_midistream.cpp index 15f4caec80..5cb04ed0ee 100644 --- a/src/sound/music_midistream.cpp +++ b/src/sound/music_midistream.cpp @@ -38,6 +38,7 @@ #include "templates.h" #include "doomdef.h" #include "m_swap.h" +#include "doomerrors.h" // MACROS ------------------------------------------------------------------ @@ -277,7 +278,16 @@ MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype) const return new TimidityMIDIDevice; case MDEV_OPL: - return new OPLMIDIDevice; + try + { + return new OPLMIDIDevice; + } + catch (CRecoverableError &err) + { + // The creation of an OPL MIDI device can abort with an error if no GENMIDI lump can be found. + Printf("Unable to create OPL MIDI device: %s\nFalling back to FModEx playback", err.GetMessage()); + return new FMODMIDIDevice; + } case MDEV_TIMIDITY: return new TimidityPPMIDIDevice; diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index d17bbc75ff..6a1cf6e674 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -2912,6 +2912,8 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Respawn) self->flags3 = (defs->flags3 & ~(MF3_NOSIGHTCHECK | MF3_HUNTPLAYERS)) | (self->flags3 & (MF3_NOSIGHTCHECK | MF3_HUNTPLAYERS)); self->flags4 = (defs->flags4 & ~MF4_NOHATEPLAYERS) | (self->flags4 & MF4_NOHATEPLAYERS); self->flags5 = defs->flags5; + self->flags6 = defs->flags6; + self->flags7 = defs->flags7; self->SetState (self->SpawnState); self->renderflags &= ~RF_INVISIBLE; @@ -4348,12 +4350,6 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Warp) ACTION_PARAM_INT(flags, 5); ACTION_PARAM_STATE(success_state, 6); - fixed_t - - oldx, - oldy, - oldz; - AActor *reference = COPY_AAPTR(self, destination_selector); if (!reference) @@ -4362,6 +4358,10 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Warp) return; } + fixed_t oldx = self->x; + fixed_t oldy = self->y; + fixed_t oldz = self->z; + if (!(flags & WARPF_ABSOLUTEANGLE)) { angle += (flags & WARPF_USECALLERANGLE) ? self->angle : reference->angle; @@ -4371,20 +4371,16 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Warp) if (!(flags & WARPF_ABSOLUTEOFFSET)) { angle_t fineangle = angle >> ANGLETOFINESHIFT; - oldx = xofs; + fixed_t xofs1 = xofs; // (borrowed from A_SpawnItemEx, assumed workable) // in relative mode negative y values mean 'left' and positive ones mean 'right' // This is the inverse orientation of the absolute mode! - xofs = FixedMul(oldx, finecosine[fineangle]) + FixedMul(yofs, finesine[fineangle]); - yofs = FixedMul(oldx, finesine[fineangle]) - FixedMul(yofs, finecosine[fineangle]); + xofs = FixedMul(xofs1, finecosine[fineangle]) + FixedMul(yofs, finesine[fineangle]); + yofs = FixedMul(xofs1, finesine[fineangle]) - FixedMul(yofs, finecosine[fineangle]); } - oldx = self->x; - oldy = self->y; - oldz = self->z; - if (flags & WARPF_TOFLOOR) { // set correct xy @@ -4835,43 +4831,26 @@ enum DMSS DMSS_AFFECTARMOR = 2, DMSS_KILL = 4, DMSS_NOFACTOR = 8, + DMSS_FOILBUDDHA = 16, }; static void DoDamage(AActor *dmgtarget, AActor *self, int amount, FName DamageType, int flags) { - if ((amount > 0) || (flags & DMSS_KILL)) - { - if (!(dmgtarget->flags2 & MF2_INVULNERABLE) || (flags & DMSS_FOILINVUL)) - { - if (flags & DMSS_KILL) - { - P_DamageMobj(dmgtarget, self, self, dmgtarget->health, DamageType, DMG_NO_FACTOR | DMG_NO_ARMOR | DMG_FOILINVUL); - } - if (flags & DMSS_AFFECTARMOR) - { - if (flags & DMSS_NOFACTOR) - { - P_DamageMobj(dmgtarget, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_FACTOR); - } - else - { - P_DamageMobj(dmgtarget, self, self, amount, DamageType, DMG_FOILINVUL); - } - } - else - { - if (flags & DMSS_NOFACTOR) - { - P_DamageMobj(dmgtarget, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_ARMOR | DMG_NO_FACTOR); - } - //[MC] DMG_FOILINVUL is needed for making the damage occur on the actor. - else - { - P_DamageMobj(dmgtarget, self, self, amount, DamageType, DMG_FOILINVUL | DMG_NO_ARMOR); - } - } - } - } + int dmgFlags = 0; + if (flags & DMSS_FOILINVUL) + dmgFlags += DMG_FOILINVUL; + if (flags & DMSS_FOILBUDDHA) + dmgFlags += DMG_FOILBUDDHA; + if ((flags & DMSS_KILL) || (flags & DMSS_NOFACTOR)) //Kill implies NoFactor + dmgFlags += DMG_NO_FACTOR; + if (!(flags & DMSS_AFFECTARMOR) || (flags & DMSS_KILL)) //Kill overrides AffectArmor + dmgFlags += DMG_NO_ARMOR; + if (flags & DMSS_KILL) //Kill adds the value of the damage done to it. Allows for more controlled extreme death types. + amount += dmgtarget->health; + + if (amount > 0) + P_DamageMobj(dmgtarget, self, self, amount, DamageType, dmgFlags); //Should wind up passing them through just fine. + else if (amount < 0) { amount = -amount; @@ -4995,30 +4974,32 @@ enum KILS KILS_FOILINVUL = 1 << 0, KILS_KILLMISSILES = 1 << 1, KILS_NOMONSTERS = 1 << 2, + KILS_FOILBUDDHA = 1 << 3, }; static void DoKill(AActor *killtarget, AActor *self, FName damagetype, int flags) { + int dmgFlags = DMG_NO_ARMOR + DMG_NO_FACTOR; + + if (KILS_FOILINVUL) + dmgFlags += DMG_FOILINVUL; + if (KILS_FOILBUDDHA) + dmgFlags += DMG_FOILBUDDHA; + if ((killtarget->flags & MF_MISSILE) && (flags & KILS_KILLMISSILES)) { //[MC] Now that missiles can set masters, lets put in a check to properly destroy projectiles. BUT FIRST! New feature~! //Check to see if it's invulnerable. Disregarded if foilinvul is on, but never works on a missile with NODAMAGE //since that's the whole point of it. - if ((!(killtarget->flags2 & MF2_INVULNERABLE) || (flags & KILS_FOILINVUL)) && !(killtarget->flags5 & MF5_NODAMAGE)) + if ((!(killtarget->flags2 & MF2_INVULNERABLE) || (flags & KILS_FOILINVUL)) && + (!(killtarget->flags2 & MF7_BUDDHA) || (flags & KILS_FOILBUDDHA)) && !(killtarget->flags5 & MF5_NODAMAGE)) { P_ExplodeMissile(killtarget, NULL, NULL); } } if (!(flags & KILS_NOMONSTERS)) { - if (flags & KILS_FOILINVUL) - { - P_DamageMobj(killtarget, self, self, killtarget->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR | DMG_FOILINVUL); - } - else - { - P_DamageMobj(killtarget, self, self, killtarget->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR); - } + P_DamageMobj(killtarget, self, self, killtarget->health, damagetype, dmgFlags); } } diff --git a/src/thingdef/thingdef_data.cpp b/src/thingdef/thingdef_data.cpp index 013415cc78..90c3313a69 100644 --- a/src/thingdef/thingdef_data.cpp +++ b/src/thingdef/thingdef_data.cpp @@ -241,6 +241,9 @@ static FFlagDef ActorFlags[]= DEFINE_FLAG(MF7, NOTELESTOMP, AActor, flags7), DEFINE_FLAG(MF7, ALWAYSTELEFRAG, AActor, flags7), DEFINE_FLAG(MF7, WEAPONSPAWN, AActor, flags7), + DEFINE_FLAG(MF7, HARMFRIENDS, AActor, flags7), + DEFINE_FLAG(MF7, BUDDHA, AActor, flags7), + DEFINE_FLAG(MF7, FOILBUDDHA, AActor, flags7), // Effect flags DEFINE_FLAG(FX, VISIBILITYPULSE, AActor, effects), @@ -311,6 +314,7 @@ static FFlagDef InventoryFlags[] = DEFINE_FLAG(IF, NEVERRESPAWN, AInventory, ItemFlags), DEFINE_FLAG(IF, NOSCREENFLASH, AInventory, ItemFlags), DEFINE_FLAG(IF, TOSSED, AInventory, ItemFlags), + DEFINE_FLAG(IF, ALWAYSRESPAWN, AInventory, ItemFlags), DEFINE_DEPRECATED_FLAG(PICKUPFLASH), DEFINE_DEPRECATED_FLAG(INTERHUBSTRIP),}; diff --git a/src/version.h b/src/version.h index a7226d8bb7..d9597d2f40 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 2ab93f0905..02492f272c 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; diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index da289dc50e..f806f8224f 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -375,12 +375,14 @@ enum const int KILS_FOILINVUL = 1; const int KILS_KILLMISSILES = 2; const int KILS_NOMONSTERS = 4; +const int KILS_FOILBUDDHA = 8; // Flags for A_Damage (Master/Target/Tracer/Children/Siblings/Self) series const int DMSS_FOILINVUL = 1; const int DMSS_AFFECTARMOR = 2; const int DMSS_KILL = 4; const int DMSS_NOFACTOR = 8; +const int DMSS_FOILBUDDHA = 16; // Flags for A_AlertMonsters const int AMF_TARGETEMITTER = 1; diff --git a/wadsrc/static/actors/heretic/hereticweaps.txt b/wadsrc/static/actors/heretic/hereticweaps.txt index 972fba5fb8..ca85437e0a 100644 --- a/wadsrc/static/actors/heretic/hereticweaps.txt +++ b/wadsrc/static/actors/heretic/hereticweaps.txt @@ -110,7 +110,7 @@ ACTOR StaffPuff2 // Gold wand ---------------------------------------------------------------- -ACTOR GoldWand : HereticWeapon +ACTOR GoldWand : HereticWeapon 9042 { Game Heretic +BLOODSPLATTER @@ -120,6 +120,7 @@ ACTOR GoldWand : HereticWeapon Weapon.AmmoType "GoldWandAmmo" Weapon.SisterWeapon "GoldWandPowered" Weapon.YAdjust 5 + Inventory.PickupMessage "$TXT_WPNGOLDWAND" Obituary "$OB_MPGOLDWAND" Tag "$TAG_GOLDWAND" @@ -127,6 +128,9 @@ ACTOR GoldWand : HereticWeapon States { + Spawn: + GWAN A -1 + Stop Ready: GWND A 1 A_WeaponReady Loop diff --git a/wadsrc/static/actors/shared/inventory.txt b/wadsrc/static/actors/shared/inventory.txt index e293282b8b..e9f57a607d 100644 --- a/wadsrc/static/actors/shared/inventory.txt +++ b/wadsrc/static/actors/shared/inventory.txt @@ -271,6 +271,11 @@ ACTOR PowerFrightener : Powerup native Powerup.Duration -60 } +ACTOR PowerBuddha : Powerup native +{ + Powerup.Duration -60 +} + ACTOR PowerScanner : Powerup native { Powerup.Duration -80 diff --git a/wadsrc/static/actors/strife/peasants.txt b/wadsrc/static/actors/strife/peasants.txt index e7bbdadcf4..af12b75df1 100644 --- a/wadsrc/static/actors/strife/peasants.txt +++ b/wadsrc/static/actors/strife/peasants.txt @@ -9,7 +9,7 @@ ACTOR Peasant : StrifeHumanoid Radius 20 Height 56 Monster - +FRIENDLY + +NEVERTARGET -COUNTKILL +NOSPLASHALERT +FLOORCLIP diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index a92e22dfec..a01a03559d 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -292,6 +292,8 @@ STSTR_MUS = "Music Change"; STSTR_NOMUS = "IMPOSSIBLE SELECTION"; STSTR_DQDON = "Degreelessness Mode ON"; STSTR_DQDOFF = "Degreelessness Mode OFF"; +STSTR_DQD2ON = "Ultimate Degreelessness Mode ON"; +STSTR_DQD2OFF = "Ultimate Degreelessness Mode OFF"; STSTR_KFAADDED = "Very Happy Ammo Added"; STSTR_FAADDED = "Ammo (no keys) Added"; STSTR_NCON = "No Clipping Mode ON"; @@ -303,6 +305,8 @@ STSTR_CHOPPERS = "... doesn't suck - GM"; STSTR_CLEV = "Changing Level...\n"; TXT_BUDDHAON = "Buddha mode ON"; TXT_BUDDHAOFF = "Buddha mode OFF"; +TXT_BUDDHA2ON = "Ultimate Buddha Mode ON"; +TXT_BUDDHA2OFF = "Ultimate Buddha Mode OFF"; TXT_DEFAULTPICKUPMSG = "You got a pickup"; E1TEXT = @@ -1242,6 +1246,7 @@ TXT_AMMOPHOENIXROD2 = "INFERNO ORB"; // Weapons +TXT_WPNGOLDWAND = "GOLD WAND"; TXT_WPNMACE = "FIREMACE"; TXT_WPNCROSSBOW = "ETHEREAL CROSSBOW"; TXT_WPNBLASTER = "DRAGON CLAW"; diff --git a/wadsrc/static/mapinfo/common.txt b/wadsrc/static/mapinfo/common.txt index 8e7886987f..fc6379d01c 100644 --- a/wadsrc/static/mapinfo/common.txt +++ b/wadsrc/static/mapinfo/common.txt @@ -1,3 +1,8 @@ +Gameinfo +{ + CheatKey = "maparrows/key.txt" + EasyKey = "maparrows/ravenkey.txt" +} Intermission Inter_Titlescreen { diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 13565da7f2..3d5a3ea3cf 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -1300,6 +1300,7 @@ OptionMenu "CompatibilityOptions" Option "Ignore Y offsets on masked midtextures", "compat_MASKEDMIDTEX", "YesNo" Option "Cannot travel straight NSEW", "compat_badangles", "YesNo" Option "Use Doom's floor motion behavior", "compat_floormove", "YesNo" + Option "Sounds stop when actor vanishes", "compat_soundcutoff", "YesNo" Class "CompatibilityMenu" } diff --git a/wadsrc/static/sprites/GWANA0.png b/wadsrc/static/sprites/GWANA0.png new file mode 100644 index 0000000000..f887f04c65 Binary files /dev/null and b/wadsrc/static/sprites/GWANA0.png differ diff --git a/wadsrc/static/sprites/pista0.png b/wadsrc/static/sprites/pista0.png index b3d9cd09a5..9e3a4a0524 100644 Binary files a/wadsrc/static/sprites/pista0.png and b/wadsrc/static/sprites/pista0.png differ