|
@@ -9,6 +9,7 @@
|
|
|
*/
|
|
|
#include "StdInc.h"
|
|
|
|
|
|
+#include "../../lib/ArtifactUtils.h"
|
|
|
#include "../../lib/UnlockGuard.h"
|
|
|
#include "../../lib/mapObjects/MapObjects.h"
|
|
|
#include "../../lib/mapObjects/ObjectTemplate.h"
|
|
@@ -63,7 +64,7 @@ struct SetGlobalState
|
|
|
};
|
|
|
|
|
|
|
|
|
-#define SET_GLOBAL_STATE(ai) SetGlobalState _hlpSetState(ai);
|
|
|
+#define SET_GLOBAL_STATE(ai) SetGlobalState _hlpSetState(ai)
|
|
|
|
|
|
#define NET_EVENT_HANDLER SET_GLOBAL_STATE(this)
|
|
|
#define MAKING_TURN SET_GLOBAL_STATE(this)
|
|
@@ -100,7 +101,7 @@ void AIGateway::heroMoved(const TryMoveHero & details, bool verbose)
|
|
|
if(!hero)
|
|
|
validateObject(details.id); //enemy hero may have left visible area
|
|
|
|
|
|
- const int3 from = hero ? hero->convertToVisitablePos(details.start) : (details.start - int3(0,1,0));;
|
|
|
+ const int3 from = hero ? hero->convertToVisitablePos(details.start) : (details.start - int3(0,1,0));
|
|
|
const int3 to = hero ? hero->convertToVisitablePos(details.end) : (details.end - int3(0,1,0));
|
|
|
|
|
|
const CGObjectInstance * o1 = vstd::frontOrNull(cb->getVisitableObjs(from, verbose));
|
|
@@ -420,14 +421,14 @@ void AIGateway::requestRealized(PackageApplied * pa)
|
|
|
NET_EVENT_HANDLER;
|
|
|
if(status.haveTurn())
|
|
|
{
|
|
|
- if(pa->packType == typeList.getTypeID<EndTurn>())
|
|
|
+ if(pa->packType == CTypeList::getInstance().getTypeID<EndTurn>(nullptr))
|
|
|
{
|
|
|
if(pa->result)
|
|
|
status.madeTurn();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if(pa->packType == typeList.getTypeID<QueryReply>())
|
|
|
+ if(pa->packType == CTypeList::getInstance().getTypeID<QueryReply>(nullptr))
|
|
|
{
|
|
|
status.receivedAnswerConfirmation(pa->requestID, pa->result);
|
|
|
}
|
|
@@ -480,7 +481,7 @@ void AIGateway::objectPropertyChanged(const SetObjectProperty * sop)
|
|
|
NET_EVENT_HANDLER;
|
|
|
if(sop->what == ObjProperty::OWNER)
|
|
|
{
|
|
|
- auto relations = myCb->getPlayerRelations(playerID, (PlayerColor)sop->val);
|
|
|
+ auto relations = myCb->getPlayerRelations(playerID, sop->identifier.as<PlayerColor>());
|
|
|
auto obj = myCb->getObj(sop->id, false);
|
|
|
|
|
|
if(!nullkiller) // crash protection
|
|
@@ -585,11 +586,18 @@ void AIGateway::heroGotLevel(const CGHeroInstance * hero, PrimarySkill pskill, s
|
|
|
|
|
|
requestActionASAP([=]()
|
|
|
{
|
|
|
+ int sel = 0;
|
|
|
+
|
|
|
if(hPtr.validAndSet())
|
|
|
{
|
|
|
+ std::unique_lock<std::mutex> lockGuard(nullkiller->aiStateMutex);
|
|
|
+
|
|
|
nullkiller->heroManager->update();
|
|
|
- answerQuery(queryID, nullkiller->heroManager->selectBestSkill(hPtr, skills));
|
|
|
+
|
|
|
+ sel = nullkiller->heroManager->selectBestSkill(hPtr, skills);
|
|
|
}
|
|
|
+
|
|
|
+ answerQuery(queryID, sel);
|
|
|
});
|
|
|
}
|
|
|
|
|
@@ -624,7 +632,8 @@ void AIGateway::showBlockingDialog(const std::string & text, const std::vector<C
|
|
|
auto topObj = objects.front()->id == hero->id ? objects.back() : objects.front();
|
|
|
auto objType = topObj->ID; // top object should be our hero
|
|
|
auto goalObjectID = nullkiller->getTargetObject();
|
|
|
- auto ratio = (float)nullkiller->dangerEvaluator->evaluateDanger(target, hero.get()) / (float)hero->getTotalStrength();
|
|
|
+ auto danger = nullkiller->dangerEvaluator->evaluateDanger(target, hero.get());
|
|
|
+ auto ratio = static_cast<float>(danger) / hero->getTotalStrength();
|
|
|
|
|
|
answer = topObj->id == goalObjectID; // no if we do not aim to visit this object
|
|
|
logAi->trace("Query hook: %s(%s) by %s danger ratio %f", target.toString(), topObj->getObjectName(), hero.name, ratio);
|
|
@@ -640,7 +649,7 @@ void AIGateway::showBlockingDialog(const std::string & text, const std::vector<C
|
|
|
}
|
|
|
else if(objType == Obj::ARTIFACT || objType == Obj::RESOURCE)
|
|
|
{
|
|
|
- bool dangerUnknown = ratio == 0;
|
|
|
+ bool dangerUnknown = danger == 0;
|
|
|
bool dangerTooHigh = ratio > (1 / SAFE_ATTACK_CONSTANT);
|
|
|
|
|
|
answer = !dangerUnknown && !dangerTooHigh;
|
|
@@ -660,14 +669,18 @@ void AIGateway::showBlockingDialog(const std::string & text, const std::vector<C
|
|
|
if(selection) //select from multiple components -> take the last one (they're indexed [1-size])
|
|
|
sel = components.size();
|
|
|
|
|
|
- // TODO: Find better way to understand it is Chest of Treasures
|
|
|
- if(hero.validAndSet()
|
|
|
- && components.size() == 2
|
|
|
- && components.front().id == Component::EComponentType::RESOURCE
|
|
|
- && (nullkiller->heroManager->getHeroRole(hero) != HeroRole::MAIN
|
|
|
- || nullkiller->buildAnalyzer->getGoldPreasure() > MAX_GOLD_PEASURE))
|
|
|
{
|
|
|
- sel = 1; // for now lets pick gold from a chest.
|
|
|
+ std::unique_lock<std::mutex> mxLock(nullkiller->aiStateMutex);
|
|
|
+
|
|
|
+ // TODO: Find better way to understand it is Chest of Treasures
|
|
|
+ if(hero.validAndSet()
|
|
|
+ && components.size() == 2
|
|
|
+ && components.front().type == ComponentType::RESOURCE
|
|
|
+ && (nullkiller->heroManager->getHeroRole(hero) != HeroRole::MAIN
|
|
|
+ || nullkiller->buildAnalyzer->isGoldPreasureHigh()))
|
|
|
+ {
|
|
|
+ sel = 1;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
answerQuery(askID, sel);
|
|
@@ -746,27 +759,25 @@ void AIGateway::showMapObjectSelectDialog(QueryID askID, const Component & icon,
|
|
|
requestActionASAP([=](){ answerQuery(askID, selectedObject.getNum()); });
|
|
|
}
|
|
|
|
|
|
-void AIGateway::saveGame(BinarySerializer & h, const int version)
|
|
|
+void AIGateway::saveGame(BinarySerializer & h)
|
|
|
{
|
|
|
- LOG_TRACE_PARAMS(logAi, "version '%i'", version);
|
|
|
NET_EVENT_HANDLER;
|
|
|
nullkiller->memory->removeInvisibleObjects(myCb.get());
|
|
|
|
|
|
- CAdventureAI::saveGame(h, version);
|
|
|
- serializeInternal(h, version);
|
|
|
+ CAdventureAI::saveGame(h);
|
|
|
+ serializeInternal(h);
|
|
|
}
|
|
|
|
|
|
-void AIGateway::loadGame(BinaryDeserializer & h, const int version)
|
|
|
+void AIGateway::loadGame(BinaryDeserializer & h)
|
|
|
{
|
|
|
- LOG_TRACE_PARAMS(logAi, "version '%i'", version);
|
|
|
//NET_EVENT_HANDLER;
|
|
|
|
|
|
#if 0
|
|
|
//disabled due to issue 2890
|
|
|
registerGoals(h);
|
|
|
#endif // 0
|
|
|
- CAdventureAI::loadGame(h, version);
|
|
|
- serializeInternal(h, version);
|
|
|
+ CAdventureAI::loadGame(h);
|
|
|
+ serializeInternal(h);
|
|
|
}
|
|
|
|
|
|
bool AIGateway::makePossibleUpgrades(const CArmedInstance * obj)
|
|
@@ -858,6 +869,8 @@ void AIGateway::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h
|
|
|
{
|
|
|
makePossibleUpgrades(h.get());
|
|
|
|
|
|
+ std::unique_lock<std::mutex> lockGuard(nullkiller->aiStateMutex);
|
|
|
+
|
|
|
if(!h->visitedTown->garrisonHero || !nullkiller->isHeroLocked(h->visitedTown->garrisonHero))
|
|
|
moveCreaturesToHero(h->visitedTown);
|
|
|
|
|
@@ -995,21 +1008,21 @@ void AIGateway::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance
|
|
|
for(auto p : h->artifactsWorn)
|
|
|
{
|
|
|
if(p.second.artifact)
|
|
|
- allArtifacts.push_back(ArtifactLocation(h, p.first));
|
|
|
+ allArtifacts.push_back(ArtifactLocation(h->id, p.first));
|
|
|
}
|
|
|
}
|
|
|
for(auto slot : h->artifactsInBackpack)
|
|
|
- allArtifacts.push_back(ArtifactLocation(h, h->getArtPos(slot.artifact)));
|
|
|
+ allArtifacts.push_back(ArtifactLocation(h->id, h->getArtPos(slot.artifact)));
|
|
|
|
|
|
if(otherh)
|
|
|
{
|
|
|
for(auto p : otherh->artifactsWorn)
|
|
|
{
|
|
|
if(p.second.artifact)
|
|
|
- allArtifacts.push_back(ArtifactLocation(otherh, p.first));
|
|
|
+ allArtifacts.push_back(ArtifactLocation(otherh->id, p.first));
|
|
|
}
|
|
|
for(auto slot : otherh->artifactsInBackpack)
|
|
|
- allArtifacts.push_back(ArtifactLocation(otherh, otherh->getArtPos(slot.artifact)));
|
|
|
+ allArtifacts.push_back(ArtifactLocation(otherh->id, otherh->getArtPos(slot.artifact)));
|
|
|
}
|
|
|
//we give stuff to one hero or another, depending on giveStuffToFirstHero
|
|
|
|
|
@@ -1021,13 +1034,13 @@ void AIGateway::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance
|
|
|
|
|
|
for(auto location : allArtifacts)
|
|
|
{
|
|
|
- if(location.relatedObj() == target && location.slot < ArtifactPosition::AFTER_LAST)
|
|
|
+ if(location.artHolder == target->id && ArtifactUtils::isSlotEquipment(location.slot))
|
|
|
continue; //don't reequip artifact we already wear
|
|
|
|
|
|
if(location.slot == ArtifactPosition::MACH4) // don't attempt to move catapult
|
|
|
continue;
|
|
|
|
|
|
- auto s = location.getSlot();
|
|
|
+ auto s = cb->getHero(location.artHolder)->getSlot(location.slot);
|
|
|
if(!s || s->locked) //we can't move locks
|
|
|
continue;
|
|
|
auto artifact = s->artifact;
|
|
@@ -1038,9 +1051,9 @@ void AIGateway::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance
|
|
|
bool emptySlotFound = false;
|
|
|
for(auto slot : artifact->artType->getPossibleSlots().at(target->bearerType()))
|
|
|
{
|
|
|
- ArtifactLocation destLocation(target, slot);
|
|
|
- if(target->isPositionFree(slot) && artifact->canBePutAt(destLocation, true)) //combined artifacts are not always allowed to move
|
|
|
+ if(target->isPositionFree(slot) && artifact->canBePutAt(target, slot, true)) //combined artifacts are not always allowed to move
|
|
|
{
|
|
|
+ ArtifactLocation destLocation(target->id, slot);
|
|
|
cb->swapArtifacts(location, destLocation); //just put into empty slot
|
|
|
emptySlotFound = true;
|
|
|
changeMade = true;
|
|
@@ -1054,11 +1067,11 @@ void AIGateway::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance
|
|
|
auto otherSlot = target->getSlot(slot);
|
|
|
if(otherSlot && otherSlot->artifact) //we need to exchange artifact for better one
|
|
|
{
|
|
|
- ArtifactLocation destLocation(target, slot);
|
|
|
//if that artifact is better than what we have, pick it
|
|
|
- if(compareArtifacts(artifact, otherSlot->artifact) && artifact->canBePutAt(destLocation, true)) //combined artifacts are not always allowed to move
|
|
|
+ if(compareArtifacts(artifact, otherSlot->artifact) && artifact->canBePutAt(target, slot, true)) //combined artifacts are not always allowed to move
|
|
|
{
|
|
|
- cb->swapArtifacts(location, ArtifactLocation(target, target->getArtPos(otherSlot->artifact)));
|
|
|
+ ArtifactLocation destLocation(target->id, slot);
|
|
|
+ cb->swapArtifacts(location, ArtifactLocation(target->id, target->getArtPos(otherSlot->artifact)));
|
|
|
changeMade = true;
|
|
|
break;
|
|
|
}
|
|
@@ -1119,15 +1132,6 @@ void AIGateway::battleEnd(const BattleID & battleID, const BattleResult * br, Qu
|
|
|
logAi->debug("Player %d (%s): I %s the %s!", playerID, playerID.toString(), (won ? "won" : "lost"), battlename);
|
|
|
battlename.clear();
|
|
|
|
|
|
- if (queryID != QueryID::NONE)
|
|
|
- {
|
|
|
- status.addQuery(queryID, "Combat result dialog");
|
|
|
- const int confirmAction = 0;
|
|
|
- requestActionASAP([=]()
|
|
|
- {
|
|
|
- answerQuery(queryID, confirmAction);
|
|
|
- });
|
|
|
- }
|
|
|
CAdventureAI::battleEnd(battleID, br, queryID);
|
|
|
}
|
|
|
|
|
@@ -1403,7 +1407,7 @@ void AIGateway::tryRealize(Goals::Trade & g) //trade
|
|
|
int accquiredResources = 0;
|
|
|
if(const CGObjectInstance * obj = cb->getObj(ObjectInstanceID(g.objid), false))
|
|
|
{
|
|
|
- if(const IMarket * m = IMarket::castFrom(obj, false))
|
|
|
+ if(const auto * m = dynamic_cast<const IMarket*>(obj))
|
|
|
{
|
|
|
auto freeRes = cb->getResourceAmount(); //trade only resources which are not reserved
|
|
|
for(auto it = ResourceSet::nziterator(freeRes); it.valid(); it++)
|
|
@@ -1412,13 +1416,14 @@ void AIGateway::tryRealize(Goals::Trade & g) //trade
|
|
|
if(res.getNum() == g.resID) //sell any other resource
|
|
|
continue;
|
|
|
|
|
|
- int toGive, toGet;
|
|
|
+ int toGive;
|
|
|
+ int toGet;
|
|
|
m->getOffer(res, g.resID, toGive, toGet, EMarketMode::RESOURCE_RESOURCE);
|
|
|
toGive = static_cast<int>(toGive * (it->resVal / toGive)); //round down
|
|
|
//TODO trade only as much as needed
|
|
|
if (toGive) //don't try to sell 0 resources
|
|
|
{
|
|
|
- cb->trade(m, EMarketMode::RESOURCE_RESOURCE, res, g.resID, toGive);
|
|
|
+ cb->trade(m, EMarketMode::RESOURCE_RESOURCE, res, GameResID(g.resID), toGive);
|
|
|
accquiredResources = static_cast<int>(toGet * (it->resVal / toGive));
|
|
|
logAi->debug("Traded %d of %s for %d of %s at %s", toGive, res, accquiredResources, g.resID, obj->getObjectName());
|
|
|
}
|