1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-04-11 11:31:52 +02:00

Nullkiller: startup scripts

This commit is contained in:
Andrii Danylchenko 2021-05-15 22:02:52 +03:00 committed by Andrii Danylchenko
parent 5fe2630c64
commit bcf8db3d05
13 changed files with 238 additions and 9 deletions

View File

@ -215,4 +215,9 @@ void AIhelper::updateHeroRoles()
float AIhelper::evaluateSecSkill(SecondarySkill skill, const CGHeroInstance * hero) const
{
return heroManager->evaluateSecSkill(skill, hero);
}
float AIhelper::evaluateHero(const CGHeroInstance * hero) const
{
return heroManager->evaluateHero(hero);
}

View File

@ -85,6 +85,7 @@ public:
int selectBestSkill(const HeroPtr & hero, const std::vector<SecondarySkill> & skills) const override;
void updateHeroRoles() override;
float evaluateSecSkill(SecondarySkill skill, const CGHeroInstance * hero) const override;
float evaluateHero(const CGHeroInstance * hero) const override;
private:
bool notifyGoalCompleted(Goals::TSubgoal goal) override;

View File

@ -13,6 +13,7 @@
#include "../AIhelper.h"
#include "../AIUtility.h"
#include "../Goals/RecruitHero.h"
#include "../Goals/ExecuteHeroChain.h"
#include "lib/mapping/CMap.h" //for victory conditions
#include "lib/CPathfinder.h"
@ -33,13 +34,133 @@ Goals::TGoalVec RecruitHeroBehavior::getTasks()
if(ai->canRecruitAnyHero())
{
if(cb->getDate(Date::DAY) == 1
|| cb->getHeroesInfo().size() < cb->getTownsInfo().size() + 1
if(cb->getHeroesInfo().size() < cb->getTownsInfo().size() + 1
|| cb->getResourceAmount(Res::GOLD) > 10000)
{
tasks.push_back(Goals::sptr(Goals::RecruitHero()));
}
}
return tasks;
}
std::string StartupBehavior::toString() const
{
return "Startup";
}
const AIPath getShortestPath(const CGTownInstance * town, const std::vector<AIPath> & paths)
{
auto shortestPath = *vstd::minElementByFun(paths, [town](const AIPath & path) -> float
{
if(town->garrisonHero && path.targetHero == town->garrisonHero.get())
return 1;
return path.movementCost();
});
return shortestPath;
}
const CGHeroInstance * getNearestHero(const CGTownInstance * town)
{
auto paths = ai->ah->getPathsToTile(town->visitablePos());
if(paths.empty())
return nullptr;
auto shortestPath = getShortestPath(town, paths);
if(shortestPath.nodes.size() > 1
|| shortestPath.targetHero->visitablePos().dist2dSQ(town->visitablePos()) > 4
|| town->garrisonHero && shortestPath.targetHero == town->garrisonHero.get())
return nullptr;
return shortestPath.targetHero;
}
Goals::TGoalVec StartupBehavior::getTasks()
{
Goals::TGoalVec tasks;
auto towns = cb->getTownsInfo();
if(!towns.size())
return tasks;
const CGTownInstance * startupTown = towns.front();
bool canRecruitHero = ai->canRecruitAnyHero(startupTown);
if(towns.size() > 1)
{
startupTown = *vstd::maxElementByFun(towns, [](const CGTownInstance * town) -> float
{
auto closestHero = getNearestHero(town);
if(!closestHero)
return 0;
return ai->ah->evaluateHero(closestHero);
});
}
auto closestHero = getNearestHero(startupTown);
if(closestHero)
{
if(!startupTown->visitingHero)
{
if(ai->ah->howManyReinforcementsCanGet(startupTown->getUpperArmy(), closestHero) > 200)
{
auto paths = ai->ah->getPathsToTile(startupTown->visitablePos());
if(paths.size())
{
auto path = getShortestPath(startupTown, paths);
tasks.push_back(Goals::sptr(Goals::ExecuteHeroChain(path, startupTown).setpriority(100)));
}
}
}
else
{
auto visitingHero = startupTown->visitingHero.get();
auto visitingHeroScore = ai->ah->evaluateHero(visitingHero);
if(startupTown->garrisonHero)
{
auto garrisonHero = startupTown->garrisonHero.get();
auto garrisonHeroScore = ai->ah->evaluateHero(garrisonHero);
if(garrisonHeroScore > visitingHeroScore)
{
if(ai->ah->howManyReinforcementsCanGet(garrisonHero, visitingHero) > 200)
tasks.push_back(Goals::sptr(ExchangeSwapTownHeroes(startupTown, garrisonHero).setpriority(100)));
}
else
{
tasks.push_back(Goals::sptr(ExchangeSwapTownHeroes(startupTown, visitingHero).setpriority(100)));
}
}
else if(canRecruitHero)
{
tasks.push_back(Goals::sptr(ExchangeSwapTownHeroes(startupTown, visitingHero).setpriority(100)));
}
}
}
if(tasks.empty() && canRecruitHero && !startupTown->visitingHero)
{
tasks.push_back(Goals::sptr(Goals::RecruitHero()));
}
if(tasks.empty() && towns.size())
{
for(const CGTownInstance * town : towns)
{
if(town->garrisonHero)
tasks.push_back(Goals::sptr(ExchangeSwapTownHeroes(town, nullptr).setpriority(0.0001f)));
}
}
return tasks;
}

