summaryrefslogtreecommitdiff
path: root/src/actormanager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/actormanager.cpp')
-rw-r--r--src/actormanager.cpp349
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());