diff --git a/src/creatures/creature.cpp b/src/creatures/creature.cpp index 22763139d2d..46804d6b51b 100644 --- a/src/creatures/creature.cpp +++ b/src/creatures/creature.cpp @@ -1022,9 +1022,7 @@ void Creature::goToFollowCreature() { } } - if (followCreature->getPlayer() && followCreature->getPlayer()->isDisconnected()) { - hasFollowPath = false; - } else if (listDir.empty()) { + if (listDir.empty()) { hasFollowPath = getPathTo(followCreature->getPosition(), listDir, fpp); } diff --git a/src/creatures/monsters/monster.cpp b/src/creatures/monsters/monster.cpp index d29845864bd..b2660933a27 100644 --- a/src/creatures/monsters/monster.cpp +++ b/src/creatures/monsters/monster.cpp @@ -225,13 +225,9 @@ void Monster::onCreatureMove(const std::shared_ptr &creature, const st } updateIdleStatus(); - if (!m_attackedCreature.expired()) { - return; - } if (!isSummon()) { - auto followCreature = getFollowCreature(); - if (followCreature) { + if (const auto &followCreature = getFollowCreature()) { const Position &followPosition = followCreature->getPosition(); const Position &pos = getPosition(); @@ -239,12 +235,11 @@ void Monster::onCreatureMove(const std::shared_ptr &creature, const st int32_t offset_y = Position::getDistanceY(followPosition, pos); if ((offset_x > 1 || offset_y > 1) && mType->info.changeTargetChance > 0) { Direction dir = getDirectionTo(pos, followPosition); - const Position &checkPosition = getNextPosition(dir, pos); + const auto &checkPosition = getNextPosition(dir, pos); - auto nextTile = g_game().map.getTile(checkPosition); - if (nextTile) { - auto topCreature = nextTile->getTopCreature(); - if (topCreature && followCreature != topCreature && isOpponent(topCreature)) { + if (const auto &nextTile = g_game().map.getTile(checkPosition)) { + const auto &topCreature = nextTile->getTopCreature(); + if (followCreature != topCreature && isOpponent(topCreature)) { selectTarget(topCreature); } } @@ -289,77 +284,68 @@ void Monster::onCreatureSay(std::shared_ptr creature, SpeakClasses typ } } -void Monster::addFriend(std::shared_ptr creature) { +void Monster::addFriend(const std::shared_ptr &creature) { assert(creature.get() != this); friendList.try_emplace(creature->getID(), creature); } -void Monster::removeFriend(std::shared_ptr creature) { - friendList.erase(creature->getID()); +void Monster::removeFriend(const std::shared_ptr &creature) { + std::erase_if(friendList, [id = creature->getID()](const auto &it) { + const auto &target = it.second.lock(); + return !target || target->getID() == id; + }); } -void Monster::addTarget(std::shared_ptr creature, bool pushFront /* = false*/) { +bool Monster::addTarget(const std::shared_ptr &creature, bool pushFront /* = false*/) { assert(creature.get() != this); - auto cid = creature->getID(); - targetListMap.try_emplace(cid, creature); - if (std::find(targetIDList.begin(), targetIDList.end(), cid) == targetIDList.end()) { - if (pushFront) { - targetIDList.push_front(cid); - } else { - targetIDList.push_back(cid); - } - if (!getMaster() && getFaction() != FACTION_DEFAULT && creature->getPlayer()) { - totalPlayersOnScreen++; - } + + const auto &it = getTargetIterator(creature); + if (it != targetList.end()) { + return false; + } + + if (pushFront) { + targetList.emplace_front(creature); + } else { + targetList.emplace_back(creature); + } + + if (!getMaster() && getFaction() != FACTION_DEFAULT && creature->getPlayer()) { + totalPlayersOnScreen++; } + + return true; } -void Monster::removeTarget(std::shared_ptr creature) { +bool Monster::removeTarget(const std::shared_ptr &creature) { if (!creature) { - return; + return false; } - auto it = std::find(targetIDList.begin(), targetIDList.end(), creature->getID()); - if (it != targetIDList.end()) { - if (!getMaster() && getFaction() != FACTION_DEFAULT && creature->getPlayer()) { - totalPlayersOnScreen--; - } - - targetIDList.erase(it); - targetListMap.erase(creature->getID()); + const auto &it = getTargetIterator(creature); + if (it == targetList.end()) { + return false; } -} -void Monster::updateTargetList() { - auto friendIterator = friendList.begin(); - while (friendIterator != friendList.end()) { - auto creature = (*friendIterator).second.lock(); - if (!creature || creature->getHealth() <= 0 || !canSee(creature->getPosition())) { - friendIterator = friendList.erase(friendIterator); - } else { - ++friendIterator; - } + if (!getMaster() && getFaction() != FACTION_DEFAULT && creature->getPlayer()) { + totalPlayersOnScreen--; } - auto targetIterator = targetIDList.begin(); - while (targetIterator != targetIDList.end()) { - const uint32_t targetId = *targetIterator; + targetList.erase(it); + + return true; +} - auto itTLM = targetListMap.find(targetId); - const bool existTarget = itTLM != targetListMap.end(); +void Monster::updateTargetList() { + std::erase_if(friendList, [this](const auto &it) { + const auto &target = it.second.lock(); + return !target || target->getHealth() <= 0 || !canSee(target->getPosition()); + }); - if (existTarget) { - const auto &creature = itTLM->second.lock(); - if (!creature || creature->getHealth() <= 0 || !canSee(creature->getPosition())) { - targetIterator = targetIDList.erase(targetIterator); - targetListMap.erase(itTLM); - } else { - ++targetIterator; - } - } else { - targetIterator = targetIDList.erase(targetIterator); - } - } + std::erase_if(targetList, [this](const std::weak_ptr &ref) { + const auto &target = ref.lock(); + return !target || target->getHealth() <= 0 || !canSee(target->getPosition()); + }); for (const auto &spectator : Spectators().find(position, true)) { if (spectator.get() != this && canSee(spectator->getPosition())) { @@ -369,8 +355,7 @@ void Monster::updateTargetList() { } void Monster::clearTargetList() { - targetIDList.clear(); - targetListMap.clear(); + targetList.clear(); } void Monster::clearFriendList() { @@ -393,16 +378,13 @@ void Monster::onCreatureEnter(std::shared_ptr creature) { onCreatureFound(creature, true); } -bool Monster::isFriend(std::shared_ptr creature) const { +bool Monster::isFriend(const std::shared_ptr &creature) const { if (isSummon() && getMaster()->getPlayer()) { - std::shared_ptr masterPlayer = getMaster()->getPlayer(); - std::shared_ptr tmpPlayer = nullptr; - - if (creature->getPlayer()) { - tmpPlayer = creature->getPlayer(); - } else { - std::shared_ptr creatureMaster = creature->getMaster(); + const auto &masterPlayer = getMaster()->getPlayer(); + auto tmpPlayer = creature->getPlayer(); + if (!tmpPlayer) { + const auto &creatureMaster = creature->getMaster(); if (creatureMaster && creatureMaster->getPlayer()) { tmpPlayer = creatureMaster->getPlayer(); } @@ -411,27 +393,30 @@ bool Monster::isFriend(std::shared_ptr creature) const { if (tmpPlayer && (tmpPlayer == getMaster() || masterPlayer->isPartner(tmpPlayer))) { return true; } - } else if (creature->getMonster() && !creature->isSummon()) { - return true; } - return false; + return creature->getMonster() && !creature->isSummon(); } -bool Monster::isOpponent(std::shared_ptr creature) const { +bool Monster::isOpponent(const std::shared_ptr &creature) const { + if (!creature) { + return false; + } + if (isSummon() && getMaster()->getPlayer()) { - if (creature != getMaster()) { - return true; - } - } else if (creature->getPlayer() && creature->getPlayer()->hasFlag(PlayerFlags_t::IgnoredByMonsters)) { + return creature != getMaster(); + } + + if (creature->getPlayer() && creature->getPlayer()->hasFlag(PlayerFlags_t::IgnoredByMonsters)) { return false; - } else { - if (getFaction() != FACTION_DEFAULT) { - return isEnemyFaction(creature->getFaction()) || creature->getFaction() == FACTION_PLAYER; - } - if ((creature->getPlayer()) || (creature->getMaster() && creature->getMaster()->getPlayer())) { - return true; - } + } + + if (getFaction() != FACTION_DEFAULT) { + return isEnemyFaction(creature->getFaction()) || creature->getFaction() == FACTION_PLAYER; + } + + if ((creature->getPlayer()) || (creature->getMaster() && creature->getMaster()->getPlayer())) { + return true; } return false; @@ -446,7 +431,7 @@ void Monster::onCreatureLeave(std::shared_ptr creature) { // update targetList if (isOpponent(creature)) { removeTarget(creature); - if (targetIDList.empty()) { + if (targetList.empty()) { updateIdleStatus(); } } @@ -473,11 +458,11 @@ bool Monster::searchTarget(TargetSearchType_t searchType /*= TARGETSEARCH_DEFAUL } } - std::list> resultList; + std::vector> resultList; const Position &myPos = getPosition(); - for (auto cid : targetIDList) { - auto creature = targetListMap[cid].lock(); + for (const auto &cref : targetList) { + const auto &creature = cref.lock(); if (creature && isTarget(creature)) { if ((static_self_cast()->targetDistance == 1) || canUseAttack(myPos, creature)) { resultList.push_back(creature); @@ -513,7 +498,7 @@ bool Monster::searchTarget(TargetSearchType_t searchType /*= TARGETSEARCH_DEFAUL } } else { int32_t minRange = std::numeric_limits::max(); - for (auto creature : getTargetList()) { + for (const auto &creature : getTargetList()) { if (!isTarget(creature)) { continue; } @@ -588,42 +573,14 @@ bool Monster::searchTarget(TargetSearchType_t searchType /*= TARGETSEARCH_DEFAUL } // lets just pick the first target in the list - for (auto target : getTargetList()) { - if (selectTarget(target)) { - return true; - } - } - return false; + return std::ranges::any_of(getTargetList(), [this](const std::shared_ptr &creature) { + return selectTarget(creature); + }); } void Monster::onFollowCreatureComplete(const std::shared_ptr &creature) { - if (!creature) { - return; - } - - const auto &it = std::find(targetIDList.begin(), targetIDList.end(), creature->getID()); - if (it != targetIDList.end()) { - if (const auto &target = targetListMap[*it].lock()) { - targetIDList.erase(it); - - if (hasFollowPath) { - targetIDList.push_front(target->getID()); - } else if (!isSummon()) { - targetIDList.push_back(target->getID()); - } else { - targetListMap.erase(target->getID()); - } - } - } - - // Change the target if necessary - if (!hasFollowPath && !isSummon() && !targetIDList.empty() && targetIDList.front() != creature->getID()) { - const auto &itMap = targetListMap.find(targetIDList.front()); - if (itMap != targetListMap.end()) { - if (const auto &target = itMap->second.lock()) { - selectTarget(target); - } - } + if (removeTarget(creature) && (hasFollowPath || !isSummon())) { + addTarget(creature, hasFollowPath); } } @@ -663,20 +620,27 @@ bool Monster::isTarget(std::shared_ptr creature) { if (creature->getPosition().z != getPosition().z) { return false; } - Faction_t targetFaction = creature->getFaction(); - if (getFaction() != FACTION_DEFAULT && !isSummon()) { - return isEnemyFaction(targetFaction); + + if (!isSummon()) { + if (creature->getPlayer() && creature->getPlayer()->isDisconnected()) { + return false; + } + + if (getFaction() != FACTION_DEFAULT) { + return isEnemyFaction(creature->getFaction()); + } } + return true; } -bool Monster::selectTarget(std::shared_ptr creature) { +bool Monster::selectTarget(const std::shared_ptr &creature) { if (!isTarget(creature)) { return false; } - auto it = std::find(targetIDList.begin(), targetIDList.end(), creature->getID()); - if (it == targetIDList.end()) { + const auto &it = getTargetIterator(creature); + if (it == targetList.end()) { // Target not found in our target list. return false; } @@ -708,13 +672,13 @@ void Monster::setIdle(bool idle) { void Monster::updateIdleStatus() { bool idle = false; - auto master = getMaster(); - if (conditions.empty()) { - if (!isSummon() && targetIDList.empty()) { - idle = true; - } else if (master && (!isSummon() && totalPlayersOnScreen == 0 || isSummon() && master->getMonster() && master->getMonster()->totalPlayersOnScreen == 0) && getFaction() != FACTION_DEFAULT) { + if (!isSummon() && targetList.empty()) { idle = true; + } else if (const auto &master = getMaster()) { + if ((!isSummon() && totalPlayersOnScreen == 0 || isSummon() && master->getMonster() && master->getMonster()->totalPlayersOnScreen == 0) && getFaction() != FACTION_DEFAULT) { + idle = true; + } } } @@ -781,45 +745,47 @@ void Monster::onThink(uint32_t interval) { if (!isInSpawnRange(position)) { g_game().internalTeleport(static_self_cast(), masterPos); setIdle(true); - } else { - updateIdleStatus(); + return; + } - if (!isIdle) { - addEventWalk(); - - auto attackedCreature = getAttackedCreature(); - auto followCreature = getFollowCreature(); - if (isSummon()) { - if (!attackedCreature) { - if (getMaster() && getMaster()->getAttackedCreature()) { - // This happens if the monster is summoned during combat - selectTarget(getMaster()->getAttackedCreature()); - } else if (getMaster() != followCreature) { - // Our master has not ordered us to attack anything, lets follow him around instead. - setFollowCreature(getMaster()); - } - } else if (attackedCreature.get() == this) { - setFollowCreature(nullptr); - } else if (followCreature != attackedCreature) { - // This happens just after a master orders an attack, so lets follow it aswell. - setFollowCreature(attackedCreature); - } - } else if (!attackedCreature && !targetIDList.empty()) { - if (!followCreature || !hasFollowPath) { - searchTarget(TARGETSEARCH_NEAREST); - } else if (isFleeing()) { - if (attackedCreature && !canUseAttack(getPosition(), attackedCreature)) { - searchTarget(TARGETSEARCH_DEFAULT); - } - } - } + updateIdleStatus(); - onThinkTarget(interval); - onThinkYell(interval); - onThinkDefense(interval); - onThinkSound(interval); - } + if (isIdle) { + return; } + + addEventWalk(); + + const auto &attackedCreature = getAttackedCreature(); + const auto &followCreature = getFollowCreature(); + if (isSummon()) { + if (attackedCreature.get() == this) { + setFollowCreature(nullptr); + } else if (attackedCreature && followCreature != attackedCreature) { + // This happens just after a master orders an attack, so lets follow it aswell. + setFollowCreature(attackedCreature); + } else if (getMaster() && getMaster()->getAttackedCreature()) { + // This happens if the monster is summoned during combat + selectTarget(getMaster()->getAttackedCreature()); + } else if (getMaster() != followCreature) { + // Our master has not ordered us to attack anything, lets follow him around instead. + setFollowCreature(getMaster()); + } + } else if (!targetList.empty()) { + const bool attackedCreatureIsDisconnected = attackedCreature && attackedCreature->getPlayer() && attackedCreature->getPlayer()->isDisconnected(); + if (!attackedCreature || attackedCreatureIsDisconnected) { + if (!followCreature || !hasFollowPath || attackedCreatureIsDisconnected) { + searchTarget(TARGETSEARCH_NEAREST); + } else if (attackedCreature && isFleeing() && !canUseAttack(getPosition(), attackedCreature)) { + searchTarget(TARGETSEARCH_DEFAULT); + } + } + } + + onThinkTarget(interval); + onThinkYell(interval); + onThinkDefense(interval); + onThinkSound(interval); } void Monster::doAttacking(uint32_t interval) { diff --git a/src/creatures/monsters/monster.hpp b/src/creatures/monsters/monster.hpp index 29182fe6865..2422ae7679b 100644 --- a/src/creatures/monsters/monster.hpp +++ b/src/creatures/monsters/monster.hpp @@ -17,11 +17,6 @@ class Creature; class Game; class Spawn; -using CreatureList = std::list>; - -using CreatureWeakHashMap = phmap::flat_hash_map>; -using CreatureIDList = std::list; - class Monster final : public Creature { public: static std::shared_ptr createMonster(const std::string &name); @@ -187,34 +182,37 @@ class Monster final : public Creature { } bool searchTarget(TargetSearchType_t searchType = TARGETSEARCH_DEFAULT); - bool selectTarget(std::shared_ptr creature); - - CreatureList getTargetList() { - std::list> list; - for (auto it = targetIDList.begin(); it != targetIDList.end();) { - auto cid = *it; - if (auto targetCreature = targetListMap[cid].lock()) { - list.push_back(targetCreature); - ++it; - } else { - it = targetIDList.erase(it); - targetListMap.erase(cid); + bool selectTarget(const std::shared_ptr &creature); + + auto getTargetList() { + CreatureVector list; + list.reserve(targetList.size()); + + std::erase_if(targetList, [&list](const std::weak_ptr &ref) { + if (const auto &creature = ref.lock()) { + list.emplace_back(creature); + return false; } - } + + return true; + }); + return list; } - std::vector> getFriendList() { - std::vector> list; + auto getFriendList() { + CreatureVector list; + list.reserve(friendList.size()); - for (auto it = friendList.begin(); it != friendList.end();) { - if (auto friendCreature = it->second.lock()) { - list.emplace_back(friendCreature); - ++it; - } else { - it = friendList.erase(it); + std::erase_if(friendList, [&list](const auto &it) { + if (const auto &creature = it.second.lock()) { + list.emplace_back(creature); + return false; } - } + + return true; + }); + return list; } @@ -340,9 +338,15 @@ class Monster final : public Creature { } private: - CreatureWeakHashMap friendList; - CreatureIDList targetIDList; - CreatureWeakHashMap targetListMap; + auto getTargetIterator(const std::shared_ptr &creature) { + return std::ranges::find_if(targetList.begin(), targetList.end(), [id = creature->getID()](const std::weak_ptr &ref) { + const auto &target = ref.lock(); + return target && target->getID() == id; + }); + } + + std::unordered_map> friendList; + std::deque> targetList; time_t timeToChangeFiendish = 0; @@ -391,10 +395,10 @@ class Monster final : public Creature { void updateLookDirection(); - void addFriend(std::shared_ptr creature); - void removeFriend(std::shared_ptr creature); - void addTarget(std::shared_ptr creature, bool pushFront = false); - void removeTarget(std::shared_ptr creature); + void addFriend(const std::shared_ptr &creature); + void removeFriend(const std::shared_ptr &creature); + bool addTarget(const std::shared_ptr &creature, bool pushFront = false); + bool removeTarget(const std::shared_ptr &creature); void death(std::shared_ptr lastHitCreature) override; std::shared_ptr getCorpse(std::shared_ptr lastHitCreature, std::shared_ptr mostDamageCreature) override; @@ -425,8 +429,8 @@ class Monster final : public Creature { void onThinkDefense(uint32_t interval); void onThinkSound(uint32_t interval); - bool isFriend(std::shared_ptr creature) const; - bool isOpponent(std::shared_ptr creature) const; + bool isFriend(const std::shared_ptr &creature) const; + bool isOpponent(const std::shared_ptr &creature) const; uint64_t getLostExperience() const override { return skillLoss ? mType->info.experience : 0;