diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index 414c81a79..05e00ec2c 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -100,6 +100,8 @@ std::string goalName(EGoals goalType) return "GET OBJECT"; case FIND_OBJ: return "FIND OBJECT"; + case VISIT_HERO: + return "VISIT HERO"; case GET_ART_TYPE: return "GET ARTIFACT OF TYPE"; case ISSUE_COMMAND: @@ -359,12 +361,12 @@ ui64 evaluateDanger(crint3 tile, const CGHeroInstance *visitor) if(const CGObjectInstance * dangerousObject = backOrNull(cb->getVisitableObjs(tile))) { objectDanger = evaluateDanger(dangerousObject); //unguarded objects can also be dangerous or unhandled - if (dangerousObject) + if (objectDanger) { //TODO: don't downcast objects AI shouldnt know about! auto armedObj = dynamic_cast(dangerousObject); if(armedObj) - objectDanger *= fh->getTacticalAdvantage(visitor, armedObj); + objectDanger *= fh->getTacticalAdvantage(visitor, armedObj); //this line tends to go infinite for allied towns (?) } } @@ -374,8 +376,6 @@ ui64 evaluateDanger(crint3 tile, const CGHeroInstance *visitor) //TODO mozna odwiedzic blockvis nie ruszajac straznika return std::max(objectDanger, guardDanger); - - return 0; } ui64 evaluateDanger(const CGObjectInstance *obj) @@ -647,9 +647,9 @@ void VCAI::heroExchangeStarted(si32 hero1, si32 hero2) requestActionASAP([=]() { - if (firstHero->getHeroStrength() > secondHero->getHeroStrength()) + if (firstHero->getHeroStrength() > secondHero->getHeroStrength() && canGetArmy (firstHero, secondHero)) pickBestCreatures (firstHero, secondHero); - else + else if (canGetArmy (secondHero, firstHero)) pickBestCreatures (secondHero, firstHero); completeGoal(CGoal(VISIT_HERO).sethero(firstHero)); //TODO: what if we were visited by other hero in the meantime? @@ -721,6 +721,11 @@ void VCAI::showHillFortWindow(const CGObjectInstance *object, const CGHeroInstan { NET_EVENT_HANDLER; LOG_ENTRY; + + requestActionASAP([=]() + { + makePossibleUpgrades(visitor); + }); } void VCAI::playerBonusChanged(const Bonus &bonus, bool gain) @@ -1111,6 +1116,36 @@ void VCAI::moveCreaturesToHero(const CGTownInstance * t) } } +bool VCAI::canGetArmy (const CGHeroInstance * h, const CGHeroInstance * source) +{ + int totalStacks = h->Slots().size() + source->Slots().size(); + if (totalStacks > GameConstants::ARMY_SIZE) + { //exchange stacks or move full stack + BOOST_FOREACH (auto slot, source->Slots()) + { + if (h->getSlotFor(slot.second->type->idNumber)) //we can take full stack + return true; + BOOST_FOREACH (auto ourSlot, h->Slots()) + { + if (slot.second->getPower() > ourSlot.second->getPower()) + return true; + } + } + } + else //only exchange + { + BOOST_FOREACH (auto slot, source->Slots()) + { + BOOST_FOREACH (auto ourSlot, h->Slots()) + { + if (slot.second->getPower() > ourSlot.second->getPower()) + return true; + } + } + } + return false; +} + void VCAI::pickBestCreatures(const CArmedInstance * army, const CArmedInstance * source) { //TODO - what if source is a hero (the last stack problem) -> it'd good to create a single stack of weakest cre @@ -1138,10 +1173,16 @@ void VCAI::pickBestCreatures(const CArmedInstance * army, const CArmedInstance * //foreach best type -> iterate over slots in both armies and if it's the appropriate type, send it to the slot where it belongs for (int i = 0; i < bestArmy.size(); i++) //i-th strongest creature type will go to i-th slot + { + if (source->needsLastStack() && source->Slots().size() <= 1) + break; BOOST_FOREACH(auto armyPtr, armies) for (int j = 0; j < GameConstants::ARMY_SIZE; j++) + { if(armyPtr->getCreature(j) == bestArmy[i] && (i != j || armyPtr != army)) //it's a searched creature not in dst slot - cb->mergeOrSwapStacks(armyPtr, army, j, i); + cb->mergeOrSwapStacks(armyPtr, army, j, i); + } + } //TODO - having now strongest possible army, we may want to think about arranging stacks @@ -3162,7 +3203,7 @@ TSubgoal CGoal::whatToDoToAchieve() auto heroDummy = hero; erase_if(otherHeroes, [heroDummy](const CGHeroInstance * h) { - return (h == heroDummy.h || !ai->isAccessibleForHero(heroDummy->pos, h, true)); + return (h == heroDummy.h || !ai->isAccessibleForHero(heroDummy->pos, h, true) || !ai->canGetArmy(heroDummy.h, h)); }); if (otherHeroes.size()) { @@ -3375,8 +3416,11 @@ bool isWeeklyRevisitable (const CGObjectInstance * obj) return true; switch (obj->ID) { - case Obj::STABLES: //any other potential visitable objects? + case Obj::STABLES: + case Obj::MAGIC_WELL: + case Obj::HILL_FORT: return true; + break; case Obj::BORDER_GATE: case Obj::BORDERGUARD: return (dynamic_cast (obj))->wasMyColorVisited (ai->playerID); //FIXME: they could be revisited sooner than in a week @@ -3425,6 +3469,16 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj) return canRecruitCreatures; break; } + case Obj::HILL_FORT: + { + BOOST_FOREACH (auto slot, h->Slots()) + { + if (slot.second->type->upgrades.size()) + return true; //TODO: check price? + } + return false; + break; + } case Obj::MONOLITH1: case Obj::MONOLITH2: case Obj::MONOLITH3: @@ -3451,6 +3505,9 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj) return false; } break; + case Obj::MAGIC_WELL: + return h->mana < h->manaLimit(); + break; } if (obj->wasVisited(*h)) //it must pointer to hero instance, heroPtr calls function wasVisited(ui8 player); diff --git a/AI/VCAI/VCAI.h b/AI/VCAI/VCAI.h index 7789cb27f..75645cb22 100644 --- a/AI/VCAI/VCAI.h +++ b/AI/VCAI/VCAI.h @@ -327,6 +327,7 @@ public: void buildStructure(const CGTownInstance * t); //void recruitCreatures(const CGTownInstance * t); void recruitCreatures(const CGDwelling * d); + bool canGetArmy (const CGHeroInstance * h, const CGHeroInstance * source); //can we get any better stacks from other hero? void pickBestCreatures(const CArmedInstance * army, const CArmedInstance * source); //called when we can't find a slot for new stack void moveCreaturesToHero(const CGTownInstance * t); bool goVisitObj(const CGObjectInstance * obj, HeroPtr h); @@ -375,4 +376,6 @@ bool objWithID(const CGObjectInstance *obj) bool isBlockedBorderGate(int3 tileToHit); bool isWeeklyRevisitable (const CGObjectInstance * obj); -bool shouldVisit (HeroPtr h, const CGObjectInstance * obj); \ No newline at end of file +bool shouldVisit (HeroPtr h, const CGObjectInstance * obj); + +void makePossibleUpgrades(const CArmedInstance *obj); \ No newline at end of file diff --git a/lib/GameConstants.h b/lib/GameConstants.h index 90cc83e88..f788ee736 100644 --- a/lib/GameConstants.h +++ b/lib/GameConstants.h @@ -210,12 +210,14 @@ namespace Obj DERELICT_SHIP = 24, DRAGON_UTOPIA = 25, GARRISON = 33, + HILL_FORT = 35, LIBRARY_OF_ENLIGHTENMENT = 41, MONOLITH1 = 43, MONOLITH2 = 44, MONOLITH3 = 45, MAGIC_PLAINS1 = 46, SCHOOL_OF_MAGIC = 47, + MAGIC_WELL = 49, MINE = 53, MONSTER = 54, OBELISK = 57,