mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +02:00
Nullkiller: update / fix build, core changes required for Nullkiller AI
This commit is contained in:
parent
b4241670ba
commit
3fa7e0976f
@ -167,11 +167,22 @@ BattleAction CBattleAI::activeStack( const CStack * stack )
|
||||
if(bestSpellcast.is_initialized() && bestSpellcast->value > bestAttack.damageDiff())
|
||||
return BattleAction::makeCreatureSpellcast(stack, bestSpellcast->dest, bestSpellcast->spell->id);
|
||||
else if(bestAttack.attack.shooting)
|
||||
{
|
||||
auto &target = bestAttack;
|
||||
logAi->debug("BattleAI: %s -> %s x %d, shot, from %d curpos %d dist %d speed %d: %lld %lld %lld",
|
||||
target.attackerState->unitType()->identifier,
|
||||
target.affectedUnits[0]->unitType()->identifier,
|
||||
(int)target.affectedUnits.size(), (int)target.from, (int)bestAttack.attack.attacker->getPosition().hex,
|
||||
bestAttack.attack.chargedFields, bestAttack.attack.attacker->Speed(0, true),
|
||||
target.damageDealt, target.damageReceived, target.attackValue()
|
||||
);
|
||||
|
||||
return BattleAction::makeShotAttack(stack, bestAttack.attack.defender);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto &target = bestAttack;
|
||||
logAi->debug("BattleAI: %s -> %s %d from, %d curpos %d dist %d speed %d: %lld %lld %lld",
|
||||
logAi->debug("BattleAI: %s -> %s x %d, mellee, from %d curpos %d dist %d speed %d: %lld %lld %lld",
|
||||
target.attackerState->unitType()->identifier,
|
||||
target.affectedUnits[0]->unitType()->identifier,
|
||||
(int)target.affectedUnits.size(), (int)target.from, (int)bestAttack.attack.attacker->getPosition().hex,
|
||||
|
@ -146,13 +146,16 @@ std::vector<SlotInfo> ArmyManager::getBestArmy(const IBonusBearer * armyCarrier,
|
||||
auto morale = slot.second->MoraleVal();
|
||||
auto multiplier = 1.0f;
|
||||
|
||||
const float BadMoraleChance = 0.083f;
|
||||
const float HighMoraleChance = 0.04f;
|
||||
|
||||
if(morale < 0)
|
||||
{
|
||||
multiplier += morale * 0.083f;
|
||||
multiplier += morale * BadMoraleChance;
|
||||
}
|
||||
else if(morale > 0)
|
||||
{
|
||||
multiplier += morale * 0.04f;
|
||||
multiplier += morale * HighMoraleChance;
|
||||
}
|
||||
|
||||
newValue += multiplier * slot.second->getPower();
|
||||
@ -439,7 +442,7 @@ std::vector<StackUpgradeInfo> ArmyManager::getPossibleUpgrades(const CCreatureSe
|
||||
return upgrades;
|
||||
}
|
||||
|
||||
ArmyUpgradeInfo ArmyManager::calculateCreateresUpgrade(
|
||||
ArmyUpgradeInfo ArmyManager::calculateCreaturesUpgrade(
|
||||
const CCreatureSet * army,
|
||||
const CGObjectInstance * upgrader,
|
||||
const TResources & availableResources) const
|
||||
|
@ -55,7 +55,7 @@ public:
|
||||
virtual std::vector<creInfo> getArmyAvailableToBuy(const CCreatureSet * hero, const CGDwelling * dwelling, TResources availableRes) const = 0;
|
||||
virtual uint64_t evaluateStackPower(const CCreature * creature, int count) const = 0;
|
||||
virtual SlotInfo getTotalCreaturesAvailable(CreatureID creatureID) const = 0;
|
||||
virtual ArmyUpgradeInfo calculateCreateresUpgrade(
|
||||
virtual ArmyUpgradeInfo calculateCreaturesUpgrade(
|
||||
const CCreatureSet * army,
|
||||
const CGObjectInstance * upgrader,
|
||||
const TResources & availableResources) const = 0;
|
||||
@ -90,7 +90,7 @@ public:
|
||||
std::shared_ptr<CCreatureSet> getArmyAvailableToBuyAsCCreatureSet(const CGDwelling * dwelling, TResources availableRes) const override;
|
||||
uint64_t evaluateStackPower(const CCreature * creature, int count) const override;
|
||||
SlotInfo getTotalCreaturesAvailable(CreatureID creatureID) const override;
|
||||
ArmyUpgradeInfo calculateCreateresUpgrade(
|
||||
ArmyUpgradeInfo calculateCreaturesUpgrade(
|
||||
const CCreatureSet * army,
|
||||
const CGObjectInstance * upgrader,
|
||||
const TResources & availableResources) const override;
|
||||
|
@ -224,7 +224,7 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader)
|
||||
continue;
|
||||
}
|
||||
|
||||
auto upgrade = ai->nullkiller->armyManager->calculateCreateresUpgrade(path.heroArmy, upgrader, availableResources);
|
||||
auto upgrade = ai->nullkiller->armyManager->calculateCreaturesUpgrade(path.heroArmy, upgrader, availableResources);
|
||||
auto armyValue = (float)upgrade.upgradeValue / path.getHeroStrength();
|
||||
|
||||
if(armyValue < 0.1f || upgrade.upgradeValue < 300) // avoid small upgrades
|
||||
|
@ -95,8 +95,16 @@ TResources getCreatureBankResources(const CGObjectInstance * target, const CGHer
|
||||
auto objectInfo = VLC->objtypeh->getHandlerFor(target->ID, target->subID)->getObjectInfo(target->appearance);
|
||||
CBankInfo * bankInfo = dynamic_cast<CBankInfo *>(objectInfo.get());
|
||||
auto resources = bankInfo->getPossibleResourcesReward();
|
||||
TResources result = TResources();
|
||||
int sum = 0;
|
||||
|
||||
return resources;
|
||||
for(auto & reward : resources)
|
||||
{
|
||||
result += reward.data * reward.chance;
|
||||
sum += reward.chance;
|
||||
}
|
||||
|
||||
return sum > 1 ? result / sum : result;
|
||||
}
|
||||
|
||||
uint64_t getCreatureBankArmyReward(const CGObjectInstance * target, const CGHeroInstance * hero)
|
||||
@ -108,7 +116,7 @@ uint64_t getCreatureBankArmyReward(const CGObjectInstance * target, const CGHero
|
||||
|
||||
for(auto c : creatures)
|
||||
{
|
||||
result += c.type->AIValue * c.count;
|
||||
result += c.data.type->AIValue * c.data.count * c.chance / 100;
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -205,7 +213,7 @@ uint64_t RewardEvaluator::getArmyReward(
|
||||
case Obj::TOWN:
|
||||
return target->tempOwner == PlayerColor::NEUTRAL ? 1000 : 10000;
|
||||
case Obj::HILL_FORT:
|
||||
return ai->armyManager->calculateCreateresUpgrade(army, target, ai->cb->getResourceAmount()).upgradeValue;
|
||||
return ai->armyManager->calculateCreaturesUpgrade(army, target, ai->cb->getResourceAmount()).upgradeValue;
|
||||
case Obj::CREATURE_BANK:
|
||||
return getCreatureBankArmyReward(target, hero);
|
||||
case Obj::CREATURE_GENERATOR1:
|
||||
@ -239,7 +247,7 @@ int RewardEvaluator::getGoldCost(const CGObjectInstance * target, const CGHeroIn
|
||||
switch(target->ID)
|
||||
{
|
||||
case Obj::HILL_FORT:
|
||||
return ai->armyManager->calculateCreateresUpgrade(army, target, ai->cb->getResourceAmount()).upgradeCost[Res::GOLD];
|
||||
return ai->armyManager->calculateCreaturesUpgrade(army, target, ai->cb->getResourceAmount()).upgradeCost[Res::GOLD];
|
||||
case Obj::SCHOOL_OF_MAGIC:
|
||||
case Obj::SCHOOL_OF_WAR:
|
||||
return 1000;
|
||||
|
@ -1267,7 +1267,7 @@ std::vector<AIPath> AINodeStorage::getChainInfo(const int3 & pos, bool isOnLand)
|
||||
path.targetHero = node.actor->hero;
|
||||
path.heroArmy = node.actor->creatureSet;
|
||||
path.armyLoss = node.armyLoss;
|
||||
path.targetObjectDanger = evaluateDanger(pos, path.targetHero, false);
|
||||
path.targetObjectDanger = evaluateDanger(pos, path.targetHero, !node.actor->allowBattle);
|
||||
path.targetObjectArmyLoss = evaluateArmyLoss(path.targetHero, path.heroArmy->getArmyStrength(), path.targetObjectDanger);
|
||||
path.chainMask = node.actor->chainMask;
|
||||
path.exchangeCount = node.actor->actorExchangeCount;
|
||||
|
@ -11,7 +11,7 @@
|
||||
#pragma once
|
||||
|
||||
#define PATHFINDER_TRACE_LEVEL 0
|
||||
#define AI_TRACE_LEVEL 1
|
||||
#define AI_TRACE_LEVEL 0
|
||||
#define SCOUT_TURN_DISTANCE_LIMIT 3
|
||||
#define MAIN_TURN_DISTANCE_LIMIT 5
|
||||
|
||||
|
@ -343,7 +343,7 @@ HeroExchangeArmy * HeroExchangeMap::tryUpgrade(
|
||||
TResources resources) const
|
||||
{
|
||||
HeroExchangeArmy * target = new HeroExchangeArmy();
|
||||
auto upgradeInfo = ai->armyManager->calculateCreateresUpgrade(army, upgrader, resources);
|
||||
auto upgradeInfo = ai->armyManager->calculateCreaturesUpgrade(army, upgrader, resources);
|
||||
|
||||
if(upgradeInfo.upgradeValue)
|
||||
{
|
||||
|
@ -26,7 +26,7 @@ AINodeStorage::AINodeStorage(const int3 & Sizes)
|
||||
|
||||
AINodeStorage::~AINodeStorage() = default;
|
||||
|
||||
void AINodeStorage::initialize(const PathfinderOptions & options, const CGameState * gs, const CGHeroInstance * hero)
|
||||
void AINodeStorage::initialize(const PathfinderOptions & options, const CGameState * gs)
|
||||
{
|
||||
//TODO: fix this code duplication with NodeStorage::initialize, problem is to keep `resetTile` inline
|
||||
|
||||
@ -109,7 +109,7 @@ boost::optional<AIPathNode *> AINodeStorage::getOrCreateNode(const int3 & pos, c
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
CGPathNode * AINodeStorage::getInitialNode()
|
||||
std::vector<CGPathNode *> AINodeStorage::getInitialNodes()
|
||||
{
|
||||
auto hpos = hero->getPosition(false);
|
||||
auto initialNode =
|
||||
@ -121,7 +121,7 @@ CGPathNode * AINodeStorage::getInitialNode()
|
||||
initialNode->danger = 0;
|
||||
initialNode->setCost(0.0);
|
||||
|
||||
return initialNode;
|
||||
return {initialNode};
|
||||
}
|
||||
|
||||
void AINodeStorage::resetTile(const int3 & coord, EPathfindingLayer layer, CGPathNode::EAccessibility accessibility)
|
||||
|
@ -80,9 +80,9 @@ public:
|
||||
AINodeStorage(const int3 & sizes);
|
||||
~AINodeStorage();
|
||||
|
||||
void initialize(const PathfinderOptions & options, const CGameState * gs, const CGHeroInstance * hero) override;
|
||||
void initialize(const PathfinderOptions & options, const CGameState * gs) override;
|
||||
|
||||
virtual CGPathNode * getInitialNode() override;
|
||||
virtual std::vector<CGPathNode *> getInitialNodes() override;
|
||||
|
||||
virtual std::vector<CGPathNode *> calculateNeighbours(
|
||||
const PathNodeInfo & source,
|
||||
@ -106,11 +106,7 @@ public:
|
||||
bool isTileAccessible(const int3 & pos, const EPathfindingLayer layer) const;
|
||||
|
||||
void setHero(HeroPtr heroPtr, const VCAI * ai);
|
||||
|
||||
const CGHeroInstance * getHero() const
|
||||
{
|
||||
return hero;
|
||||
}
|
||||
const CGHeroInstance * getHero() const { return hero; }
|
||||
|
||||
uint64_t evaluateDanger(const int3 & tile) const
|
||||
{
|
||||
|
@ -56,8 +56,8 @@ void AIPathfinder::updatePaths(std::vector<HeroPtr> heroes)
|
||||
auto calculatePaths = [&](const CGHeroInstance * hero, std::shared_ptr<AIPathfinding::AIPathfinderConfig> config)
|
||||
{
|
||||
logAi->debug("Recalculate paths for %s", hero->name);
|
||||
|
||||
cb->calculatePaths(config, hero);
|
||||
|
||||
cb->calculatePaths(config);
|
||||
};
|
||||
|
||||
std::vector<CThreadHelper::Task> calculationTasks;
|
||||
|
@ -37,7 +37,17 @@ namespace AIPathfinding
|
||||
CPlayerSpecificInfoCallback * cb,
|
||||
VCAI * ai,
|
||||
std::shared_ptr<AINodeStorage> nodeStorage)
|
||||
:PathfinderConfig(nodeStorage, makeRuleset(cb, ai, nodeStorage))
|
||||
:PathfinderConfig(nodeStorage, makeRuleset(cb, ai, nodeStorage)), hero(nodeStorage->getHero())
|
||||
{
|
||||
}
|
||||
|
||||
CPathfinderHelper * AIPathfinderConfig::getOrCreatePathfinderHelper(const PathNodeInfo & source, CGameState * gs)
|
||||
{
|
||||
if(!helper)
|
||||
{
|
||||
helper.reset(new CPathfinderHelper(gs, hero, options));
|
||||
}
|
||||
|
||||
return helper.get();
|
||||
}
|
||||
}
|
||||
|
@ -17,10 +17,16 @@ namespace AIPathfinding
|
||||
{
|
||||
class AIPathfinderConfig : public PathfinderConfig
|
||||
{
|
||||
private:
|
||||
const CGHeroInstance * hero;
|
||||
std::unique_ptr<CPathfinderHelper> helper;
|
||||
|
||||
public:
|
||||
AIPathfinderConfig(
|
||||
CPlayerSpecificInfoCallback * cb,
|
||||
VCAI * ai,
|
||||
std::shared_ptr<AINodeStorage> nodeStorage);
|
||||
|
||||
virtual CPathfinderHelper * getOrCreatePathfinderHelper(const PathNodeInfo & source, CGameState * gs) override;
|
||||
};
|
||||
}
|
||||
|
@ -2670,10 +2670,16 @@ void AIStatus::attemptedAnsweringQuery(QueryID queryID, int answerRequestID)
|
||||
|
||||
void AIStatus::receivedAnswerConfirmation(int answerRequestID, int result)
|
||||
{
|
||||
assert(vstd::contains(requestToQueryID, answerRequestID));
|
||||
QueryID query = requestToQueryID[answerRequestID];
|
||||
assert(vstd::contains(remainingQueries, query));
|
||||
requestToQueryID.erase(answerRequestID);
|
||||
QueryID query;
|
||||
|
||||
{
|
||||
boost::unique_lock<boost::mutex> lock(mx);
|
||||
|
||||
assert(vstd::contains(requestToQueryID, answerRequestID));
|
||||
query = requestToQueryID[answerRequestID];
|
||||
assert(vstd::contains(remainingQueries, query));
|
||||
requestToQueryID.erase(answerRequestID);
|
||||
}
|
||||
|
||||
if(result)
|
||||
{
|
||||
|
@ -4,7 +4,7 @@ git submodule update --init --recursive
|
||||
cd ..
|
||||
|
||||
curl -LfsS -o "vcpkg-export-${VCMI_BUILD_PLATFORM}-windows-v140.7z" \
|
||||
"https://github.com/nullkiller/vcmi-deps-windows/releases/download/v1.3/vcpkg-export-${VCMI_BUILD_PLATFORM}-windows-v140.7z"
|
||||
"https://github.com/vcmi/vcmi-deps-windows/releases/download/v1.3/vcpkg-export-${VCMI_BUILD_PLATFORM}-windows-v140.7z"
|
||||
7z x "vcpkg-export-${VCMI_BUILD_PLATFORM}-windows-v140.7z"
|
||||
|
||||
rmdir vcpkg\installed\${VCMI_BUILD_PLATFORM}-windows\debug /S/Q
|
||||
|
@ -15,7 +15,7 @@ sudo apt-get install -f --yes
|
||||
|
||||
if false; then
|
||||
# Add MXE repository and key
|
||||
echo "deb http://pkg.mxe.cc/repos/apt trusty main" \
|
||||
echo "deb http://pkg.mxe.cc/repos/apt/debian wheezy main" \
|
||||
| sudo tee /etc/apt/sources.list.d/mxeapt.list
|
||||
|
||||
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys D43A795B73B16ABE9643FE1AFD8FFF16DB45C6AB
|
||||
@ -35,6 +35,7 @@ if false; then
|
||||
mxe-$MXE_TARGET-ffmpeg \
|
||||
mxe-$MXE_TARGET-qt \
|
||||
mxe-$MXE_TARGET-qtbase \
|
||||
mxe-$MXE_TARGET-intel-tbb \
|
||||
mxe-i686-w64-mingw32.static-luajit
|
||||
|
||||
fi # Disable
|
||||
|
@ -159,7 +159,7 @@ if(ENABLE_DEBUG_CONSOLE)
|
||||
else()
|
||||
add_executable(vcmiclient WIN32 ${client_SRCS} ${client_HEADERS} ${client_ICON})
|
||||
endif(ENABLE_DEBUG_CONSOLE)
|
||||
add_dependencies(vcmiclient vcmiserver BattleAI StupidAI VCAI)
|
||||
add_dependencies(vcmiclient vcmiserver BattleAI StupidAI VCAI Nullkiller)
|
||||
|
||||
if(WIN32)
|
||||
set_target_properties(vcmiclient
|
||||
|
@ -923,9 +923,9 @@ void CGameInfoCallback::getVisibleTilesInRange(std::unordered_set<int3, ShashInt
|
||||
gs->getTilesInRange(tiles, pos, radious, getLocalPlayer(), -1, distanceFormula);
|
||||
}
|
||||
|
||||
void CGameInfoCallback::calculatePaths(std::shared_ptr<PathfinderConfig> config, const CGHeroInstance * hero)
|
||||
void CGameInfoCallback::calculatePaths(std::shared_ptr<PathfinderConfig> config)
|
||||
{
|
||||
gs->calculatePaths(config, hero);
|
||||
gs->calculatePaths(config);
|
||||
}
|
||||
|
||||
const CArtifactInstance * CGameInfoCallback::getArtInstance( ArtifactInstanceID aid ) const
|
||||
|
@ -188,7 +188,7 @@ public:
|
||||
virtual std::shared_ptr<boost::multi_array<TerrainTile*, 3>> getAllVisibleTiles() const;
|
||||
virtual bool isInTheMap(const int3 &pos) const;
|
||||
virtual void getVisibleTilesInRange(std::unordered_set<int3, ShashInt3> &tiles, int3 pos, int radious, int3::EDistanceFormula distanceFormula = int3::DIST_2D) const;
|
||||
virtual void calculatePaths(std::shared_ptr<PathfinderConfig> config, const CGHeroInstance * hero);
|
||||
virtual void calculatePaths(std::shared_ptr<PathfinderConfig> config);
|
||||
|
||||
//town
|
||||
virtual const CGTownInstance* getTown(ObjectInstanceID objid) const;
|
||||
|
@ -2056,9 +2056,9 @@ void CGameState::calculatePaths(const CGHeroInstance *hero, CPathsInfo &out)
|
||||
pathfinder.calculatePaths();
|
||||
}
|
||||
|
||||
void CGameState::calculatePaths(std::shared_ptr<PathfinderConfig> config, const CGHeroInstance * hero)
|
||||
void CGameState::calculatePaths(std::shared_ptr<PathfinderConfig> config)
|
||||
{
|
||||
CPathfinder pathfinder(this, hero, config);
|
||||
CPathfinder pathfinder(this, config);
|
||||
pathfinder.calculatePaths();
|
||||
}
|
||||
|
||||
|
@ -184,7 +184,7 @@ public:
|
||||
PlayerRelations::PlayerRelations getPlayerRelations(PlayerColor color1, PlayerColor color2);
|
||||
bool checkForVisitableDir(const int3 & src, const int3 & dst) const; //check if src tile is visitable from dst tile
|
||||
void calculatePaths(const CGHeroInstance *hero, CPathsInfo &out); //calculates possible paths for hero, by default uses current hero position and movement left; returns pointer to newly allocated CPath or nullptr if path does not exists
|
||||
void calculatePaths(std::shared_ptr<PathfinderConfig> config, const CGHeroInstance * hero) override;
|
||||
void calculatePaths(std::shared_ptr<PathfinderConfig> config) override;
|
||||
int3 guardingCreaturePosition (int3 pos) const;
|
||||
std::vector<CGObjectInstance*> guardingCreatures (int3 pos) const;
|
||||
void updateRumor();
|
||||
|
@ -26,14 +26,14 @@ bool canSeeObj(const CGObjectInstance * obj)
|
||||
return obj != nullptr && obj->ID != Obj::EVENT;
|
||||
}
|
||||
|
||||
void NodeStorage::initialize(const PathfinderOptions & options, const CGameState * gs, const CGHeroInstance * hero)
|
||||
void NodeStorage::initialize(const PathfinderOptions & options, const CGameState * gs)
|
||||
{
|
||||
//TODO: fix this code duplication with AINodeStorage::initialize, problem is to keep `resetTile` inline
|
||||
|
||||
int3 pos;
|
||||
const PlayerColor player = out.hero->tempOwner;
|
||||
const int3 sizes = gs->getMapSize();
|
||||
const auto & fow = static_cast<const CGameInfoCallback *>(gs)->getPlayerTeam(hero->tempOwner)->fogOfWarMap;
|
||||
const PlayerColor player = hero->tempOwner;
|
||||
const auto & fow = static_cast<const CGameInfoCallback *>(gs)->getPlayerTeam(player)->fogOfWarMap;
|
||||
|
||||
//make 200% sure that these are loop invariants (also a bit shorter code), let compiler do the rest(loop unswitching)
|
||||
const bool useFlying = options.useFlying;
|
||||
@ -155,15 +155,20 @@ void NodeStorage::resetTile(
|
||||
getNode(tile, layer)->update(tile, layer, accessibility);
|
||||
}
|
||||
|
||||
CGPathNode * NodeStorage::getInitialNode()
|
||||
std::vector<CGPathNode *> NodeStorage::getInitialNodes()
|
||||
{
|
||||
auto initialNode = getNode(out.hpos, out.hero->boat ? EPathfindingLayer::SAIL : EPathfindingLayer::LAND);
|
||||
auto initialNode = getNode(out.hpos, out.hero->boat ? EPathfindingLayer::SAIL : EPathfindingLayer::LAND);
|
||||
|
||||
initialNode->turns = 0;
|
||||
initialNode->moveRemains = out.hero->movement;
|
||||
initialNode->setCost(0.0);
|
||||
|
||||
return initialNode;
|
||||
if(!initialNode->coord.valid())
|
||||
{
|
||||
initialNode->coord = out.hpos;
|
||||
}
|
||||
|
||||
return std::vector<CGPathNode *> { initialNode };
|
||||
}
|
||||
|
||||
void NodeStorage::commit(CDestinationNodeInfo & destination, const PathNodeInfo & source)
|
||||
@ -256,36 +261,40 @@ CPathfinder::CPathfinder(
|
||||
const CGHeroInstance * _hero)
|
||||
: CPathfinder(
|
||||
_gs,
|
||||
_hero,
|
||||
std::make_shared<PathfinderConfig>(
|
||||
std::make_shared<NodeStorage>(_out, _hero),
|
||||
std::vector<std::shared_ptr<IPathfindingRule>>{
|
||||
std::make_shared<LayerTransitionRule>(),
|
||||
std::make_shared<DestinationActionRule>(),
|
||||
std::make_shared<MovementToDestinationRule>(),
|
||||
std::make_shared<MovementCostRule>(),
|
||||
std::make_shared<MovementAfterDestinationRule>()
|
||||
}))
|
||||
std::make_shared<SingleHeroPathfinderConfig>(_out, _gs, _hero))
|
||||
{
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<IPathfindingRule>> SingleHeroPathfinderConfig::buildRuleSet()
|
||||
{
|
||||
return std::vector<std::shared_ptr<IPathfindingRule>>{
|
||||
std::make_shared<LayerTransitionRule>(),
|
||||
std::make_shared<DestinationActionRule>(),
|
||||
std::make_shared<MovementToDestinationRule>(),
|
||||
std::make_shared<MovementCostRule>(),
|
||||
std::make_shared<MovementAfterDestinationRule>()
|
||||
};
|
||||
}
|
||||
|
||||
SingleHeroPathfinderConfig::SingleHeroPathfinderConfig(CPathsInfo & out, CGameState * gs, const CGHeroInstance * hero)
|
||||
: PathfinderConfig(std::make_shared<NodeStorage>(out, hero), buildRuleSet())
|
||||
{
|
||||
pathfinderHelper.reset(new CPathfinderHelper(gs, hero, options));
|
||||
}
|
||||
|
||||
CPathfinderHelper * SingleHeroPathfinderConfig::getOrCreatePathfinderHelper(const PathNodeInfo & source, CGameState * gs)
|
||||
{
|
||||
return pathfinderHelper.get();
|
||||
}
|
||||
|
||||
CPathfinder::CPathfinder(
|
||||
CGameState * _gs,
|
||||
const CGHeroInstance * _hero,
|
||||
std::shared_ptr<PathfinderConfig> config)
|
||||
: CGameInfoCallback(_gs, boost::optional<PlayerColor>())
|
||||
, hero(_hero)
|
||||
, patrolTiles({})
|
||||
, config(config)
|
||||
, source()
|
||||
, destination()
|
||||
{
|
||||
assert(hero);
|
||||
assert(hero == getHero(hero->id));
|
||||
|
||||
hlp = make_unique<CPathfinderHelper>(_gs, hero, config->options);
|
||||
|
||||
initializePatrol();
|
||||
initializeGraph();
|
||||
}
|
||||
|
||||
@ -316,31 +325,40 @@ void CPathfinder::calculatePaths()
|
||||
//logGlobal->info("Calculating paths for hero %s (adress %d) of player %d", hero->name, hero , hero->tempOwner);
|
||||
|
||||
//initial tile - set cost on 0 and add to the queue
|
||||
CGPathNode * initialNode = config->nodeStorage->getInitialNode();
|
||||
std::vector<CGPathNode *> initialNodes = config->nodeStorage->getInitialNodes();
|
||||
int counter = 0;
|
||||
|
||||
if(!isInTheMap(initialNode->coord)/* || !gs->map->isInTheMap(dest)*/) //check input
|
||||
for(auto initialNode : initialNodes)
|
||||
{
|
||||
logGlobal->error("CGameState::calculatePaths: Hero outside the gs->map? How dare you...");
|
||||
throw std::runtime_error("Wrong checksum");
|
||||
if(!isInTheMap(initialNode->coord)/* || !gs->map->isInTheMap(dest)*/) //check input
|
||||
{
|
||||
logGlobal->error("CGameState::calculatePaths: Hero outside the gs->map? How dare you...");
|
||||
throw std::runtime_error("Wrong checksum");
|
||||
}
|
||||
|
||||
source.setNode(gs, initialNode);
|
||||
auto hlp = config->getOrCreatePathfinderHelper(source, gs);
|
||||
|
||||
if(hlp->isHeroPatrolLocked())
|
||||
break;
|
||||
|
||||
pq.push(initialNode);
|
||||
}
|
||||
|
||||
if(isHeroPatrolLocked())
|
||||
return;
|
||||
|
||||
push(initialNode);
|
||||
|
||||
while(!pq.empty())
|
||||
{
|
||||
counter++;
|
||||
auto node = topAndPop();
|
||||
auto excludeOurHero = node->coord == initialNode->coord;
|
||||
|
||||
source.setNode(gs, node, excludeOurHero);
|
||||
source.setNode(gs, node);
|
||||
source.node->locked = true;
|
||||
|
||||
int movement = source.node->moveRemains;
|
||||
uint8_t turn = source.node->turns;
|
||||
float cost = source.node->getCost();
|
||||
|
||||
auto hlp = config->getOrCreatePathfinderHelper(source, gs);
|
||||
|
||||
hlp->updateTurnInfo(turn);
|
||||
if(!movement)
|
||||
{
|
||||
@ -350,24 +368,24 @@ void CPathfinder::calculatePaths()
|
||||
continue;
|
||||
}
|
||||
|
||||
source.guarded = isSourceGuarded();
|
||||
if(source.nodeObject)
|
||||
source.objectRelations = gs->getPlayerRelations(hero->tempOwner, source.nodeObject->tempOwner);
|
||||
source.isInitialPosition = source.nodeHero == hlp->hero;
|
||||
source.updateInfo(hlp, gs);
|
||||
|
||||
//add accessible neighbouring nodes to the queue
|
||||
auto neighbourNodes = config->nodeStorage->calculateNeighbours(source, config.get(), hlp.get());
|
||||
auto neighbourNodes = config->nodeStorage->calculateNeighbours(source, config.get(), hlp);
|
||||
for(CGPathNode * neighbour : neighbourNodes)
|
||||
{
|
||||
if(neighbour->locked)
|
||||
continue;
|
||||
|
||||
if(!isPatrolMovementAllowed(neighbour->coord))
|
||||
continue;
|
||||
|
||||
if(!hlp->isLayerAvailable(neighbour->layer))
|
||||
continue;
|
||||
|
||||
destination.setNode(gs, neighbour);
|
||||
hlp = config->getOrCreatePathfinderHelper(destination, gs);
|
||||
|
||||
if(!hlp->isPatrolMovementAllowed(neighbour->coord))
|
||||
continue;
|
||||
|
||||
/// Check transition without tile accessability rules
|
||||
if(source.node->layer != neighbour->layer && !isLayerTransitionPossible())
|
||||
@ -376,14 +394,12 @@ void CPathfinder::calculatePaths()
|
||||
destination.turn = turn;
|
||||
destination.movementLeft = movement;
|
||||
destination.cost = cost;
|
||||
destination.guarded = isDestinationGuarded();
|
||||
destination.updateInfo(hlp, gs);
|
||||
destination.isGuardianTile = destination.guarded && isDestinationGuardian();
|
||||
if(destination.nodeObject)
|
||||
destination.objectRelations = gs->getPlayerRelations(hero->tempOwner, destination.nodeObject->tempOwner);
|
||||
|
||||
for(auto rule : config->rules)
|
||||
{
|
||||
rule->process(source, destination, config.get(), hlp.get());
|
||||
rule->process(source, destination, config.get(), hlp);
|
||||
|
||||
if(destination.blocked)
|
||||
break;
|
||||
@ -395,13 +411,14 @@ void CPathfinder::calculatePaths()
|
||||
} //neighbours loop
|
||||
|
||||
//just add all passable teleport exits
|
||||
hlp = config->getOrCreatePathfinderHelper(source, gs);
|
||||
|
||||
/// For now we disable teleports usage for patrol movement
|
||||
/// VCAI not aware about patrol and may stuck while attempt to use teleport
|
||||
if(patrolState == PATROL_RADIUS)
|
||||
if(hlp->patrolState == CPathfinderHelper::PATROL_RADIUS)
|
||||
continue;
|
||||
|
||||
auto teleportationNodes = config->nodeStorage->calculateTeleportations(source, config.get(), hlp.get());
|
||||
auto teleportationNodes = config->nodeStorage->calculateTeleportations(source, config.get(), hlp);
|
||||
for(CGPathNode * teleportNode : teleportationNodes)
|
||||
{
|
||||
if(teleportNode->locked)
|
||||
@ -429,6 +446,8 @@ void CPathfinder::calculatePaths()
|
||||
}
|
||||
}
|
||||
} //queue loop
|
||||
|
||||
logAi->trace("CPathfinder finished with %s iterations", std::to_string(counter));
|
||||
}
|
||||
|
||||
std::vector<int3> CPathfinderHelper::getAllowedTeleportChannelExits(TeleportChannelID channelID) const
|
||||
@ -498,12 +517,12 @@ std::vector<int3> CPathfinderHelper::getTeleportExits(const PathNodeInfo & sourc
|
||||
return teleportationExits;
|
||||
}
|
||||
|
||||
bool CPathfinder::isHeroPatrolLocked() const
|
||||
bool CPathfinderHelper::isHeroPatrolLocked() const
|
||||
{
|
||||
return patrolState == PATROL_LOCKED;
|
||||
}
|
||||
|
||||
bool CPathfinder::isPatrolMovementAllowed(const int3 & dst) const
|
||||
bool CPathfinderHelper::isPatrolMovementAllowed(const int3 & dst) const
|
||||
{
|
||||
if(patrolState == PATROL_RADIUS)
|
||||
{
|
||||
@ -527,7 +546,7 @@ bool CPathfinder::isLayerTransitionPossible() const
|
||||
case ELayer::LAND:
|
||||
if(destLayer == ELayer::AIR)
|
||||
{
|
||||
if(!config->options.lightweightFlyingMode || isSourceInitialPosition())
|
||||
if(!config->options.lightweightFlyingMode || source.isInitialPosition)
|
||||
return true;
|
||||
}
|
||||
else if(destLayer == ELayer::SAIL)
|
||||
@ -667,7 +686,7 @@ PathfinderBlockingRule::BlockingReason MovementToDestinationRule::getBlockingRea
|
||||
if(!destination.isNodeObjectVisitable())
|
||||
return BlockingReason::DESTINATION_BLOCKED;
|
||||
|
||||
if(destination.nodeObject->ID != Obj::BOAT && destination.nodeObject->ID != Obj::HERO)
|
||||
if(destination.nodeObject->ID != Obj::BOAT && !destination.nodeHero)
|
||||
return BlockingReason::DESTINATION_BLOCKED;
|
||||
}
|
||||
else if(destination.isNodeObjectVisitable() && destination.nodeObject->ID == Obj::BOAT)
|
||||
@ -811,9 +830,9 @@ void DestinationActionRule::process(
|
||||
|
||||
if(destination.nodeObject->ID == Obj::BOAT)
|
||||
action = CGPathNode::EMBARK;
|
||||
else if(destination.nodeObject->ID == Obj::HERO)
|
||||
else if(destination.nodeHero)
|
||||
{
|
||||
if(objRel == PlayerRelations::ENEMIES)
|
||||
if(destination.heroRelations == PlayerRelations::ENEMIES)
|
||||
action = CGPathNode::BATTLE;
|
||||
else
|
||||
action = CGPathNode::BLOCKING_VISIT;
|
||||
@ -870,10 +889,10 @@ void DestinationActionRule::process(
|
||||
CGPathNode::ENodeAction CPathfinder::getTeleportDestAction() const
|
||||
{
|
||||
CGPathNode::ENodeAction action = CGPathNode::TELEPORT_NORMAL;
|
||||
if(destination.isNodeObjectVisitable() && destination.nodeObject->ID == Obj::HERO)
|
||||
|
||||
if(destination.isNodeObjectVisitable() && destination.nodeHero)
|
||||
{
|
||||
auto objRel = getPlayerRelations(destination.nodeObject->tempOwner, hero->tempOwner);
|
||||
if(objRel == PlayerRelations::ENEMIES)
|
||||
if(destination.heroRelations == PlayerRelations::ENEMIES)
|
||||
action = CGPathNode::TELEPORT_BATTLE;
|
||||
else
|
||||
action = CGPathNode::TELEPORT_BLOCKING_VISIT;
|
||||
@ -882,41 +901,15 @@ CGPathNode::ENodeAction CPathfinder::getTeleportDestAction() const
|
||||
return action;
|
||||
}
|
||||
|
||||
bool CPathfinder::isSourceInitialPosition() const
|
||||
{
|
||||
return source.node->coord == config->nodeStorage->getInitialNode()->coord;
|
||||
}
|
||||
|
||||
bool CPathfinder::isSourceGuarded() const
|
||||
{
|
||||
/// Hero can move from guarded tile if movement started on that tile
|
||||
/// It's possible at least in these cases:
|
||||
/// - Map start with hero on guarded tile
|
||||
/// - Dimention door used
|
||||
/// TODO: check what happen when there is several guards
|
||||
if(gs->guardingCreaturePosition(source.node->coord).valid() && !isSourceInitialPosition())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CPathfinder::isDestinationGuarded() const
|
||||
{
|
||||
/// isDestinationGuarded is exception needed for garrisons.
|
||||
/// When monster standing behind garrison it's visitable and guarded at the same time.
|
||||
return gs->guardingCreaturePosition(destination.node->coord).valid();
|
||||
}
|
||||
|
||||
bool CPathfinder::isDestinationGuardian() const
|
||||
{
|
||||
return gs->guardingCreaturePosition(source.node->coord) == destination.node->coord;
|
||||
}
|
||||
|
||||
void CPathfinder::initializePatrol()
|
||||
void CPathfinderHelper::initializePatrol()
|
||||
{
|
||||
auto state = PATROL_NONE;
|
||||
|
||||
if(hero->patrol.patrolling && !getPlayerState(hero->tempOwner)->human)
|
||||
{
|
||||
if(hero->patrol.patrolRadius)
|
||||
@ -934,7 +927,7 @@ void CPathfinder::initializePatrol()
|
||||
void CPathfinder::initializeGraph()
|
||||
{
|
||||
INodeStorage * nodeStorage = config->nodeStorage.get();
|
||||
nodeStorage->initialize(config->options, gs, hero);
|
||||
nodeStorage->initialize(config->options, gs);
|
||||
}
|
||||
|
||||
bool CPathfinderHelper::canMoveBetween(const int3 & a, const int3 & b) const
|
||||
@ -1101,10 +1094,11 @@ int TurnInfo::getMaxMovePoints(const EPathfindingLayer layer) const
|
||||
}
|
||||
|
||||
CPathfinderHelper::CPathfinderHelper(CGameState * gs, const CGHeroInstance * Hero, const PathfinderOptions & Options)
|
||||
: CGameInfoCallback(gs, boost::optional<PlayerColor>()), turn(-1), hero(Hero), options(Options)
|
||||
: CGameInfoCallback(gs, boost::optional<PlayerColor>()), turn(-1), hero(Hero), options(Options), owner(Hero->tempOwner)
|
||||
{
|
||||
turnsInfo.reserve(16);
|
||||
updateTurnInfo();
|
||||
initializePatrol();
|
||||
}
|
||||
|
||||
CPathfinderHelper::~CPathfinderHelper()
|
||||
@ -1349,11 +1343,11 @@ const CGPathNode * CPathsInfo::getNode(const int3 & coord) const
|
||||
}
|
||||
|
||||
PathNodeInfo::PathNodeInfo()
|
||||
: node(nullptr), nodeObject(nullptr), tile(nullptr), coord(-1, -1, -1), guarded(false)
|
||||
: node(nullptr), nodeObject(nullptr), tile(nullptr), coord(-1, -1, -1), guarded(false), isInitialPosition(false)
|
||||
{
|
||||
}
|
||||
|
||||
void PathNodeInfo::setNode(CGameState * gs, CGPathNode * n, bool excludeTopObject)
|
||||
void PathNodeInfo::setNode(CGameState * gs, CGPathNode * n)
|
||||
{
|
||||
node = n;
|
||||
|
||||
@ -1363,12 +1357,43 @@ void PathNodeInfo::setNode(CGameState * gs, CGPathNode * n, bool excludeTopObjec
|
||||
|
||||
coord = node->coord;
|
||||
tile = gs->getTile(coord);
|
||||
nodeObject = tile->topVisitableObj(excludeTopObject);
|
||||
nodeObject = tile->topVisitableObj();
|
||||
|
||||
if(nodeObject && nodeObject->ID == Obj::HERO)
|
||||
{
|
||||
nodeHero = dynamic_cast<const CGHeroInstance *>(nodeObject);
|
||||
nodeObject = tile->topVisitableObj(true);
|
||||
|
||||
if(!nodeObject)
|
||||
nodeObject = nodeHero;
|
||||
}
|
||||
else
|
||||
{
|
||||
nodeHero = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
guarded = false;
|
||||
}
|
||||
|
||||
void PathNodeInfo::updateInfo(CPathfinderHelper * hlp, CGameState * gs)
|
||||
{
|
||||
if(gs->guardingCreaturePosition(node->coord).valid() && !isInitialPosition)
|
||||
{
|
||||
guarded = true;
|
||||
}
|
||||
|
||||
if(nodeObject)
|
||||
{
|
||||
objectRelations = gs->getPlayerRelations(hlp->owner, nodeObject->tempOwner);
|
||||
}
|
||||
|
||||
if(nodeHero)
|
||||
{
|
||||
heroRelations = gs->getPlayerRelations(hlp->owner, nodeHero->tempOwner);
|
||||
}
|
||||
}
|
||||
|
||||
CDestinationNodeInfo::CDestinationNodeInfo()
|
||||
: PathNodeInfo(),
|
||||
blocked(false),
|
||||
@ -1376,9 +1401,9 @@ CDestinationNodeInfo::CDestinationNodeInfo()
|
||||
{
|
||||
}
|
||||
|
||||
void CDestinationNodeInfo::setNode(CGameState * gs, CGPathNode * n, bool excludeTopObject)
|
||||
void CDestinationNodeInfo::setNode(CGameState * gs, CGPathNode * n)
|
||||
{
|
||||
PathNodeInfo::setNode(gs, n, excludeTopObject);
|
||||
PathNodeInfo::setNode(gs, n);
|
||||
|
||||
blocked = false;
|
||||
action = CGPathNode::ENodeAction::UNKNOWN;
|
||||
@ -1395,5 +1420,6 @@ bool CDestinationNodeInfo::isBetterWay() const
|
||||
bool PathNodeInfo::isNodeObjectVisitable() const
|
||||
{
|
||||
/// Hero can't visit objects while walking on water or flying
|
||||
return canSeeObj(nodeObject) && (node->layer == EPathfindingLayer::LAND || node->layer == EPathfindingLayer::SAIL);
|
||||
return (node->layer == EPathfindingLayer::LAND || node->layer == EPathfindingLayer::SAIL)
|
||||
&& (canSeeObj(nodeObject) || canSeeObj(nodeHero));
|
||||
}
|
||||
|
@ -196,14 +196,19 @@ struct DLL_LINKAGE PathNodeInfo
|
||||
{
|
||||
CGPathNode * node;
|
||||
const CGObjectInstance * nodeObject;
|
||||
const CGHeroInstance * nodeHero;
|
||||
const TerrainTile * tile;
|
||||
int3 coord;
|
||||
bool guarded;
|
||||
PlayerRelations::PlayerRelations objectRelations;
|
||||
PlayerRelations::PlayerRelations heroRelations;
|
||||
bool isInitialPosition;
|
||||
|
||||
PathNodeInfo();
|
||||
|
||||
virtual void setNode(CGameState * gs, CGPathNode * n, bool excludeTopObject = false);
|
||||
virtual void setNode(CGameState * gs, CGPathNode * n);
|
||||
|
||||
void updateInfo(CPathfinderHelper * hlp, CGameState * gs);
|
||||
|
||||
bool isNodeObjectVisitable() const;
|
||||
};
|
||||
@ -219,7 +224,7 @@ struct DLL_LINKAGE CDestinationNodeInfo : public PathNodeInfo
|
||||
|
||||
CDestinationNodeInfo();
|
||||
|
||||
virtual void setNode(CGameState * gs, CGPathNode * n, bool excludeTopObject = false) override;
|
||||
virtual void setNode(CGameState * gs, CGPathNode * n) override;
|
||||
|
||||
virtual bool isBetterWay() const;
|
||||
};
|
||||
@ -379,7 +384,7 @@ class DLL_LINKAGE INodeStorage
|
||||
{
|
||||
public:
|
||||
using ELayer = EPathfindingLayer;
|
||||
virtual CGPathNode * getInitialNode() = 0;
|
||||
virtual std::vector<CGPathNode *> getInitialNodes() = 0;
|
||||
|
||||
virtual std::vector<CGPathNode *> calculateNeighbours(
|
||||
const PathNodeInfo & source,
|
||||
@ -393,7 +398,7 @@ public:
|
||||
|
||||
virtual void commit(CDestinationNodeInfo & destination, const PathNodeInfo & source) = 0;
|
||||
|
||||
virtual void initialize(const PathfinderOptions & options, const CGameState * gs, const CGHeroInstance * hero) = 0;
|
||||
virtual void initialize(const PathfinderOptions & options, const CGameState * gs) = 0;
|
||||
};
|
||||
|
||||
class DLL_LINKAGE NodeStorage : public INodeStorage
|
||||
@ -413,9 +418,9 @@ public:
|
||||
return out.getNode(coord, layer);
|
||||
}
|
||||
|
||||
void initialize(const PathfinderOptions & options, const CGameState * gs, const CGHeroInstance * hero) override;
|
||||
void initialize(const PathfinderOptions & options, const CGameState * gs) override;
|
||||
|
||||
virtual CGPathNode * getInitialNode() override;
|
||||
virtual std::vector<CGPathNode *> getInitialNodes() override;
|
||||
|
||||
virtual std::vector<CGPathNode *> calculateNeighbours(
|
||||
const PathNodeInfo & source,
|
||||
@ -440,6 +445,21 @@ public:
|
||||
PathfinderConfig(
|
||||
std::shared_ptr<INodeStorage> nodeStorage,
|
||||
std::vector<std::shared_ptr<IPathfindingRule>> rules);
|
||||
|
||||
virtual CPathfinderHelper * getOrCreatePathfinderHelper(const PathNodeInfo & source, CGameState * gs) = 0;
|
||||
};
|
||||
|
||||
class DLL_LINKAGE SingleHeroPathfinderConfig : public PathfinderConfig
|
||||
{
|
||||
private:
|
||||
std::unique_ptr<CPathfinderHelper> pathfinderHelper;
|
||||
|
||||
public:
|
||||
SingleHeroPathfinderConfig(CPathsInfo & out, CGameState * gs, const CGHeroInstance * hero);
|
||||
|
||||
virtual CPathfinderHelper * getOrCreatePathfinderHelper(const PathNodeInfo & source, CGameState * gs) override;
|
||||
|
||||
static std::vector<std::shared_ptr<IPathfindingRule>> buildRuleSet();
|
||||
};
|
||||
|
||||
class CPathfinder : private CGameInfoCallback
|
||||
@ -450,7 +470,6 @@ public:
|
||||
CPathfinder(CPathsInfo & _out, CGameState * _gs, const CGHeroInstance * _hero);
|
||||
CPathfinder(
|
||||
CGameState * _gs,
|
||||
const CGHeroInstance * _hero,
|
||||
std::shared_ptr<PathfinderConfig> config);
|
||||
|
||||
void calculatePaths(); //calculates possible paths for hero, uses current hero position and movement left; returns pointer to newly allocated CPath or nullptr if path does not exists
|
||||
@ -458,34 +477,18 @@ public:
|
||||
private:
|
||||
typedef EPathfindingLayer ELayer;
|
||||
|
||||
const CGHeroInstance * hero;
|
||||
std::unique_ptr<CPathfinderHelper> hlp;
|
||||
std::shared_ptr<PathfinderConfig> config;
|
||||
|
||||
enum EPatrolState {
|
||||
PATROL_NONE = 0,
|
||||
PATROL_LOCKED = 1,
|
||||
PATROL_RADIUS
|
||||
} patrolState;
|
||||
std::unordered_set<int3, ShashInt3> patrolTiles;
|
||||
|
||||
boost::heap::fibonacci_heap<CGPathNode *, boost::heap::compare<NodeComparer<CGPathNode>> > pq;
|
||||
|
||||
PathNodeInfo source; //current (source) path node -> we took it from the queue
|
||||
CDestinationNodeInfo destination; //destination node -> it's a neighbour of source that we consider
|
||||
|
||||
bool isHeroPatrolLocked() const;
|
||||
bool isPatrolMovementAllowed(const int3 & dst) const;
|
||||
|
||||
bool isLayerTransitionPossible() const;
|
||||
CGPathNode::ENodeAction getTeleportDestAction() const;
|
||||
|
||||
bool isSourceInitialPosition() const;
|
||||
bool isSourceGuarded() const;
|
||||
bool isDestinationGuarded() const;
|
||||
bool isDestinationGuardian() const;
|
||||
|
||||
void initializePatrol();
|
||||
void initializeGraph();
|
||||
|
||||
STRONG_INLINE
|
||||
@ -527,13 +530,25 @@ struct DLL_LINKAGE TurnInfo
|
||||
class DLL_LINKAGE CPathfinderHelper : private CGameInfoCallback
|
||||
{
|
||||
public:
|
||||
enum EPatrolState
|
||||
{
|
||||
PATROL_NONE = 0,
|
||||
PATROL_LOCKED = 1,
|
||||
PATROL_RADIUS
|
||||
} patrolState;
|
||||
std::unordered_set<int3, ShashInt3> patrolTiles;
|
||||
|
||||
int turn;
|
||||
PlayerColor owner;
|
||||
const CGHeroInstance * hero;
|
||||
std::vector<TurnInfo *> turnsInfo;
|
||||
const PathfinderOptions & options;
|
||||
|
||||
CPathfinderHelper(CGameState * gs, const CGHeroInstance * Hero, const PathfinderOptions & Options);
|
||||
~CPathfinderHelper();
|
||||
void initializePatrol();
|
||||
bool isHeroPatrolLocked() const;
|
||||
bool isPatrolMovementAllowed(const int3 & dst) const;
|
||||
void updateTurnInfo(const int turn = 0);
|
||||
bool isLayerAvailable(const EPathfindingLayer layer) const;
|
||||
const TurnInfo * getTurnInfo() const;
|
||||
|
@ -901,8 +901,7 @@ TConstBonusListPtr CBonusSystemNode::getAllBonuses(const CSelector &selector, co
|
||||
if (CBonusSystemNode::cachingEnabled && limitOnUs)
|
||||
{
|
||||
// Exclusive access for one thread
|
||||
static boost::mutex m;
|
||||
boost::mutex::scoped_lock lock(m);
|
||||
boost::lock_guard<boost::mutex> lock(sync);
|
||||
|
||||
// If the bonus system tree changes(state of a single node or the relations to each other) then
|
||||
// cache all bonus objects. Selector objects doesn't matter.
|
||||
@ -993,7 +992,8 @@ CBonusSystemNode::CBonusSystemNode()
|
||||
: bonuses(true),
|
||||
exportedBonuses(true),
|
||||
nodeType(UNKNOWN),
|
||||
cachedLast(0)
|
||||
cachedLast(0),
|
||||
sync()
|
||||
{
|
||||
}
|
||||
|
||||
@ -1001,7 +1001,8 @@ CBonusSystemNode::CBonusSystemNode(ENodeTypes NodeType)
|
||||
: bonuses(true),
|
||||
exportedBonuses(true),
|
||||
nodeType(NodeType),
|
||||
cachedLast(0)
|
||||
cachedLast(0),
|
||||
sync()
|
||||
{
|
||||
}
|
||||
|
||||
@ -1010,7 +1011,8 @@ CBonusSystemNode::CBonusSystemNode(CBonusSystemNode && other):
|
||||
exportedBonuses(std::move(other.exportedBonuses)),
|
||||
nodeType(other.nodeType),
|
||||
description(other.description),
|
||||
cachedLast(0)
|
||||
cachedLast(0),
|
||||
sync()
|
||||
{
|
||||
std::swap(parents, other.parents);
|
||||
std::swap(children, other.children);
|
||||
@ -1189,7 +1191,6 @@ void CBonusSystemNode::childDetached(CBonusSystemNode *child)
|
||||
logBonus->error("Error! %s #cannot be detached from# %s", child->nodeName(), nodeName());
|
||||
throw std::runtime_error("internal error");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void CBonusSystemNode::detachFromAll()
|
||||
|
@ -761,6 +761,7 @@ private:
|
||||
// This string needs to be unique, that's why it has to be setted in the following manner:
|
||||
// [property key]_[value] => only for selector
|
||||
mutable std::map<std::string, TBonusListPtr > cachedRequests;
|
||||
mutable boost::mutex sync;
|
||||
|
||||
void getBonusesRec(BonusList &out, const CSelector &selector, const CSelector &limit) const;
|
||||
void getAllBonusesRec(BonusList &out) const;
|
||||
|
@ -89,19 +89,24 @@ void CArmedInstance::updateMoraleBonusFromArmy()
|
||||
factionsInArmy -= mixableFactions - 1;
|
||||
}
|
||||
|
||||
std::string description;
|
||||
|
||||
if(factionsInArmy == 1)
|
||||
{
|
||||
b->val = +1;
|
||||
b->description = VLC->generaltexth->arraytxt[115]; //All troops of one alignment +1
|
||||
b->description = b->description.substr(0, b->description.size()-3);//trim "+1"
|
||||
description = VLC->generaltexth->arraytxt[115]; //All troops of one alignment +1
|
||||
description = description.substr(0, description.size()-3);//trim "+1"
|
||||
}
|
||||
else if (!factions.empty()) // no bonus from empty garrison
|
||||
{
|
||||
b->val = 2 - (si32)factionsInArmy;
|
||||
b->description = boost::str(boost::format(VLC->generaltexth->arraytxt[114]) % factionsInArmy % b->val); //Troops of %d alignments %d
|
||||
b->description = b->description.substr(0, b->description.size()-2);//trim value
|
||||
description = boost::str(boost::format(VLC->generaltexth->arraytxt[114]) % factionsInArmy % b->val); //Troops of %d alignments %d
|
||||
description = b->description.substr(0, description.size()-2);//trim value
|
||||
}
|
||||
boost::algorithm::trim(b->description);
|
||||
|
||||
boost::algorithm::trim(description);
|
||||
b->description = description;
|
||||
|
||||
CBonusSystemNode::treeHasChanged();
|
||||
|
||||
//-1 modifier for any Undead unit in army
|
||||
|
@ -1,12 +1,12 @@
|
||||
/*
|
||||
* CommonConstructors.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
* CommonConstructors.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
#include "CommonConstructors.h"
|
||||
|
||||
@ -29,7 +29,7 @@ bool CObstacleConstructor::isStaticObject()
|
||||
return true;
|
||||
}
|
||||
|
||||
CTownInstanceConstructor::CTownInstanceConstructor():
|
||||
CTownInstanceConstructor::CTownInstanceConstructor() :
|
||||
faction(nullptr)
|
||||
{
|
||||
}
|
||||
@ -47,7 +47,7 @@ void CTownInstanceConstructor::initTypeData(const JsonNode & input)
|
||||
void CTownInstanceConstructor::afterLoadFinalization()
|
||||
{
|
||||
assert(faction);
|
||||
for (auto entry : filtersJson.Struct())
|
||||
for(auto entry : filtersJson.Struct())
|
||||
{
|
||||
filters[entry.first] = LogicalExpression<BuildingID>(entry.second, [this](const JsonNode & node)
|
||||
{
|
||||
@ -79,7 +79,7 @@ CGObjectInstance * CTownInstanceConstructor::create(const ObjectTemplate & tmpl)
|
||||
void CTownInstanceConstructor::configureObject(CGObjectInstance * object, CRandomGenerator & rng) const
|
||||
{
|
||||
auto templ = getOverride(object->cb->getTile(object->pos)->terType, object);
|
||||
if (templ)
|
||||
if(templ)
|
||||
object->appearance = templ.get();
|
||||
}
|
||||
|
||||
@ -91,15 +91,17 @@ CHeroInstanceConstructor::CHeroInstanceConstructor()
|
||||
|
||||
void CHeroInstanceConstructor::initTypeData(const JsonNode & input)
|
||||
{
|
||||
VLC->modh->identifiers.requestIdentifier("heroClass", input["heroClass"],
|
||||
[&](si32 index) { heroClass = VLC->heroh->classes[index]; });
|
||||
VLC->modh->identifiers.requestIdentifier(
|
||||
"heroClass",
|
||||
input["heroClass"],
|
||||
[&](si32 index) { heroClass = VLC->heroh->classes[index]; });
|
||||
|
||||
filtersJson = input["filters"];
|
||||
}
|
||||
|
||||
void CHeroInstanceConstructor::afterLoadFinalization()
|
||||
{
|
||||
for (auto entry : filtersJson.Struct())
|
||||
for(auto entry : filtersJson.Struct())
|
||||
{
|
||||
filters[entry.first] = LogicalExpression<HeroTypeID>(entry.second, [](const JsonNode & node)
|
||||
{
|
||||
@ -117,7 +119,7 @@ bool CHeroInstanceConstructor::objectFilter(const CGObjectInstance * object, con
|
||||
return hero->type->ID == id;
|
||||
};
|
||||
|
||||
if (filters.count(templ.stringID))
|
||||
if(filters.count(templ.stringID))
|
||||
{
|
||||
return filters.at(templ.stringID).test(heroTest);
|
||||
}
|
||||
@ -175,9 +177,9 @@ CGObjectInstance * CDwellingInstanceConstructor::create(const ObjectTemplate & t
|
||||
CGDwelling * obj = createTyped(tmpl);
|
||||
|
||||
obj->creatures.resize(availableCreatures.size());
|
||||
for (auto & entry : availableCreatures)
|
||||
for(auto & entry : availableCreatures)
|
||||
{
|
||||
for (const CCreature * cre : entry)
|
||||
for(const CCreature * cre : entry)
|
||||
obj->creatures.back().second.push_back(cre->idNumber);
|
||||
}
|
||||
return obj;
|
||||
@ -190,34 +192,34 @@ void CDwellingInstanceConstructor::configureObject(CGObjectInstance * object, CR
|
||||
dwelling->creatures.clear();
|
||||
dwelling->creatures.reserve(availableCreatures.size());
|
||||
|
||||
for (auto & entry : availableCreatures)
|
||||
for(auto & entry : availableCreatures)
|
||||
{
|
||||
dwelling->creatures.resize(dwelling->creatures.size() + 1);
|
||||
for (const CCreature * cre : entry)
|
||||
for(const CCreature * cre : entry)
|
||||
dwelling->creatures.back().second.push_back(cre->idNumber);
|
||||
}
|
||||
|
||||
bool guarded = false; //TODO: serialize for sanity
|
||||
|
||||
if (guards.getType() == JsonNode::JsonType::DATA_BOOL) //simple switch
|
||||
if(guards.getType() == JsonNode::JsonType::DATA_BOOL) //simple switch
|
||||
{
|
||||
if (guards.Bool())
|
||||
if(guards.Bool())
|
||||
{
|
||||
guarded = true;
|
||||
}
|
||||
}
|
||||
else if (guards.getType() == JsonNode::JsonType::DATA_VECTOR) //custom guards (eg. Elemental Conflux)
|
||||
else if(guards.getType() == JsonNode::JsonType::DATA_VECTOR) //custom guards (eg. Elemental Conflux)
|
||||
{
|
||||
for (auto & stack : JsonRandom::loadCreatures(guards, rng))
|
||||
for(auto & stack : JsonRandom::loadCreatures(guards, rng))
|
||||
{
|
||||
dwelling->putStack(SlotID(dwelling->stacksCount()), new CStackInstance(stack.type->idNumber, stack.count));
|
||||
}
|
||||
}
|
||||
else //default condition - creatures are of level 5 or higher
|
||||
{
|
||||
for (auto creatureEntry : availableCreatures)
|
||||
for(auto creatureEntry : availableCreatures)
|
||||
{
|
||||
if (creatureEntry.at(0)->level >= 5)
|
||||
if(creatureEntry.at(0)->level >= 5)
|
||||
{
|
||||
guarded = true;
|
||||
break;
|
||||
@ -225,22 +227,22 @@ void CDwellingInstanceConstructor::configureObject(CGObjectInstance * object, CR
|
||||
}
|
||||
}
|
||||
|
||||
if (guarded)
|
||||
if(guarded)
|
||||
{
|
||||
for (auto creatureEntry : availableCreatures)
|
||||
for(auto creatureEntry : availableCreatures)
|
||||
{
|
||||
const CCreature * crea = creatureEntry.at(0);
|
||||
dwelling->putStack (SlotID(dwelling->stacksCount()), new CStackInstance(crea->idNumber, crea->growth * 3));
|
||||
dwelling->putStack(SlotID(dwelling->stacksCount()), new CStackInstance(crea->idNumber, crea->growth * 3));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CDwellingInstanceConstructor::producesCreature(const CCreature * crea) const
|
||||
{
|
||||
for (auto & entry : availableCreatures)
|
||||
for(auto & entry : availableCreatures)
|
||||
{
|
||||
for (const CCreature * cre : entry)
|
||||
if (crea == cre)
|
||||
for(const CCreature * cre : entry)
|
||||
if(crea == cre)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -249,9 +251,9 @@ bool CDwellingInstanceConstructor::producesCreature(const CCreature * crea) cons
|
||||
std::vector<const CCreature *> CDwellingInstanceConstructor::getProducedCreatures() const
|
||||
{
|
||||
std::vector<const CCreature *> creatures; //no idea why it's 2D, to be honest
|
||||
for (auto & entry : availableCreatures)
|
||||
for(auto & entry : availableCreatures)
|
||||
{
|
||||
for (const CCreature * cre : entry)
|
||||
for(const CCreature * cre : entry)
|
||||
creatures.push_back(cre);
|
||||
}
|
||||
return creatures;
|
||||
@ -292,7 +294,7 @@ BankConfig CBankInstanceConstructor::generateConfig(const JsonNode & level, CRan
|
||||
bc.resources = Res::ResourceSet(level["reward"]["resources"]);
|
||||
bc.creatures = JsonRandom::loadCreatures(level["reward"]["creatures"], rng);
|
||||
bc.artifacts = JsonRandom::loadArtifacts(level["reward"]["artifacts"], rng);
|
||||
bc.spells = JsonRandom::loadSpells(level["reward"]["spells"], rng, spells);
|
||||
bc.spells = JsonRandom::loadSpells(level["reward"]["spells"], rng, spells);
|
||||
|
||||
bc.value = static_cast<ui32>(level["value"].Float());
|
||||
|
||||
@ -314,18 +316,18 @@ void CBankInstanceConstructor::configureObject(CGObjectInstance * object, CRando
|
||||
si32 selectedChance = rng.nextInt(totalChance - 1);
|
||||
|
||||
int cumulativeChance = 0;
|
||||
for (auto & node : levels)
|
||||
for(auto & node : levels)
|
||||
{
|
||||
cumulativeChance += static_cast<int>(node["chance"].Float());
|
||||
if (selectedChance < cumulativeChance)
|
||||
if(selectedChance < cumulativeChance)
|
||||
{
|
||||
bank->setConfig(generateConfig(node, rng));
|
||||
break;
|
||||
bank->setConfig(generateConfig(node, rng));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CBankInfo::CBankInfo(const JsonVector & Config):
|
||||
CBankInfo::CBankInfo(const JsonVector & Config) :
|
||||
config(Config)
|
||||
{
|
||||
assert(!Config.empty());
|
||||
@ -336,28 +338,28 @@ static void addStackToArmy(IObjectInfo::CArmyStructure & army, const CCreature *
|
||||
army.totalStrength += crea->fightValue * amount;
|
||||
|
||||
bool walker = true;
|
||||
if (crea->hasBonusOfType(Bonus::SHOOTER))
|
||||
if(crea->hasBonusOfType(Bonus::SHOOTER))
|
||||
{
|
||||
army.shootersStrength += crea->fightValue * amount;
|
||||
walker = false;
|
||||
}
|
||||
if (crea->hasBonusOfType(Bonus::FLYING))
|
||||
if(crea->hasBonusOfType(Bonus::FLYING))
|
||||
{
|
||||
army.flyersStrength += crea->fightValue * amount;
|
||||
walker = false;
|
||||
}
|
||||
if (walker)
|
||||
if(walker)
|
||||
army.walkersStrength += crea->fightValue * amount;
|
||||
}
|
||||
|
||||
IObjectInfo::CArmyStructure CBankInfo::minGuards() const
|
||||
{
|
||||
std::vector<IObjectInfo::CArmyStructure> armies;
|
||||
for (auto configEntry : config)
|
||||
for(auto configEntry : config)
|
||||
{
|
||||
auto stacks = JsonRandom::evaluateCreatures(configEntry["guards"]);
|
||||
IObjectInfo::CArmyStructure army;
|
||||
for (auto & stack : stacks)
|
||||
for(auto & stack : stacks)
|
||||
{
|
||||
assert(!stack.allowedCreatures.empty());
|
||||
auto weakest = boost::range::min_element(stack.allowedCreatures, [](const CCreature * a, const CCreature * b)
|
||||
@ -374,11 +376,11 @@ IObjectInfo::CArmyStructure CBankInfo::minGuards() const
|
||||
IObjectInfo::CArmyStructure CBankInfo::maxGuards() const
|
||||
{
|
||||
std::vector<IObjectInfo::CArmyStructure> armies;
|
||||
for (auto configEntry : config)
|
||||
for(auto configEntry : config)
|
||||
{
|
||||
auto stacks = JsonRandom::evaluateCreatures(configEntry["guards"]);
|
||||
IObjectInfo::CArmyStructure army;
|
||||
for (auto & stack : stacks)
|
||||
for(auto & stack : stacks)
|
||||
{
|
||||
assert(!stack.allowedCreatures.empty());
|
||||
auto strongest = boost::range::max_element(stack.allowedCreatures, [](const CCreature * a, const CCreature * b)
|
||||
@ -396,14 +398,14 @@ TPossibleGuards CBankInfo::getPossibleGuards() const
|
||||
{
|
||||
TPossibleGuards out;
|
||||
|
||||
for (const JsonNode & configEntry : config)
|
||||
for(const JsonNode & configEntry : config)
|
||||
{
|
||||
const JsonNode & guardsInfo = configEntry["guards"];
|
||||
auto stacks = JsonRandom::evaluateCreatures(guardsInfo);
|
||||
IObjectInfo::CArmyStructure army;
|
||||
|
||||
|
||||
for (auto stack : stacks)
|
||||
for(auto stack : stacks)
|
||||
{
|
||||
army.totalStrength += stack.allowedCreatures.front()->AIValue * (stack.minAmount + stack.maxAmount) / 2;
|
||||
//TODO: add fields for flyers, walkers etc...
|
||||
@ -415,34 +417,78 @@ TPossibleGuards CBankInfo::getPossibleGuards() const
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<PossibleReward<TResources>> CBankInfo::getPossibleResourcesReward() const
|
||||
{
|
||||
std::vector<PossibleReward<TResources>> result;
|
||||
|
||||
for(const JsonNode & configEntry : config)
|
||||
{
|
||||
const JsonNode & resourcesInfo = configEntry["reward"]["resources"];
|
||||
|
||||
if(!resourcesInfo.isNull())
|
||||
{
|
||||
result.push_back(
|
||||
PossibleReward<TResources>(
|
||||
configEntry["chance"].Integer(),
|
||||
TResources(resourcesInfo)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<PossibleReward<CStackBasicDescriptor>> CBankInfo::getPossibleCreaturesReward() const
|
||||
{
|
||||
std::vector<PossibleReward<CStackBasicDescriptor>> aproximateReward;
|
||||
|
||||
for(const JsonNode & configEntry : config)
|
||||
{
|
||||
const JsonNode & guardsInfo = configEntry["reward"]["creatures"];
|
||||
auto stacks = JsonRandom::evaluateCreatures(guardsInfo);
|
||||
|
||||
for(auto stack : stacks)
|
||||
{
|
||||
auto creature = stack.allowedCreatures.front();
|
||||
|
||||
aproximateReward.push_back(
|
||||
PossibleReward<CStackBasicDescriptor>(
|
||||
configEntry["chance"].Integer(),
|
||||
CStackBasicDescriptor(creature, (stack.minAmount + stack.maxAmount) / 2)));
|
||||
}
|
||||
}
|
||||
|
||||
return aproximateReward;
|
||||
}
|
||||
|
||||
bool CBankInfo::givesResources() const
|
||||
{
|
||||
for (const JsonNode & node : config)
|
||||
if (!node["reward"]["resources"].isNull())
|
||||
for(const JsonNode & node : config)
|
||||
if(!node["reward"]["resources"].isNull())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CBankInfo::givesArtifacts() const
|
||||
{
|
||||
for (const JsonNode & node : config)
|
||||
if (!node["reward"]["artifacts"].isNull())
|
||||
for(const JsonNode & node : config)
|
||||
if(!node["reward"]["artifacts"].isNull())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CBankInfo::givesCreatures() const
|
||||
{
|
||||
for (const JsonNode & node : config)
|
||||
if (!node["reward"]["creatures"].isNull())
|
||||
for(const JsonNode & node : config)
|
||||
if(!node["reward"]["creatures"].isNull())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CBankInfo::givesSpells() const
|
||||
{
|
||||
for (const JsonNode & node : config)
|
||||
if (!node["reward"]["spells"].isNull())
|
||||
for(const JsonNode & node : config)
|
||||
if(!node["reward"]["spells"].isNull())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
/*
|
||||
* CommonConstructors.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
* CommonConstructors.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "CObjectClassesHandler.h"
|
||||
@ -34,7 +34,7 @@ protected:
|
||||
return obj;
|
||||
}
|
||||
public:
|
||||
CDefaultObjectTypeHandler(){}
|
||||
CDefaultObjectTypeHandler() {}
|
||||
|
||||
CGObjectInstance * create(const ObjectTemplate & tmpl) const override
|
||||
{
|
||||
@ -164,6 +164,15 @@ struct BankConfig
|
||||
|
||||
typedef std::vector<std::pair<ui8, IObjectInfo::CArmyStructure>> TPossibleGuards;
|
||||
|
||||
template <typename T>
|
||||
struct DLL_LINKAGE PossibleReward
|
||||
{
|
||||
int chance;
|
||||
T data;
|
||||
|
||||
PossibleReward(int chance, const T & data) : chance(chance), data(data) {}
|
||||
};
|
||||
|
||||
class DLL_LINKAGE CBankInfo : public IObjectInfo
|
||||
{
|
||||
const JsonVector & config;
|
||||
@ -171,6 +180,8 @@ public:
|
||||
CBankInfo(const JsonVector & Config);
|
||||
|
||||
TPossibleGuards getPossibleGuards() const;
|
||||
std::vector<PossibleReward<TResources>> getPossibleResourcesReward() const;
|
||||
std::vector<PossibleReward<CStackBasicDescriptor>> getPossibleCreaturesReward() const;
|
||||
|
||||
// These functions should try to evaluate minimal possible/max possible guards to give provide information on possible thread to AI
|
||||
CArmyStructure minGuards() const override;
|
||||
|
Loading…
Reference in New Issue
Block a user