From 19a7573a5f87726f92b817b1997cf4a36db43dce Mon Sep 17 00:00:00 2001 From: devZoGok Date: Sat, 18 Apr 2026 17:26:49 +0300 Subject: [PATCH 1/3] CPU players sending an engineer to explore fog of war before building --- Assets/Scripts/Core/main.lua | 8 ++++ Assets/Scripts/Core/player.lua | 82 +++++++++++++++++++++++++--------- activeGameState.cpp | 2 +- gameObjectFrame.h | 9 +++- gameObjectFrameController.cpp | 20 ++++----- 5 files changed, 89 insertions(+), 32 deletions(-) diff --git a/Assets/Scripts/Core/main.lua b/Assets/Scripts/Core/main.lua index 0bd7e18..3c1fdea 100644 --- a/Assets/Scripts/Core/main.lua +++ b/Assets/Scripts/Core/main.lua @@ -1 +1,9 @@ game = {cpuPlayers = {}} +GameObjectFrameStatus = { + PLACEABLE = 0, + BLOCKED_BY_FOG_OF_WAR = 1, + BLOCKED_BY_UNIT = 2, + BLOCKED_BY_MAP_BOUNDS = 3, + BLOCKED_BY_DIFF_TERR = 4, + BLOCKED_BY_BUMPY_TERR = 5 +}; diff --git a/Assets/Scripts/Core/player.lua b/Assets/Scripts/Core/player.lua index 6c22f47..bdbf3e6 100644 --- a/Assets/Scripts/Core/player.lua +++ b/Assets/Scripts/Core/player.lua @@ -6,6 +6,7 @@ Player.pointDefenseTrans = {} Player.spawnPointData = {} Player.navalFactoryCellId = nil Player.navalRallyPoint = nil +Player.suitableBuildingPosIds = nil Player.unitClassMappings = { {UnitId.ACS_MECH, UnitId.AINC_MECH, UnitId.ER_MECH}, @@ -86,7 +87,7 @@ function Player:initPdSpots() frameCtrl:checkPlacement(pdFrame) - if pdFrame.status == 0 then + if pdFrame.status == GameObjectFrameStatus.PLACEABLE then self.pointDefenseTrans[#self.pointDefenseTrans + 1] = {pos = pdPos, angle = angle} end end @@ -106,7 +107,7 @@ function Player:updatePdSpots() pdFrame:placeAt(self.pointDefenseTrans[i].pos) gofCtrl:checkPlacement(pdFrame) - if pdFrame.status == 1 then + if pdFrame.status ~= GameObjectFrameStatus.PLACEABLE then table.insert(delPdTrans, i) end end @@ -127,7 +128,7 @@ function Player:landRouteToSpawnpoint_(mapPoint) pf = Pathfinder.getSingleton() cells = map:getCells() heurs = pf:calcHeuristics(cells, dest) - path = pf:findPath(cells, heurs, source, dest, -1) + path = pf:findPath(cells, heurs, source, dest, UnitType.HOVER) embarkCellId = nil disembarkCellId = nil @@ -187,33 +188,68 @@ function Player:init() end end -function Player:findSuitableSpot(buildingId, idealPos) +function Player:findSuitableSpot(buildingId, idealPos, radius) structFrame = GameObjectFrame:new(buildingId, 0, self, nil, idealPos, self.baseQuat) - numYPos = math.floor(2 * self.baseRadius / structFrame:getLength()) - numXPos = math.floor(2 * self.baseRadius / structFrame:getWidth()) + diffY = 10 + diffX = 10 + numYPos = math.floor(2 * radius / diffY) + numXPos = math.floor(2 * radius / diffX) baseDir = self.baseQuat:multVec(Vector3:new(0, 0, 1)) baseLeft = self.baseQuat:multVec(Vector3:new(1, 0, 0)) frameCtrl = GameObjectFrameController.getSingleton() - frameCtrl:checkPlacement(structFrame) + startX = 1 + startY = 1 - if structFrame.status == 0 then - structFrame:destroy() - return idealPos + if not self.suitableBuildingPosIds then + frameCtrl:checkPlacement(structFrame) + + if structFrame.status == GameObjectFrameStatus.PLACEABLE then + structFrame:destroy() + return idealPos + end + else + startX = self.suitableBuildingPosIds.x + startY = self.suitableBuildingPosIds.y end startPos = idealPos:add(baseDir:mult(self.baseRadius)):add(baseLeft:mult(self.baseRadius)) - for i = 1, numXPos do - for j = 1, numYPos do - point = startPos:add(baseLeft:neg():mult(i - 1)):add(baseDir:neg():mult(j - 1)) + for i = startX, numXPos do + for j = startY, numYPos do + point = startPos:add(baseLeft:neg():mult((i - 1) * diffX)):add(baseDir:neg():mult((j - 1) * diffY)) structFrame:placeAt(point) frameCtrl:checkPlacement(structFrame) - if structFrame.status == 0 then + if structFrame.status == GameObjectFrameStatus.PLACEABLE then + self.suitableBuildingPosIds = nil structFrame:destroy() return point + elseif structFrame.status == GameObjectFrameStatus.BLOCKED_BY_FOG_OF_WAR then + engis = self:getUnitsByClass(UnitClass.CYBORG_ENGINEER, -1) + + for i = 1, #engis do + if engis[i]:getNumOrders() > 0 and engis[i]:getOrder(0).targets[1].pos:getDistanceFrom(point) < engis[i]:getLineOfSight() then + structFrame:destroy() + return nil + end + end + + idleEngi = self:findIdleEngineer() + + if not idleEngi then + structFrame:destroy() + return nil + end + + self:deselectUnits() + self:selectUnits({idleEngi}) + self:issueOrder(OrderType.MOVE, Vector3:new(0, 0, 0), {Target:new(nil, point)}, false) + + self.suitableBuildingPosIds = {x = i, y = j} + structFrame:destroy() + return nil end end end @@ -293,7 +329,7 @@ function Player:buildLandFactory(arguments) factId = self.unitClassMappings[UnitClass.LAND_FACTORY + 1][self:getFaction() + 1] rd = self.baseQuat:multVec(Vector3:new(0, 0, 1)):mult(self.baseRadius) sp = Map.getSingleton():getSpawnPoint(self:getSpawnPointId()) - pos = self:findSuitableSpot(factId, sp) + pos = self:findSuitableSpot(factId, sp, self.baseRadius, engi) if pos then self:buildStructure(engi, factId, pos, self.baseQuat:getAngle()) @@ -316,11 +352,12 @@ function Player:buildRefinery(arguments) fp = self:getUnitsByClass(UnitClass.LAND_FACTORY, 1)[1]:getPos() buildPos = fp:add(rd) - pos = self:findSuitableSpot(refId, buildPos) + idleEngi = self:findIdleEngineer() + pos = self:findSuitableSpot(refId, buildPos, self.baseRadius, idleEngi) if pos then buildAngle = (self.baseQuat:getAngle() + math.pi) - self:buildStructure(self:findIdleEngineer(), refId, buildPos, buildAngle) + self:buildStructure(idleEngi, refId, buildPos, buildAngle) return BTNodeResult.RUNNING else return BTNodeResult.FAILURE end end @@ -413,15 +450,20 @@ function Player:buildNavalFactory(arguments) navalFactory = self:getUnitsByClass(UnitClass.NAVAL_FACTORY, 1)[1] if navalFactory then + if not self.navalRallyPoint then + self.navalRallyPoint = self:getUnitsByClass(UnitClass.NAVAL_FACTORY, 1)[1]:toFactory():getRallyPoint() + end + return (navalFactory:toStructure():isComplete() and BTNodeResult.SUCCESS or BTNodeResult.RUNNING) end navalFactId = self.unitClassMappings[UnitClass.NAVAL_FACTORY + 1][self:getFaction() + 1] - pos = self:findSuitableSpot(navalFactId, Map.getSingleton():getCell(self.navalFactoryCellId).pos) + navalCellPos = Map.getSingleton():getCell(self.navalFactoryCellId).pos + idleEngi = self:findIdleEngineer() + pos = self:findSuitableSpot(navalFactId, navalCellPos, self.baseRadius, idleEngi) if pos then - self:buildStructure(self:findIdleEngineer(), navalFactId, pos, self.baseQuat:getAngle()) - self.navalRallyPoint = self:getUnitsByClass(UnitClass.NAVAL_FACTORY, 1)[1]:toFactory():getRallyPoint() + self:buildStructure(idleEngi, navalFactId, pos, self.baseQuat:getAngle()) return BTNodeResult.RUNNING else return BTNodeResult.FAILURE end end diff --git a/activeGameState.cpp b/activeGameState.cpp index 4d26463..bb991f4 100755 --- a/activeGameState.cpp +++ b/activeGameState.cpp @@ -683,7 +683,7 @@ namespace battleship{ for(int i = 0; i < ufCtr->getNumGameObjectFrames(); i++){ GameObjectFrame gmObjFr = ufCtr->getGameObjectFrame(i); - if(gmObjFr.status == GameObjectFrame::NOT_PLACEABLE) continue; + if(gmObjFr.status == GameObjectFrame::BLOCKED_BY_DIFF_TERR) continue; Unit *buildStruct = GameObjectFactory::createUnit(mainPlayer, gmObjFr.getId(), gmObjFr.getPos(), gmObjFr.getRot()); issueOrder(type, vector{Order::Target(buildStruct, gmObjFr.getPos())}, shiftPressed); diff --git a/gameObjectFrame.h b/gameObjectFrame.h index 24dfdbd..f051308 100644 --- a/gameObjectFrame.h +++ b/gameObjectFrame.h @@ -8,7 +8,14 @@ namespace battleship{ struct GameObjectFrame : public GameObject{ - enum Status{PLACEABLE, NOT_PLACEABLE, PLACED}; + enum Status{ + PLACEABLE, + BLOCKED_BY_FOG_OF_WAR, + BLOCKED_BY_UNIT, + BLOCKED_BY_MAP_BOUNDS, + BLOCKED_BY_DIFF_TERR, + BLOCKED_BY_BUMPY_TERR + }; GameObjectFrame(int i, GameObject::Type t, Player *pl, Unit *ou = nullptr, vb01::Vector3 pos = vb01::Vector3::VEC_ZERO, vb01::Quaternion rot = vb01::Quaternion::QUAT_W) : originalUnit(ou), diff --git a/gameObjectFrameController.cpp b/gameObjectFrameController.cpp index 59905e3..58f04cf 100644 --- a/gameObjectFrameController.cpp +++ b/gameObjectFrameController.cpp @@ -59,7 +59,7 @@ namespace battleship{ } void GameObjectFrameController::snapToObj(GameObjectFrame &s, vector snapTargets, float maxDist){ - s.status = GameObjectFrame::NOT_PLACEABLE; + s.status = GameObjectFrame::BLOCKED_BY_DIFF_TERR; for(GameObject *st : snapTargets){ Vector3 stPos = st->getPos(); @@ -91,19 +91,20 @@ namespace battleship{ for(int i = 0; i < 4; i++){ Vector3 cornerPos = (s.getPos() + s.getDirVec() * .5 * dirMult[i][0] * s.getLength() + s.getLeftVec() * .5 * dirMult[i][1] * s.getWidth()); + int cellId = map->getCellId(cornerPos, false); - if(!(fabs(cornerPos.x) <= .5 * mapSize.x && fabs(cornerPos.z) <= .5 * mapSize.z)){ - s.status = GameObjectFrame::NOT_PLACEABLE; + if(cellId < 0 || !(fabs(cornerPos.x) <= .5 * mapSize.x && fabs(cornerPos.z) <= .5 * mapSize.z)){ + s.status = GameObjectFrame::BLOCKED_BY_MAP_BOUNDS; return; } - Map::Cell cell = cells[map->getCellId(cornerPos, false)]; + Map::Cell cell = cells[cellId]; bool landUnitOnWater = (ut == UnitType::LAND && cell.type == Map::Cell::Type::WATER); bool waterUnitOnLand = ((ut == UnitType::SEA_LEVEL || ut == UnitType::UNDERWATER) && cell.type == Map::Cell::Type::LAND); bool withinCell = (fabs(cell.pos.x - cornerPos.x) < .5 * cellSize.x && fabs(cell.pos.z - cornerPos.z) < .5 * cellSize.z); if((landUnitOnWater || waterUnitOnLand) && withinCell){ - s.status = GameObjectFrame::NOT_PLACEABLE; + s.status = GameObjectFrame::BLOCKED_BY_DIFF_TERR; return; } @@ -116,7 +117,7 @@ namespace battleship{ } if(!withinLos){ - s.status = GameObjectFrame::NOT_PLACEABLE; + s.status = GameObjectFrame::BLOCKED_BY_FOG_OF_WAR; return; } @@ -131,7 +132,7 @@ namespace battleship{ float diffZ = fabs(s.getPos().z - verts[i].pos->z); if(diffX < 0.5 * s.getWidth() && diffZ < 0.5 * s.getLength() && diffY > s.getMaxUnevenness()){ - s.status = GameObjectFrame::NOT_PLACEABLE; + s.status = GameObjectFrame::BLOCKED_BY_BUMPY_TERR; return; } } @@ -173,7 +174,7 @@ namespace battleship{ ); if(intersects){ - s.status = GameObjectFrame::NOT_PLACEABLE; + s.status = GameObjectFrame::BLOCKED_BY_UNIT; return; } } @@ -290,7 +291,6 @@ namespace battleship{ void GameObjectFrameController::rotateGameObjectFrames(float angle){ for(GameObjectFrame &s : gameObjectFrames) - if(s.status != GameObjectFrame::PLACED) - s.orientAt(Quaternion(angle, Vector3::VEC_J) * s.getRot()); + s.orientAt(Quaternion(angle, Vector3::VEC_J) * s.getRot()); } } From 37418b125eeec4f03e3537cc2b6259d1d12e51d1 Mon Sep 17 00:00:00 2001 From: devZoGok Date: Fri, 24 Apr 2026 19:39:47 +0300 Subject: [PATCH 2/3] collecting Lua script garbage updated vb01 --- external/vb01 | 2 +- game.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/external/vb01 b/external/vb01 index 7ef8587..7f8b6fe 160000 --- a/external/vb01 +++ b/external/vb01 @@ -1 +1 @@ -Subproject commit 7ef858737c7a987f95934359cc9e35fb1a6b164b +Subproject commit 7f8b6fe03a48a8579286fb75404d96dcb2b9bdea diff --git a/game.cpp b/game.cpp index 10715c1..429f3a1 100644 --- a/game.cpp +++ b/game.cpp @@ -49,6 +49,7 @@ namespace battleship{ for(int i = 0; i < getCpuPlayers().size(); i++) { string plStr = "game.cpuPlayers[" + to_string(i + 1) + "]"; SOL_LUA_VIEW.script("executeBtNode(" + plStr + ", " + plStr + ".behaviour)"); + SOL_LUA_VIEW.collect_garbage(); } int numPlayersWithUnits = 0; From a7a89e00a0101b13a6a2db3cebb559c0517f7bfa Mon Sep 17 00:00:00 2001 From: devZoGok Date: Fri, 24 Apr 2026 19:52:55 +0300 Subject: [PATCH 3/3] reduced cyborg engineer LOSs minor CPU AI refactoring --- Assets/Scripts/Core/player.lua | 4 +--- Assets/Scripts/GameObjects/Units/unitData.lua | 8 ++++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/Assets/Scripts/Core/player.lua b/Assets/Scripts/Core/player.lua index bdbf3e6..1ae231b 100644 --- a/Assets/Scripts/Core/player.lua +++ b/Assets/Scripts/Core/player.lua @@ -450,9 +450,7 @@ function Player:buildNavalFactory(arguments) navalFactory = self:getUnitsByClass(UnitClass.NAVAL_FACTORY, 1)[1] if navalFactory then - if not self.navalRallyPoint then - self.navalRallyPoint = self:getUnitsByClass(UnitClass.NAVAL_FACTORY, 1)[1]:toFactory():getRallyPoint() - end + if not self.navalRallyPoint then self.navalRallyPoint = navalFactory:toFactory():getRallyPoint() end return (navalFactory:toStructure():isComplete() and BTNodeResult.SUCCESS or BTNodeResult.RUNNING) end diff --git a/Assets/Scripts/GameObjects/Units/unitData.lua b/Assets/Scripts/GameObjects/Units/unitData.lua index 76b23db..404e0d2 100644 --- a/Assets/Scripts/GameObjects/Units/unitData.lua +++ b/Assets/Scripts/GameObjects/Units/unitData.lua @@ -392,7 +392,7 @@ units = { albedoPath = 'tank.jpg', colorNodes = {'TankASHead'}, selectionSfx = PATH .. 'Sounds/Units/Tanks/selection.ogg', - speed = .5, + speed = 5, destinationOffset = .1, anglePrecision = .1, maxTurnAngle = .1, @@ -759,7 +759,7 @@ units = { cost = 500, size = {x = 4.95, y = 6.59, z = 4.11}, hitboxOffset = {x = .178, y = 3.27, z = 0}, - lineOfSight = 400, + lineOfSight = 150, name = 'Engineer', basePath = PATH .. vehiclePrefix .. 'Engineers/', albedoPath = 'engineer.jpg', @@ -840,7 +840,7 @@ units = { cost = 500, size = {x = 3.12, y = 5.31, z = 2.82}, hitboxOffset = {x = 0, y = 2.83, z = .4}, - lineOfSight = 600, + lineOfSight = 150, name = 'Engineer', basePath = PATH .. vehiclePrefix .. 'Engineers/', albedoPath = 'engineer.jpg', @@ -921,7 +921,7 @@ units = { cost = 500, size = {x = 3.65, y = 5.71, z = 3.86}, hitboxOffset = {x = 0, y = 2.9, z = .55}, - lineOfSight = 200, + lineOfSight = 150, name = 'Engineer', basePath = PATH .. vehiclePrefix .. 'Engineers/', albedoPath = 'engineer.jpg',