123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247 |
- /*
- * AILayerTransitionRule.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 "AILayerTransitionRule.h"
- #include "../../Engine/Nullkiller.h"
- #include "../../../../lib/pathfinder/CPathfinder.h"
- #include "../../../../lib/pathfinder/TurnInfo.h"
- namespace NKAI
- {
- namespace AIPathfinding
- {
- AILayerTransitionRule::AILayerTransitionRule(
- CPlayerSpecificInfoCallback * cb,
- Nullkiller * ai,
- std::shared_ptr<AINodeStorage> nodeStorage)
- :cb(cb), ai(ai), nodeStorage(nodeStorage)
- {
- setup();
- }
- void AILayerTransitionRule::process(
- const PathNodeInfo & source,
- CDestinationNodeInfo & destination,
- const PathfinderConfig * pathfinderConfig,
- CPathfinderHelper * pathfinderHelper) const
- {
- LayerTransitionRule::process(source, destination, pathfinderConfig, pathfinderHelper);
- #if NKAI_PATHFINDER_TRACE_LEVEL >= 2
- logAi->trace("Layer transitioning %s -> %s, action: %d, blocked: %s",
- source.coord.toString(),
- destination.coord.toString(),
- static_cast<int32_t>(destination.action),
- destination.blocked ? "true" : "false");
- #endif
- if(!destination.blocked)
- {
- if(source.node->layer == EPathfindingLayer::LAND
- && (destination.node->layer == EPathfindingLayer::AIR || destination.node->layer == EPathfindingLayer::WATER))
- {
- if(pathfinderHelper->getTurnInfo()->isLayerAvailable(destination.node->layer))
- return;
- else
- destination.blocked = true;
- }
- else
- {
- return;
- }
- }
- if(source.node->layer == EPathfindingLayer::LAND && destination.node->layer == EPathfindingLayer::SAIL)
- {
- std::shared_ptr<const VirtualBoatAction> virtualBoat = findVirtualBoat(destination, source);
- if(virtualBoat && tryUseSpecialAction(destination, source, virtualBoat, EPathNodeAction::EMBARK))
- {
- #if NKAI_PATHFINDER_TRACE_LEVEL >= 1
- logAi->trace("Embarking to virtual boat while moving %s -> %s!", source.coord.toString(), destination.coord.toString());
- #endif
- }
- }
- if(source.node->layer == EPathfindingLayer::LAND && destination.node->layer == EPathfindingLayer::WATER)
- {
- if(nodeStorage->getAINode(source.node)->dayFlags & DayFlags::WATER_WALK_CAST)
- {
- destination.blocked = false;
- return;
- }
- auto action = waterWalkingActions.find(nodeStorage->getHero(source.node));
- if(action != waterWalkingActions.end() && tryUseSpecialAction(destination, source, action->second, EPathNodeAction::NORMAL))
- {
- #if NKAI_PATHFINDER_TRACE_LEVEL >= 2
- logAi->trace("Casting water walk while moving %s -> %s!", source.coord.toString(), destination.coord.toString());
- #endif
- }
- }
- if(source.node->layer == EPathfindingLayer::LAND && destination.node->layer == EPathfindingLayer::AIR)
- {
- if(nodeStorage->getAINode(source.node)->dayFlags & DayFlags::FLY_CAST)
- {
- destination.blocked = false;
- return;
- }
- auto action = airWalkingActions.find(nodeStorage->getHero(source.node));
- if(action != airWalkingActions.end() && tryUseSpecialAction(destination, source, action->second, EPathNodeAction::NORMAL))
- {
- #if NKAI_PATHFINDER_TRACE_LEVEL >= 2
- logAi->trace("Casting fly while moving %s -> %s!", source.coord.toString(), destination.coord.toString());
- #endif
- }
- }
- }
- void AILayerTransitionRule::setup()
- {
- SpellID waterWalk = SpellID::WATER_WALK;
- SpellID airWalk = SpellID::FLY;
- for(const CGHeroInstance * hero : nodeStorage->getAllHeroes())
- {
- if(hero->canCastThisSpell(waterWalk.toSpell()) && hero->mana >= hero->getSpellCost(waterWalk.toSpell()))
- {
- waterWalkingActions[hero] = std::make_shared<WaterWalkingAction>(hero);
- }
- if(hero->canCastThisSpell(airWalk.toSpell()) && hero->mana >= hero->getSpellCost(airWalk.toSpell()))
- {
- airWalkingActions[hero] = std::make_shared<AirWalkingAction>(hero);
- }
- }
- collectVirtualBoats();
- }
- void AILayerTransitionRule::collectVirtualBoats()
- {
- std::vector<const IShipyard *> shipyards;
- for(const CGTownInstance * t : cb->getTownsInfo())
- {
- if(t->hasBuilt(BuildingID::SHIPYARD))
- shipyards.push_back(t);
- }
- for(const CGObjectInstance * obj : ai->memory->visitableObjs)
- {
- if(obj->ID != Obj::TOWN) //towns were handled in the previous loop
- {
- if(const auto * shipyard = dynamic_cast<const IShipyard *>(obj))
- shipyards.push_back(shipyard);
- }
- }
- for(const IShipyard * shipyard : shipyards)
- {
- if(shipyard->shipyardStatus() == IShipyard::GOOD)
- {
- int3 boatLocation = shipyard->bestLocation();
- virtualBoats[boatLocation] = std::make_shared<BuildBoatAction>(cb, shipyard);
- logAi->debug("Virtual boat added at %s", boatLocation.toString());
- }
- }
- for(const CGHeroInstance * hero : nodeStorage->getAllHeroes())
- {
- auto summonBoatSpell = SpellID(SpellID::SUMMON_BOAT).toSpell();
- if(hero->canCastThisSpell(summonBoatSpell)
- && hero->getSpellSchoolLevel(summonBoatSpell) >= MasteryLevel::ADVANCED)
- {
- // TODO: For lower school level we might need to check the existence of some boat
- summonableVirtualBoats[hero] = std::make_shared<SummonBoatAction>();
- }
- }
- }
- std::shared_ptr<const VirtualBoatAction> AILayerTransitionRule::findVirtualBoat(
- CDestinationNodeInfo & destination,
- const PathNodeInfo & source) const
- {
- std::shared_ptr<const VirtualBoatAction> virtualBoat;
- if(vstd::contains(virtualBoats, destination.coord))
- {
- virtualBoat = virtualBoats.at(destination.coord);
- }
- else
- {
- const CGHeroInstance * hero = nodeStorage->getHero(source.node);
- if(vstd::contains(summonableVirtualBoats, hero)
- && summonableVirtualBoats.at(hero)->canAct(ai, nodeStorage->getAINode(source.node)))
- {
- virtualBoat = summonableVirtualBoats.at(hero);
- }
- }
- return virtualBoat;
- }
- bool AILayerTransitionRule::tryUseSpecialAction(
- CDestinationNodeInfo & destination,
- const PathNodeInfo & source,
- std::shared_ptr<const SpecialAction> specialAction,
- EPathNodeAction targetAction) const
- {
- bool result = false;
- nodeStorage->updateAINode(destination.node, [&](AIPathNode * node)
- {
- auto castNodeOptional = nodeStorage->getOrCreateNode(
- node->coord,
- node->layer,
- specialAction->getActor(node->actor));
- if(castNodeOptional)
- {
- AIPathNode * castNode = castNodeOptional.value();
- if(castNode->action == EPathNodeAction::UNKNOWN)
- {
- castNode->addSpecialAction(specialAction);
- destination.blocked = false;
- destination.action = targetAction;
- destination.node = castNode;
- result = true;
- }
- else
- {
- #if NKAI_PATHFINDER_TRACE_LEVEL >= 1
- logAi->trace(
- "Special transition node already allocated. Blocked moving %s -> %s",
- source.coord.toString(),
- destination.coord.toString());
- #endif
- }
- }
- else
- {
- logAi->debug(
- "Can not allocate special transition node while moving %s -> %s",
- source.coord.toString(),
- destination.coord.toString());
- }
- });
- return result;
- }
- }
- }
|