View File

@ -24,3 +24,14 @@ public:
virtual std::string toString() const override;
};
class StartupBehavior : public Behavior
{
public:
StartupBehavior()
{
}
virtual Goals::TGoalVec getTasks() override;
virtual std::string toString() const override;
};

View File

@ -86,6 +86,11 @@ void Nullkiller::makeTurn()
choseBestTask(std::make_shared<RecruitHeroBehavior>())
};
if(cb->getDate(Date::DAY) == 1)
{
bestTasks.push_back(choseBestTask(std::make_shared<StartupBehavior>()));
}
Goals::TSubgoal bestTask = choseBestTask(bestTasks);
if(bestTask->invalid())

View File

@ -20,6 +20,7 @@ public:
bool isActive(const CGHeroInstance * hero) const { return activeHero.h == hero; }
void setActive(const HeroPtr & hero) { activeHero = hero; }
void lockHero(const HeroPtr & hero) { lockedHeroes.insert(hero); }
void unlockHero(const HeroPtr & hero) { lockedHeroes.erase(hero); }
private:
void resetAiState();

View File

@ -176,9 +176,9 @@ float getSkillReward(const CGObjectInstance * target, const CGHeroInstance * her
case Obj::MARLETTO_TOWER:
case Obj::MERCENARY_CAMP:
case Obj::SHRINE_OF_MAGIC_GESTURE:
case Obj::SHRINE_OF_MAGIC_INCANTATION:
return 1;
case Obj::ARENA:
case Obj::SHRINE_OF_MAGIC_INCANTATION:
case Obj::SHRINE_OF_MAGIC_THOUGHT:
return 2;
case Obj::LIBRARY_OF_ENLIGHTENMENT:
@ -240,6 +240,9 @@ int32_t getGoldReward(const CGObjectInstance * target, const CGHeroInstance * he
/// importance
float PriorityEvaluator::evaluate(Goals::TSubgoal task)
{
if(task->priority > 0)
return task->priority;
auto heroPtr = task->hero;
if(!heroPtr.validAndSet())
@ -263,10 +266,7 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task)
uint64_t danger = task->evaluationContext.danger;
double result = 0;
int rewardType = (goldReward > 0 ? 1 : 0) + (armyReward > 0 ? 1 : 0) + (skillReward > 0 ? 1 : 0);
if(day == 1)
goldReward *= 2;
try
{
armyLossPersentageVariable->setValue(armyLossPersentage);

View File

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

View File

@ -110,3 +110,59 @@ std::string ExecuteHeroChain::completeMessage() const
{
return "Hero chain completed";
}
ExchangeSwapTownHeroes::ExchangeSwapTownHeroes(const CGTownInstance * town, const CGHeroInstance * garrisonHero)
:CGoal(Goals::EXCHANGE_SWAP_TOWN_HEROES), town(town), garrisonHero(garrisonHero)
{
}
std::string ExchangeSwapTownHeroes::name() const
{
return "Exchange and swap heroes of " + town->name;
}
std::string ExchangeSwapTownHeroes::completeMessage() const
{
return "Exchange and swap heroes of " + town->name + " compelete";
}
bool ExchangeSwapTownHeroes::operator==(const ExchangeSwapTownHeroes & other) const
{
return town == other.town;
}
TSubgoal ExchangeSwapTownHeroes::whatToDoToAchieve()
{
return iAmElementar();
}
void ExchangeSwapTownHeroes::accept(VCAI * ai)
{
if(!garrisonHero && town->garrisonHero && town->visitingHero)
throw cannotFulfillGoalException("Invalid configuration. Garrison hero is null.");
if(!garrisonHero)
{
if(!town->garrisonHero)
throw cannotFulfillGoalException("Invalid configuration. There is no hero in town garrison.");
cb->swapGarrisonHero(town);
ai->nullkiller->unlockHero(town->visitingHero.get());
logAi->debug("Extracted hero %s from garrison of %s", town->visitingHero->name, town->name);
return;
}
if(town->visitingHero && town->visitingHero.get() != garrisonHero)
cb->swapGarrisonHero(town);
ai->moveHeroToTile(town->visitablePos(), garrisonHero);
cb->swapGarrisonHero(town); // selected hero left in garrison with strongest army
ai->nullkiller->lockHero(town->garrisonHero.get());
if(town->visitingHero)
ai->nullkiller->unlockHero(town->visitingHero.get());
logAi->debug("Put hero %s to garrison of %s", town->garrisonHero->name, town->name);
}

View File

@ -33,4 +33,25 @@ namespace Goals
std::string completeMessage() const override;
virtual bool operator==(const ExecuteHeroChain & other) const override;
};
class DLL_EXPORT ExchangeSwapTownHeroes : public CGoal<ExchangeSwapTownHeroes>
{
private:
const CGTownInstance * town;
const CGHeroInstance * garrisonHero;
public:
ExchangeSwapTownHeroes(const CGTownInstance * town, const CGHeroInstance * garrisonHero = nullptr);
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 ExchangeSwapTownHeroes & other) const override;
};
}

