2021-05-15 19:59:43 +02:00
|
|
|
/*
|
|
|
|
* AINodeStorage.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 "Actors.h"
|
2019-03-17 23:27:09 +02:00
|
|
|
#include "../Goals/VisitHero.h"
|
|
|
|
#include "../VCAI.h"
|
|
|
|
#include "../AIhelper.h"
|
2021-05-15 19:59:43 +02:00
|
|
|
#include "../../../CCallback.h"
|
|
|
|
#include "../../../lib/mapping/CMap.h"
|
|
|
|
#include "../../../lib/mapObjects/MapObjects.h"
|
|
|
|
|
|
|
|
class ExchangeAction : public ISpecialAction
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
const CGHeroInstance * target;
|
|
|
|
const CGHeroInstance * source;
|
|
|
|
|
|
|
|
public:
|
|
|
|
ExchangeAction(const CGHeroInstance * target, const CGHeroInstance * source)
|
|
|
|
:target(target), source(source)
|
|
|
|
{ }
|
|
|
|
|
|
|
|
virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const override
|
|
|
|
{
|
|
|
|
return Goals::sptr(Goals::VisitHero(target->id.getNum()).sethero(hero));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-03-17 23:27:09 +02:00
|
|
|
ChainActor::ChainActor(const CGHeroInstance * hero, uint64_t chainMask)
|
|
|
|
:hero(hero), isMovable(true), chainMask(chainMask), creatureSet(hero),
|
|
|
|
baseActor(this), carrierParent(nullptr), otherParent(nullptr)
|
2021-05-15 19:59:43 +02:00
|
|
|
{
|
|
|
|
initialPosition = hero->visitablePos();
|
|
|
|
layer = hero->boat ? EPathfindingLayer::SAIL : EPathfindingLayer::LAND;
|
|
|
|
initialMovement = hero->movement;
|
|
|
|
initialTurn = 0;
|
2019-03-17 23:27:09 +02:00
|
|
|
armyValue = hero->getArmyStrength();
|
2021-05-15 19:59:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
ChainActor::ChainActor(const ChainActor * carrier, const ChainActor * other, const CCreatureSet * heroArmy)
|
2019-03-17 23:27:09 +02:00
|
|
|
:hero(carrier->hero), isMovable(true), creatureSet(heroArmy), chainMask(carrier->chainMask | other->chainMask),
|
|
|
|
baseActor(this), carrierParent(carrier), otherParent(other)
|
2021-05-15 19:59:43 +02:00
|
|
|
{
|
2019-03-17 23:27:09 +02:00
|
|
|
armyValue = heroArmy->getArmyStrength();
|
2021-05-15 19:59:43 +02:00
|
|
|
}
|
|
|
|
|
2019-03-17 23:27:09 +02:00
|
|
|
ChainActor::ChainActor(const CGObjectInstance * obj, const CCreatureSet * creatureSet, uint64_t chainMask, int initialTurn)
|
|
|
|
:hero(nullptr), isMovable(false), creatureSet(creatureSet), chainMask(chainMask),
|
|
|
|
baseActor(this), carrierParent(nullptr), otherParent(nullptr), initialTurn(initialTurn), initialMovement(0)
|
2021-05-15 19:59:43 +02:00
|
|
|
{
|
2019-03-17 23:27:09 +02:00
|
|
|
initialPosition = obj->visitablePos();
|
|
|
|
layer = EPathfindingLayer::LAND;
|
|
|
|
armyValue = creatureSet->getArmyStrength();
|
2021-05-15 19:59:43 +02:00
|
|
|
}
|
|
|
|
|
2019-03-17 23:27:09 +02:00
|
|
|
HeroActor::HeroActor(const CGHeroInstance * hero, uint64_t chainMask, const VCAI * ai)
|
|
|
|
:ChainActor(hero, chainMask)
|
2021-05-15 19:59:43 +02:00
|
|
|
{
|
2019-03-17 23:27:09 +02:00
|
|
|
exchangeMap = new HeroExchangeMap(this, ai);
|
2021-05-15 19:59:43 +02:00
|
|
|
setupSpecialActors();
|
|
|
|
}
|
|
|
|
|
2019-03-17 23:27:09 +02:00
|
|
|
HeroActor::HeroActor(
|
|
|
|
const ChainActor * carrier,
|
|
|
|
const ChainActor * other,
|
|
|
|
const CCreatureSet * army,
|
|
|
|
const VCAI * ai)
|
|
|
|
:ChainActor(carrier, other, army)
|
|
|
|
{
|
|
|
|
exchangeMap = new HeroExchangeMap(this, ai);
|
|
|
|
setupSpecialActors();
|
2021-05-15 19:59:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void ChainActor::setBaseActor(HeroActor * base)
|
|
|
|
{
|
|
|
|
baseActor = base;
|
|
|
|
hero = base->hero;
|
|
|
|
layer = base->layer;
|
|
|
|
initialMovement = base->initialMovement;
|
|
|
|
initialTurn = base->initialTurn;
|
|
|
|
armyValue = base->armyValue;
|
|
|
|
chainMask = base->chainMask;
|
|
|
|
creatureSet = base->creatureSet;
|
2021-05-15 20:04:48 +02:00
|
|
|
isMovable = base->isMovable;
|
2021-05-15 19:59:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void HeroActor::setupSpecialActors()
|
|
|
|
{
|
|
|
|
auto allActors = std::vector<ChainActor *>{ this };
|
|
|
|
|
2021-05-15 20:04:48 +02:00
|
|
|
for(ChainActor & specialActor : specialActors)
|
2021-05-15 19:59:43 +02:00
|
|
|
{
|
|
|
|
specialActor.setBaseActor(this);
|
|
|
|
allActors.push_back(&specialActor);
|
|
|
|
}
|
|
|
|
|
|
|
|
for(int i = 0; i <= SPECIAL_ACTORS_COUNT; i++)
|
|
|
|
{
|
|
|
|
ChainActor * actor = allActors[i];
|
|
|
|
|
2021-05-15 20:04:48 +02:00
|
|
|
actor->allowBattle = (i & 1) > 0;
|
|
|
|
actor->allowSpellCast = (i & 2) > 0;
|
|
|
|
actor->allowUseResources = (i & 4) > 0;
|
2021-05-15 19:59:43 +02:00
|
|
|
actor->battleActor = allActors[i | 1];
|
|
|
|
actor->castActor = allActors[i | 2];
|
|
|
|
actor->resourceActor = allActors[i | 4];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-17 23:27:09 +02:00
|
|
|
ChainActor * ChainActor::exchange(const ChainActor * specialActor, const ChainActor * other) const
|
2021-05-15 19:59:43 +02:00
|
|
|
{
|
2019-03-17 23:27:09 +02:00
|
|
|
return baseActor->exchange(specialActor, other);
|
2021-05-15 19:59:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ChainActor::canExchange(const ChainActor * other) const
|
|
|
|
{
|
2019-03-17 23:27:09 +02:00
|
|
|
return isMovable && baseActor->canExchange(other);
|
2021-05-15 19:59:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
namespace vstd
|
|
|
|
{
|
|
|
|
template <class M, class Key, class F>
|
|
|
|
typename M::mapped_type & getOrCompute(M &m, Key const& k, F f)
|
|
|
|
{
|
|
|
|
typedef typename M::mapped_type V;
|
|
|
|
|
|
|
|
std::pair<typename M::iterator, bool> r = m.insert(typename M::value_type(k, V()));
|
|
|
|
V &v = r.first->second;
|
|
|
|
|
|
|
|
if(r.second)
|
|
|
|
f(v);
|
|
|
|
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-17 23:27:09 +02:00
|
|
|
bool HeroActor::canExchange(const ChainActor * other) const
|
|
|
|
{
|
|
|
|
return exchangeMap->canExchange(other);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HeroExchangeMap::canExchange(const ChainActor * other)
|
2021-05-15 19:59:43 +02:00
|
|
|
{
|
|
|
|
return vstd::getOrCompute(canExchangeCache, other, [&](bool & result) {
|
2019-03-17 23:27:09 +02:00
|
|
|
result = (actor->chainMask & other->chainMask) == 0;
|
|
|
|
|
|
|
|
if(result)
|
|
|
|
{
|
|
|
|
uint64_t reinforcment = ai->ah->howManyReinforcementsCanGet(actor->creatureSet, other->creatureSet);
|
|
|
|
|
|
|
|
result = reinforcment > actor->armyValue / 10 || reinforcment > 1000;
|
|
|
|
}
|
2021-05-15 19:59:43 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-03-17 23:27:09 +02:00
|
|
|
ChainActor * HeroActor::exchange(const ChainActor * specialActor, const ChainActor * other) const
|
2021-05-15 19:59:43 +02:00
|
|
|
{
|
2019-03-17 23:27:09 +02:00
|
|
|
const ChainActor * otherBase = other->baseActor;
|
|
|
|
HeroActor * result = exchangeMap->exchange(otherBase);
|
2021-05-15 19:59:43 +02:00
|
|
|
|
|
|
|
if(specialActor == this)
|
|
|
|
return result;
|
|
|
|
|
2019-03-17 23:27:09 +02:00
|
|
|
int index = vstd::find_pos_if(specialActors, [specialActor](const ChainActor & actor) -> bool
|
|
|
|
{
|
2021-05-15 19:59:43 +02:00
|
|
|
return &actor == specialActor;
|
|
|
|
});
|
|
|
|
|
|
|
|
return &result->specialActors[index];
|
|
|
|
}
|
|
|
|
|
2019-03-17 23:27:09 +02:00
|
|
|
HeroActor * HeroExchangeMap::exchange(const ChainActor * other)
|
|
|
|
{
|
|
|
|
HeroActor * result;
|
|
|
|
|
|
|
|
if(vstd::contains(exchangeMap, other))
|
|
|
|
result = exchangeMap.at(other);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// TODO: decide where to release this CCreatureSet and HeroActor. Probably custom ~ctor?
|
|
|
|
CCreatureSet * newArmy = pickBestCreatures(actor->creatureSet, other->creatureSet);
|
|
|
|
result = new HeroActor(actor, other, newArmy, ai);
|
|
|
|
exchangeMap[other] = result;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
CCreatureSet * HeroExchangeMap::pickBestCreatures(const CCreatureSet * army1, const CCreatureSet * army2) const
|
2021-05-15 19:59:43 +02:00
|
|
|
{
|
|
|
|
CCreatureSet * target = new CCreatureSet();
|
2019-03-17 23:27:09 +02:00
|
|
|
auto bestArmy = ai->ah->getBestArmy(army1, army2);
|
2021-05-15 19:59:43 +02:00
|
|
|
|
2019-03-17 23:27:09 +02:00
|
|
|
for(auto & slotInfo : bestArmy)
|
2021-05-15 19:59:43 +02:00
|
|
|
{
|
2019-03-17 23:27:09 +02:00
|
|
|
auto targetSlot = target->getFreeSlot();
|
|
|
|
|
|
|
|
target->addToSlot(targetSlot, slotInfo.creature->idNumber, TQuantity(slotInfo.count));
|
2021-05-15 19:59:43 +02:00
|
|
|
}
|
|
|
|
|
2019-03-17 23:27:09 +02:00
|
|
|
return target;
|
|
|
|
}
|
2021-05-15 19:59:43 +02:00
|
|
|
|
2019-03-17 23:27:09 +02:00
|
|
|
DwellingActor::DwellingActor(const CGDwelling * dwelling, uint64_t chainMask, bool waitForGrowth, int dayOfWeek)
|
|
|
|
:ChainActor(
|
|
|
|
dwelling,
|
|
|
|
getDwellingCreatures(dwelling, waitForGrowth),
|
|
|
|
chainMask,
|
|
|
|
getInitialTurn(waitForGrowth, dayOfWeek))
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
DwellingActor::~DwellingActor()
|
|
|
|
{
|
|
|
|
delete creatureSet;
|
|
|
|
}
|
|
|
|
|
|
|
|
int DwellingActor::getInitialTurn(bool waitForGrowth, int dayOfWeek)
|
|
|
|
{
|
|
|
|
if(!waitForGrowth)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return 8 - dayOfWeek;
|
|
|
|
}
|
|
|
|
|
|
|
|
CCreatureSet * DwellingActor::getDwellingCreatures(const CGDwelling * dwelling, bool waitForGrowth)
|
|
|
|
{
|
|
|
|
CCreatureSet * dwellingCreatures = new CCreatureSet();
|
|
|
|
|
|
|
|
for(auto & creatureInfo : dwelling->creatures)
|
2021-05-15 19:59:43 +02:00
|
|
|
{
|
2019-03-17 23:27:09 +02:00
|
|
|
if(!creatureInfo.second.size())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
auto creature = creatureInfo.second.back().toCreature();
|
|
|
|
auto count = creatureInfo.first;
|
|
|
|
|
|
|
|
if(waitForGrowth)
|
2021-05-15 19:59:43 +02:00
|
|
|
{
|
2019-03-17 23:27:09 +02:00
|
|
|
const CGTownInstance * town = dynamic_cast<const CGTownInstance *>(dwelling);
|
|
|
|
|
|
|
|
count += town ? town->creatureGrowth(creature->level) : creature->growth;
|
|
|
|
}
|
2021-05-15 19:59:43 +02:00
|
|
|
|
2019-03-17 23:27:09 +02:00
|
|
|
dwellingCreatures->addToSlot(
|
|
|
|
dwellingCreatures->getSlotFor(creature),
|
|
|
|
creature->idNumber,
|
|
|
|
TQuantity(creatureInfo.first));
|
2021-05-15 19:59:43 +02:00
|
|
|
}
|
|
|
|
|
2019-03-17 23:27:09 +02:00
|
|
|
return dwellingCreatures;
|
|
|
|
}
|
|
|
|
|
|
|
|
TownGarrisonActor::TownGarrisonActor(const CGTownInstance * town, uint64_t chainMask)
|
|
|
|
:ChainActor(town, town->getUpperArmy(), chainMask, 0)
|
|
|
|
{
|
2021-05-15 19:59:43 +02:00
|
|
|
}
|