diff --git a/Source/monster.cpp b/Source/monster.cpp index ecf6bf42e..3e66523d4 100644 --- a/Source/monster.cpp +++ b/Source/monster.cpp @@ -442,10 +442,11 @@ void ClrAllMonsters() monster.position.old = { 0, 0 }; monster.direction = static_cast(GenerateRnd(8)); monster.animInfo = {}; - monster.flags = 0; + monster.flags = MFLAG_NO_ENEMY; monster.isInvalid = false; - monster.enemy = GenerateRnd(gbActivePlayers); - monster.enemyPosition = Players[monster.enemy].position.future; + monster.enemy = 0; + monster.enemyPosition = {}; + DiscardRandomValues(1); } } @@ -581,13 +582,21 @@ tl::expected LoadDiabMonsts() void DeleteMonster(size_t activeIndex) { - const Monster &monster = Monsters[ActiveMonsters[activeIndex]]; + const unsigned monsterId = ActiveMonsters[activeIndex]; + const Monster &monster = Monsters[monsterId]; if ((monster.flags & MFLAG_BERSERK) != 0) { AddUnLight(monster.lightId); } ActiveMonsterCount--; std::swap(ActiveMonsters[activeIndex], ActiveMonsters[ActiveMonsterCount]); // This ensures alive monsters are before ActiveMonsterCount in the array and any deleted monster after + + for (size_t i = 0; i < ActiveMonsterCount; i++) { + Monster &activeMonster = Monsters[ActiveMonsters[i]]; + if ((activeMonster.flags & MFLAG_TARGETS_MONSTER) != 0 && activeMonster.enemy == monsterId) { + activeMonster.flags |= MFLAG_NO_ENEMY; + } + } } void NewMonsterAnim(Monster &monster, MonsterGraphic graphic, Direction md, AnimationDistributionFlags flags = AnimationDistributionFlags::None, int8_t numSkippedFrames = 0, int8_t distributeFramesBeforeFrame = 0) @@ -1212,10 +1221,12 @@ void MonsterAttackPlayer(Monster &monster, Player &player, int hit, int minDam, void MonsterAttackEnemy(Monster &monster, int hit, int minDam, int maxDam) { - if ((monster.flags & MFLAG_TARGETS_MONSTER) != 0) - MonsterAttackMonster(monster, Monsters[monster.enemy], hit, minDam, maxDam); - else - MonsterAttackPlayer(monster, Players[monster.enemy], hit, minDam, maxDam); + if ((monster.flags & MFLAG_NO_ENEMY) == 0) { + if ((monster.flags & MFLAG_TARGETS_MONSTER) != 0) + MonsterAttackMonster(monster, Monsters[monster.enemy], hit, minDam, maxDam); + else + MonsterAttackPlayer(monster, Players[monster.enemy], hit, minDam, maxDam); + } } bool MonsterAttack(Monster &monster) @@ -4056,6 +4067,20 @@ void DeleteMonsterList() } } +void RemoveEnemyReferences(const Player &player) +{ + if (&player == MyPlayer || !player.isOnActiveLevel()) + return; + + const size_t playerId = player.getId(); + for (size_t i = 0; i < ActiveMonsterCount; i++) { + Monster &activeMonster = Monsters[ActiveMonsters[i]]; + if ((activeMonster.flags & MFLAG_TARGETS_MONSTER) == 0 && activeMonster.enemy == playerId) { + activeMonster.flags |= MFLAG_NO_ENEMY; + } + } +} + void ProcessMonsters() { DeleteMonsterList(); @@ -4096,20 +4121,21 @@ void ProcessMonsters() UpdateEnemy(monster); } - if ((monster.flags & MFLAG_TARGETS_MONSTER) != 0) { - assert(monster.enemy >= 0 && monster.enemy < MaxMonsters); - // BUGFIX: enemy target may be dead at time of access, thus reading garbage data from `Monsters[monster.enemy].position.future`. - monster.position.last = Monsters[monster.enemy].position.future; - monster.enemyPosition = monster.position.last; - } else { - assert(monster.enemy >= 0 && monster.enemy < MAX_PLRS); - Player &player = Players[monster.enemy]; - monster.enemyPosition = player.position.future; - if (IsTileVisible(monster.position.tile)) { - monster.activeForTicks = UINT8_MAX; - monster.position.last = player.position.future; - } else if (monster.activeForTicks != 0 && monster.type().type != MT_DIABLO) { - monster.activeForTicks--; + if ((monster.flags & MFLAG_NO_ENEMY) == 0) { + if ((monster.flags & MFLAG_TARGETS_MONSTER) != 0) { + assert(monster.enemy >= 0 && monster.enemy < MaxMonsters); + monster.position.last = Monsters[monster.enemy].position.future; + monster.enemyPosition = monster.position.last; + } else { + assert(monster.enemy >= 0 && monster.enemy < MAX_PLRS); + Player &player = Players[monster.enemy]; + monster.enemyPosition = player.position.future; + if (IsTileVisible(monster.position.tile)) { + monster.activeForTicks = UINT8_MAX; + monster.position.last = player.position.future; + } else if (monster.activeForTicks != 0 && monster.type().type != MT_DIABLO) { + monster.activeForTicks--; + } } } while (true) { diff --git a/Source/monster.h b/Source/monster.h index c789f78d7..aa0dba879 100644 --- a/Source/monster.h +++ b/Source/monster.h @@ -534,6 +534,7 @@ void PrepDoEnding(); bool Walk(Monster &monster, Direction md); void GolumAi(Monster &monster); void DeleteMonsterList(); +void RemoveEnemyReferences(const Player &player); void ProcessMonsters(); void FreeMonsters(); bool DirOK(const Monster &monster, Direction mdir); diff --git a/Source/msg.cpp b/Source/msg.cpp index 017f97ce8..4075ce46f 100644 --- a/Source/msg.cpp +++ b/Source/msg.cpp @@ -34,6 +34,7 @@ #include "levels/trigs.h" #include "lighting.h" #include "missiles.h" +#include "monster.h" #include "monsters/validation.hpp" #include "nthread.h" #include "objects.h" @@ -2280,6 +2281,11 @@ size_t OnPlayerJoinLevel(const TCmdLocParam2 &message, Player &player) } if (player.plractive && &player != MyPlayer) { + if (player.isOnActiveLevel()) { + RemoveEnemyReferences(player); + RemovePlrMissiles(player); + FixPlrWalkTags(player); + } player.position.tile = position; SetPlayerOld(player); if (isSetLevel) diff --git a/Source/multi.cpp b/Source/multi.cpp index 7a5255dd0..c2a7ce06d 100644 --- a/Source/multi.cpp +++ b/Source/multi.cpp @@ -21,6 +21,7 @@ #include "engine/random.hpp" #include "engine/world_tile.hpp" #include "menu.h" +#include "monster.h" #include "msg.h" #include "nthread.h" #include "options.h" @@ -275,6 +276,7 @@ void PlayerLeftMsg(Player &player, bool left) RemovePortalMissile(player); DeactivatePortal(player); delta_close_portal(player); + RemoveEnemyReferences(player); RemovePlrMissiles(player); if (left) { std::string_view pszFmt = _("Player '{:s}' just left the game"); diff --git a/Source/player.cpp b/Source/player.cpp index ab9e2f965..e5cd1d61c 100644 --- a/Source/player.cpp +++ b/Source/player.cpp @@ -36,6 +36,7 @@ #include "loadsave.h" #include "minitext.h" #include "missiles.h" +#include "monster.h" #include "nthread.h" #include "objects.h" #include "options.h" @@ -358,6 +359,7 @@ void InitLevelChange(Player &player) { Player &myPlayer = *MyPlayer; + RemoveEnemyReferences(player); RemovePlrMissiles(player); player.pManaShield = false; player.wReflections = 0;