123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474 |
- /*
- * 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"
- #include "../AIGateway.h"
- #include "../Engine/Nullkiller.h"
- #include "../../../CCallback.h"
- #include "../../../lib/mapObjects/MapObjects.h"
- #include "../../../lib/mapping/CMapDefines.h"
- #include "../../../lib/pathfinder/TurnInfo.h"
- #include "Actions/BuyArmyAction.h"
- using namespace NKAI;
- const CCreatureSet emptyArmy;
- bool HeroExchangeArmy::needsLastStack() const
- {
- return true;
- }
- std::shared_ptr<SpecialAction> HeroExchangeArmy::getActorAction() const
- {
- std::shared_ptr<SpecialAction> result;
- if(requireBuyArmy)
- {
- result.reset(new AIPathfinding::BuyArmyAction());
- }
- return result;
- }
- ChainActor::ChainActor(const CGHeroInstance * hero, HeroRole heroRole, uint64_t chainMask)
- :hero(hero), heroRole(heroRole), isMovable(true), chainMask(chainMask), creatureSet(hero),
- baseActor(this), carrierParent(nullptr), otherParent(nullptr), actorExchangeCount(1), armyCost(), actorAction()
- {
- initialPosition = hero->visitablePos();
- layer = hero->boat ? hero->boat->layer : EPathfindingLayer::LAND;
- initialMovement = hero->movementPointsRemaining();
- initialTurn = 0;
- armyValue = getHeroArmyStrengthWithCommander(hero, hero);
- heroFightingStrength = hero->getHeroStrength();
- }
- ChainActor::ChainActor(const ChainActor * carrier, const ChainActor * other, const CCreatureSet * heroArmy)
- :hero(carrier->hero), heroRole(carrier->heroRole), isMovable(true), creatureSet(heroArmy), chainMask(carrier->chainMask | other->chainMask),
- baseActor(this), carrierParent(carrier), otherParent(other), heroFightingStrength(carrier->heroFightingStrength),
- actorExchangeCount(carrier->actorExchangeCount + other->actorExchangeCount), armyCost(carrier->armyCost + other->armyCost), actorAction()
- {
- armyValue = getHeroArmyStrengthWithCommander(hero, heroArmy);
- }
- ChainActor::ChainActor(const CGObjectInstance * obj, const CCreatureSet * creatureSet, uint64_t chainMask, int initialTurn)
- :hero(nullptr), heroRole(HeroRole::MAIN), isMovable(false), creatureSet(creatureSet), chainMask(chainMask),
- baseActor(this), carrierParent(nullptr), otherParent(nullptr), initialTurn(initialTurn), initialMovement(0),
- heroFightingStrength(0), actorExchangeCount(1), armyCost(), actorAction()
- {
- initialPosition = obj->visitablePos();
- layer = EPathfindingLayer::LAND;
- armyValue = creatureSet->getArmyStrength();
- }
- int ChainActor::maxMovePoints(CGPathNode::ELayer layer)
- {
- #if NKAI_TRACE_LEVEL > 0
- if(!hero)
- throw std::logic_error("Asking movement points for static actor");
- #endif
- return hero->movementPointsLimit(layer);
- }
- std::string ChainActor::toString() const
- {
- return hero->getNameTranslated();
- }
- ObjectActor::ObjectActor(const CGObjectInstance * obj, const CCreatureSet * army, uint64_t chainMask, int initialTurn)
- :ChainActor(obj, army, chainMask, initialTurn), object(obj)
- {
- }
- const CGObjectInstance * ObjectActor::getActorObject() const
- {
- return object;
- }
- std::string ObjectActor::toString() const
- {
- return object->getObjectName() + " at " + object->visitablePos().toString();
- }
- HeroActor::HeroActor(const CGHeroInstance * hero, HeroRole heroRole, uint64_t chainMask, const Nullkiller * ai)
- :ChainActor(hero, heroRole, chainMask)
- {
- exchangeMap.reset(new HeroExchangeMap(this, ai));
- setupSpecialActors();
- }
- HeroActor::HeroActor(
- const ChainActor * carrier,
- const ChainActor * other,
- const HeroExchangeArmy * army,
- const Nullkiller * ai)
- :ChainActor(carrier, other, army)
- {
- exchangeMap.reset(new HeroExchangeMap(this, ai));
- armyCost += army->armyCost;
- actorAction = army->getActorAction();
- setupSpecialActors();
- }
- void ChainActor::setBaseActor(HeroActor * base)
- {
- baseActor = base;
- hero = base->hero;
- heroRole = base->heroRole;
- layer = base->layer;
- initialMovement = base->initialMovement;
- initialTurn = base->initialTurn;
- armyValue = base->armyValue;
- chainMask = base->chainMask;
- creatureSet = base->creatureSet;
- isMovable = base->isMovable;
- heroFightingStrength = base->heroFightingStrength;
- armyCost = base->armyCost;
- actorAction = base->actorAction;
- actorExchangeCount = base->actorExchangeCount;
- }
- void HeroActor::setupSpecialActors()
- {
- auto allActors = std::vector<ChainActor *>{this};
- for(ChainActor & specialActor : specialActors)
- {
- specialActor.setBaseActor(this);
- allActors.push_back(&specialActor);
- }
- for(int i = 0; i <= SPECIAL_ACTORS_COUNT; i++)
- {
- ChainActor * actor = allActors[i];
- actor->allowBattle = (i & 1) > 0;
- actor->allowSpellCast = (i & 2) > 0;
- actor->allowUseResources = (i & 4) > 0;
- actor->battleActor = allActors[i | 1];
- actor->castActor = allActors[i | 2];
- actor->resourceActor = allActors[i | 4];
- }
- }
- ExchangeResult ChainActor::tryExchangeNoLock(const ChainActor * specialActor, const ChainActor * other) const
- {
- if(!isMovable) return ExchangeResult();
- return baseActor->tryExchangeNoLock(specialActor, other);
- }
- ExchangeResult HeroActor::tryExchangeNoLock(const ChainActor * specialActor, const ChainActor * other) const
- {
- const ChainActor * otherBase = other->baseActor;
- ExchangeResult result = exchangeMap->tryExchangeNoLock(otherBase);
- if(!result.actor || !result.lockAcquired) return result;
- if(specialActor == this)
- return result;
- int index = vstd::find_pos_if(specialActors, [specialActor](const ChainActor & actor) -> bool
- {
- return &actor == specialActor;
- });
- result.actor = &(dynamic_cast<HeroActor *>(result.actor)->specialActors.at(index));
- return result;
- }
- HeroExchangeMap::HeroExchangeMap(const HeroActor * actor, const Nullkiller * ai)
- :actor(actor), ai(ai), sync()
- {
- }
- HeroExchangeMap::~HeroExchangeMap()
- {
- for(auto & exchange : exchangeMap)
- {
- if(!exchange.second) continue;
- delete exchange.second->creatureSet;
- }
- for(auto & exchange : exchangeMap)
- {
- if(!exchange.second) continue;
- delete exchange.second;
- }
- exchangeMap.clear();
- }
- ExchangeResult HeroExchangeMap::tryExchangeNoLock(const ChainActor * other)
- {
- ExchangeResult result;
- {
- std::shared_lock lock(sync, std::try_to_lock);
- if(!lock.owns_lock())
- {
- result.lockAcquired = false;
- return result;
- }
- auto position = exchangeMap.find(other);
- if(position != exchangeMap.end())
- {
- result.actor = position->second;
- return result;
- }
- }
- {
- std::unique_lock uniqueLock(sync, std::try_to_lock);
- if(!uniqueLock.owns_lock())
- {
- result.lockAcquired = false;
- return result;
- }
- auto inserted = exchangeMap.insert(std::pair<const ChainActor *, HeroActor *>(other, nullptr));
- if(!inserted.second)
- {
- result.actor = inserted.first->second;
- return result; // already inserted
- }
- auto differentMasks = (actor->chainMask & other->chainMask) == 0;
- if(!differentMasks) return result;
- if(actor->allowSpellCast || other->allowSpellCast)
- return result;
- TResources resources = ai->cb->getResourceAmount();
- if(!resources.canAfford(actor->armyCost + other->armyCost))
- {
- #if NKAI_PATHFINDER_TRACE_LEVEL >= 2
- logAi->trace(
- "Can not afford exchange because of total cost %s but we have %s",
- (actor->armyCost + other->armyCost).toString(),
- resources.toString());
- #endif
- return result;
- }
- if(other->isMovable && other->armyValue <= actor->armyValue / 10 && other->armyValue < MIN_ARMY_STRENGTH_FOR_CHAIN)
- return result;
- TResources availableResources = resources - actor->armyCost - other->armyCost;
- HeroExchangeArmy * upgradedInitialArmy = tryUpgrade(actor->creatureSet, other->getActorObject(), availableResources);
- HeroExchangeArmy * newArmy;
- if(other->creatureSet->Slots().size())
- {
- if(upgradedInitialArmy)
- {
- newArmy = pickBestCreatures(upgradedInitialArmy, other->creatureSet);
- newArmy->armyCost = upgradedInitialArmy->armyCost;
- newArmy->requireBuyArmy = upgradedInitialArmy->requireBuyArmy;
- delete upgradedInitialArmy;
- }
- else
- {
- newArmy = pickBestCreatures(actor->creatureSet, other->creatureSet);
- }
- }
- else
- {
- newArmy = upgradedInitialArmy;
- }
- if(!newArmy) return result;
- auto newArmyStrength = newArmy->getArmyStrength();
- auto oldArmyStrength = actor->creatureSet->getArmyStrength();
- if(newArmyStrength <= oldArmyStrength) return result;
- auto reinforcement = newArmyStrength - oldArmyStrength;
- #if NKAI_PATHFINDER_TRACE_LEVEL >= 2
- logAi->trace(
- "Exchange %s->%s reinforcement: %d, %f%%",
- actor->toString(),
- other->toString(),
- reinforcement,
- 100.0f * reinforcement / actor->armyValue);
- #endif
- if(reinforcement <= actor->armyValue / 10 && reinforcement < MIN_ARMY_STRENGTH_FOR_CHAIN)
- {
- delete newArmy;
- return result;
- }
- auto * exchanged = new HeroActor(actor, other, newArmy, ai);
- exchanged->armyCost += newArmy->armyCost;
- result.actor = exchanged;
- exchangeMap[other] = exchanged;
- return result;
- }
- }
- HeroExchangeArmy * HeroExchangeMap::tryUpgrade(
- const CCreatureSet * army,
- const CGObjectInstance * upgrader,
- TResources resources) const
- {
- auto * target = new HeroExchangeArmy();
- auto upgradeInfo = ai->armyManager->calculateCreaturesUpgrade(army, upgrader, resources);
- if(upgradeInfo.upgradeValue)
- {
- for(auto & slotInfo : upgradeInfo.resultingArmy)
- {
- auto targetSlot = target->getFreeSlot();
- target->addToSlot(targetSlot, slotInfo.creature->getId(), TQuantity(slotInfo.count));
- }
- resources -= upgradeInfo.upgradeCost;
- target->armyCost += upgradeInfo.upgradeCost;
- }
- else
- {
- for(const auto & slot : army->Slots())
- {
- const auto & targetSlot = target->getSlotFor(slot.second->getCreatureID());
- target->addToSlot(targetSlot, slot.second->getCreatureID(), slot.second->count);
- }
- }
- if(upgrader->ID == Obj::TOWN)
- {
- auto buyArmy = ai->armyManager->getArmyAvailableToBuy(target, ai->cb->getTown(upgrader->id), resources);
- for(auto & creatureToBuy : buyArmy)
- {
- auto targetSlot = target->getSlotFor(creatureToBuy.creID.toCreature());
- if (targetSlot.validSlot())
- {
- target->addToSlot(targetSlot, creatureToBuy.creID, creatureToBuy.count);
- target->armyCost += creatureToBuy.creID.toCreature()->getFullRecruitCost() * creatureToBuy.count;
- target->requireBuyArmy = true;
- }
- }
- }
- if(target->getArmyStrength() <= army->getArmyStrength())
- {
- delete target;
- return nullptr;
- }
- return target;
- }
- HeroExchangeArmy * HeroExchangeMap::pickBestCreatures(const CCreatureSet * army1, const CCreatureSet * army2) const
- {
- auto * target = new HeroExchangeArmy();
- auto bestArmy = ai->armyManager->getBestArmy(actor->hero, army1, army2, ai->cb->getTile(actor->hero->visitablePos())->getTerrainID());
- for(auto & slotInfo : bestArmy)
- {
- auto targetSlot = target->getFreeSlot();
- target->addToSlot(targetSlot, slotInfo.creature->getId(), TQuantity(slotInfo.count));
- }
- return target;
- }
- HillFortActor::HillFortActor(const CGObjectInstance * hillFort, uint64_t chainMask)
- :ObjectActor(hillFort, &emptyArmy, chainMask, 0)
- {
- }
- DwellingActor::DwellingActor(const CGDwelling * dwelling, uint64_t chainMask, bool waitForGrowth, int dayOfWeek)
- : ObjectActor(
- dwelling,
- getDwellingCreatures(dwelling, waitForGrowth),
- chainMask,
- getInitialTurn(waitForGrowth, dayOfWeek)),
- dwelling(dwelling)
- {
- for(auto & slot : creatureSet->Slots())
- {
- armyCost += slot.second->getCreatureID().toCreature()->getFullRecruitCost() * slot.second->count;
- }
- }
- DwellingActor::~DwellingActor()
- {
- delete creatureSet;
- }
- int DwellingActor::getInitialTurn(bool waitForGrowth, int dayOfWeek)
- {
- if(!waitForGrowth)
- return 0;
- return 8 - dayOfWeek;
- }
- std::string DwellingActor::toString() const
- {
- return dwelling->getTypeName() + dwelling->visitablePos().toString();
- }
- CCreatureSet * DwellingActor::getDwellingCreatures(const CGDwelling * dwelling, bool waitForGrowth)
- {
- auto * dwellingCreatures = new CCreatureSet();
- for(auto & creatureInfo : dwelling->creatures)
- {
- if(!creatureInfo.second.size())
- continue;
- auto creature = creatureInfo.second.back().toCreature();
- dwellingCreatures->addToSlot(
- dwellingCreatures->getSlotFor(creature),
- creature->getId(),
- TQuantity(creatureInfo.first));
- }
- return dwellingCreatures;
- }
- TownGarrisonActor::TownGarrisonActor(const CGTownInstance * town, uint64_t chainMask)
- :ObjectActor(town, town->getUpperArmy(), chainMask, 0), town(town)
- {
- }
- std::string TownGarrisonActor::toString() const
- {
- return town->getNameTranslated();
- }
|