View File

@ -163,6 +163,11 @@ int HeroManager::selectBestSkill(const HeroPtr & hero, const std::vector<Seconda
return result;
}
float HeroManager::evaluateHero(const CGHeroInstance * hero) const
{
return evaluateFightingStrength(hero);
}
SecondarySkillScoreMap::SecondarySkillScoreMap(std::map<SecondarySkill, float> scoreMap)
:scoreMap(scoreMap)
{

View File

@ -35,6 +35,7 @@ public:
virtual HeroRole getHeroRole(const HeroPtr & hero) const = 0;
virtual void updateHeroRoles() = 0;
virtual float evaluateSecSkill(SecondarySkill skill, const CGHeroInstance * hero) const = 0;
virtual float evaluateHero(const CGHeroInstance * hero) const = 0;
};
class DLL_EXPORT ISecondarySkillRule
@ -72,6 +73,7 @@ public:
int selectBestSkill(const HeroPtr & hero, const std::vector<SecondarySkill> & skills) const override;
void updateHeroRoles() override;
float evaluateSecSkill(SecondarySkill skill, const CGHeroInstance * hero) const override;
float evaluateHero(const CGHeroInstance * hero) const override;
private:
float evaluateFightingStrength(const CGHeroInstance * hero) const;

View File

@ -1077,7 +1077,7 @@ void VCAI::moveCreaturesToHero(const CGTownInstance * t)
{
if(t->visitingHero && t->armedGarrison() && t->visitingHero->tempOwner == t->tempOwner)
{
pickBestCreatures(t->visitingHero, t);
pickBestCreatures(t->visitingHero, t->getUpperArmy());
}
}