Prechádzať zdrojové kódy

moat bypass when no targets to attack

Andrii Danylchenko 4 rokov pred
rodič
commit
3614330b3d

+ 101 - 15
AI/BattleAI/BattleAI.cpp

@@ -16,6 +16,7 @@
 #include "EnemyInfo.h"
 #include "../../lib/CStopWatch.h"
 #include "../../lib/CThreadHelper.h"
+#include "../../lib/mapObjects/CGTownInstance.h"
 #include "../../lib/spells/CSpellHandler.h"
 #include "../../lib/spells/ISpellMechanics.h"
 #include "../../lib/CStack.h" // TODO: remove
@@ -61,6 +62,26 @@ SpellTypes spellType(const CSpell * spell)
 	return SpellTypes::OTHER;
 }
 
+std::vector<BattleHex> CBattleAI::getBrokenWallMoatHexes() const
+{
+	std::vector<BattleHex> result;
+	
+	for(int wallPart = EWallPart::BOTTOM_WALL; wallPart < EWallPart::UPPER_WALL; wallPart++)
+	{
+		auto state = cb->battleGetWallState(wallPart);
+
+		if(state != EWallState::DESTROYED)
+			continue;
+		
+		auto wallHex = cb->wallPartToBattleHex((EWallPart::EWallPart)wallPart);
+		auto moatHex = wallHex.cloneInDirection(BattleHex::LEFT);
+
+		result.push_back(moatHex);
+	}
+
+	return result;
+}
+
 CBattleAI::CBattleAI()
 	: side(-1), wasWaitingForRealize(false), wasUnlockingGs(false)
 {
@@ -199,7 +220,7 @@ BattleAction CBattleAI::activeStack( const CStack * stack )
 
 					if(dists.distToNearestNeighbour(stack, *closestEnemy) < GameConstants::BFIELD_SIZE)
 					{
-						return goTowards(stack, *closestEnemy);
+						return goTowardsNearest(stack, (*closestEnemy)->getAttackableHexes(stack));
 					}
 				}
 			}
@@ -208,6 +229,21 @@ BattleAction CBattleAI::activeStack( const CStack * stack )
 				return BattleAction::makeWait(stack);
 			}
 		}
+
+		if(!stack->hasBonusOfType(Bonus::FLYING)
+			&& stack->unitSide() == BattleSide::ATTACKER
+			&& cb->battleGetSiegeLevel() >= CGTownInstance::CITADEL)
+		{
+			auto brokenWallMoat = getBrokenWallMoatHexes();
+
+			if(brokenWallMoat.size())
+			{
+				if(stack->doubleWide() && vstd::contains(brokenWallMoat, stack->getPosition()))
+					return BattleAction::makeMove(stack, stack->getPosition().cloneInDirection(BattleHex::RIGHT));
+				else
+					return goTowardsNearest(stack, brokenWallMoat);
+			}
+		}
 	}
 	catch(boost::thread_interrupted &)
 	{
@@ -217,33 +253,41 @@ BattleAction CBattleAI::activeStack( const CStack * stack )
 	{
 		logAi->error("Exception occurred in %s %s",__FUNCTION__, e.what());
 	}
+
 	return BattleAction::makeDefend(stack);
 }
 
