mirror of
https://github.com/vcmi/vcmi.git
synced 2025-01-26 03:52:01 +02:00
moat bypass when no targets to attack
This commit is contained in:
parent
4e359cd2a3
commit
3614330b3d
@ -16,6 +16,7 @@
|
|||||||
#include "EnemyInfo.h"
|
#include "EnemyInfo.h"
|
||||||
#include "../../lib/CStopWatch.h"
|
#include "../../lib/CStopWatch.h"
|
||||||
#include "../../lib/CThreadHelper.h"
|
#include "../../lib/CThreadHelper.h"
|
||||||
|
#include "../../lib/mapObjects/CGTownInstance.h"
|
||||||
#include "../../lib/spells/CSpellHandler.h"
|
#include "../../lib/spells/CSpellHandler.h"
|
||||||
#include "../../lib/spells/ISpellMechanics.h"
|
#include "../../lib/spells/ISpellMechanics.h"
|
||||||
#include "../../lib/CStack.h" // TODO: remove
|
#include "../../lib/CStack.h" // TODO: remove
|
||||||
@ -61,6 +62,26 @@ SpellTypes spellType(const CSpell * spell)
|
|||||||
return SpellTypes::OTHER;
|
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()
|
CBattleAI::CBattleAI()
|
||||||
: side(-1), wasWaitingForRealize(false), wasUnlockingGs(false)
|
: side(-1), wasWaitingForRealize(false), wasUnlockingGs(false)
|
||||||
{
|
{
|
||||||
@ -199,7 +220,7 @@ BattleAction CBattleAI::activeStack( const CStack * stack )
|
|||||||
|
|
||||||
if(dists.distToNearestNeighbour(stack, *closestEnemy) < GameConstants::BFIELD_SIZE)
|
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);
|
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 &)
|
catch(boost::thread_interrupted &)
|
||||||
{
|
{
|
||||||
@ -217,33 +253,41 @@ BattleAction CBattleAI::activeStack( const CStack * stack )
|
|||||||
{
|
{
|
||||||
logAi->error("Exception occurred in %s %s",__FUNCTION__, e.what());
|
logAi->error("Exception occurred in %s %s",__FUNCTION__, e.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
return BattleAction::makeDefend(stack);
|
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 reachability = cb->getReachability(stack);
|
||||||
auto avHexes = cb->battleGetAvailableHexes(reachability, stack);
|
auto avHexes = cb->battleGetAvailableHexes(reachability, stack);
|
||||||
auto destination = enemy->getPosition();
|
|
||||||
|
|
||||||
if(vstd::contains(avHexes, destination))
|
if(!avHexes.size() || !hexes.size()) //we are blocked or dest is blocked
|
||||||
return BattleAction::makeMove(stack, destination);
|
|
||||||
|
|
||||||
auto destNeighbours = destination.neighbouringTiles();
|
|
||||||
if(vstd::contains_if(destNeighbours, [&](BattleHex n) { return stack->coversPos(destination); }))
|
|
||||||
{
|
|
||||||
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
|
|
||||||
{
|
{
|
||||||
return BattleAction::makeDefend(stack);
|
return BattleAction::makeDefend(stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
BattleHex bestNeighbor = destination;
|
std::sort(hexes.begin(), hexes.end(), [&](BattleHex h1, BattleHex h2) -> bool
|
||||||
if(reachability.distToNearestNeighbour(stack, enemy, &bestNeighbor) > GameConstants::BFIELD_SIZE)
|
{
|
||||||
|
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 = hexes.front();
|
||||||
|
|
||||||
|
if(reachability.distances[bestNeighbor] > GameConstants::BFIELD_SIZE)
|
||||||
{
|
{
|
||||||
return BattleAction::makeDefend(stack);
|
return BattleAction::makeDefend(stack);
|
||||||
}
|
}
|
||||||
@ -280,7 +324,49 @@ BattleAction CBattleAI::goTowards(const CStack * stack, const battle::Unit * ene
|
|||||||
|
|
||||||
BattleAction CBattleAI::useCatapult(const CStack * stack)
|
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()
|
void CBattleAI::attemptCastingSpell()
|
||||||
|
@ -87,5 +87,6 @@ public:
|
|||||||
//void battleCatapultAttacked(const CatapultAttack & ca) override; //called when catapult makes an attack
|
//void battleCatapultAttacked(const CatapultAttack & ca) override; //called when catapult makes an attack
|
||||||
|
|
||||||
private:
|
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;
|
||||||
};
|
};
|
||||||
|
@ -42,31 +42,14 @@ bool ReachabilityInfo::isReachable(BattleHex hex) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
int ReachabilityInfo::distToNearestNeighbour(
|
int ReachabilityInfo::distToNearestNeighbour(
|
||||||
const battle::Unit * attacker,
|
const std::vector<BattleHex> & targetHexes,
|
||||||
const battle::Unit * defender,
|
|
||||||
BattleHex * chosenHex) const
|
BattleHex * chosenHex) const
|
||||||
{
|
{
|
||||||
int ret = 1000000;
|
int ret = 1000000;
|
||||||
auto defenderHexes = battle::Unit::getHexes(
|
|
||||||
defender->getPosition(),
|
|
||||||
defender->doubleWide(),
|
|
||||||
defender->unitSide());
|
|
||||||
|
|
||||||
std::vector<BattleHex> targetableHexes;
|
for(auto targetHex : targetHexes)
|
||||||
|
|
||||||
for(auto defenderHex : defenderHexes)
|
|
||||||
{
|
{
|
||||||
vstd::concatenate(targetableHexes, battle::Unit::getHexes(
|
for(auto & n : targetHex.neighbouringTiles())
|
||||||
defenderHex,
|
|
||||||
attacker->doubleWide(),
|
|
||||||
defender->unitSide()));
|
|
||||||
}
|
|
||||||
|
|
||||||
vstd::removeDuplicates(targetableHexes);
|
|
||||||
|
|
||||||
for(auto targetableHex : targetableHexes)
|
|
||||||
{
|
|
||||||
for(auto & n : targetableHex.neighbouringTiles())
|
|
||||||
{
|
{
|
||||||
if(distances[n] >= 0 && distances[n] < ret)
|
if(distances[n] >= 0 && distances[n] < ret)
|
||||||
{
|
{
|
||||||
@ -79,3 +62,13 @@ int ReachabilityInfo::distToNearestNeighbour(
|
|||||||
|
|
||||||
return ret;
|
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);
|
||||||
|
}
|
||||||
|
@ -44,6 +44,10 @@ struct DLL_LINKAGE ReachabilityInfo
|
|||||||
|
|
||||||
bool isReachable(BattleHex hex) const;
|
bool isReachable(BattleHex hex) const;
|
||||||
|
|
||||||
|
int distToNearestNeighbour(
|
||||||
|
const std::vector<BattleHex> & targetHexes,
|
||||||
|
BattleHex * chosenHex = nullptr) const;
|
||||||
|
|
||||||
int distToNearestNeighbour(
|
int distToNearestNeighbour(
|
||||||
const battle::Unit * attacker,
|
const battle::Unit * attacker,
|
||||||
const battle::Unit * defender,
|
const battle::Unit * defender,
|
||||||
|
@ -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
|
bool Unit::coversPos(BattleHex pos) const
|
||||||
{
|
{
|
||||||
return getPosition() == pos || (doubleWide() && (occupiedHex() == pos));
|
return getPosition() == pos || (doubleWide() && (occupiedHex() == pos));
|
||||||
|
@ -80,6 +80,7 @@ public:
|
|||||||
virtual std::string getDescription() const;
|
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> 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);
|
static std::vector<BattleHex> getSurroundingHexes(BattleHex position, bool twoHex, ui8 side);
|
||||||
|
|
||||||
bool coversPos(BattleHex position) const; //checks also if unit is double-wide
|
bool coversPos(BattleHex position) const; //checks also if unit is double-wide
|
||||||
|
@ -6117,7 +6117,8 @@ void CGameHandler::runBattle()
|
|||||||
|
|
||||||
for(auto & elem : gs->curB->stacks)
|
for(auto & elem : gs->curB->stacks)
|
||||||
{
|
{
|
||||||
if(elem->owner != next->owner
|
if(elem->getCreature()->idNumber != CreatureID::CATAPULT
|
||||||
|
&& elem->owner != next->owner
|
||||||
&& elem->isValidTarget()
|
&& elem->isValidTarget()
|
||||||
&& gs->curB->battleCanShoot(next, elem->getPosition()))
|
&& gs->curB->battleCanShoot(next, elem->getPosition()))
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user