diff --git a/AI/Nullkiller/AIUtility.h b/AI/Nullkiller/AIUtility.h index faeb458a8..76c3100b0 100644 --- a/AI/Nullkiller/AIUtility.h +++ b/AI/Nullkiller/AIUtility.h @@ -36,9 +36,9 @@ extern const int GOLD_RESERVE; enum HeroRole { - SCOUT, + SCOUT = 0, - MAIN + MAIN = 1 }; //provisional class for AI to store a reference to an owned hero object diff --git a/AI/Nullkiller/ArmyManager.cpp b/AI/Nullkiller/ArmyManager.cpp index c8f09c212..d40dc2a4d 100644 --- a/AI/Nullkiller/ArmyManager.cpp +++ b/AI/Nullkiller/ArmyManager.cpp @@ -363,12 +363,12 @@ ArmyUpgradeInfo ArmyManager::calculateCreateresUpgrade( { SlotInfo upgradedArmy; - upgradedArmy.creature = upgrade.upgradedCreature; + upgradedArmy.creature = upgrade.upgradedCreature.toCreature(); upgradedArmy.count = upgrade.count; upgradedArmy.power = evaluateStackPower(upgradedArmy.creature, upgradedArmy.count); auto slotToReplace = std::find_if(result.resultingArmy.begin(), result.resultingArmy.end(), [&](const SlotInfo & slot) -> bool { - return slot.count == upgradedArmy.count && slot.creature == upgrade.initialCreature; + return slot.count == upgradedArmy.count && slot.creature->idNumber == upgrade.initialCreature; }); resourcesLeft -= upgrade.cost; diff --git a/AI/Nullkiller/Behaviors/CompleteQuestBehavior.cpp b/AI/Nullkiller/Behaviors/CompleteQuestBehavior.cpp index 088a97776..70585736d 100644 --- a/AI/Nullkiller/Behaviors/CompleteQuestBehavior.cpp +++ b/AI/Nullkiller/Behaviors/CompleteQuestBehavior.cpp @@ -129,10 +129,12 @@ TGoalVec CompleteQuest::missionArt() const if(!solutions.empty()) return solutions; - /*for(auto art : q.quest->m5arts) + CaptureObjectsBehavior findArts; + + for(auto art : q.quest->m5arts) { - solutions.push_back(sptr(GetArtOfType(art))); //TODO: transport? - }*/ + solutions.push_back(sptr(CaptureObjectsBehavior().ofType(Obj::ARTIFACT, art))); + } return solutions; } @@ -223,21 +225,18 @@ TGoalVec CompleteQuest::missionDestroyObj() const if(!obj) return CaptureObjectsBehavior(q.obj).decompose(); - if(obj->ID == Obj::HERO) + auto relations = cb->getPlayerRelations(ai->playerID, obj->tempOwner); + + //if(relations == PlayerRelations::SAME_PLAYER) + //{ + // auto heroToProtect = cb->getHero(obj->id); + + // //solutions.push_back(sptr(GatherArmy().sethero(heroToProtect))); + //} + //else + if(relations == PlayerRelations::ENEMIES) { - auto relations = cb->getPlayerRelations(ai->playerID, obj->tempOwner); - - //if(relations == PlayerRelations::SAME_PLAYER) - //{ - // auto heroToProtect = cb->getHero(obj->id); - - // //solutions.push_back(sptr(GatherArmy().sethero(heroToProtect))); - //} - //else - if(relations == PlayerRelations::ENEMIES) - { - return CaptureObjectsBehavior(obj).decompose(); - } + return CaptureObjectsBehavior(obj).decompose(); } return TGoalVec(); diff --git a/AI/Nullkiller/Behaviors/DefenceBehavior.cpp b/AI/Nullkiller/Behaviors/DefenceBehavior.cpp index 730f086e0..9fe4a479b 100644 --- a/AI/Nullkiller/Behaviors/DefenceBehavior.cpp +++ b/AI/Nullkiller/Behaviors/DefenceBehavior.cpp @@ -35,14 +35,9 @@ Goals::TGoalVec DefenceBehavior::decompose() const { Goals::TGoalVec tasks; - auto heroes = cb->getHeroesInfo(); - - if(heroes.size()) + for(auto town : cb->getTownsInfo()) { - for(auto town : cb->getTownsInfo()) - { - evaluateDefence(tasks, town); - } + evaluateDefence(tasks, town); } return tasks; diff --git a/AI/Nullkiller/Engine/PriorityEvaluator.cpp b/AI/Nullkiller/Engine/PriorityEvaluator.cpp index 32c28cf9c..2b0022713 100644 --- a/AI/Nullkiller/Engine/PriorityEvaluator.cpp +++ b/AI/Nullkiller/Engine/PriorityEvaluator.cpp @@ -474,7 +474,7 @@ public: auto army = path.heroArmy; vstd::amax(evaluationContext.armyLossPersentage, path.getTotalArmyLoss() / (double)path.getHeroStrength()); - vstd::amax(evaluationContext.heroRole, ai->ah->getHeroRole(heroPtr)); + evaluationContext.heroRole = ai->ah->getHeroRole(heroPtr); evaluationContext.goldReward += getGoldReward(target, hero); evaluationContext.armyReward += getArmyReward(target, hero, army, checkGold); evaluationContext.skillReward += getSkillReward(target, hero, evaluationContext.heroRole); @@ -604,17 +604,18 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task) } assert(result >= 0); -#ifdef VCMI_TRACE_PATHFINDER - logAi->trace("Evaluated %s, loss: %f, turns main: %f, scout: %f, gold: %d, cost: %d, army gain: %d, danger: %d, role: %s, strategical value: %f, cwr: %f, result %f", +#ifdef AI_TRACE_LEVEL >= 1 + logAi->trace("Evaluated %s, loss: %f, turn: %d, turns main: %f, scout: %f, gold: %d, cost: %d, army gain: %d, danger: %d, role: %s, strategical value: %f, cwr: %f, result %f", task->toString(), evaluationContext.armyLossPersentage, + (int)evaluationContext.turn, evaluationContext.movementCostByRole[HeroRole::MAIN], evaluationContext.movementCostByRole[HeroRole::SCOUT], evaluationContext.goldReward, evaluationContext.goldCost, evaluationContext.armyReward, evaluationContext.danger, - evaluationContext.heroRole ? "scout" : "main", + evaluationContext.heroRole == HeroRole::MAIN ? "main" : "scout", evaluationContext.strategicalValue, evaluationContext.closestWayRatio, result); diff --git a/AI/Nullkiller/Pathfinding/AINodeStorage.cpp b/AI/Nullkiller/Pathfinding/AINodeStorage.cpp index eb2c90cba..ee17c9d70 100644 --- a/AI/Nullkiller/Pathfinding/AINodeStorage.cpp +++ b/AI/Nullkiller/Pathfinding/AINodeStorage.cpp @@ -214,7 +214,7 @@ void AINodeStorage::commit( destination->theNodeBefore = source->theNodeBefore; destination->chainOther = nullptr; -#if AI_TRACE_LEVEL >= 2 +#if PATHFINDER_TRACE_LEVEL >= 2 logAi->trace( "Commited %s -> %s, cost: %f, turn: %s, mp: %d, hero: %s, mask: %x, army: %lld", source->coord.toString(), @@ -422,7 +422,7 @@ void AINodeStorage::calculateHeroChain( || (node->action == CGPathNode::ENodeAction::UNKNOWN && node->actor->hero) || (node->actor->chainMask & srcNode->actor->chainMask) != 0) { -#if AI_TRACE_LEVEL >= 2 +#if PATHFINDER_TRACE_LEVEL >= 2 logAi->trace( "Skip exchange %s[%x] -> %s[%x] at %s because of %s", node->actor->toString(), @@ -439,7 +439,7 @@ void AINodeStorage::calculateHeroChain( continue; } -#if AI_TRACE_LEVEL >= 2 +#if PATHFINDER_TRACE_LEVEL >= 2 logAi->trace( "Thy exchange %s[%x] -> %s[%x] at %s", node->actor->toString(), @@ -464,7 +464,7 @@ void AINodeStorage::calculateHeroChain( && (other->armyLoss == 0 || other->armyLoss < other->actor->armyValue) && carrier->actor->canExchange(other->actor)) { -#if AI_TRACE_LEVEL >= 2 +#if PATHFINDER_TRACE_LEVEL >= 2 logAi->trace( "Exchange allowed %s[%x] -> %s[%x] at %s", other->actor->toString(), @@ -481,7 +481,7 @@ void AINodeStorage::calculateHeroChain( if(hasLessMp && hasLessExperience) { -#if AI_TRACE_LEVEL >= 2 +#if PATHFINDER_TRACE_LEVEL >= 2 logAi->trace("Exchange at %s is ineficient. Blocked.", carrier->coord.toString()); #endif return; @@ -505,7 +505,7 @@ void AINodeStorage::addHeroChain(const std::vector & result) if(!chainNodeOptional) { -#if AI_TRACE_LEVEL >= 2 +#if PATHFINDER_TRACE_LEVEL >= 2 logAi->trace("Exchange at %s can not allocate node. Blocked.", carrier->coord.toString()); #endif continue; @@ -515,7 +515,7 @@ void AINodeStorage::addHeroChain(const std::vector & result) if(exchangeNode->action != CGPathNode::ENodeAction::UNKNOWN) { -#if AI_TRACE_LEVEL >= 2 +#if PATHFINDER_TRACE_LEVEL >= 2 logAi->trace("Exchange at %s node is already in use. Blocked.", carrier->coord.toString()); #endif continue; @@ -523,7 +523,7 @@ void AINodeStorage::addHeroChain(const std::vector & result) if(exchangeNode->turns != 0xFF && exchangeNode->cost < chainInfo.cost) { -#if AI_TRACE_LEVEL >= 2 +#if PATHFINDER_TRACE_LEVEL >= 2 logAi->trace( "Exchange at %s is is not effective enough. %f < %f", exchangeNode->coord.toString(), @@ -544,7 +544,7 @@ void AINodeStorage::addHeroChain(const std::vector & result) exchangeNode->chainOther = other; exchangeNode->armyLoss = chainInfo.armyLoss; -#if AI_TRACE_LEVEL >= 2 +#if PATHFINDER_TRACE_LEVEL >= 2 logAi->trace( "Chain accepted at %s %s -> %s, mask %x, cost %f, turn: %s, mp: %d, army %i", exchangeNode->coord.toString(), @@ -858,7 +858,7 @@ void AINodeStorage::calculateTownPortalTeleportations(std::vector if(nodeOptional) { -#if AI_TRACE_LEVEL >= 1 +#if PATHFINDER_TRACE_LEVEL >= 1 logAi->trace("Adding town portal node at %s", targetTown->name); #endif initialNodes.push_back(nodeOptional.get()); @@ -897,7 +897,7 @@ bool AINodeStorage::hasBetterChain( { if(node.cost < candidateNode->cost) { -#if AI_TRACE_LEVEL >= 2 +#if PATHFINDER_TRACE_LEVEL >= 2 logAi->trace( "Block ineficient battle move %s->%s, hero: %s[%X], army %lld, mp diff: %i", source->coord.toString(), @@ -921,7 +921,7 @@ bool AINodeStorage::hasBetterChain( if(nodeArmyValue > candidateArmyValue && node.cost <= candidateNode->cost) { -#if AI_TRACE_LEVEL >= 2 +#if PATHFINDER_TRACE_LEVEL >= 2 logAi->trace( "Block ineficient move because of stronger army %s->%s, hero: %s[%X], army %lld, mp diff: %i", source->coord.toString(), diff --git a/AI/Nullkiller/Pathfinding/AINodeStorage.h b/AI/Nullkiller/Pathfinding/AINodeStorage.h index dbce17348..3ac989bb8 100644 --- a/AI/Nullkiller/Pathfinding/AINodeStorage.h +++ b/AI/Nullkiller/Pathfinding/AINodeStorage.h @@ -10,8 +10,8 @@ #pragma once -#define VCMI_TRACE_PATHFINDER 1 -#define AI_TRACE_LEVEL 1 +#define PATHFINDER_TRACE_LEVEL 1 +#define AI_TRACE_LEVEL 2 #include "../../../lib/CPathfinder.h" #include "../../../lib/mapObjects/CGHeroInstance.h" diff --git a/AI/Nullkiller/Pathfinding/Actions/BattleAction.cpp b/AI/Nullkiller/Pathfinding/Actions/BattleAction.cpp index dca06c318..d1dca248d 100644 --- a/AI/Nullkiller/Pathfinding/Actions/BattleAction.cpp +++ b/AI/Nullkiller/Pathfinding/Actions/BattleAction.cpp @@ -36,7 +36,8 @@ namespace AIPathfinding return dynamic_cast(questInfo.obj)->checkQuest(node->actor->hero); } - return questInfo.quest->checkQuest(node->actor->hero); + return questInfo.quest->progress == CQuest::NOT_ACTIVE + || questInfo.quest->checkQuest(node->actor->hero); } Goals::TSubgoal QuestAction::decompose(const CGHeroInstance * hero) const diff --git a/AI/Nullkiller/Pathfinding/Actors.cpp b/AI/Nullkiller/Pathfinding/Actors.cpp index f96c502a5..e9f3e8644 100644 --- a/AI/Nullkiller/Pathfinding/Actors.cpp +++ b/AI/Nullkiller/Pathfinding/Actors.cpp @@ -172,7 +172,7 @@ bool HeroExchangeMap::canExchange(const ChainActor * other) if(!resources.canAfford(actor->armyCost + other->armyCost)) { result = false; -#if AI_TRACE_LEVEL >= 2 +#if PATHFINDER_TRACE_LEVEL >= 2 logAi->trace( "Can not afford exchange because of total cost %s but we have %s", (actor->armyCost + other->armyCost).toString(), @@ -191,7 +191,7 @@ bool HeroExchangeMap::canExchange(const ChainActor * other) if(other->creatureSet->Slots().size()) reinforcment += ai->ah->howManyReinforcementsCanGet(actor->creatureSet, other->creatureSet); -#if AI_TRACE_LEVEL >= 2 +#if PATHFINDER_TRACE_LEVEL >= 2 logAi->trace( "Exchange %s->%s reinforcement: %d, %f%%", actor->toString(), diff --git a/AI/Nullkiller/Pathfinding/Rules/AILayerTransitionRule.cpp b/AI/Nullkiller/Pathfinding/Rules/AILayerTransitionRule.cpp index 157ee4813..dec8a8123 100644 --- a/AI/Nullkiller/Pathfinding/Rules/AILayerTransitionRule.cpp +++ b/AI/Nullkiller/Pathfinding/Rules/AILayerTransitionRule.cpp @@ -37,7 +37,7 @@ namespace AIPathfinding if(virtualBoat && tryEmbarkVirtualBoat(destination, source, virtualBoat)) { -#ifdef VCMI_TRACE_PATHFINDER +#if PATHFINDER_TRACE_LEVEL >= 1 logAi->trace("Embarking to virtual boat while moving %s -> %s!", source.coord.toString(), destination.coord.toString()); #endif } @@ -138,7 +138,7 @@ namespace AIPathfinding } else { -#ifdef VCMI_TRACE_PATHFINDER +#if PATHFINDER_TRACE_LEVEL >= 1 logAi->trace( "Special transition node already allocated. Blocked moving %s -> %s", source.coord.toString(), diff --git a/AI/Nullkiller/Pathfinding/Rules/AIMovementAfterDestinationRule.cpp b/AI/Nullkiller/Pathfinding/Rules/AIMovementAfterDestinationRule.cpp index 02684a7cd..1a730be37 100644 --- a/AI/Nullkiller/Pathfinding/Rules/AIMovementAfterDestinationRule.cpp +++ b/AI/Nullkiller/Pathfinding/Rules/AIMovementAfterDestinationRule.cpp @@ -102,6 +102,11 @@ namespace AIPathfinding auto questObj = dynamic_cast(destination.nodeObject); auto nodeHero = pathfinderHelper->hero; + if(destination.nodeObject->ID == Obj::QUEST_GUARD && questObj->quest->missionType == CQuest::MISSION_NONE) + { + return false; + } + if(!destination.nodeObject->wasVisited(nodeHero->tempOwner) || !questObj->checkQuest(nodeHero)) { @@ -154,7 +159,7 @@ namespace AIPathfinding if(guardsAlreadyBypassed && srcNode->actor->allowBattle) { -#ifdef VCMI_TRACE_PATHFINDER +#if PATHFINDER_TRACE_LEVEL >= 1 logAi->trace( "Bypass guard at destination while moving %s -> %s", source.coord.toString(), @@ -182,7 +187,7 @@ namespace AIPathfinding if(!battleNodeOptional) { -#ifdef VCMI_TRACE_PATHFINDER +#if PATHFINDER_TRACE_LEVEL >= 1 logAi->trace( "Can not allocate battle node while moving %s -> %s", source.coord.toString(), @@ -195,7 +200,7 @@ namespace AIPathfinding if(battleNode->locked) { -#ifdef VCMI_TRACE_PATHFINDER +#if PATHFINDER_TRACE_LEVEL >= 1 logAi->trace( "Block bypass guard at destination while moving %s -> %s", source.coord.toString(), @@ -222,7 +227,7 @@ namespace AIPathfinding battleNode->specialAction = std::make_shared(destination.coord); -#ifdef VCMI_TRACE_PATHFINDER +#if PATHFINDER_TRACE_LEVEL >= 1 logAi->trace( "Begin bypass guard at destination with danger %s while moving %s -> %s", std::to_string(danger), diff --git a/AI/Nullkiller/Pathfinding/Rules/AIMovementToDestinationRule.cpp b/AI/Nullkiller/Pathfinding/Rules/AIMovementToDestinationRule.cpp index c6fec5dc2..4e1d10681 100644 --- a/AI/Nullkiller/Pathfinding/Rules/AIMovementToDestinationRule.cpp +++ b/AI/Nullkiller/Pathfinding/Rules/AIMovementToDestinationRule.cpp @@ -37,7 +37,7 @@ namespace AIPathfinding if(blocker == BlockingReason::SOURCE_GUARDED && nodeStorage->getAINode(source.node)->actor->allowBattle) { -#ifdef VCMI_TRACE_PATHFINDER +#if PATHFINDER_TRACE_LEVEL >= 1 logAi->trace( "Bypass src guard while moving from %s to %s", source.coord.toString(), diff --git a/AI/Nullkiller/Pathfinding/Rules/AIPreviousNodeRule.cpp b/AI/Nullkiller/Pathfinding/Rules/AIPreviousNodeRule.cpp index 68ebf7a54..7e3a92f4b 100644 --- a/AI/Nullkiller/Pathfinding/Rules/AIPreviousNodeRule.cpp +++ b/AI/Nullkiller/Pathfinding/Rules/AIPreviousNodeRule.cpp @@ -27,7 +27,8 @@ namespace AIPathfinding { // we can not directly bypass objects, we need to interact with them first destination.node->theNodeBefore = source.node; -#ifdef VCMI_TRACE_PATHFINDER + +#if PATHFINDER_TRACE_LEVEL >= 1 logAi->trace( "Link src node %s to destination node %s while bypassing visitable obj", source.coord.toString(), diff --git a/AI/Nullkiller/VCAI.cpp b/AI/Nullkiller/VCAI.cpp index 359a825b2..c8f6a1ef0 100644 --- a/AI/Nullkiller/VCAI.cpp +++ b/AI/Nullkiller/VCAI.cpp @@ -569,12 +569,11 @@ void VCAI::showBlockingDialog(const std::string & text, const std::vector take the last one (they're indexed [1-size]) - sel = components.size(); + auto hero = nullkiller->getActiveHero(); + auto target = nullkiller->getTargetTile(); if(!selection && cancel) { @@ -582,8 +581,6 @@ void VCAI::showBlockingDialog(const std::string & text, const std::vector always answer yes, we are a brave AI :) auto answer = 1; - auto hero = nullkiller->getActiveHero(); - auto target = nullkiller->getTargetTile(); auto objects = cb->getVisitableObjs(target); if(hero.validAndSet() && target.valid() && objects.size()) @@ -608,14 +605,21 @@ void VCAI::showBlockingDialog(const std::string & text, const std::vector take the last one (they're indexed [1-size]) + sel = components.size(); + + // TODO: Find better way to understand it is Chest of Treasures + if(components.size() == 2 + && components.front().id == Component::RESOURCE + && ah->getHeroRole(hero) != HeroRole::MAIN) + { + sel = 1; // for now lets pick gold from a chest. + } + answerQuery(askID, sel); }); } @@ -2026,6 +2030,9 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj) break; case Obj::TREE_OF_KNOWLEDGE: { + if(ai->ah->getHeroRole(h) == HeroRole::SCOUT) + return false; + TResources myRes = cb->getResourceAmount(); if(myRes[Res::GOLD] < 2000 || myRes[Res::GEMS] < 10) return false;