-BattleAction CBattleAI::goTowards(const CStack * stack, const battle::Unit * enemy) const
+BattleAction CBattleAI::goTowardsNearest(const CStack * stack, std::vector<BattleHex> hexes) const
 {
 	auto reachability = cb->getReachability(stack);
 	auto avHexes = cb->battleGetAvailableHexes(reachability, stack);
-	auto destination = enemy->getPosition();
-
-	if(vstd::contains(avHexes, destination))
-		return BattleAction::makeMove(stack, destination);
 
-	auto destNeighbours = destination.neighbouringTiles();
-	if(vstd::contains_if(destNeighbours, [&](BattleHex n) { return stack->coversPos(destination); }))
+	if(!avHexes.size() || !hexes.size()) //we are blocked or dest is blocked
 	{
-		logAi->warn("Warning: already standing on neighbouring tile!");
-		//We shouldn't even be here...
 		return BattleAction::makeDefend(stack);
 	}
 
-	if(!avHexes.size()) //we are blocked or dest is blocked
+	std::sort(hexes.begin(), hexes.end(), [&](BattleHex h1, BattleHex h2) -> bool
 	{
-		return BattleAction::makeDefend(stack);
+		return reachability.distances[h1] < reachability.distances[h2];
+	});
+
+	for(auto hex : hexes)
+	{
+		if(vstd::contains(avHexes, hex))
+			return BattleAction::makeMove(stack, hex);
+
+		if(stack->coversPos(hex))
+		{
+			logAi->warn("Warning: already standing on neighbouring tile!");
+			//We shouldn't even be here...
+			return BattleAction::makeDefend(stack);
+		}
 	}
 
-	BattleHex bestNeighbor = destination;
-	if(reachability.distToNearestNeighbour(stack, enemy, &bestNeighbor) > GameConstants::BFIELD_SIZE)
+	BattleHex bestNeighbor = hexes.front();
+
+	if(reachability.distances[bestNeighbor] > GameConstants::BFIELD_SIZE)
 	{
 		return BattleAction::makeDefend(stack);
 	}
@@ -280,7 +324,49 @@ BattleAction CBattleAI::goTowards(const CStack * stack, const battle::Unit * ene
 
 BattleAction CBattleAI::useCatapult(const CStack * stack)
 {
-	throw std::runtime_error("CBattleAI::useCatapult is not implemented.");
+	BattleAction attack;
+	BattleHex targetHex = BattleHex::INVALID;
+
+	if(cb->battleGetGateState() == EGateState::CLOSED)
+	{
+		targetHex = cb->wallPartToBattleHex(EWallPart::GATE);
+	}
+	else
+	{
+		EWallPart::EWallPart wallParts[] = {
+			EWallPart::KEEP,
+			EWallPart::BOTTOM_TOWER,
+			EWallPart::UPPER_TOWER,
+			EWallPart::BELOW_GATE,
+			EWallPart::OVER_GATE,
+			EWallPart::BOTTOM_WALL,
+			EWallPart::UPPER_WALL
+		};
+
+		for(auto wallPart : wallParts)
+		{
+			auto wallState = cb->battleGetWallState(wallPart);
+
+			if(wallState == EWallState::INTACT || wallState == EWallState::DAMAGED)
+			{
+				targetHex = cb->wallPartToBattleHex(wallPart);
+
+				break;
+			}
+		}
+	}
+
+	if(!targetHex.isValid())
+	{
+		return BattleAction::makeDefend(stack);
+	}
+
+	attack.aimToHex(targetHex);
+	attack.actionType = EActionType::CATAPULT;
+	attack.side = side;
+	attack.stackNumber = stack->ID;
+
+	return attack;
 }
 
 void CBattleAI::attemptCastingSpell()

+ 2 - 1
AI/BattleAI/BattleAI.h

@@ -87,5 +87,6 @@ public:
 	//void battleCatapultAttacked(const CatapultAttack & ca) override; //called when catapult makes an attack
 
 private:
-	BattleAction goTowards(const CStack * stack, const battle::Unit * enemy) const;
+	BattleAction goTowardsNearest(const CStack * stack, std::vector<BattleHex> hexes) const;
+	std::vector<BattleHex> getBrokenWallMoatHexes() const;
 };

+ 13 - 20
lib/battle/ReachabilityInfo.cpp

@@ -42,31 +42,14 @@ bool ReachabilityInfo::isReachable(BattleHex hex) const
 }
 
 int ReachabilityInfo::distToNearestNeighbour(
-	const battle::Unit * attacker,
-	const battle::Unit * defender,
+	const std::vector<BattleHex> & targetHexes,
 	BattleHex * chosenHex) const
 {
 	int ret = 1000000;
-	auto defenderHexes = battle::Unit::getHexes(
-		defender->getPosition(),
-		defender->doubleWide(),
-		defender->unitSide());
-
-	std::vector<BattleHex> targetableHexes;
-
-	for(auto defenderHex : defenderHexes)
-	{
-		vstd::concatenate(targetableHexes, battle::Unit::getHexes(
-			defenderHex,
-			attacker->doubleWide(),
-			defender->unitSide()));
-	}
 
-	vstd::removeDuplicates(targetableHexes);
-
-	for(auto targetableHex : targetableHexes)
+	for(auto targetHex : targetHexes)
 	{
-		for(auto & n : targetableHex.neighbouringTiles())
+		for(auto & n : targetHex.neighbouringTiles())
 		{
 			if(distances[n] >= 0 && distances[n] < ret)
 			{
@@ -79,3 +62,13 @@ int ReachabilityInfo::distToNearestNeighbour(
 
 	return ret;
 }
+
+int ReachabilityInfo::distToNearestNeighbour(
+	const battle::Unit * attacker,
+	const battle::Unit * defender,
+	BattleHex * chosenHex) const
+{
+	auto attackableHexes = defender->getAttackableHexes(attacker);
+
+	return distToNearestNeighbour(attackableHexes, chosenHex);
+}

+ 4 - 0
lib/battle/ReachabilityInfo.h

@@ -44,6 +44,10 @@ struct DLL_LINKAGE ReachabilityInfo
 
 	bool isReachable(BattleHex hex) const;
 
+	int distToNearestNeighbour(
+		const std::vector<BattleHex> & targetHexes,
+		BattleHex * chosenHex = nullptr) const;
+
 	int distToNearestNeighbour(
 		const battle::Unit * attacker,
 		const battle::Unit * defender,

+ 28 - 0
lib/battle/Unit.cpp

@@ -82,6 +82,34 @@ std::vector<BattleHex> Unit::getSurroundingHexes(BattleHex position, bool twoHex
 	}
 }
 
+std::vector<BattleHex> Unit::getAttackableHexes(const Unit * attacker) const
+{
+	auto defenderHexes = battle::Unit::getHexes(
+		getPosition(),
+		doubleWide(),
+		unitSide());
+	
+	std::vector<BattleHex> targetableHexes;
+
+	for(auto defenderHex : defenderHexes)
+	{
+		auto hexes = battle::Unit::getHexes(
+			defenderHex,
+			attacker->doubleWide(),
+			unitSide());
+
+		if(hexes.size() == 2 && BattleHex::getDistance(hexes.front(), hexes.back()) != 1)
+			hexes.pop_back();
+
+		for(auto hex : hexes)
+			vstd::concatenate(targetableHexes, hex.neighbouringTiles());
+	}
+
+	vstd::removeDuplicates(targetableHexes);
+
+	return targetableHexes;
+}
+
 bool Unit::coversPos(BattleHex pos) const
 {
 	return getPosition() == pos || (doubleWide() && (occupiedHex() == pos));

+ 1 - 0
lib/battle/Unit.h

@@ -80,6 +80,7 @@ public:
 	virtual std::string getDescription() const;
 
 	std::vector<BattleHex> getSurroundingHexes(BattleHex assumedPosition = BattleHex::INVALID) const; // get six or 8 surrounding hexes depending on creature size
+	std::vector<BattleHex> getAttackableHexes(const Unit * attacker) const;
 	static std::vector<BattleHex> getSurroundingHexes(BattleHex position, bool twoHex, ui8 side);
 
 	bool coversPos(BattleHex position) const; //checks also if unit is double-wide

+ 2 - 1
server/CGameHandler.cpp

@@ -6117,7 +6117,8 @@ void CGameHandler::runBattle()
 
 				for(auto & elem : gs->curB->stacks)
 				{
-					if(elem->owner != next->owner
+					if(elem->getCreature()->idNumber != CreatureID::CATAPULT
+						&& elem->owner != next->owner
 						&& elem->isValidTarget()
 						&& gs->curB->battleCanShoot(next, elem->getPosition()))
 					{