diff options
Diffstat (limited to 'src/actormanager.cpp')
-rw-r--r-- | src/actormanager.cpp | 349 |
1 files changed, 192 insertions, 157 deletions
diff --git a/src/actormanager.cpp b/src/actormanager.cpp index 9d2ea0664..b1ed604d5 100644 --- a/src/actormanager.cpp +++ b/src/actormanager.cpp @@ -1,11 +1,11 @@ /* - * The ManaPlus Client + * The ManaVerse Client * Copyright (C) 2004-2009 The Mana World Development Team * Copyright (C) 2009-2010 The Mana Developers * Copyright (C) 2011-2020 The ManaPlus Developers - * Copyright (C) 2020-2023 The ManaVerse Developers + * Copyright (C) 2020-2025 The ManaVerse Developers * - * This file is part of The ManaPlus Client. + * This file is part of The ManaVerse 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 @@ -153,6 +153,7 @@ class SortBeingFunctor final if (w1 != w2) return w1 < w2; } + if (being1->getDistance() != being2->getDistance()) { if (specialDistance && being1->getDistance() <= 2 @@ -171,12 +172,13 @@ class SortBeingFunctor final } const int d1 = abs(being1->getTileX() - x) - + abs(being1->getTileY() - y); + + abs(being1->getTileY() - y); const int d2 = abs(being2->getTileX() - x) - + abs(being2->getTileY() - y); + + abs(being2->getTileY() - y); if (d1 != d2) return d1 < d2; + if (attackBeings != nullptr) { int w1 = defaultAttackIndex; @@ -275,9 +277,10 @@ void ActorManager::setPlayer(LocalPlayer *const player) mActors.insert(player); mActorsIdMap[player->getId()] = player; if (socialWindow != nullptr) + { socialWindow->updateAttackFilter(); - if (socialWindow != nullptr) socialWindow->updatePickupFilter(); + } } Being *ActorManager::createBeing(const BeingId id, @@ -470,14 +473,15 @@ Being *ActorManager::findBeingByPixel(const int x, const int y, if (mMap == nullptr) return nullptr; - const bool targetDead = mTargetDeadPlayers; const bool modActive = inputManager.isActionActive( InputAction::STOP_ATTACK); if (mExtMouseTargeting) { - Being *tempBeing = nullptr; - bool noBeing(false); + // if there is no suitable being within the usual tolerance + // then return a suitable one nearby (larger tolerance). + Being *nearbyBeing = nullptr; + bool includeNearby = true; for_actorsm { @@ -490,7 +494,7 @@ Being *ActorManager::findBeingByPixel(const int x, const int y, if ((*it)->getType() == ActorType::FloorItem) { - if (!noBeing) + if (includeNearby) { const FloorItem *const floor = static_cast<const FloorItem*>(*it); @@ -501,7 +505,9 @@ Being *ActorManager::findBeingByPixel(const int x, const int y, (py - mapTileSize * 2 <= y) && (py + mapTileSize / 2 > y)) { - noBeing = true; + // flooritems cancel coarse selection, but + // we don't care about them. + includeNearby = false; } } continue; @@ -515,8 +521,10 @@ Being *ActorManager::findBeingByPixel(const int x, const int y, continue; } - if ((being->mAction != BeingAction::DEAD || - (targetDead && being->getType() == ActorType::Player)) && + // dead players have to be returned so you can still see + // their equipment, do whisper or trade (yes you can trade + // with the dead :D) or use any other option in the menu. + if ((being->isAlive() || being->getType() == ActorType::Player) && (allPlayers == AllPlayers_true || being != localPlayer)) { const int px = being->getPixelX(); @@ -528,23 +536,26 @@ Being *ActorManager::findBeingByPixel(const int x, const int y, { return being; } - else if (!noBeing && + else if (includeNearby && (px - mapTileSize <= x) && (px + mapTileSize > x) && (py - mapTileSize * 2 <= y) && (py + mapTileSize / 2 > y)) { - if (tempBeing != nullptr) - noBeing = true; + // also disable coarse selection if more than one being + // is found nearby. + if (nearbyBeing != nullptr) + includeNearby = false; else - tempBeing = being; + nearbyBeing = being; } } } - if (noBeing) + if (includeNearby) + return nearbyBeing; + else return nullptr; - return tempBeing; } for_actorsm { @@ -633,11 +644,11 @@ void ActorManager::findBeingsByPixel(STD_VECTOR<ActorSprite*> &beings, { continue; } - if ((being->isAlive() || - (mTargetDeadPlayers && - actorType == ActorType::Player)) && - (allPlayers == AllPlayers_true || - being != localPlayer)) + // dead players have to be returned so you can still see + // their equipment, do whisper or trade (yes you can trade + // with the dead :D) or use any other option in the menu. + if ((being->isAlive() || actorType == ActorType::Player) && + (allPlayers == AllPlayers_true || being != localPlayer)) { if ((actor->getPixelX() - xtol <= x) && (actor->getPixelX() + xtol > x) && @@ -718,8 +729,11 @@ bool ActorManager::pickUpAll(const int x1, const int y1, return false; bool finded(false); - const bool allowAll = mPickupItemsSet.find(std::string()) != - mPickupItemsSet.end(); + // if "default" is in pickup items set, then the ignore list acts + // as a blacklist. Otherwise, the pickup list acts as a whitelist. + const bool allowAll = mPickupItemsSet.find(std::string()) + != mPickupItemsSet.end(); + if (!serverBuggy) { for_actorsm @@ -811,16 +825,19 @@ bool ActorManager::pickUpAll(const int x1, const int y1, } bool ActorManager::pickUpNearest(const int x, const int y, - int maxdist) const + int maxDist) const { if (localPlayer == nullptr) return false; - maxdist = maxdist * maxdist; FloorItem *closestItem = nullptr; - int dist = 0; - const bool allowAll = mPickupItemsSet.find(std::string()) != - mPickupItemsSet.end(); + // working with squared distances avoids calculating square root. + int closestDistSq = maxDist * maxDist + 1; + + // if "default" is in pickup items set, then the ignore list acts + // as a blacklist. Otherwise, the pickup list acts as a whitelist. + const bool allowAll = mPickupItemsSet.find(std::string()) + != mPickupItemsSet.end(); for_actorsm { @@ -832,36 +849,38 @@ bool ActorManager::pickUpNearest(const int x, const int y, { FloorItem *const item = static_cast<FloorItem*>(*it); - const int d = (item->getTileX() - x) * (item->getTileX() - x) - + (item->getTileY() - y) * (item->getTileY() - y); + const int dx = item->getTileX() - x; + const int dy = item->getTileY() - y; + const int distSq = dx*dx + dy*dy; - if ((d < dist || closestItem == nullptr) && + if ((distSq < closestDistSq) && (!mTargetOnlyReachable || localPlayer->isReachable( item->getTileX(), item->getTileY(), false))) { if (allowAll) { - if (mIgnorePickupItemsSet.find(item->getName()) - == mIgnorePickupItemsSet.end()) - { - dist = d; + if (mIgnorePickupItemsSet.find(item->getName()) == + mIgnorePickupItemsSet.end()) + { // item is NOT in ignore set + closestDistSq = distSq; closestItem = item; } } else { - if (mPickupItemsSet.find(item->getName()) - != mPickupItemsSet.end()) + if (mPickupItemsSet.find(item->getName()) != + mPickupItemsSet.end()) { - dist = d; + closestDistSq = distSq; closestItem = item; } } } } } - if ((closestItem != nullptr) && dist <= maxdist) + + if (closestItem != nullptr) return localPlayer->pickUp(closestItem); return false; @@ -898,10 +917,10 @@ Being *ActorManager::findNearestByName(const std::string &name, if (localPlayer == nullptr) return nullptr; - int dist = 0; Being* closestBeing = nullptr; - int x = localPlayer->getTileX(); - int y = localPlayer->getTileY(); + int closestDistSq = INT_MAX; + const int x = localPlayer->getTileX(); + const int y = localPlayer->getTileY(); for_actorsm { @@ -909,8 +928,8 @@ Being *ActorManager::findNearestByName(const std::string &name, // if (reportTrue(*it == nullptr)) // continue; - if ((*it)->getType() == ActorType::FloorItem - || (*it)->getType() == ActorType::Portal) + if ((*it)->getType() == ActorType::FloorItem || + (*it)->getType() == ActorType::Portal) { continue; } @@ -921,16 +940,16 @@ Being *ActorManager::findNearestByName(const std::string &name, (type == ActorType::Unknown || type == being->getType())) { if (being->getType() == ActorType::Player) - { return being; - } - 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)) + const int dx = being->getTileX() - x; + const int dy = being->getTileY() - y; + const int distSq = dx*dx + dy*dy; + + if (distSq < closestDistSq + && validateBeing(nullptr, being, type, nullptr, 50)) { - dist = d; + closestDistSq = distSq; closestBeing = being; } } @@ -1070,7 +1089,7 @@ Being *ActorManager::findNearestPvpPlayer() const if (!((mapPvpMode != 0) || (teamId != 0))) continue; - if (!LocalPlayer::checAttackPermissions(being)) + if (!LocalPlayer::checkAttackPermissions(being)) continue; const int dx = being->getTileX() - localPlayer->getTileX(); @@ -1119,7 +1138,7 @@ Being *ActorManager::findNearestLivingBeing(const Being *const aroundBeing, } Being *ActorManager::findNearestLivingBeing(const Being *const aroundBeing, - int maxDist, + const int maxDist, const ActorTypeT &type, const int x, const int y, const Being *const excluded, @@ -1138,13 +1157,13 @@ Being *ActorManager::findNearestLivingBeing(const Being *const aroundBeing, const int attackRange = localPlayer->getAttackRange(); bool specialDistance = false; - if (settings.moveToTargetType == 11 + if (settings.moveToTargetType == 11 // archer && localPlayer->getAttackRange() > 2) { specialDistance = true; } - maxDist = maxDist * maxDist; + const int maxDistSq = maxDist * maxDist; const bool cycleSelect = allowSort == AllowSort_true && ((mCyclePlayers && type == ActorType::Player) @@ -1277,9 +1296,9 @@ Being *ActorManager::findNearestLivingBeing(const Being *const aroundBeing, return *i; } - int dist = 0; - int index = defaultPriorityIndex; Being *closestBeing = nullptr; + int closestDistSq = maxDistSq + 1; + int closestPriority = defaultPriorityIndex; FOR_EACH (ActorSprites::iterator, i, mActors) { @@ -1294,95 +1313,77 @@ Being *ActorManager::findNearestLivingBeing(const Being *const aroundBeing, } 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() != nullptr) && !(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) + // Cheap bounding box check, first, before engaging the pathfinder + // (in validateBeing call). Target is _at minimum_ distSq away + const int dx = being->getTileX() - x; + const int dy = being->getTileY() - y; + int distSq = dx*dx + dy*dy; + if (distSq > maxDistSq) continue; - if (specialDistance && being->getDistance() <= 2 - && being->getType() == type) + // Check if target is too close + if (specialDistance + && (distSq <= 2*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 == nullptr))) - { - dist = d; - closestBeing = being; - } - else if (filtered) + int priority = defaultPriorityIndex; + if (filtered) { - int w2 = defaultPriorityIndex; - if (closestBeing != nullptr) + // Skip ignored + if (ignoreAttackMobs.find(being->getName()) != + ignoreAttackMobs.end()) { - 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; - } + continue; } - if (closestBeing == nullptr) + const StringIntMapCIter prioIter + = priorityMobsMap.find(being->getName()); + + if (prioIter != priorityMobsMap.end()) + priority = prioIter->second; + + // if (default) is in ignore list, then only attack + // those in priority or general attack list. + if (ignoreDefault + && attackMobs.find(being->getName()) == attackMobs.end() + && prioIter == priorityMobsMap.end()) { - dist = d; - closestBeing = being; - const StringIntMapCIter it1 = priorityMobsMap.find( - being->getName()); - if (it1 != priorityMobsMap.end()) - index = (*it1).second; - else - index = defaultPriorityIndex; + continue; } } + + if (!validateBeing(aroundBeing, being, type, excluded, 50)) + continue; + + // see validateBeing for when real distance is valid + if (mTargetOnlyReachable && being->getType() == ActorType::Monster) + { + const int d = being->getDistance(); + distSq = d*d; + } + + //logger->log("being prio:%3d, dist^2:%3d, name: '%s'", + // priority, distSq, being->getName().c_str()); + + // without filtering, priority and closestPriority are always the same + if ((priority == closestPriority && distSq < closestDistSq) || + (priority < closestPriority && distSq <= maxDistSq)) + { + closestDistSq = distSq; + closestBeing = being; + closestPriority = priority; + } } - return (maxDist >= dist) ? closestBeing : nullptr; + return closestBeing; } bool ActorManager::validateBeing(const Being *const aroundBeing, @@ -1393,12 +1394,32 @@ bool ActorManager::validateBeing(const Being *const aroundBeing, { if (localPlayer == nullptr) return false; - return (being != nullptr) && ((being->getType() == type - || type == ActorType::Unknown) && (being->isAlive() - || (mTargetDeadPlayers && type == ActorType::Player)) - && being != aroundBeing) && being != excluded - && (type != ActorType::Monster || !mTargetOnlyReachable - || localPlayer->isReachable(being, maxCost)); + + if (being == nullptr) + return false; + + if (being == aroundBeing) + return false; + + if (being == excluded) + return false; + + // If specific being type is given, it must match. + if (!(being->getType() == type || type == ActorType::Unknown)) + return false; + + if (!being->isAlive()) + if (!mTargetDeadPlayers || type != ActorType::Player) + return false; + + // Why are we ignoring real reachability checks for non-monsters? + if (type != ActorType::Monster) + return true; + + if (!mTargetOnlyReachable) + return true; + + return localPlayer->isReachable(being, maxCost); } #ifdef TMWA_SUPPORT @@ -1503,8 +1524,12 @@ Being* ActorManager::findMostDamagedPlayer(const int maxTileDist) const if (localPlayer == nullptr) return nullptr; + const int maxTileDistSq = maxTileDist * maxTileDist; int maxDamageTaken = 0; - Being *target = nullptr; + Being *maxDamagePlayer = nullptr; + + const int my_x = localPlayer->getTileX(); + const int my_y = localPlayer->getTileY(); for_actors { @@ -1513,29 +1538,31 @@ Being* ActorManager::findMostDamagedPlayer(const int maxTileDist) const Being *const being = static_cast<Being*>(*it); - if ((being == nullptr) || !being->isAlive() || // don't heal dead - playerRelations.getRelation(being->getName()) == - Relation::ENEMY2 || // don't heal enemy - localPlayer == being) // don't heal self + if ((being == nullptr) + || (localPlayer == being) // don't heal self + || (!being->isAlive()) ) // don't heal the dead { continue; } - const int dx = being->getTileX() - localPlayer->getTileX(); - const int dy = being->getTileY() - localPlayer->getTileY(); - const int distance = fastSqrtInt(dx * dx + dy * dy); + if (being->getDamageTaken() <= maxDamageTaken) + continue; + + const int dx = being->getTileX() - my_x; + const int dy = being->getTileY() - my_y; + const int distSq = dx*dx + dy*dy; - if (distance > maxTileDist) + if (distSq > maxTileDistSq) continue; - if (being->getDamageTaken() > maxDamageTaken) - { - maxDamageTaken = being->getDamageTaken(); - target = being; - } + if (playerRelations.getRelation(being->getName()) == Relation::ENEMY2) + continue; // don't heal enemy + + maxDamageTaken = being->getDamageTaken(); + maxDamagePlayer = being; } - return target; + return maxDamagePlayer; } #ifdef TMWA_SUPPORT @@ -2052,18 +2079,26 @@ void ActorManager::storeAttackList() const bool ActorManager::checkForPickup(const FloorItem *const item) const { - if (mPickupItemsSet.find(std::string()) != mPickupItemsSet.end()) + // if "default" is in pickup items set, then the ignore list acts + // as a blacklist. Otherwise, the pickup list acts as a whitelist. + const bool allowAll = mPickupItemsSet.find(std::string()) + != mPickupItemsSet.end(); + + if (allowAll) { - if (mIgnorePickupItemsSet.find(item->getName()) - == mIgnorePickupItemsSet.end()) + if (mIgnorePickupItemsSet.find(item->getName()) == + mIgnorePickupItemsSet.end()) { return true; } } - else if ((item != nullptr) && mPickupItemsSet.find(item->getName()) - != mPickupItemsSet.end()) + else { - return true; + if (mPickupItemsSet.find(item->getName()) != + mPickupItemsSet.end()) + { + return true; + } } return false; } @@ -2109,7 +2144,7 @@ Being *ActorManager::cloneBeing(const Being *const srcBeing, dstBeing->setGender(srcBeing->getGender()); dstBeing->setAction(srcBeing->getCurrentAction(), 0); dstBeing->setTileCoords(srcBeing->getTileX() + dx, - srcBeing->getTileY() + dy); + srcBeing->getTileY() + dy); dstBeing->setName(srcBeing->getName()); dstBeing->setDirection(srcBeing->getDirection()); const int sz = CAST_S32(srcBeing->mSprites.size()); |