Răsfoiți Sursa

Implemented simple target selection logic for arrow towers

Ivan Savenko 1 an în urmă
părinte
comite
5bd9a32d97

+ 11 - 0
lib/battle/CBattleInfoCallback.cpp

@@ -50,6 +50,12 @@ static bool sameSideOfWall(BattleHex pos1, BattleHex pos2)
 	return stackLeft == destLeft;
 }
 
+static bool isInsideWalls(BattleHex pos)
+{
+	const int wallInStackLine = lineToWallHex(pos.getY());
+	return wallInStackLine < pos;
+}
+
 // parts of wall
 static const std::pair<int, EWallPart> wallParts[] =
 {
@@ -158,6 +164,11 @@ std::pair< std::vector<BattleHex>, int > CBattleInfoCallback::getPath(BattleHex
 	return std::make_pair(path, reachability.distances[dest]);
 }
 
+bool CBattleInfoCallback::battleIsInsideWalls(BattleHex from) const
+{
+	return isInsideWalls(from);
+}
+
 bool CBattleInfoCallback::battleHasPenaltyOnLine(BattleHex from, BattleHex dest, bool checkWall, bool checkMoat) const
 {
 	auto isTileBlocked = [&](BattleHex tile)

+ 1 - 0
lib/battle/CBattleInfoCallback.h

@@ -104,6 +104,7 @@ public:
 	DamageEstimation battleEstimateDamage(const battle::Unit * attacker, const battle::Unit * defender, BattleHex attackerPosition, DamageEstimation * retaliationDmg = nullptr) const;
 	DamageEstimation battleEstimateDamage(const battle::Unit * attacker, const battle::Unit * defender, int getMovementRange, DamageEstimation * retaliationDmg = nullptr) const;
 
+	bool battleIsInsideWalls(BattleHex from) const;
 	bool battleHasPenaltyOnLine(BattleHex from, BattleHex dest, bool checkWall, bool checkMoat) const;
 	bool battleHasDistancePenalty(const IBonusBearer * shooter, BattleHex shooterPosition, BattleHex destHex) const;
 	bool battleHasWallPenalty(const IBonusBearer * shooter, BattleHex shooterPosition, BattleHex destHex) const;

+ 36 - 9
server/battles/BattleFlowProcessor.cpp

@@ -389,20 +389,47 @@ bool BattleFlowProcessor::tryMakeAutomaticAction(const CBattleInfoCallback & bat
 		attack.side = next->unitSide();
 		attack.stackNumber = next->unitId();
 
-		//TODO: select target by priority
+		// TODO: unify logic with AI?
+		// Find best target using logic similar to H3 AI
+
+		const auto & isBetterTarget = [&battle](const battle::Unit * candidate, const battle::Unit * current)
+		{
+			bool candidateInsideWalls = battle.battleIsInsideWalls(candidate->getPosition());
+			bool currentInsideWalls = battle.battleIsInsideWalls(current->getPosition());
+
+			if (candidateInsideWalls != currentInsideWalls)
+				return candidateInsideWalls > currentInsideWalls;
+
+			// also check for war machines - shooters are more dangerous than war machines, ballista or catapult
+			bool candidateCanShoot = candidate->canShoot() && candidate->unitType()->warMachine == ArtifactID::NONE;
+			bool currentCanShoot = current->canShoot() && current->unitType()->warMachine == ArtifactID::NONE;
+
+			if (candidateCanShoot != currentCanShoot)
+				return candidateCanShoot > currentCanShoot;
+
+			int64_t candidateTargetValue = static_cast<int64_t>(candidate->unitType()->getAIValue() * candidate->getCount());
+			int64_t currentTargetValue = static_cast<int64_t>(current->unitType()->getAIValue() * current->getCount());
+
+			return candidateTargetValue > currentTargetValue;
+		};
 
 		const battle::Unit * target = nullptr;
 
 		for(auto & elem : battle.battleGetAllStacks(true))
 		{
-			if(elem->unitType()->getId() != CreatureID::CATAPULT
-			   && elem->unitOwner() != next->unitOwner()
-			   && elem->isValidTarget()
-			   && battle.battleCanShoot(next, elem->getPosition()))
-			{
-				target = elem;
-				break;
-			}
+			if (elem->unitOwner() == next->unitOwner())
+				continue;
+
+			if (!elem->isValidTarget())
+				continue;
+
+			if (!battle.battleCanShoot(next, elem->getPosition()))
+				continue;
+
+			if (target && !isBetterTarget(elem, target))
+				continue;
+
+			target = elem;
 		}
 
 		if(target == nullptr)