/* * The ManaPlus Client * Copyright (C) 2004-2009 The Mana World Development Team * Copyright (C) 2009-2010 The Mana Developers * Copyright (C) 2011-2015 The ManaPlus Developers * * This file is part of The ManaPlus Client. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "actormanager.h" #include "configuration.h" #include "settings.h" #include "being/playerinfo.h" #include "being/playerrelations.h" #include "gui/viewport.h" #include "gui/widgets/tabs/chat/chattab.h" #include "gui/windows/chatwindow.h" #include "gui/windows/equipmentwindow.h" #include "gui/windows/socialwindow.h" #include "gui/windows/questswindow.h" #include "input/inputmanager.h" #include "utils/checkutils.h" #include "utils/gettext.h" #include "net/beinghandler.h" #include "net/charserverhandler.h" #include "net/packetlimiter.h" #include "net/playerhandler.h" #include "net/serverfeatures.h" #include "resources/chatobject.h" #include "resources/iteminfo.h" #include "resources/db/itemdb.h" #include <algorithm> #include "debug.h" #define for_actors for (ActorSpritesConstIterator it = mActors.begin(), \ it_end = mActors.end() ; it != it_end; ++it) #define for_actorsm for (ActorSpritesIterator it = mActors.begin(), \ it_end = mActors.end() ; it != it_end; ++it) ActorManager *actorManager = nullptr; class FindBeingFunctor final { public: bool operator() (const ActorSprite *const actor) const { if (!actor || actor->getType() == ActorType::FloorItem || actor->getType() == ActorType::Portal) { return false; } const Being *const b = static_cast<const Being* const>(actor); const unsigned other_y = y + ((b->getType() == ActorType::Npc) ? 1 : 0); const Vector &pos = b->getPosition(); return (static_cast<unsigned>(pos.x) / mapTileSize == x && (static_cast<unsigned>(pos.y) / mapTileSize == y || static_cast<unsigned>(pos.y) / mapTileSize == other_y) && b->isAlive() && (type == ActorType::Unknown || b->getType() == type)); } uint16_t x, y; ActorTypeT type; } beingActorFinder; class FindBeingEqualFunctor final { public: bool operator() (const Being *const being) const { if (!being || !findBeing) return false; return being->getId() == findBeing->getId(); } Being *findBeing; } beingEqualActorFinder; class SortBeingFunctor final { public: bool operator() (const Being *const being1, const Being *const being2) const { if (!being1 || !being2) return false; if (priorityBeings) { int w1 = defaultPriorityIndex; int w2 = defaultPriorityIndex; const StringIntMapCIter it1 = priorityBeings->find( being1->getName()); const StringIntMapCIter it2 = priorityBeings->find( being2->getName()); if (it1 != priorityBeings->end()) w1 = (*it1).second; if (it2 != priorityBeings->end()) w2 = (*it2).second; if (w1 != w2) return w1 < w2; } if (being1->getDistance() != being2->getDistance()) { if (specialDistance && being1->getDistance() <= 2 && being2->getDistance() <= attackRange && being2->getDistance() > 2) { return false; } else if (specialDistance && being2->getDistance() <= 2 && being1->getDistance() <= attackRange && being1->getDistance() > 2) { return true; } return being1->getDistance() < being2->getDistance(); } const int d1 = abs(being1->getTileX() - x) + abs(being1->getTileY() - y); const int d2 = abs(being2->getTileX() - x) + abs(being2->getTileY() - y); if (d1 != d2) return d1 < d2; if (attackBeings) { int w1 = defaultAttackIndex; int w2 = defaultAttackIndex; const StringIntMapCIter it1 = attackBeings->find( being1->getName()); const StringIntMapCIter it2 = attackBeings->find( being2->getName()); if (it1 != attackBeings->end()) w1 = (*it1).second; if (it2 != attackBeings->end()) w2 = (*it2).second; if (w1 != w2) return w1 < w2; } return (being1->getName() < being2->getName()); } StringIntMap *attackBeings; StringIntMap *priorityBeings; int x; int y; int defaultAttackIndex; int defaultPriorityIndex; int attackRange; bool specialDistance; } beingActorSorter; ActorManager::ActorManager() : mActors(), mDeleteActors(), mBlockedBeings(), mMap(nullptr), mSpellHeal1(serverConfig.getValue("spellHeal1", "#lum")), mSpellHeal2(serverConfig.getValue("spellHeal2", "#inma")), mSpellItenplz(serverConfig.getValue("spellItenplz", "#itenplz")), mTargetDeadPlayers(config.getBoolValue("targetDeadPlayers")), mTargetOnlyReachable(config.getBoolValue("targetOnlyReachable")), mCyclePlayers(config.getBoolValue("cyclePlayers")), mCycleMonsters(config.getBoolValue("cycleMonsters")), mCycleNPC(config.getBoolValue("cycleNPC")), mExtMouseTargeting(config.getBoolValue("extMouseTargeting")) { config.addListener("targetDeadPlayers", this); config.addListener("targetOnlyReachable", this); config.addListener("cyclePlayers", this); config.addListener("cycleMonsters", this); config.addListener("cycleNPC", this); config.addListener("extMouseTargeting", this); loadAttackList(); } ActorManager::~ActorManager() { config.removeListeners(this); CHECKLISTENERS storeAttackList(); clear(); } void ActorManager::setMap(Map *const map) { mMap = map; if (localPlayer) localPlayer->setMap(map); } void ActorManager::setPlayer(LocalPlayer *const player) { localPlayer = player; mActors.insert(player); if (socialWindow) socialWindow->updateAttackFilter(); if (socialWindow) socialWindow->updatePickupFilter(); } Being *ActorManager::createBeing(const BeingId id, const ActorTypeT type, const BeingTypeId subtype) { Being *const being = new Being(id, type, subtype, mMap); mActors.insert(being); if (type == ActorType::Player #ifdef EATHENA_SUPPORT || type == ActorType::Mercenary || type == ActorType::Pet #endif || type == ActorType::Npc) { being->updateFromCache(); beingHandler->requestNameById(id); if (localPlayer) localPlayer->checkNewName(being); } else if (type == ActorType::Monster) { if (serverFeatures->haveMonsterName()) beingHandler->requestNameById(id); } else if (type == ActorType::Portal) { if (serverFeatures->haveServerWarpNames()) beingHandler->requestNameById(id); } if (type == ActorType::Player) { if (socialWindow) socialWindow->updateActiveList(); } else if (type == ActorType::Npc) { if (questsWindow) questsWindow->addEffect(being); } return being; } FloorItem *ActorManager::createItem(const BeingId id, const int itemId, const int x, const int y, const int amount, const unsigned char color, const int subX, const int subY) { FloorItem *const floorItem = new FloorItem(id, itemId, x, y, amount, color); floorItem->postInit(mMap, subX, subY); if (!checkForPickup(floorItem)) floorItem->disableHightlight(); mActors.insert(floorItem); return floorItem; } void ActorManager::destroy(ActorSprite *const actor) { if (!actor || actor == localPlayer) return; mDeleteActors.insert(actor); } void ActorManager::erase(ActorSprite *const actor) { if (!actor || actor == localPlayer) return; mActors.erase(actor); } void ActorManager::undelete(const ActorSprite *const actor) { if (!actor || actor == localPlayer) return; FOR_EACH (ActorSpritesConstIterator, it, mDeleteActors) { if (*it == actor) { mDeleteActors.erase(*it); return; } } } Being *ActorManager::findBeing(const BeingId id) const { for_actors { ActorSprite *const actor = *it; if (actor->getId() == id && actor->getType() != ActorType::FloorItem) { return static_cast<Being*>(actor); } } return nullptr; } Being *ActorManager::findBeing(const int x, const int y, const ActorTypeT type) const { beingActorFinder.x = static_cast<uint16_t>(x); beingActorFinder.y = static_cast<uint16_t>(y); beingActorFinder.type = type; const ActorSpritesConstIterator it = std::find_if( mActors.begin(), mActors.end(), beingActorFinder); return (it == mActors.end()) ? nullptr : static_cast<Being*>(*it); } Being *ActorManager::findBeingByPixel(const int x, const int y, const AllPlayers allPlayers) const { if (!mMap) return nullptr; const bool targetDead = mTargetDeadPlayers; const bool modActive = inputManager.isActionActive( InputAction::STOP_ATTACK); if (mExtMouseTargeting) { Being *tempBeing = nullptr; bool noBeing(false); for_actorsm { if (!*it) continue; if ((*it)->getType() == ActorType::Portal) continue; if ((*it)->getType() == ActorType::FloorItem) { if (!noBeing) { const FloorItem *const floor = static_cast<const FloorItem*>(*it); const int px = floor->getPixelX(); const int py = floor->getPixelY(); if ((px - mapTileSize <= x) && (px + mapTileSize > x) && (py - mapTileSize * 2 <= y) && (py + mapTileSize / 2 > y)) { noBeing = true; } } continue; } Being *const being = static_cast<Being*>(*it); if (being->getInfo() && !(being->getInfo()->isTargetSelection() || modActive)) { continue; } if ((being->mAction != BeingAction::DEAD || (targetDead && being->getType() == ActorType::Player)) && (allPlayers == AllPlayers_true || being != localPlayer)) { const int px = being->getPixelX(); const int py = being->getPixelY(); if ((px - mapTileSize / 2 <= x) && (px + mapTileSize / 2 > x) && (py - mapTileSize <= y) && (py > y)) { return being; } else if (!noBeing && (px - mapTileSize <= x) && (px + mapTileSize > x) && (py - mapTileSize * 2 <= y) && (py + mapTileSize / 2 > y)) { if (tempBeing) noBeing = true; else tempBeing = being; } } } if (noBeing) return nullptr; return tempBeing; } else { for_actorsm { if (!*it) continue; if ((*it)->getType() == ActorType::Portal || (*it)->getType() == ActorType::FloorItem) { continue; } Being *const being = static_cast<Being*>(*it); if (being->getInfo() && !(being->getInfo()->isTargetSelection() || modActive)) { continue; } const int px = being->getPixelX(); const int py = being->getPixelY(); if ((px - mapTileSize / 2 <= x) && (px + mapTileSize / 2 > x) && (py - mapTileSize <= y) && (py > y)) { return being; } } return nullptr; } } void ActorManager::findBeingsByPixel(std::vector<ActorSprite*> &beings, const int x, const int y, const AllPlayers allPlayers) const { if (!mMap) return; const int xtol = mapTileSize / 2; const int uptol = mapTileSize; const bool modActive = inputManager.isActionActive( InputAction::STOP_ATTACK); for_actors { if (!*it) continue; if ((*it)->getType() == ActorType::Portal) continue; const Being *const being = dynamic_cast<const Being*>(*it); if (being && being->getInfo() && !(being->getInfo()->isTargetSelection() || modActive)) { continue; } ActorSprite *const actor = *it; if ((being && (being->isAlive() || (mTargetDeadPlayers && being->getType() == ActorType::Player)) && (allPlayers == AllPlayers_true || being != localPlayer)) || actor->getType() == ActorType::FloorItem) { if ((actor->getPixelX() - xtol <= x) && (actor->getPixelX() + xtol > x) && (actor->getPixelY() - uptol <= y) && (actor->getPixelY() > y)) { beings.push_back(actor); } } } } Being *ActorManager::findPortalByTile(const int x, const int y) const { if (!mMap) return nullptr; for_actorsm { if (!*it) continue; if ((*it)->getType() != ActorType::Portal) continue; Being *const being = static_cast<Being*>(*it); if (being->getTileX() == x && being->getTileY() == y) return being; } return nullptr; } FloorItem *ActorManager::findItem(const BeingId id) const { for_actorsm { if (!*it) continue; if ((*it)->getId() == id && (*it)->getType() == ActorType::FloorItem) { return static_cast<FloorItem*>(*it); } } return nullptr; } FloorItem *ActorManager::findItem(const int x, const int y) const { for_actorsm { if (!*it) continue; if ((*it)->getTileX() == x && (*it)->getTileY() == y && (*it)->getType() == ActorType::FloorItem) { return static_cast<FloorItem*>(*it); } } return nullptr; } bool ActorManager::pickUpAll(const int x1, const int y1, const int x2, const int y2, const bool serverBuggy) { if (!localPlayer) return false; bool finded(false); const bool allowAll = mPickupItemsSet.find("") != mPickupItemsSet.end(); if (!serverBuggy) { for_actorsm { if (!*it) continue; if ((*it)->getType() == ActorType::FloorItem && ((*it)->getTileX() >= x1 && (*it)->getTileX() <= x2) && ((*it)->getTileY() >= y1 && (*it)->getTileY() <= y2)) { FloorItem *const item = static_cast<FloorItem*>(*it); if (allowAll) { if (mIgnorePickupItemsSet.find(item->getName()) == mIgnorePickupItemsSet.end()) { if (localPlayer->pickUp(item)) finded = true; } } else { if (mPickupItemsSet.find(item->getName()) != mPickupItemsSet.end()) { if (localPlayer->pickUp(item)) finded = true; } } } } } else if (PacketLimiter::checkPackets(PacketType::PACKET_PICKUP)) { FloorItem *item = nullptr; unsigned cnt = 65535; for_actorsm { if (!*it) continue; if ((*it)->getType() == ActorType::FloorItem && ((*it)->getTileX() >= x1 && (*it)->getTileX() <= x2) && ((*it)->getTileY() >= y1 && (*it)->getTileY() <= y2)) { FloorItem *const tempItem = static_cast<FloorItem*>(*it); if (tempItem->getPickupCount() < cnt) { if (allowAll) { if (mIgnorePickupItemsSet.find(tempItem->getName()) == mIgnorePickupItemsSet.end()) { item = tempItem; cnt = item->getPickupCount(); if (cnt == 0) { item->incrementPickup(); localPlayer->pickUp(item); return true; } } } else { if (mPickupItemsSet.find(tempItem->getName()) != mPickupItemsSet.end()) { item = tempItem; cnt = item->getPickupCount(); if (cnt == 0) { item->incrementPickup(); localPlayer->pickUp(item); return true; } } } } } } if (item && localPlayer->pickUp(item)) finded = true; } return finded; } bool ActorManager::pickUpNearest(const int x, const int y, int maxdist) const { if (!localPlayer) return false; maxdist = maxdist * maxdist; FloorItem *closestItem = nullptr; int dist = 0; const bool allowAll = mPickupItemsSet.find("") != mPickupItemsSet.end(); for_actorsm { if (!*it) continue; if ((*it)->getType() == ActorType::FloorItem) { FloorItem *const item = static_cast<FloorItem*>(*it); const int d = (item->getTileX() - x) * (item->getTileX() - x) + (item->getTileY() - y) * (item->getTileY() - y); if ((d < dist || !closestItem) && (!mTargetOnlyReachable || localPlayer->isReachable(item->getTileX(), item->getTileY(), false))) { if (allowAll) { if (mIgnorePickupItemsSet.find(item->getName()) == mIgnorePickupItemsSet.end()) { dist = d; closestItem = item; } } else { if (mPickupItemsSet.find(item->getName()) != mPickupItemsSet.end()) { dist = d; closestItem = item; } } } } } if (closestItem && dist <= maxdist) return localPlayer->pickUp(closestItem); return false; } Being *ActorManager::findBeingByName(const std::string &name, const ActorTypeT type) const { for_actorsm { if (!*it) continue; if ((*it)->getType() == ActorType::FloorItem || (*it)->getType() == ActorType::Portal) { continue; } Being *const being = static_cast<Being*>(*it); if (being->getName() == name && (type == ActorType::Unknown || type == being->getType())) { return being; } } return nullptr; } Being *ActorManager::findNearestByName(const std::string &name, const ActorTypeT &type) const { if (!localPlayer) return nullptr; int dist = 0; Being* closestBeing = nullptr; int x, y; x = localPlayer->getTileX(); y = localPlayer->getTileY(); for_actorsm { if (reportTrue(!*it)) continue; if ((*it)->getType() == ActorType::FloorItem || (*it)->getType() == ActorType::Portal) { continue; } Being *const being = static_cast<Being*>(*it); if (being && being->getName() == name && (type == ActorType::Unknown || type == being->getType())) { if (being->getType() == ActorType::Player) { return being; } else { const int d = (being->getTileX() - x) * (being->getTileX() - x) + (being->getTileY() - y) * (being->getTileY() - y); if (validateBeing(nullptr, being, type, nullptr, 50) && (d < dist || closestBeing == nullptr)) { dist = d; closestBeing = being; } } } } return closestBeing; } const ActorSprites &ActorManager::getAll() const { return mActors; } void ActorManager::logic() { BLOCK_START("ActorManager::logic") for_actors { if (*it) (*it)->logic(); } if (mDeleteActors.empty()) { BLOCK_END("ActorManager::logic") return; } BLOCK_START("ActorManager::logic 1") FOR_EACH (ActorSpritesConstIterator, it, mDeleteActors) { const ActorSprite *const actor = *it; if (!actor) continue; const ActorTypeT &type = actor->getType(); if (type == ActorType::Player) { const Being *const being = static_cast<const Being*>(actor); being->addToCache(); if (beingEquipmentWindow) beingEquipmentWindow->resetBeing(being); } if (localPlayer) { if (localPlayer->getTarget() == actor) localPlayer->setTarget(nullptr); if (localPlayer->getPickUpTarget() == actor) localPlayer->unSetPickUpTarget(); } if (viewport) viewport->clearHover(*it); } FOR_EACH (ActorSpritesConstIterator, it, mDeleteActors) { ActorSprite *actor = *it; mActors.erase(actor); delete actor; } mDeleteActors.clear(); BLOCK_END("ActorManager::logic 1") BLOCK_END("ActorManager::logic") } void ActorManager::clear() { if (beingEquipmentWindow) beingEquipmentWindow->setBeing(nullptr); if (localPlayer) { localPlayer->setTarget(nullptr); localPlayer->unSetPickUpTarget(); mActors.erase(localPlayer); } for_actors delete *it; mActors.clear(); mDeleteActors.clear(); if (localPlayer) mActors.insert(localPlayer); } Being *ActorManager::findNearestLivingBeing(const int x, const int y, const int maxTileDist, const ActorTypeT type, const Being *const excluded) const { const int maxDist = maxTileDist * mapTileSize; return findNearestLivingBeing(nullptr, maxDist, type, x, y, excluded, AllowSort_true); } Being *ActorManager::findNearestLivingBeing(const Being *const aroundBeing, const int maxDist, const ActorTypeT type, const AllowSort allowSort) const { if (!aroundBeing) return nullptr; return findNearestLivingBeing(aroundBeing, maxDist, type, aroundBeing->getTileX(), aroundBeing->getTileY(), aroundBeing, allowSort); } Being *ActorManager::findNearestLivingBeing(const Being *const aroundBeing, int maxDist, const ActorTypeT &type, const int x, const int y, const Being *const excluded, const AllowSort allowSort) const { if (!aroundBeing || !localPlayer) return nullptr; std::set<std::string> attackMobs; std::set<std::string> priorityMobs; std::set<std::string> ignoreAttackMobs; StringIntMap attackMobsMap; StringIntMap priorityMobsMap; int defaultAttackIndex = 10000; int defaultPriorityIndex = 10000; const int attackRange = localPlayer->getAttackRange(); bool specialDistance = false; if (settings.moveToTargetType == 7 && localPlayer->getAttackRange() > 2) { specialDistance = true; } maxDist = maxDist * maxDist; const bool cycleSelect = allowSort == AllowSort_true && ((mCyclePlayers && type == ActorType::Player) || (mCycleMonsters && type == ActorType::Monster) || (mCycleNPC && type == ActorType::Npc)); const bool filtered = allowSort == AllowSort_true && config.getBoolValue("enableAttackFilter") && type == ActorType::Monster; const bool modActive = inputManager.isActionActive( InputAction::STOP_ATTACK); bool ignoreDefault = false; if (filtered) { attackMobs = mAttackMobsSet; priorityMobs = mPriorityAttackMobsSet; ignoreAttackMobs = mIgnoreAttackMobsSet; attackMobsMap = mAttackMobsMap; priorityMobsMap = mPriorityAttackMobsMap; beingActorSorter.attackBeings = &attackMobsMap; beingActorSorter.priorityBeings = &priorityMobsMap; beingActorSorter.specialDistance = specialDistance; beingActorSorter.attackRange = attackRange; if (ignoreAttackMobs.find("") != ignoreAttackMobs.end()) ignoreDefault = true; StringIntMapCIter itr = attackMobsMap.find(""); if (itr != attackMobsMap.end()) defaultAttackIndex = (*itr).second; itr = priorityMobsMap.find(""); if (itr != priorityMobsMap.end()) defaultPriorityIndex = (*itr).second; } if (cycleSelect) { std::vector<Being*> sortedBeings; FOR_EACH (ActorSprites::iterator, i, mActors) { if (!*i) continue; if ((*i)->getType() == ActorType::FloorItem || (*i)->getType() == ActorType::Portal) { continue; } Being *const being = static_cast<Being*>(*i); if (filtered) { if (ignoreAttackMobs.find(being->getName()) != ignoreAttackMobs.end()) { continue; } if (ignoreDefault && attackMobs.find(being->getName()) == attackMobs.end() && priorityMobs.find(being->getName()) == priorityMobs.end()) { continue; } } if (being->getInfo() && !(being->getInfo()->isTargetSelection() || modActive)) { continue; } if (validateBeing(aroundBeing, being, type, nullptr, maxDist)) { if (being != excluded) sortedBeings.push_back(being); } } // no selectable beings if (sortedBeings.empty()) return nullptr; beingActorSorter.x = x; beingActorSorter.y = y; if (filtered) { beingActorSorter.attackBeings = &attackMobsMap; beingActorSorter.defaultAttackIndex = defaultAttackIndex; beingActorSorter.priorityBeings = &priorityMobsMap; beingActorSorter.defaultPriorityIndex = defaultPriorityIndex; } else { beingActorSorter.attackBeings = nullptr; beingActorSorter.priorityBeings = nullptr; } std::sort(sortedBeings.begin(), sortedBeings.end(), beingActorSorter); if (filtered) { beingActorSorter.attackBeings = nullptr; beingActorSorter.priorityBeings = nullptr; } if (localPlayer->getTarget() == nullptr) { Being *const target = sortedBeings.at(0); if (specialDistance && target->getType() == ActorType::Monster && target->getDistance() <= 2) { return nullptr; } // if no selected being in vector, return first nearest being return target; } beingEqualActorFinder.findBeing = localPlayer->getTarget(); std::vector<Being*>::const_iterator i = std::find_if( sortedBeings.begin(), sortedBeings.end(), beingEqualActorFinder); if (i == sortedBeings.end() || ++i == sortedBeings.end()) { // if no selected being in vector, return first nearest being return sortedBeings.at(0); } // we find next being after target return *i; } else { int dist = 0; int index = defaultPriorityIndex; Being *closestBeing = nullptr; FOR_EACH (ActorSprites::iterator, i, mActors) { if (!*i) continue; if ((*i)->getType() == ActorType::FloorItem || (*i)->getType() == ActorType::Portal) { continue; } Being *const being = static_cast<Being*>(*i); if (filtered) { if (ignoreAttackMobs.find(being->getName()) != ignoreAttackMobs.end()) { continue; } if (ignoreDefault && attackMobs.find(being->getName()) == attackMobs.end() && priorityMobs.find(being->getName()) == priorityMobs.end()) { continue; } } if (being->getInfo() && !(being->getInfo()->isTargetSelection() || modActive)) { continue; } const bool valid = validateBeing(aroundBeing, being, type, excluded, 50); int d = being->getDistance(); if (being->getType() != ActorType::Monster || !mTargetOnlyReachable) { // if distance not calculated, use old distance d = (being->getTileX() - x) * (being->getTileX() - x) + (being->getTileY() - y) * (being->getTileY() - y); } if (!valid) continue; if (specialDistance && being->getDistance() <= 2 && being->getType() == type) { continue; } // logger->log("being name:" + being->getName()); // logger->log("index:" + toString(index)); // logger->log("d:" + toString(d)); if (!filtered && (d <= dist || !closestBeing)) { dist = d; closestBeing = being; } else if (filtered) { int w2 = defaultPriorityIndex; if (closestBeing) { const StringIntMapCIter it2 = priorityMobsMap.find( being->getName()); if (it2 != priorityMobsMap.end()) w2 = (*it2).second; if (w2 < index) { dist = d; closestBeing = being; index = w2; continue; } if (w2 == index && d <= dist) { dist = d; closestBeing = being; index = w2; continue; } } if (!closestBeing) { dist = d; closestBeing = being; const StringIntMapCIter it1 = priorityMobsMap.find( being->getName()); if (it1 != priorityMobsMap.end()) index = (*it1).second; else index = defaultPriorityIndex; } } } return (maxDist >= dist) ? closestBeing : nullptr; } } bool ActorManager::validateBeing(const Being *const aroundBeing, Being *const being, const ActorTypeT &type, const Being* const excluded, const int maxCost) const { if (!localPlayer) return false; return being && ((being->getType() == type || type == ActorType::Unknown) && (being->isAlive() || (mTargetDeadPlayers && type == ActorType::Player)) && being != aroundBeing) && being != excluded && (type != ActorType::Monster || !mTargetOnlyReachable || localPlayer->isReachable(being, maxCost)); } void ActorManager::healTarget() const { if (!localPlayer) return; heal(localPlayer->getTarget()); } void ActorManager::heal(const Being *const target) const { if (!localPlayer || !chatWindow || !localPlayer->isAlive() || !playerHandler->canUseMagic()) { return; } // self if (target && localPlayer->getName() == target->getName()) { if (PlayerInfo::getAttribute(Attributes::MP) >= 6 && PlayerInfo::getAttribute(Attributes::HP) != PlayerInfo::getAttribute(Attributes::MAX_HP)) { if (!PacketLimiter::limitPackets(PacketType::PACKET_CHAT)) return; chatWindow->localChatInput(mSpellHeal1); } } // magic levels < 2 else if (PlayerInfo::getSkillLevel(340) < 2 || PlayerInfo::getSkillLevel(341) < 2) { if (PlayerInfo::getAttribute(Attributes::MP) >= 6) { if (target && target->getType() != ActorType::Monster) { if (!PacketLimiter::limitPackets(PacketType::PACKET_CHAT)) return; chatWindow->localChatInput(mSpellHeal1 + " " + target->getName()); } else if (PlayerInfo::getAttribute(Attributes::HP) != PlayerInfo::getAttribute(Attributes::MAX_HP)) { if (!PacketLimiter::limitPackets(PacketType::PACKET_CHAT)) return; chatWindow->localChatInput(mSpellHeal1); } } } // magic level >= 2 and not self else { // mp > 10 and target not monster if (PlayerInfo::getAttribute(Attributes::MP) >= 10 && target && target->getType() != ActorType::Monster) { // target not enemy if (player_relations.getRelation(target->getName()) != Relation::ENEMY2) { if (!PacketLimiter::limitPackets(PacketType::PACKET_CHAT)) return; chatWindow->localChatInput(mSpellHeal2 + " " + target->getName()); } // target enemy else { if (!PacketLimiter::limitPackets(PacketType::PACKET_CHAT)) return; chatWindow->localChatInput(mSpellHeal1); } } // heal self if selected monster or selection empty else if ((!target || target->getType() == ActorType::Monster) && PlayerInfo::getAttribute(Attributes::MP) >= 6 && PlayerInfo::getAttribute(Attributes::HP) != PlayerInfo::getAttribute(Attributes::MAX_HP)) { if (!PacketLimiter::limitPackets(PacketType::PACKET_CHAT)) return; chatWindow->localChatInput(mSpellHeal1); } } } Being* ActorManager::findMostDamagedPlayer() const { if (!localPlayer) return nullptr; int maxDamageTaken = 0; Being *target = nullptr; for_actors { if ((*it)->getType() != ActorType::Player) continue; Being *const being = static_cast<Being*>(*it); if ((!being) || (!being->isAlive()) || // don't heal dead (player_relations.getRelation(being->getName()) == Relation::ENEMY2) || // don't heal enemy (localPlayer == being)) // don't heal self { continue; } if (being->getDamageTaken() > maxDamageTaken) { maxDamageTaken = being->getDamageTaken(); target = being; } } return target; } void ActorManager::itenplz() const { if (!localPlayer || !chatWindow || !localPlayer->isAlive() || !playerHandler->canUseMagic()) { return; } if (!PacketLimiter::limitPackets(PacketType::PACKET_CHAT)) return; chatWindow->localChatInput(mSpellItenplz); } bool ActorManager::hasActorSprite(const ActorSprite *const actor) const { for_actors { if (actor == *it) return true; } return false; } void ActorManager::addBlock(const BeingId id) { mBlockedBeings.insert(id); } void ActorManager::deleteBlock(const BeingId id) { mBlockedBeings.erase(id); } bool ActorManager::isBlocked(const BeingId id) const { return mBlockedBeings.find(id) != mBlockedBeings.end(); } void ActorManager::printAllToChat() const { // TRANSLATORS: visible beings on map printBeingsToChat(getAll(), _("Visible on map")); } void ActorManager::printBeingsToChat(const ActorSprites &beings, const std::string &header) { if (!debugChatTab) return; debugChatTab->chatLog("---------------------------------------", ChatMsgType::BY_SERVER); debugChatTab->chatLog(header, ChatMsgType::BY_SERVER); FOR_EACH (std::set<ActorSprite*>::const_iterator, it, beings) { if (!*it) continue; if ((*it)->getType() == ActorType::FloorItem) continue; const Being *const being = static_cast<const Being*>(*it); debugChatTab->chatLog(strprintf("%s (%d,%d) %d", being->getName().c_str(), being->getTileX(), being->getTileY(), toInt(being->getSubType(), int)), ChatMsgType::BY_SERVER); } debugChatTab->chatLog("---------------------------------------", ChatMsgType::BY_SERVER); } void ActorManager::printBeingsToChat(const std::vector<Being*> &beings, const std::string &header) { if (!debugChatTab) return; debugChatTab->chatLog("---------------------------------------", ChatMsgType::BY_SERVER); debugChatTab->chatLog(header, ChatMsgType::BY_SERVER); FOR_EACH (std::vector<Being*>::const_iterator, i, beings) { if (!*i) continue; const Being *const being = *i; debugChatTab->chatLog(strprintf("%s (%d,%d) %d", being->getName().c_str(), being->getTileX(), being->getTileY(), toInt(being->getSubType(), int)), ChatMsgType::BY_SERVER); } debugChatTab->chatLog("---------------------------------------", ChatMsgType::BY_SERVER); } void ActorManager::getPlayerNames(StringVect &names, const NpcNames npcNames) const { names.clear(); for_actors { if (!*it) continue; if ((*it)->getType() == ActorType::FloorItem || (*it)->getType() == ActorType::Portal) { continue; } const Being *const being = static_cast<const Being*>(*it); if ((being->getType() == ActorType::Player || (being->getType() == ActorType::Npc && npcNames == NpcNames_true)) && being->getName() != "") { names.push_back(being->getName()); } } } void ActorManager::getMobNames(StringVect &names) const { names.clear(); for_actors { if (!*it) continue; if ((*it)->getType() == ActorType::FloorItem || (*it)->getType() == ActorType::Portal) { continue; } const Being *const being = static_cast<const Being*>(*it); if (being->getType() == ActorType::Monster && being->getName() != "") names.push_back(being->getName()); } } void ActorManager::updatePlayerNames() const { for_actorsm { if (!*it) continue; if ((*it)->getType() == ActorType::FloorItem || (*it)->getType() == ActorType::Portal) { continue; } Being *const being = static_cast<Being*>(*it); being->setGoodStatus(-1); if (being->getType() == ActorType::Player && being->getName() != "") being->updateName(); } } void ActorManager::updatePlayerColors() const { for_actorsm { if (!*it) continue; if ((*it)->getType() == ActorType::FloorItem || (*it)->getType() == ActorType::Portal) { continue; } Being *const being = static_cast<Being*>(*it); if (being->getType() == ActorType::Player && being->getName() != "") being->updateColors(); } } void ActorManager::updatePlayerGuild() const { for_actorsm { if (!*it) continue; if ((*it)->getType() == ActorType::FloorItem || (*it)->getType() == ActorType::Portal) { continue; } Being *const being = static_cast<Being*>(*it); if (being->getType() == ActorType::Player && being->getName() != "") being->updateGuild(); } } #ifdef TMWA_SUPPORT void ActorManager::parseLevels(std::string levels) const { levels.append(", "); size_t f = 0; const std::string brkEnd("), "); size_t pos = levels.find(brkEnd, f); while (pos != std::string::npos) { std::string part = levels.substr(f, pos - f); if (part.empty()) break; const size_t bktPos = part.rfind("("); if (bktPos != std::string::npos) { Being *const being = findBeingByName(part.substr(0, bktPos), ActorType::Player); if (being) { being->setLevel(atoi(part.substr(bktPos + 1).c_str())); being->addToCache(); } } f = static_cast<size_t>(pos + brkEnd.length()); pos = levels.find(brkEnd, f); } updatePlayerNames(); } #endif void ActorManager::optionChanged(const std::string &name) { if (name == "targetDeadPlayers") mTargetDeadPlayers = config.getBoolValue("targetDeadPlayers"); else if (name == "targetOnlyReachable") mTargetOnlyReachable = config.getBoolValue("targetOnlyReachable"); else if (name == "cyclePlayers") mCyclePlayers = config.getBoolValue("cyclePlayers"); else if (name == "cycleMonsters") mCycleMonsters = config.getBoolValue("cycleMonsters"); else if (name == "cycleNPC") mCycleNPC = config.getBoolValue("cycleNPC"); else if (name == "extMouseTargeting") mExtMouseTargeting = config.getBoolValue("extMouseTargeting"); } void ActorManager::removeAttackMob(const std::string &name) { mPriorityAttackMobs.remove(name); mAttackMobs.remove(name); mIgnoreAttackMobs.remove(name); mPriorityAttackMobsSet.erase(name); mAttackMobsSet.erase(name); mIgnoreAttackMobsSet.erase(name); rebuildAttackMobs(); rebuildPriorityAttackMobs(); storeAttackList(); } void ActorManager::removePickupItem(const std::string &name) { mPickupItems.remove(name); mPickupItemsSet.erase(name); mIgnorePickupItems.remove(name); mIgnorePickupItemsSet.erase(name); rebuildPickupItems(); storeAttackList(); } #define addMobToList(name, mob) \ {\ const int size = get##mob##sSize();\ if (size > 0)\ {\ const int idx = get##mob##Index("");\ if (idx + 1 == size)\ {\ std::list<std::string>::iterator itr = m##mob##s.end();\ -- itr;\ m##mob##s.insert(itr, name);\ }\ else\ {\ m##mob##s.push_back(name);\ }\ }\ else\ {\ m##mob##s.push_back(name);\ }\ m##mob##sSet.insert(name);\ rebuild##mob##s();\ } #define rebuildMobsList(mob) \ {\ m##mob##sMap.clear();\ std::list<std::string>::const_iterator i = m##mob##s.begin();\ int cnt = 0;\ while (i != m##mob##s.end())\ {\ m##mob##sMap[*i] = cnt;\ ++ i;\ ++ cnt;\ }\ } void ActorManager::addAttackMob(const std::string &name) { addMobToList(name, AttackMob); rebuildPriorityAttackMobs(); storeAttackList(); } void ActorManager::addPriorityAttackMob(const std::string &name) { addMobToList(name, PriorityAttackMob); storeAttackList(); } void ActorManager::addIgnoreAttackMob(const std::string &name) { mIgnoreAttackMobs.push_back(name); mIgnoreAttackMobsSet.insert(name); rebuildAttackMobs(); rebuildPriorityAttackMobs(); storeAttackList(); } void ActorManager::addPickupItem(const std::string &name) { addMobToList(name, PickupItem); rebuildPickupItems(); storeAttackList(); } void ActorManager::addIgnorePickupItem(const std::string &name) { mIgnorePickupItems.push_back(name); mIgnorePickupItemsSet.insert(name); rebuildPickupItems(); storeAttackList(); } void ActorManager::rebuildPriorityAttackMobs() { rebuildMobsList(PriorityAttackMob); } void ActorManager::rebuildAttackMobs() { rebuildMobsList(AttackMob); } void ActorManager::rebuildPickupItems() { rebuildMobsList(PickupItem); } int ActorManager::getIndexByName(const std::string &name, const StringIntMap &map) { const StringIntMapCIter i = map.find(name); if (i == map.end()) return -1; return (*i).second; } int ActorManager::getPriorityAttackMobIndex(const std::string &name) const { return getIndexByName(name, mPriorityAttackMobsMap); } int ActorManager::getAttackMobIndex(const std::string &name) const { return getIndexByName(name, mAttackMobsMap); } int ActorManager::getPickupItemIndex(const std::string &name) const { return getIndexByName(name, mPickupItemsMap); } #define loadList(key, mob) \ {\ list = unpackList(serverConfig.getValue(key, ""));\ i = list.begin();\ i_end = list.end();\ while (i != i_end)\ {\ if (*i == "")\ empty = true;\ m##mob##s.push_back(*i);\ m##mob##sSet.insert(*i);\ ++ i;\ }\ } void ActorManager::loadAttackList() { bool empty = false; std::list<std::string> list; std::list<std::string>::const_iterator i; std::list<std::string>::const_iterator i_end; loadList("attackPriorityMobs", PriorityAttackMob); loadList("attackMobs", AttackMob); loadList("ignoreAttackMobs", IgnoreAttackMob); if (!empty) { mAttackMobs.push_back(""); mAttackMobsSet.insert(""); } empty = false; loadList("pickupItems", PickupItem); loadList("ignorePickupItems", IgnorePickupItem); if (!empty) { mPickupItems.push_back(""); mPickupItemsSet.insert(""); } rebuildAttackMobs(); rebuildPriorityAttackMobs(); rebuildPickupItems(); } void ActorManager::storeAttackList() const { serverConfig.setValue("attackPriorityMobs", packList(mPriorityAttackMobs)); serverConfig.setValue("attackMobs", packList(mAttackMobs)); serverConfig.setValue("ignoreAttackMobs", packList(mIgnoreAttackMobs)); serverConfig.setValue("pickupItems", packList(mPickupItems)); serverConfig.setValue("ignorePickupItems", packList(mIgnorePickupItems)); } bool ActorManager::checkForPickup(const FloorItem *const item) const { if (mPickupItemsSet.find("") != mPickupItemsSet.end()) { if (mIgnorePickupItemsSet.find(item->getName()) == mIgnorePickupItemsSet.end()) { return true; } } else if (item && mPickupItemsSet.find(item->getName()) != mPickupItemsSet.end()) { return true; } return false; } void ActorManager::updateEffects(const std::map<BeingTypeId, int> &addEffects, const std::set<BeingTypeId> &removeEffects) const { for_actorsm { if (!*it || (*it)->getType() != ActorType::Npc) continue; Being *const being = static_cast<Being*>(*it); const BeingTypeId type = being->getSubType(); if (removeEffects.find(type) != removeEffects.end()) being->removeSpecialEffect(); const std::map<BeingTypeId, int>::const_iterator idAdd = addEffects.find(type); if (idAdd != addEffects.end()) being->addSpecialEffect((*idAdd).second); } } Being *ActorManager::cloneBeing(const Being *const srcBeing, const int dx, const int dy, const int id) { Being *const dstBeing = actorManager->createBeing(fromInt( toInt(srcBeing->getId(), int) + id, BeingId), ActorType::Player, srcBeing->getSubType()); if (!dstBeing) return nullptr; dstBeing->setGender(srcBeing->getGender()); dstBeing->setAction(srcBeing->getCurrentAction(), 0); dstBeing->setTileCoords(srcBeing->getTileX() + dx, srcBeing->getTileY() + dy); dstBeing->setName(srcBeing->getName()); dstBeing->setDirection(srcBeing->getDirection()); const int sz = static_cast<int>(srcBeing->getSpritesCount()); for (int slot = 0; slot < sz; slot ++) { const int spriteId = srcBeing->getSpriteID(slot); const unsigned char color = srcBeing->getSpriteColor(slot); dstBeing->setSprite(slot, spriteId, "", color, false); } const int hairSlot = charServerHandler->hairSprite(); const int hairStyle = -srcBeing->getSpriteID(hairSlot); const unsigned char hairColor = srcBeing->getHairColor(); dstBeing->setSprite(hairSlot, hairStyle * -1, ItemDB::get(-hairStyle).getDyeColorsString(hairColor)); dstBeing->setHairColor(hairColor); return dstBeing; } #ifdef EATHENA_SUPPORT void ActorManager::removeRoom(const int chatId) { for_actors { ActorSprite *const actor = *it; if (actor && actor->getType() != ActorType::Player) { Being *const being = static_cast<Being*>(actor); const ChatObject *const chat = being->getChat(); if (chat && chat->chatId == chatId) { being->setChat(nullptr); } } } } void ActorManager::updateRoom(const ChatObject *const newChat) { if (!newChat) return; for_actors { ActorSprite *const actor = *it; if (actor && actor->getType() != ActorType::Player) { Being *const being = static_cast<Being*>(actor); ChatObject *const chat = being->getChat(); if (chat && chat->chatId == newChat->chatId) { chat->ownerId = newChat->ownerId; chat->maxUsers = newChat->maxUsers; chat->type = newChat->type; chat->title = newChat->title; } } } } #endif