diff --git a/AI/Nullkiller/AIGateway.cpp b/AI/Nullkiller/AIGateway.cpp index 9ad7c8421..947fb6c7e 100644 --- a/AI/Nullkiller/AIGateway.cpp +++ b/AI/Nullkiller/AIGateway.cpp @@ -373,6 +373,7 @@ void AIGateway::objectRemoved(const CGObjectInstance * obj, const PlayerColor & return; nullkiller->memory->removeFromMemory(obj); + nullkiller->baseGraph->removeObject(obj); if(obj->ID == Obj::HERO && obj->tempOwner == playerID) { diff --git a/AI/Nullkiller/Pathfinding/ObjectGraph.cpp b/AI/Nullkiller/Pathfinding/ObjectGraph.cpp index 00d52f661..6182703f6 100644 --- a/AI/Nullkiller/Pathfinding/ObjectGraph.cpp +++ b/AI/Nullkiller/Pathfinding/ObjectGraph.cpp @@ -10,6 +10,7 @@ #include "StdInc.h" #include "ObjectGraph.h" #include "AIPathfinderConfig.h" +#include "../../../lib/CRandomGenerator.h" #include "../../../CCallback.h" #include "../../../lib/mapping/CMap.h" #include "../Engine/Nullkiller.h" @@ -21,10 +22,10 @@ namespace NKAI void ObjectGraph::updateGraph(const Nullkiller * ai) { auto cb = ai->cb; - auto mapSize = cb->getMapSize(); std::map actors; std::map actorObjectMap; + std::vector boats; auto addObjectActor = [&](const CGObjectInstance * obj) { @@ -37,8 +38,14 @@ void ObjectGraph::updateGraph(const Nullkiller * ai) objectActor->pos = objectActor->convertFromVisitablePos(visitablePos); objectActor->initObj(rng); + if(cb->getTile(visitablePos)->isWater()) + { + boats.push_back(new CGBoat(objectActor->cb)); + objectActor->boat = boats.back(); + } + actorObjectMap[objectActor] = obj; - actors[objectActor] = obj->ID == Obj::TOWN ? HeroRole::MAIN : HeroRole::SCOUT; + actors[objectActor] = obj->ID == Obj::TOWN || obj->ID == Obj::SHIPYARD ? HeroRole::MAIN : HeroRole::SCOUT; addObject(obj); }; @@ -85,6 +92,17 @@ void ObjectGraph::updateGraph(const Nullkiller * ai) auto obj1 = actorObjectMap[path1.targetHero]; auto obj2 = actorObjectMap[path2.targetHero]; + auto tile1 = cb->getTile(obj1->visitablePos()); + auto tile2 = cb->getTile(obj2->visitablePos()); + + if(tile2->isWater() && !tile1->isWater()) + { + auto linkTile = cb->getTile(pos); + + if(!linkTile->isWater() || obj1->ID != Obj::BOAT || obj1->ID != Obj::SHIPYARD) + continue; + } + auto danger = ai->pathfinder->getStorage()->evaluateDanger(obj2->visitablePos(), path1.targetHero, true); auto updated = nodes[obj1->visitablePos()].connections[obj2->visitablePos()].update( @@ -108,11 +126,16 @@ void ObjectGraph::updateGraph(const Nullkiller * ai) } }); - for(auto h : actors) + for(auto h : actorObjectMap) { delete h.first; } + for(auto boat : boats) + { + delete boat; + } + #if NKAI_GRAPH_TRACE_LEVEL >= 1 dumpToLog("graph"); #endif @@ -123,6 +146,21 @@ void ObjectGraph::addObject(const CGObjectInstance * obj) nodes[obj->visitablePos()].init(obj); } +void ObjectGraph::removeObject(const CGObjectInstance * obj) +{ + nodes[obj->visitablePos()].objectExists = false; + + if(obj->ID == Obj::BOAT) + { + vstd::erase_if(nodes[obj->visitablePos()].connections, [&](const std::pair & link) -> bool + { + auto tile = cb->getTile(link.first, false); + + return tile && tile->isWater(); + }); + } +} + void ObjectGraph::connectHeroes(const Nullkiller * ai) { for(auto obj : ai->memory->visitableObjs) @@ -170,7 +208,7 @@ void ObjectGraph::dumpToLog(std::string visualKey) const node.second.danger); #endif - logBuilder.addLine(node.first, tile.first); + logBuilder.addLine(tile.first, node.first); } } }); @@ -206,9 +244,7 @@ void GraphPaths::calculatePaths(const CGHeroInstance * targetHero, const Nullkil graph.iterateConnections(pos.coord, [&](int3 target, ObjectLink o) { - auto graphNode = graph.getNode(target); auto targetNodeType = o.danger ? GrapthPathNodeType::BATTLE : pos.nodeType; - auto targetPointer = GraphPathNodePointer(target, targetNodeType); auto & targetNode = getNode(targetPointer); diff --git a/AI/Nullkiller/Pathfinding/ObjectGraph.h b/AI/Nullkiller/Pathfinding/ObjectGraph.h index 11b9e84e3..faf6fb499 100644 --- a/AI/Nullkiller/Pathfinding/ObjectGraph.h +++ b/AI/Nullkiller/Pathfinding/ObjectGraph.h @@ -60,6 +60,7 @@ public: void updateGraph(const Nullkiller * ai); void addObject(const CGObjectInstance * obj); void connectHeroes(const Nullkiller * ai); + void removeObject(const CGObjectInstance * obj); void dumpToLog(std::string visualKey) const; template diff --git a/Global.h b/Global.h index 3ef1a64e4..2b6b66be5 100644 --- a/Global.h +++ b/Global.h @@ -512,6 +512,20 @@ namespace vstd } } + //works for std::unordered_map, maybe something else + template + void erase_if(std::unordered_map & container, Predicate pred) + { + auto itr = container.begin(); + auto endItr = container.end(); + while(itr != endItr) + { + auto tmpItr = itr++; + if(pred(*tmpItr)) + container.erase(tmpItr); + } + } + template OutputIterator copy_if(const InputRange &input, OutputIterator result, Predicate pred) { diff --git a/client/mapView/MapView.cpp b/client/mapView/MapView.cpp index be6124b6b..008d28fc5 100644 --- a/client/mapView/MapView.cpp +++ b/client/mapView/MapView.cpp @@ -80,9 +80,12 @@ public: auto pEnd = model->getTargetTileArea(end).topLeft(); auto viewPort = target.getRenderArea(); + pStart.x += 3; + pEnd.x -= 3; + if(viewPort.isInside(pStart) && viewPort.isInside(pEnd)) { - target.drawLine(pStart, pEnd, ColorRGBA(255, 255, 0), ColorRGBA(255, 255, 0)); + target.drawLine(pStart, pEnd, ColorRGBA(255, 255, 0), ColorRGBA(255, 0, 0)); } } };