1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-08-13 19:54:17 +02:00

Nullkiller: dismising and hiring hero in order to defend town

This commit is contained in:
Andrii Danylchenko
2021-05-16 14:11:35 +03:00
committed by Andrii Danylchenko
parent 84e5e6ac17
commit e6eb9ccc03
9 changed files with 151 additions and 14 deletions

View File

@@ -134,7 +134,10 @@ Goals::TGoalVec CaptureObjectsBehavior::getTasks()
way->evaluationContext.closestWayRatio
= way->evaluationContext.movementCost / closestWay->evaluationContext.movementCost;
tasks.push_back(sptr(*way));
if(way->hero && ai->nullkiller->canMove(way->hero.h))
{
tasks.push_back(sptr(*way));
}
}
}
};

View File

@@ -16,6 +16,7 @@
#include "../Goals/BuyArmy.h"
#include "../Goals/VisitTile.h"
#include "../Goals/ExecuteHeroChain.h"
#include "../Goals/DismissHero.h"
#include "../Goals/ExchangeSwapTownHeroes.h"
#include "lib/mapping/CMap.h" //for victory conditions
#include "lib/CPathfinder.h"
@@ -109,13 +110,6 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
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(
@@ -131,7 +125,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
{
if(path.getHeroStrength() > treat.danger)
{
if(dayOfWeek + treat.turn < 6 && isSafeToVisit(path.targetHero, path.heroArmy, treat.danger)
if(path.turn() <= treat.turn && dayOfWeek + treat.turn < 6 && isSafeToVisit(path.targetHero, path.heroArmy, treat.danger)
|| path.exchangeCount == 1 && path.turn() < treat.turn
|| path.turn() < treat.turn - 1)
{
@@ -150,7 +144,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
if(treatIsUnderControl)
continue;
if(ai->canRecruitAnyHero(town))
if(cb->getResourceAmount(Res::GOLD) > GameConstants::HERO_GOLD_COST)
{
auto heroesInTavern = cb->getAvailableHeroes(town);
@@ -158,12 +152,49 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
{
if(hero->getTotalStrength() > treat.danger)
{
tasks.push_back(Goals::sptr(Goals::RecruitHero().settown(town).setobjid(hero->id.getNum()).setpriority(1)));
continue;
auto myHeroes = cb->getHeroesInfo();
if(cb->getHeroesInfo().size() < ALLOWED_ROAMING_HEROES)
{
logAi->debug("Hero %s can be recruited to defend %s", hero->name, town->name);
tasks.push_back(Goals::sptr(Goals::RecruitHero().settown(town).setobjid(hero->id.getNum()).setpriority(1)));
continue;
}
else
{
const CGHeroInstance * weakestHero = nullptr;
for(auto existingHero : myHeroes)
{
if(ai->nullkiller->isHeroLocked(existingHero)
|| existingHero->getArmyStrength() > hero->getArmyStrength()
|| ai->ah->getHeroRole(existingHero) == HeroRole::MAIN
|| existingHero->movement
|| existingHero->artifactsWorn.size() > (existingHero->hasSpellbook() ? 2 : 1))
continue;
if(!weakestHero || weakestHero->getFightingStrength() > existingHero->getFightingStrength())
{
weakestHero = existingHero;
}
if(weakestHero)
{
tasks.push_back(Goals::sptr(Goals::DismissHero(weakestHero)));
}
}
}
}
}
}
if(paths.empty())
{
logAi->debug("No ways to defend town %s", town->name);
continue;
}
for(AIPath & path : paths)
{
#if AI_TRACE_LEVEL >= 1

View File

@@ -30,6 +30,7 @@ set(VCAI_SRCS
Goals/BuildThis.cpp
Goals/Explore.cpp
Goals/GatherArmy.cpp
Goals/DismissHero.cpp
Goals/GatherTroops.cpp
Goals/BuyArmy.cpp
Goals/AdventureSpellCast.cpp
@@ -96,6 +97,7 @@ set(VCAI_HEADERS
Goals/BuildThis.h
Goals/Explore.h
Goals/GatherArmy.h
Goals/DismissHero.h
Goals/GatherTroops.h
Goals/BuyArmy.h
Goals/AdventureSpellCast.h

View File

@@ -31,6 +31,7 @@ public:
void setActive(const CGHeroInstance * hero) { activeHero = hero; }
void lockHero(const CGHeroInstance * hero) { lockedHeroes.insert(hero); }
void unlockHero(const CGHeroInstance * hero) { lockedHeroes.erase(hero); }
bool canMove(const CGHeroInstance * hero) { return hero->movement; }
private:
void resetAiState();

View File

@@ -66,7 +66,8 @@ namespace Goals
COMPLETE_QUEST,
ADVENTURE_SPELL_CAST,
EXECUTE_HERO_CHAIN,
EXCHANGE_SWAP_TOWN_HEROES
EXCHANGE_SWAP_TOWN_HEROES,
DISMISS_HERO
};
class DLL_EXPORT TSubgoal : public std::shared_ptr<AbstractGoal>

View File

@@ -0,0 +1,55 @@
/*
* DismissHero.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 "DismissHero.h"
#include "../VCAI.h"
#include "../FuzzyHelper.h"
#include "../AIhelper.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;
bool DismissHero::operator==(const DismissHero & other) const
{
return hero.h == other.hero.h;
}
TSubgoal DismissHero::whatToDoToAchieve()
{
if(!hero.validAndSet())
throw cannotFulfillGoalException("Invalid hero!");
return iAmElementar();
}
void DismissHero::accept(VCAI * ai)
{
if(!hero.validAndSet())
throw cannotFulfillGoalException("Invalid hero!");
cb->dismissHero(hero.h);
throw goalFulfilledException(sptr(*this));
}
std::string DismissHero::name() const
{
return "DismissHero " + hero.name;
}
std::string DismissHero::completeMessage() const
{
return "Hero dismissed successfully " + hero.name;
}

View File

@@ -0,0 +1,36 @@
/*
* DismissHero.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 "CGoal.h"
namespace Goals
{
class DLL_EXPORT DismissHero : public CGoal<DismissHero>
{
public:
DismissHero(HeroPtr hero)
: CGoal(Goals::DISMISS_HERO)
{
sethero(hero);
}
TGoalVec getAllPossibleSubgoals() override
{
return TGoalVec();
}
TSubgoal whatToDoToAchieve() override;
void accept(VCAI * ai) override;
std::string name() const override;
std::string completeMessage() const override;
virtual bool operator==(const DismissHero & other) const override;
};
}

View File

@@ -126,6 +126,10 @@ void ExecuteHeroChain::accept(VCAI * ai)
return;
}
// do not lock hero if it is simple one hero chain
if(chainPath.exchangeCount == 1)
return;
// no exception means we were not able to rich the tile
ai->nullkiller->lockHero(hero.get());
blockedIndexes.insert(node.parentIndex);

View File

@@ -2035,7 +2035,11 @@ void VCAI::tryRealize(Goals::Explore & g)
void VCAI::tryRealize(Goals::RecruitHero & g)
{
if(const CGTownInstance * t = findTownWithTavern())
const CGTownInstance * t = g.town;
if(!t) t = findTownWithTavern();
if(t)
{
recruitHero(t, true);
//TODO try to free way to blocked town