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:
committed by
Andrii Danylchenko
parent
84e5e6ac17
commit
e6eb9ccc03
@@ -134,7 +134,10 @@ Goals::TGoalVec CaptureObjectsBehavior::getTasks()
|
|||||||
way->evaluationContext.closestWayRatio
|
way->evaluationContext.closestWayRatio
|
||||||
= way->evaluationContext.movementCost / closestWay->evaluationContext.movementCost;
|
= 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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -16,6 +16,7 @@
|
|||||||
#include "../Goals/BuyArmy.h"
|
#include "../Goals/BuyArmy.h"
|
||||||
#include "../Goals/VisitTile.h"
|
#include "../Goals/VisitTile.h"
|
||||||
#include "../Goals/ExecuteHeroChain.h"
|
#include "../Goals/ExecuteHeroChain.h"
|
||||||
|
#include "../Goals/DismissHero.h"
|
||||||
#include "../Goals/ExchangeSwapTownHeroes.h"
|
#include "../Goals/ExchangeSwapTownHeroes.h"
|
||||||
#include "lib/mapping/CMap.h" //for victory conditions
|
#include "lib/mapping/CMap.h" //for victory conditions
|
||||||
#include "lib/CPathfinder.h"
|
#include "lib/CPathfinder.h"
|
||||||
@@ -109,13 +110,6 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
|||||||
|
|
||||||
auto paths = ai->ah->getPathsToTile(town->visitablePos());
|
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)
|
for(auto & treat : treats)
|
||||||
{
|
{
|
||||||
logAi->debug(
|
logAi->debug(
|
||||||
@@ -131,7 +125,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
|||||||
{
|
{
|
||||||
if(path.getHeroStrength() > treat.danger)
|
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.exchangeCount == 1 && path.turn() < treat.turn
|
||||||
|| path.turn() < treat.turn - 1)
|
|| path.turn() < treat.turn - 1)
|
||||||
{
|
{
|
||||||
@@ -150,7 +144,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
|||||||
if(treatIsUnderControl)
|
if(treatIsUnderControl)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if(ai->canRecruitAnyHero(town))
|
if(cb->getResourceAmount(Res::GOLD) > GameConstants::HERO_GOLD_COST)
|
||||||
{
|
{
|
||||||
auto heroesInTavern = cb->getAvailableHeroes(town);
|
auto heroesInTavern = cb->getAvailableHeroes(town);
|
||||||
|
|
||||||
@@ -158,12 +152,49 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
|||||||
{
|
{
|
||||||
if(hero->getTotalStrength() > treat.danger)
|
if(hero->getTotalStrength() > treat.danger)
|
||||||
{
|
{
|
||||||
tasks.push_back(Goals::sptr(Goals::RecruitHero().settown(town).setobjid(hero->id.getNum()).setpriority(1)));
|
auto myHeroes = cb->getHeroesInfo();
|
||||||
continue;
|
|
||||||
|
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)
|
for(AIPath & path : paths)
|
||||||
{
|
{
|
||||||
#if AI_TRACE_LEVEL >= 1
|
#if AI_TRACE_LEVEL >= 1
|
||||||
|
@@ -30,6 +30,7 @@ set(VCAI_SRCS
|
|||||||
Goals/BuildThis.cpp
|
Goals/BuildThis.cpp
|
||||||
Goals/Explore.cpp
|
Goals/Explore.cpp
|
||||||
Goals/GatherArmy.cpp
|
Goals/GatherArmy.cpp
|
||||||
|
Goals/DismissHero.cpp
|
||||||
Goals/GatherTroops.cpp
|
Goals/GatherTroops.cpp
|
||||||
Goals/BuyArmy.cpp
|
Goals/BuyArmy.cpp
|
||||||
Goals/AdventureSpellCast.cpp
|
Goals/AdventureSpellCast.cpp
|
||||||
@@ -96,6 +97,7 @@ set(VCAI_HEADERS
|
|||||||
Goals/BuildThis.h
|
Goals/BuildThis.h
|
||||||
Goals/Explore.h
|
Goals/Explore.h
|
||||||
Goals/GatherArmy.h
|
Goals/GatherArmy.h
|
||||||
|
Goals/DismissHero.h
|
||||||
Goals/GatherTroops.h
|
Goals/GatherTroops.h
|
||||||
Goals/BuyArmy.h
|
Goals/BuyArmy.h
|
||||||
Goals/AdventureSpellCast.h
|
Goals/AdventureSpellCast.h
|
||||||
|
@@ -31,6 +31,7 @@ public:
|
|||||||
void setActive(const CGHeroInstance * hero) { activeHero = hero; }
|
void setActive(const CGHeroInstance * hero) { activeHero = hero; }
|
||||||
void lockHero(const CGHeroInstance * hero) { lockedHeroes.insert(hero); }
|
void lockHero(const CGHeroInstance * hero) { lockedHeroes.insert(hero); }
|
||||||
void unlockHero(const CGHeroInstance * hero) { lockedHeroes.erase(hero); }
|
void unlockHero(const CGHeroInstance * hero) { lockedHeroes.erase(hero); }
|
||||||
|
bool canMove(const CGHeroInstance * hero) { return hero->movement; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void resetAiState();
|
void resetAiState();
|
||||||
|
@@ -66,7 +66,8 @@ namespace Goals
|
|||||||
COMPLETE_QUEST,
|
COMPLETE_QUEST,
|
||||||
ADVENTURE_SPELL_CAST,
|
ADVENTURE_SPELL_CAST,
|
||||||
EXECUTE_HERO_CHAIN,
|
EXECUTE_HERO_CHAIN,
|
||||||
EXCHANGE_SWAP_TOWN_HEROES
|
EXCHANGE_SWAP_TOWN_HEROES,
|
||||||
|
DISMISS_HERO
|
||||||
};
|
};
|
||||||
|
|
||||||
class DLL_EXPORT TSubgoal : public std::shared_ptr<AbstractGoal>
|
class DLL_EXPORT TSubgoal : public std::shared_ptr<AbstractGoal>
|
||||||
|
55
AI/Nullkiller/Goals/DismissHero.cpp
Normal file
55
AI/Nullkiller/Goals/DismissHero.cpp
Normal 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;
|
||||||
|
}
|
36
AI/Nullkiller/Goals/DismissHero.h
Normal file
36
AI/Nullkiller/Goals/DismissHero.h
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
@@ -126,6 +126,10 @@ void ExecuteHeroChain::accept(VCAI * ai)
|
|||||||
return;
|
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
|
// no exception means we were not able to rich the tile
|
||||||
ai->nullkiller->lockHero(hero.get());
|
ai->nullkiller->lockHero(hero.get());
|
||||||
blockedIndexes.insert(node.parentIndex);
|
blockedIndexes.insert(node.parentIndex);
|
||||||
|
@@ -2035,7 +2035,11 @@ void VCAI::tryRealize(Goals::Explore & g)
|
|||||||
|
|
||||||
void VCAI::tryRealize(Goals::RecruitHero & 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);
|
recruitHero(t, true);
|
||||||
//TODO try to free way to blocked town
|
//TODO try to free way to blocked town
|
||||||
|
Reference in New Issue
Block a user