| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223 |
- /*
- * DefenceBehavior.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 "DefenceBehavior.h"
- #include "../VCAI.h"
- #include "../Engine/Nullkiller.h"
- #include "../AIhelper.h"
- #include "../AIUtility.h"
- #include "../Goals/BuyArmy.h"
- #include "../Goals/VisitTile.h"
- #include "../Goals/ExecuteHeroChain.h"
- #include "../Goals/ExchangeSwapTownHeroes.h"
- #include "lib/mapping/CMap.h" //for victory conditions
- #include "lib/CPathfinder.h"
- extern boost::thread_specific_ptr<CCallback> cb;
- extern boost::thread_specific_ptr<VCAI> ai;
- extern FuzzyHelper * fh;
- using namespace Goals;
- std::string DefenceBehavior::toString() const
- {
- return "Defend towns";
- }
- Goals::TGoalVec DefenceBehavior::getTasks()
- {
- Goals::TGoalVec tasks;
-
- auto heroes = cb->getHeroesInfo();
- if(heroes.size())
- {
- for(auto town : cb->getTownsInfo())
- {
- evaluateDefence(tasks, town);
- }
- }
- return tasks;
- }
- uint64_t townArmyIncome(const CGTownInstance * town)
- {
- uint64_t result = 0;
- for(auto creatureInfo : town->creatures)
- {
- if(creatureInfo.second.empty())
- continue;
- auto creature = creatureInfo.second.back().toCreature();
- result += creature->AIValue * town->getGrowthInfo(creature->level).totalGrowth();
- }
- return result;
- }
- void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInstance * town)
- {
- auto basicPriority = 0.3f + std::sqrt(townArmyIncome(town) / 20000.0f)
- + town->dailyIncome()[Res::GOLD] / 10000.0f;
- logAi->debug("Evaluating defence for %s, basic priority %f", town->name, basicPriority);
- auto treatNode = ai->nullkiller->dangerHitMap->getObjectTreat(town);
- auto treats = { treatNode.fastestDanger, treatNode.maximumDanger };
- if(!treatNode.fastestDanger.hero)
- {
- logAi->debug("No treat found for town %s", town->name);
- return;
- }
- int dayOfWeek = cb->getDate(Date::DAY_OF_WEEK);
- if(town->garrisonHero)
- {
- if(!ai->nullkiller->isHeroLocked(town->garrisonHero.get()))
- {
- tasks.push_back(Goals::sptr(Goals::ExchangeSwapTownHeroes(town, nullptr).setpriority(5)));
- return;
- }
- logAi->debug(
- "Hero %s in garrison of town %s is suposed to defend the town",
- town->garrisonHero->name,
- town->name);
- return;
- }
-
- uint64_t reinforcement = ai->ah->howManyReinforcementsCanBuy(town->getUpperArmy(), town);
- if(reinforcement)
- {
- logAi->debug("Town %s can buy defence army %lld", town->name, reinforcement);
- tasks.push_back(Goals::sptr(Goals::BuyArmy(town, reinforcement).setpriority(0.5f)));
- }
- auto paths = ai->ah->getPathsToTile(town->visitablePos());
- if(paths.empty())
- {
- logAi->debug("No ways to defend town %s", town->name);
- return;
- }
- for(auto & treat : treats)
- {
- logAi->debug(
- "Town %s has treat %lld in %s turns, hero: %s",
- town->name,
- treat.danger,
- std::to_string(treat.turn),
- treat.hero->name);
- bool treatIsUnderControl = false;
- for(AIPath & path : paths)
- {
- if(path.getHeroStrength() > treat.danger)
- {
- if(dayOfWeek + treat.turn < 6 && isSafeToVisit(path.targetHero, path.heroArmy, treat.danger)
- || path.exchangeCount == 1 && path.turn() < treat.turn
- || path.turn() < treat.turn - 1)
- {
- logAi->debug(
- "Hero %s can eliminate danger for town %s using path %s.",
- path.targetHero->name,
- town->name,
- path.toString());
- treatIsUnderControl = true;
- break;
- }
- }
- }
- if(treatIsUnderControl)
- continue;
- if(ai->canRecruitAnyHero(town))
- {
- auto heroesInTavern = cb->getAvailableHeroes(town);
- for(auto hero : heroesInTavern)
- {
- if(hero->getTotalStrength() > treat.danger)
- {
- tasks.push_back(Goals::sptr(Goals::RecruitHero().settown(town).setobjid(hero->id.getNum()).setpriority(1)));
- continue;
- }
- }
- }
- for(AIPath & path : paths)
- {
- #if AI_TRACE_LEVEL >= 1
- logAi->trace(
- "Hero %s can defend town with force %lld in %s turns, cost: %f, path: %s",
- path.targetHero->name,
- path.getHeroStrength(),
- std::to_string(path.turn()),
- path.movementCost(),
- path.toString());
- #endif
- float priority = basicPriority
- + std::min(SAFE_ATTACK_CONSTANT, (float)path.getHeroStrength() / treat.danger) / (treat.turn + 1);
- if(path.targetHero == town->visitingHero && path.exchangeCount == 1)
- {
- #if AI_TRACE_LEVEL >= 1
- logAi->trace("Put %s to garrison of town %s with priority %f",
- path.targetHero->name,
- town->name,
- priority);
- #endif
- tasks.push_back(Goals::sptr(Goals::ExchangeSwapTownHeroes(town, town->visitingHero.get()).setpriority(priority)));
- continue;
- }
-
- if(path.turn() <= treat.turn && path.getHeroStrength() * SAFE_ATTACK_CONSTANT >= treat.danger)
- {
- #if AI_TRACE_LEVEL >= 1
- logAi->trace("Move %s to defend town %s with priority %f",
- path.targetHero->name,
- town->name,
- priority);
- #endif
- tasks.push_back(Goals::sptr(Goals::ExecuteHeroChain(path, town).setpriority(priority)));
- continue;
- }
- }
- }
- logAi->debug("Found %d tasks", tasks.size());
- /*for(auto & treat : treats)
- {
- auto paths = ai->ah->getPathsToTile(treat.hero->visitablePos());
- for(AIPath & path : paths)
- {
- tasks.push_back(Goals::sptr(Goals::ExecuteHeroChain(path)));
- }
- }*/
- }
|