From 3c1caa5f959c1d570336f4abf8f6b31942e768a1 Mon Sep 17 00:00:00 2001 From: devZoGok Date: Thu, 30 Apr 2026 18:56:44 +0300 Subject: [PATCH] using dest clamping to determine whether to build naval factories for CPU players --- Assets/Scripts/Core/player.lua | 56 +++++++++------ gameManager.cpp | 9 ++- pathfinder.cpp | 30 ++++++++ pathfinder.h | 1 + vehicle.cpp | 126 +++++++++++---------------------- 5 files changed, 112 insertions(+), 110 deletions(-) diff --git a/Assets/Scripts/Core/player.lua b/Assets/Scripts/Core/player.lua index 1ae231b..d7d2a31 100644 --- a/Assets/Scripts/Core/player.lua +++ b/Assets/Scripts/Core/player.lua @@ -126,33 +126,41 @@ function Player:landRouteToSpawnpoint_(mapPoint) source = map:getCellId(map:getSpawnPoint(self:getSpawnPointId()), false) pf = Pathfinder.getSingleton() - cells = map:getCells() - heurs = pf:calcHeuristics(cells, dest) - path = pf:findPath(cells, heurs, source, dest, UnitType.HOVER) + forwClampedDest = pf:clampDestToSourceRegion(source, dest) - embarkCellId = nil - disembarkCellId = nil - navalSupportCellId = nil - navalRallyCellId = nil + if dest ~= forwClampedDest then + cells = map:getCells() - for i = 1, #path do - if map:getCell(path[i]).type == 1 then - navalRallyCellId = path[i] - embarkCellId = path[i - 1] - if not self.navalFactoryCellId then self.navalFactoryCellId = path[i] end - break; + embarkCellId = nil + navalRallyCellId = nil + embarkCellId = forwClampedDest + + for i = 1, #cells[embarkCellId + 1].edges do + destCellId = cells[embarkCellId + 1].edges[i].destCellId + + if cells[destCellId + 1].type == 1 then + if not self.navalFactoryCellId then self.navalFactoryCellId = destCellId end + + navalRallyCellId = destCellId + break + end end - end - for i = #path, 1, -1 do - if map:getCell(path[i]).type == 1 then - navalSupportCellId = path[i] - disembarkCellId = path[i + 1] - break; + disembarkCellId = nil + navalSupportCellId = nil + backwClampedDest = pf:clampDestToSourceRegion(dest, source) + disembarkCellId = backwClampedDest + + for i = 1, #cells[disembarkCellId + 1].edges do + destCellId = cells[disembarkCellId + 1].edges[i].destCellId + + if cells[destCellId + 1].type == 1 then + navalSupportCellId = destCellId + break + end end - end - - if embarkCellId or disembarkCellId then + + return { reachable = false, embarkCellId = embarkCellId, @@ -160,7 +168,9 @@ function Player:landRouteToSpawnpoint_(mapPoint) navalSupportCellId = navalSupportCellId, disembarkCellId = disembarkCellId } - else return {reachable = true} end + else + return {reachable = true} + end end function Player:landRouteToSpawnpoint(arguments) diff --git a/gameManager.cpp b/gameManager.cpp index ca2167f..1193e23 100755 --- a/gameManager.cpp +++ b/gameManager.cpp @@ -201,10 +201,16 @@ namespace battleship{ "multVec", [](Quaternion q, Vector3 v){return q * v;} ); + SOL_LUA_STATE.new_usertype( + "Edge", + "destCellId", &Map::Edge::destCellId + ); + SOL_LUA_STATE.new_usertype( "Cell", "pos", &Map::Cell::pos, - "type", &Map::Cell::type + "type", &Map::Cell::type, + "edges", &Map::Cell::edges ); SOL_LUA_STATE.new_usertype( @@ -221,6 +227,7 @@ namespace battleship{ SOL_LUA_STATE.new_usertype( "Pathfinder", "getSingleton", &Pathfinder::getSingleton, + "clampDestToSourceRegion", &Pathfinder::clampDestToSourceRegion, "calcHeuristics", &Pathfinder::calcHeuristics, "findPath", &Pathfinder::findPath ); diff --git a/pathfinder.cpp b/pathfinder.cpp index 8320b42..5b4f632 100644 --- a/pathfinder.cpp +++ b/pathfinder.cpp @@ -16,6 +16,36 @@ namespace battleship{ return pathfinder; } + int Pathfinder::clampDestToSourceRegion(int source, int dest){ + Map *map = Map::getSingleton(); + vector &cells = map->getCells(); + pair> srcRegion; + + for(int i = 0; i < map->getNumRegions(); i++){ + pair> region = map->getRegion(i); + + if(find(region.second.begin(), region.second.end(), &cells[source]) != region.second.end()){ + srcRegion = map->getRegion(i); + break; + } + } + + if(find(srcRegion.second.begin(), srcRegion.second.end(), &cells[dest]) == srcRegion.second.end()){ + int minDistId = 0; + + for(int i = 0; i < srcRegion.second.size(); i++){ + float currDist = cells[dest].pos.getDistanceFrom(srcRegion.second[i]->pos); + float minDist = cells[dest].pos.getDistanceFrom(srcRegion.second[minDistId]->pos); + + if(currDist < minDist) minDistId = i; + } + + dest = map->getCellId(srcRegion.second[minDistId]->pos); + } + + return dest; + } + vector Pathfinder::calcHeuristics(vector &cells, int dest){ vector heuristics; diff --git a/pathfinder.h b/pathfinder.h index 66a9000..636bb02 100644 --- a/pathfinder.h +++ b/pathfinder.h @@ -13,6 +13,7 @@ namespace battleship{ class Pathfinder{ public: static Pathfinder* getSingleton(); + int clampDestToSourceRegion(int, int); std::vector calcHeuristics(std::vector&, int); std::vector findPath(std::vector&, std::vector&, int, int, int = -1); inline vb01::u32 getImpassibleNodeVal(){return impassibleNodeVal;} diff --git a/vehicle.cpp b/vehicle.cpp index 2b23d70..0c604ca 100644 --- a/vehicle.cpp +++ b/vehicle.cpp @@ -317,100 +317,66 @@ namespace battleship{ debugPathPoints.push_back(n); } - //TODO allow ships to attack land targets and vice versa //TODO recursively search for vacant dest cell neibourghss - bool Vehicle::adjustDest(Vector3 destPos, int &source, int &dest){ + void Vehicle::preparePathpoints(Order &order, Vector3 destPos, bool appendDestPos){ + removeAllPathpoints(); + Map *map = Map::getSingleton(); vector &cells = map->getCells(); - source = map->getCellId(pos); + int source = map->getCellId(pos); bool ship = (type == UnitType::UNDERWATER || type == UnitType::SEA_LEVEL); bool waterVehCanMove = (ship && cells[source].type == Map::Cell::WATER); bool landVehCanMove = (type == UnitType::LAND && cells[source].type == Map::Cell::LAND); - if(type != UnitType::HOVER && !(waterVehCanMove || landVehCanMove)) return false; + if(type != UnitType::HOVER && !(waterVehCanMove || landVehCanMove)) return; - dest = map->getCellId(destPos); + int dest = map->getCellId(destPos); - if(type == UnitType::SEA_LEVEL && fabs(destPos.y - cells[dest].pos.y) > .1) return false; + if(type == UnitType::SEA_LEVEL && fabs(destPos.y - cells[dest].pos.y) > .1) return; - if(type != UnitType::HOVER){ - pair> srcRegion; + Pathfinder *pf = Pathfinder::getSingleton(); - for(int i = 0; i < map->getNumRegions(); i++){ - pair> region = map->getRegion(i); - - if(find(region.second.begin(), region.second.end(), &cells[source]) != region.second.end()){ - srcRegion = map->getRegion(i); - break; - } - } - - if(find(srcRegion.second.begin(), srcRegion.second.end(), &cells[dest]) == srcRegion.second.end()){ - int minDistId = 0; - - for(int i = 0; i < srcRegion.second.size(); i++){ - float currDist = cells[dest].pos.getDistanceFrom(srcRegion.second[i]->pos); - float minDist = cells[dest].pos.getDistanceFrom(srcRegion.second[minDistId]->pos); - - if(currDist < minDist) minDistId = i; - } - - dest = map->getCellId(srcRegion.second[minDistId]->pos); - } - } + if(type != UnitType::HOVER) + dest = pf->clampDestToSourceRegion(source, dest); if(cells[dest].blockedBy){ vector surrCellIds = map->getSurroundingCells(cells[dest].pos, 1); for(int scid : surrCellIds){ + int altDest = -1; + if(!cells[scid].blockedBy) switch(type){ case UnitType::HOVER: - dest = scid; - return true; + altDest = scid; + break; case UnitType::LAND: - if(cells[scid].type == Map::Cell::LAND){ - dest = scid; - return true; - } + if(cells[scid].type == Map::Cell::LAND) + altDest = scid; + break; case UnitType::SEA_LEVEL: case UnitType::UNDERWATER: - if(cells[scid].type == Map::Cell::WATER){ - dest = scid; - return true; - } + if(cells[scid].type == Map::Cell::WATER) + altDest = scid; + break; } - } - return false; + if(altDest != -1){ + dest = altDest; + break; + } + else return; + } } - else return true; - } + vector heurs; + vector path = pf->findPath(cells, heurs, source, dest, (int)type); + Vector3 *truncPoint = nullptr; - bool Vehicle::truncatePath(Order &order, vector &path, Vector3 destPos, bool appendDestPos){ - bool pathTruncated = false; - bool ship = (type == UnitType::UNDERWATER || type == UnitType::SEA_LEVEL); - vector &cells = Map::getSingleton()->getCells(); - - for(int i = 1; i < path.size(); i++){ - if((ship && cells[path[i]].type != Map::Cell::WATER) || (order.type != Order::TYPE::GARRISON && type == UnitType::LAND && cells[path[i]].type != Map::Cell::LAND)){ - path = vector(path.begin(), path.begin() + i); - order.targets[0].unit = nullptr; - order.targets[0].pos = cells[path[i - 1]].pos; - pathTruncated = true; - break; - } - else if(order.type == Order::TYPE::GARRISON && type == UnitType::LAND && cells[path[i]].type != Map::Cell::LAND && path.size() - 1 != i) - return false; - } - - for(int p : path) addPathpoint(cells[p].pos); - - if(order.targets[0].unit && !pathTruncated){ + if(order.targets[0].unit){ GameObject *targObj = order.targets[0].unit; for(int i = path.size() - 1; i >= 0; i--){ @@ -443,35 +409,23 @@ namespace battleship{ if(dirAngle <= a1) dist = (.5 * length) / cos(dirAngle); else if(leftAngle <= a2) dist = (.5 * width) / cos(leftAngle); - addPathpoint(targPos + pointDir.norm() * dist); - pathTruncated = true; + truncPoint = new Vector3; + *truncPoint = (targPos + pointDir.norm() * dist); break; } - else removePathpoint(pathPoints.size() - 1); + else path.pop_back(); } } - if(appendDestPos && !pathTruncated) + for(int p : path) addPathpoint(cells[p].pos); + + if(appendDestPos && !truncPoint) addPathpoint(destPos); - - return true; - } - - void Vehicle::preparePathpoints(Order &order, Vector3 destPos, bool appendDestPos){ - removeAllPathpoints(); - - Map *map = Map::getSingleton(); - vector &cells = map->getCells(); - int source, dest; - - if(!adjustDest(destPos, source, dest)) return; - - vector heurs; - Pathfinder *pf = Pathfinder::getSingleton(); - vector path = pf->findPath(cells, heurs, source, dest, (int)type); - - if(path.empty() || !truncatePath(order, path, destPos, appendDestPos)) return; + else if(truncPoint){ + addPathpoint(*truncPoint); + delete truncPoint; + } path.erase(path.begin()); }