From a3b6dbc93c2e7a6bf4afa8e7948a8a018a6520f8 Mon Sep 17 00:00:00 2001 From: devZoGok Date: Thu, 12 Feb 2026 13:54:32 +0200 Subject: [PATCH 01/22] redone CPU player behaviour --- Assets/Scripts/Core/player.lua | 328 ++++++++++-------- Assets/Scripts/Core/playerInit.lua | 57 ++- Assets/Scripts/GameObjects/Units/unitData.lua | 71 ++-- activeGameState.cpp | 7 +- freezer.cpp | 2 +- game.cpp | 33 +- game.h | 3 +- gameManager.cpp | 8 + gameObjectFactory.cpp | 5 +- inGameAppState.cpp | 2 +- structure.cpp | 2 +- unit.cpp | 5 +- unit.h | 8 +- vehicle.cpp | 4 +- 14 files changed, 323 insertions(+), 212 deletions(-) diff --git a/Assets/Scripts/Core/player.lua b/Assets/Scripts/Core/player.lua index 416c70d..6d3a94d 100644 --- a/Assets/Scripts/Core/player.lua +++ b/Assets/Scripts/Core/player.lua @@ -2,105 +2,43 @@ Player.numStartEngis = 3 Player.baseDir = nil Player.numDefWarMechs = 1 Player.behaviour = {} +Player.pointDefenseTrans = {} + +Player.unitClassMappings = { + {UnitId.ACS_MECH, UnitId.AINC_MECH, UnitId.ER_MECH}, --WAR_MECH = 0, + {UnitId.ACS_TANK, UnitId.AINC_TANK, UnitId.ER_TANK}, --TANK = 1, + {UnitId.ACS_ARTILLERY, UnitId.AINC_ARTILLERY, UnitId.ER_ARTILLERY}, --ARTILLERY = 2, + {UnitId.ACS_ROBO_ENGINEER, UnitId.AINC_ROBO_ENGINEER, UnitId.ER_ROBO_ENGINEER}, --ENGINEER = 3, + {UnitId.ACS_CYBORG_ENGINEER, UnitId.AINC_CYBORG_ENGINEER, UnitId.ER_CYBORG_ENGINEER}, --ENGINEER = 3, + {UnitId.ACS_MECH, UnitId.AINC_MECH, UnitId.ER_MECH}, --TRANSPORT = 4, + {UnitId.ACS_CARGO_SHIP, UnitId.AINC_CARGO_SHIP, UnitId.ER_CARGO_SHIP}, --RESOURCE_ROVER = 5, + {UnitId.ACS_CRUISER, UnitId.AINC_CRUISER, UnitId.ER_CRUISER}, --CRUISER = 6, + {UnitId.ACS_ANTI_SUB_CRUISER, UnitId.AINC_ANTI_SUB_CRUISER, UnitId.ER_ANTI_SUB_CRUISER}, --CRUISER = 6, + {nil, nil, nil}, --CARRIER = 8, + {UnitId.ACS_SUBMARINE, UnitId.AINC_SUBMARINE, UnitId.ER_SUBMARINE}, --SUBMARINE = 9, + {UnitId.ACS_MISSILE_SUBMARINE, UnitId.AINC_MISSILE_SUBMARINE, UnitId.ER_MISSILE_SUBMARINE}, --SUBMARINE = 9, + {UnitId.ACS_ICEBREAKER, UnitId.AINC_ICEBREAKER, UnitId.ER_ICEBREAKER}, --ICEBREAKER = 10, + {nil, UnitId.FREEZER, nil}, --FREEZER = 11, + {nil, UnitId.EMP_BOAT, nil}, --EMP_BOAT = 12, + {UnitId.ACS_LAND_FACTORY, UnitId.AINC_LAND_FACTORY, UnitId.ER_LAND_FACTORY}, --LAND_FACTORY = 13, + {UnitId.ACS_NAVAL_FACTORY, UnitId.AINC_NAVAL_FACTORY, UnitId.ER_NAVAL_FACTORY}, --NAVAL_FACTORY = 14, + {UnitId.ACS_TRADE_CENTER, UnitId.AINC_TRADE_CENTER, UnitId.ER_TRADE_CENTER}, --TRADE_CENTER = 15, + {UnitId.ACS_LAB, UnitId.AINC_LAB, UnitId.ER_LAB}, --LAB = 16, + {UnitId.POINT_DEFENSE, UnitId.POINT_DEFENSE, UnitId.POINT_DEFENSE}, --POINT_DEFENSE = 17, + {UnitId.EXTRACTOR, UnitId.EXTRACTOR, UnitId.EXTRACTOR}, --EXTRACTOR = 18, + {UnitId.ACS_REFINERY, UnitId.AINC_REFINERY, UnitId.ER_REFINERY}, --REFINERY = 19, + --{UnitId.ACS_MECH, UnitId.AINC_MECH, UnitId.ER_MECH}, --ICE_SHEET = 20, +} Player.taskForceData = { - {class = UnitClass.WAR_MECH, buildId = 0, numUnits = 2}, - {class = UnitClass.TANK, buildId = 1, numUnits = 1}, - {class = UnitClass.ARTILLERY, buildId = 2, numUnits = 1} + {class = UnitClass.MECH, numUnits = 2}, + {class = UnitClass.TANK, numUnits = 1}, + {class = UnitClass.ARTILLERY, numUnits = 1} } Player.taskForces = {} -function Player:getBuildableUnitIdFromClass(unit, unitClass) - buildableUnits = unit:getBuildableUnits() - - for i = 1, #buildableUnits do - if buildableUnits[i].buildable and units[buildableUnits[i].id + 1].unitClass == unitClass then - return i - end - end - - return -1 -end - -function Player:getFactionUnitIdFromClass(unitClass) - faction = self:getFaction() - - if unitClass == UnitClass.LAND_FACTORY then - if faction == 0 then - return UnitId.AINC_LAND_FACTORY - elseif faction == 1 then - return UnitId.ER_LAND_FACTORY - elseif faction == 2 then - return UnitId.ACS_LAND_FACTORY - end - elseif unitClass == UnitClass.REFINERY then - if faction == 0 then - return UnitId.AINC_REFINERY - elseif faction == 1 then - return UnitId.ER_REFINERY - elseif faction == 2 then - return UnitId.ACS_REFINERY - end - elseif unitClass == UnitClass.LAB then - if faction == 0 then - return UnitId.AINC_LAB - elseif faction == 1 then - return UnitId.ER_LAB - elseif faction == 2 then - return UnitId.ACS_LAB - end - elseif unitClass == UnitClass.ENGINEER then - if faction == 0 then - return UnitId.AINC_ROBO_ENGINEER - elseif faction == 1 then - return UnitId.ER_ROBO_ENGINEER - elseif faction == 2 then - return UnitId.ACS_ROBO_ENGINEER - end - elseif unitClass == UnitClass.MECH then - if faction == 0 then - return UnitId.AINC_MECH - elseif faction == 1 then - return UnitId.ER_MECH - elseif faction == 2 then - return UnitId.ACS_MECH - end - elseif unitClass == UnitClass.TANK then - if faction == 0 then - return UnitId.AINC_TANK - elseif faction == 1 then - return UnitId.ER_TANK - elseif faction == 2 then - return UnitId.ACS_TANK - end - elseif unitClass == UnitClass.ARTILLERY then - if faction == 0 then - return UnitId.AINC_ARTILLERY - elseif faction == 1 then - return UnitId.ER_ARTILLERY - elseif faction == 2 then - return UnitId.ACS_ARTILLERY - end - end - - return -1 -end - ---TODO simplify building construction -function Player:buildFort(arguments) - forts = self:getUnitsByClass(UnitClass.LAND_FACTORY, 1) - - if #forts > 0 then - return forts[1]:toStructure():getBuildStatus() == 100 and BTNodeResult.SUCCESS or BTNodeResult.RUNNING - end - - engis = self:getUnitsByClass(UnitClass.ENGINEER, 1) - if #engis == 0 then return BTNodeResult.FAILURE end - - self:deselectUnits() - self:selectUnits({engis[1]}) - +function Player:initBaseDir() map = Map.getSingleton() sp = map:getSpawnPoint(self:getSpawnPointId()) minDistId = nil @@ -118,31 +56,48 @@ function Player:buildFort(arguments) end self.baseDir = map:getSpawnPoint(minDistId):subtr(sp):norm() - right = (self.baseDir:getAngleBetween(Vector3:new(1, 0, 0)) > 1.57) - angle = self.baseDir:getAngleBetween(Vector3:new(0, 0, 1)) * (right and -1 or 1) - fort = GameObjectFactory.createUnit(self, self:getFactionUnitIdFromClass(UnitClass.LAND_FACTORY), sp, Quaternion:new(angle, Vector3:new(0, 1, 0)), 0) - self:addUnit(fort) - self:issueOrder(OrderType.BUILD, Vector3:new(0, 0, 0), {Target:new(fort:toGameObject(), Vector3:new(0, 0, 0))}, false) - - return BTNodeResult.RUNNING end -function Player:trainEngineers(arguments) - fort = self:getUnitsByClass(UnitClass.LAND_FACTORY, 1)[1]:toFactory() - engineers = self:getUnitsByClass(UnitClass.ENGINEER, -1) - engineerDiffNum = self.numStartEngis - #engineers - queueDiff = engineerDiffNum - #fort:getQueue() +function Player:initPdSpots() + numPd = 6 + radius = 40 + map = Map.getSingleton() + mapSize = map:getMapSize() - engiBuildId = self:getBuildableUnitIdFromClass(fort, UnitClass.ENGINEER) + for i = 1, numPd do + angle = (i - 1) * 2 * math.pi / numPd + rotDir = Quaternion:new(angle, Vector3:new(0, 1, 0)):multVec(self.baseDir) + pdPos = map:getSpawnPoint(self:getSpawnPointId()):add(rotDir:norm():mult(radius)) - for i = 1, queueDiff do fort:appendToQueue(engiBuildId) end + if math.abs(pdPos.x) < .5 * mapSize.x and math.abs(pdPos.z) < .5 * mapSize.z then + self.pointDefenseTrans[#self.pointDefenseTrans + 1] = {pos = pdPos, angle = angle} + end + end - engisStartNumBuilt = (#self:getUnitsByClass(UnitClass.ENGINEER, -1) == self.numStartEngis) - return (engisStartNumBuilt and BTNodeResult.SUCCESS or BTNodeResult.RUNNING) + print('num pd pos: ' .. #self.pointDefenseTrans) +end + +function Player:init() + self:initBaseDir() + self:initPdSpots() +end + +function Player:buildStructure(engineer, buildingId, buildPos, buildAngle) + if not engineer then return BTNodeResult.FAILURE end + + self:deselectUnits() + self:selectUnits({engineer}) + + building = GameObjectFactory.createUnit(self, buildingId, buildPos, Quaternion:new(buildAngle, Vector3:new(0, 1, 0)), 0) + self:issueOrder(OrderType.BUILD, Vector3:new(0, 0, 0), {Target:new(building:toGameObject(), Vector3:new(0, 0, 0))}, false) +end + +function Player:findSuitableSpot() + return Vector3:new(0, 0, 0) end function Player:findIdleEngineer() - engineers = self:getUnitsByClass(UnitClass.ENGINEER, -1) + engineers = self:getUnitsByClass(UnitClass.CYBORG_ENGINEER, -1) for i = 1, #engineers do if engineers[i]:getNumOrders() == 0 then return engineers[i] end @@ -151,55 +106,127 @@ function Player:findIdleEngineer() return nil end -function Player:buildStructure(engineer, buildingId, buildPos, buildAngle) - if not engineer then return BTNodeResult.FAILURE end +function Player:getBuildableUnitSlotId(buildingUnit, buildableUnitId) + for i = 1, #buildingUnit:getBuildableUnits() do + bu = buildingUnit:getBuildableUnit(i - 1) - unitClass = units[buildingId + 1].unitClass - - if #self:getUnitsByClass(unitClass, 1) == 0 then - self:deselectUnits() - self:selectUnits({engineer}) - - building = GameObjectFactory.createUnit(self, buildingId, buildPos, Quaternion:new(1, 0, 0, 0), 0) - self:addUnit(building) - self:issueOrder(OrderType.BUILD, Vector3:new(0, 0, 0), {Target:new(building:toGameObject(), Vector3:new(0, 0, 0))}, false) + if bu.buildable and bu.id == buildableUnitId then + print('returning ' .. i - 1) + return i - 1 + end end - building = self:getUnitsByClass(unitClass, 1)[1] - return (building:toStructure():getBuildStatus() == 100 and BTNodeResult.SUCCESS or BTNodeResult.RUNNING) + print('returning nil') + return nil +end + +--TODO simplify building construction +function Player:buildLandFactory(arguments) + factory = self:getUnitsByClass(UnitClass.LAND_FACTORY, 1)[1] + + if factory then + return factory:toStructure():isComplete() and BTNodeResult.SUCCESS or BTNodeResult.RUNNING + end + + engis = self:getUnitsByClass(UnitClass.CYBORG_ENGINEER, 1) + if #engis == 0 then return BTNodeResult.FAILURE end + + self:deselectUnits() + self:selectUnits({engis[1]}) + + right = (self.baseDir:getAngleBetween(Vector3:new(1, 0, 0)) > .5 * math.pi) + angle = self.baseDir:getAngleBetween(Vector3:new(0, 0, 1)) * (right and -1 or 1) + rot = Quaternion:new(angle, Vector3:new(0, 1, 0)) + + factId = self.unitClassMappings[UnitClass.LAND_FACTORY + 1][self:getFaction() + 1] + sp = Map.getSingleton():getSpawnPoint(self:getSpawnPointId()) + self:buildStructure(self:findIdleEngineer(), factId, sp, angle) + + return BTNodeResult.RUNNING +end + +function Player:trainEngineers(arguments) + factory = self:getUnitsByClass(UnitClass.LAND_FACTORY, 1)[1]:toFactory() + engineers = self:getUnitsByClass(UnitClass.CYBORG_ENGINEER, -1) + engineerDiffNum = self.numStartEngis - #engineers + queueDiff = engineerDiffNum - #factory:getQueue() + + engiBuildId = self.unitClassMappings[UnitClass.CYBORG_ENGINEER + 1][self:getFaction() + 1] + + for i = 1, queueDiff do + factory:appendToQueue(self:getBuildableUnitSlotId(factory, engiBuildId)) + end + + engisStartNumBuilt = (#self:getUnitsByClass(UnitClass.CYBORG_ENGINEER, -1) == self.numStartEngis) + return (engisStartNumBuilt and BTNodeResult.SUCCESS or BTNodeResult.RUNNING) end function Player:buildRefinery(arguments) + refinery = self:getUnitsByClass(UnitClass.REFINERY, 1)[1] + + if refinery then + print('cpu player: ') + print(self) + print('ref1: ') + print(refinery) + print('ref2: ') + print(refinery:toStructure()) + return (refinery:toStructure():isComplete() and BTNodeResult.SUCCESS or BTNodeResult.RUNNING) + end + buildPos = Map.getSingleton():getSpawnPoint(self:getSpawnPointId()):add(self.baseDir:mult(20)) - return self:buildStructure(self:findIdleEngineer(), self:getFactionUnitIdFromClass(UnitClass.REFINERY), buildPos, -.1) + factId = self.unitClassMappings[UnitClass.REFINERY + 1][self:getFaction() + 1] + self:buildStructure(self:findIdleEngineer(), factId, buildPos, -.0) + + return BTNodeResult.RUNNING end -- TODO optimize deposit position check function Player:buildExtractor(arguments) - deposits = {} + extractor = self:getUnitsByClass(UnitClass.EXTRACTOR, -1)[1] + + if extractor then + return (extractor:toStructure():isComplete() and BTNodeResult.SUCCESS or BTNodeResult.RUNNING) + end + + visibleDeposits = {} players = Game.getSingleton():getPlayers(true) for i = 1, #players do - deps = players[i]:getResourceDeposits() - table.move(deps, 1, #deps, #deposits + 1, deposits) + deposits = players[i]:getResourceDeposits() + + for j = 1, #deposits do + for k = 1, #self:getUnits() do + unit = self:getUnit(k - 1) + + if unit:getPos():getDistanceFrom(deposits[j]:getPos()) <= unit:getLineOfSight() and not deposits[j]:getExtractor() then + visibleDeposits[#visibleDeposits + 1] = deposits[i] + goto continue + end + end + + ::continue:: + end end - if #deposits == 0 then return BTNodeResult.FAILURE end + if #visibleDeposits == 0 then return BTNodeResult.FAILURE end - depPos = deposits[1]:getPos() + depPos = visibleDeposits[1]:getPos() spawnPoint = Map.getSingleton():getSpawnPoint(self:getSpawnPointId()) - for i = 1, #deposits do - if deposits[i]:getPos():getDistanceFrom(spawnPoint) < depPos:getDistanceFrom(spawnPoint) then + for i = 1, #visibleDeposits do + if visibleDeposits[i]:getPos():getDistanceFrom(spawnPoint) < depPos:getDistanceFrom(spawnPoint) then depPos = deposits[i]:getPos() end end - return self:buildStructure(self:findIdleEngineer(), UnitId.EXTRACTOR, depPos, 0) + self:buildStructure(self:findIdleEngineer(), UnitId.EXTRACTOR, depPos, 0) + + return BTNodeResult.RUNNING end function Player:buildHarvester(arguments) - if self:getUnitsByClass(UnitClass.RESOURCE_ROVER, 1)[1] then + if #self:getUnitsByClass(UnitClass.RESOURCE_ROVER, 1) > 0 then return BTNodeResult.SUCCESS end @@ -207,8 +234,11 @@ function Player:buildHarvester(arguments) if not factory then return BTNodeResult.FAILURE end factory = factory:toFactory() + roverSlotId = self.unitClassMappings[UnitClass.RESOURCE_ROVER + 1][self:getFaction() + 1] - if #factory:getQueue() == 0 then factory:appendToQueue(3) end + if #factory:getQueue() == 0 then + factory:appendToQueue(self:getBuildableUnitSlotId(factory, roverSlotId)) + end return BTNodeResult.RUNNING end @@ -232,7 +262,7 @@ function Player:startHarvesting() return BTNodeResult.SUCCESS end -function Player:buildTaskForces(arguments) +function Player:buildLandDefForce(arguments) landFactory = self:getUnitsByClass(UnitClass.LAND_FACTORY, 1)[1] if not landFactory then return BTNodeResult.FAILURE end @@ -252,15 +282,22 @@ function Player:buildTaskForces(arguments) end end - if enoughUnits then return BTNodeResult.SUCCESS end + print('about to...') + if enoughUnits then + print('here it comes...') + return BTNodeResult.SUCCESS + end + else + return BTNodeResult.RUNNING end - if #landFactory:getQueue() > 0 then return BTNodeResult.RUNNING end - for i = 1, numAttackableSpawnPoints do for j = 1, #self.taskForceData do for k = 1, self.taskForceData[j].numUnits do - landFactory:appendToQueue(self.taskForceData[j].buildId) + print('queueing...') + unitId = self.unitClassMappings[self.taskForceData[j].class + 1][self:getFaction() + 1] + slotId = self:getBuildableUnitSlotId(landFactory, unitId) + landFactory:appendToQueue(slotId) end end end @@ -268,6 +305,17 @@ function Player:buildTaskForces(arguments) return BTNodeResult.RUNNING end +function Player:buildPointDefenseRing(arguments) + pointDefs = self:getUnitsByClass(UnitClass.POINT_DEFENSE, -1) + if #pointDefs == #self.pointDefenseTrans then return BTNodeResult.SUCCESS end + + pdId = self.unitClassMappings[UnitClass.POINT_DEFENSE + 1][self:getFaction() + 1] + pdTrans = self.pointDefenseTrans[#pointDefs + 1] + self:buildStructure(self:findIdleEngineer(), pdId, pdTrans.pos, pdTrans.angle) + + return BTNodeResult.RUNNING +end + function Player:formTaskForce(arguments) if self.taskForces[arguments.tfId] then return BTNodeResult.SUCCESS end @@ -358,6 +406,7 @@ function Player:attackSpawnPoint(arguments) return BTNodeResult.RUNNING end +--[[ function Player:generateTaskforceActions() numSpawnPoints = Map.getSingleton():getNumSpawnPoints() actionsTable = {} @@ -382,3 +431,4 @@ function Player:generateTaskforceActions() return actionsTable end +]]-- diff --git a/Assets/Scripts/Core/playerInit.lua b/Assets/Scripts/Core/playerInit.lua index 9f001df..7ef2bcb 100644 --- a/Assets/Scripts/Core/playerInit.lua +++ b/Assets/Scripts/Core/playerInit.lua @@ -1,28 +1,62 @@ -for i = 1, #game.players do - game.players[i].behaviour = { +for i = 1, #game.cpuPlayers do + game.cpuPlayers[i]:init() + + game.cpuPlayers[i].behaviour = { type = BTNodeType.SEQUENCE, children = { - {type = BTNodeType.FUNCTION, func = 'buildFort'}, + {type = BTNodeType.FUNCTION, func = 'buildLandFactory'}, {type = BTNodeType.FUNCTION, func = 'trainEngineers'}, { type = BTNodeType.PARALLEL, children = { - { - type = BTNodeType.SEQUENCE, - children = { - {type = BTNodeType.FUNCTION, func = 'buildHarvester'}, - } - }, {type = BTNodeType.FUNCTION, func = 'buildRefinery'}, { type = BTNodeType.SEQUENCE, children = { {type = BTNodeType.FUNCTION, func = 'buildExtractor'}, - {type = BTNodeType.FUNCTION, func = 'startHarvesting'}, + {type = BTNodeType.FUNCTION, func = 'buildHarvester'}, } } } }, + { + type = BTNodeType.SEQUENCE, + children = { + {type = BTNodeType.FUNCTION, func = 'buildLandDefForce'}, + {type = BTNodeType.FUNCTION, func = 'buildPointDefenseRing'} + } + }, + --[[ + ]]-- + --[[ + { + type = BTNodeType.SELECTOR, + children = { + {type = BTNodeType.FUNCTION, func = 'hasLandRouteToAllSpawnPoints'}, + { + type = BTNodeType.SEQUENCE, + children = { + {type = BTNodeType.FUNCTION, func = 'buildNavalFactory'}, + {type = BTNodeType.FUNCTION, func = 'buildNavalForce'} + } + }, + } + }, + { + type = BTNodeType.SELECTOR, + children = { + {type = BTNodeType.FUNCTION, func = 'hasLandRouteToSpawnPoint'}, + { + type = BTNodeType.SEQUENCE, + children = { + {type = BTNodeType.FUNCTION, func = 'boardTransports'}, + {type = BTNodeType.FUNCTION, func = 'moveTransports'} + {type = BTNodeType.FUNCTION, func = 'unloadTransports'} + } + }, + } + }, + {type = BTNodeType.FUNCTION, func = 'occupySpawnpoint'}, { type = BTNodeType.PARALLEL, children = { @@ -30,11 +64,10 @@ for i = 1, #game.players do { type = BTNodeType.PARALLEL, numMinSuccesses = 2, - children = game.players[i]:generateTaskforceActions() + children = game.cpuPlayers[i]:generateTaskforceActions() }, } }, - --[[ ]]-- } } diff --git a/Assets/Scripts/GameObjects/Units/unitData.lua b/Assets/Scripts/GameObjects/Units/unitData.lua index ac10fed..163ed50 100644 --- a/Assets/Scripts/GameObjects/Units/unitData.lua +++ b/Assets/Scripts/GameObjects/Units/unitData.lua @@ -73,26 +73,29 @@ UnitId = { } WeaponClass = {DAMAGE = 0, FREEZE = 1} UnitClass = { - WAR_MECH = 0, + MECH = 0, TANK = 1, ARTILLERY = 2, - ENGINEER = 3, - TRANSPORT = 4, - RESOURCE_ROVER = 5, - CRUISER = 6, - CARRIER = 7, - SUBMARINE = 8, - ICEBREAKER = 9, - FREEZER = 10, - EMP_BOAT = 11, - LAND_FACTORY = 12, - NAVAL_FACTORY = 13, - TRADE_CENTER = 14, - LAB = 15, - POINT_DEFENSE = 16, - EXTRACTOR = 17, - REFINERY = 18, - ICE_SHEET = 19, + ROBO_ENGINEER = 3, + CYBORG_ENGINEER = 4, + TRANSPORT = 5, + RESOURCE_ROVER = 6, + CRUISER = 7, + ANTI_SUB_CRUISER = 8, + CARRIER = 9, + SUBMARINE = 10, + MISSILE_SUBMARINE = 11, + ICEBREAKER = 12, + FREEZER = 13, + EMP_BOAT = 14, + LAND_FACTORY = 15, + NAVAL_FACTORY = 16, + TRADE_CENTER = 17, + LAB = 18, + POINT_DEFENSE = 19, + EXTRACTOR = 20, + REFINERY = 21, + ICE_SHEET = 22, } UnitType = {UNDERWATER = 0, SEA_LEVEL = 1, HOVER = 2, LAND = 3, AIR = 4} @@ -169,7 +172,7 @@ units = { }, } }, - unitClass = UnitClass.WAR_MECH, + unitClass = UnitClass.MECH, unitType = UnitType.LAND, armor = {ArmorType.MECHANIC}, maxUnevenness = .5, @@ -242,7 +245,7 @@ units = { }, } }, - unitClass = UnitClass.WAR_MECH, + unitClass = UnitClass.MECH, unitType = UnitType.LAND, armor = {ArmorType.MECHANIC}, maxUnevenness = .5, @@ -314,7 +317,7 @@ units = { }, } }, - unitClass = UnitClass.WAR_MECH, + unitClass = UnitClass.MECH, unitType = UnitType.LAND, armor = {ArmorType.MECHANIC}, maxUnevenness = .5, @@ -699,7 +702,7 @@ units = { {id = UnitId.POINT_DEFENSE, buildable = true, trigger = 80}, {id = UnitId.EXTRACTOR, buildable = true, trigger = 69}, }, - unitClass = UnitClass.ENGINEER, + unitClass = UnitClass.ROBO_ENGINEER, unitType = UnitType.HOVER, armor = {ArmorType.MECHANIC}, maxUnevenness = .5, @@ -744,7 +747,7 @@ units = { {id = UnitId.EXTRACTOR, buildable = true, trigger = 69}, }, abilityButtons = {{buttonType = ButtonType.ORDER, name = 'Hack', orderType = OrderType.HACK}}, - unitClass = UnitClass.ENGINEER, + unitClass = UnitClass.CYBORG_ENGINEER, unitType = UnitType.HOVER, armor = {ArmorType.MECHANIC}, maxUnevenness = .5, @@ -780,7 +783,7 @@ units = { {id = UnitId.POINT_DEFENSE, buildable = true, trigger = 80}, {id = UnitId.EXTRACTOR, buildable = true, trigger = 69}, }, - unitClass = UnitClass.ENGINEER, + unitClass = UnitClass.ROBO_ENGINEER, unitType = UnitType.HOVER, armor = {ArmorType.MECHANIC}, maxUnevenness = .5, @@ -825,7 +828,7 @@ units = { {id = UnitId.EXTRACTOR, buildable = true, trigger = 69}, }, abilityButtons = {{buttonType = ButtonType.ORDER, name = 'Hack', orderType = OrderType.HACK}}, - unitClass = UnitClass.ENGINEER, + unitClass = UnitClass.CYBORG_ENGINEER, unitType = UnitType.HOVER, armor = {ArmorType.MECHANIC}, maxUnevenness = .5, @@ -861,7 +864,7 @@ units = { {id = UnitId.POINT_DEFENSE, buildable = true, trigger = 80}, {id = UnitId.EXTRACTOR, buildable = true, trigger = 69}, }, - unitClass = UnitClass.ENGINEER, + unitClass = UnitClass.ROBO_ENGINEER, unitType = UnitType.HOVER, armor = {ArmorType.MECHANIC}, maxUnevenness = .5, @@ -906,7 +909,7 @@ units = { {id = UnitId.EXTRACTOR, buildable = true, trigger = 69}, }, abilityButtons = {{buttonType = ButtonType.ORDER, name = 'Hack', orderType = OrderType.HACK}}, - unitClass = UnitClass.ENGINEER, + unitClass = UnitClass.CYBORG_ENGINEER, unitType = UnitType.HOVER, armor = {ArmorType.MECHANIC}, maxUnevenness = .5, @@ -1219,7 +1222,7 @@ units = { armor = {ArmorType.CAST}, maxUnevenness = .5, alignToSurface = true, - capacity = 3000, + capacity = 1000, loadRate = 1, loadSpeed = 5, isVehicle = true, @@ -1673,7 +1676,7 @@ units = { hitFx = {} }, }, - unitClass = UnitClass.CRUISER, + unitClass = UnitClass.ANTI_SUB_CRUISER, unitType = UnitType.SEA_LEVEL, armor = {ArmorType.STEEL}, isVehicle = true, @@ -1803,7 +1806,7 @@ units = { }, }, }, - unitClass = UnitClass.CRUISER, + unitClass = UnitClass.ANTI_SUB_CRUISER, unitType = UnitType.SEA_LEVEL, armor = {ArmorType.STEEL}, isVehicle = true, @@ -1963,7 +1966,7 @@ units = { }, }, }, - unitClass = UnitClass.CRUISER, + unitClass = UnitClass.ANTI_SUB_CRUISER, unitType = UnitType.SEA_LEVEL, armor = {ArmorType.STEEL}, isVehicle = true, @@ -2016,7 +2019,7 @@ units = { projectile = {id = ProjectileId.CRUISE_MISSILE, pos = {x = 0, y = 15, z = -3.5}, rot = {w = .707, x = -.707, y = 0, z = 0}} }, }, - unitClass = UnitClass.SUBMARINE, + unitClass = UnitClass.MISSILE_SUBMARINE, unitType = UnitType.UNDERWATER, isVehicle = true, health = 500, @@ -2068,7 +2071,7 @@ units = { projectile = {id = ProjectileId.CRUISE_MISSILE, pos = {x = 0, y = 15, z = -3.5}, rot = {w = .707, x = -.707, y = 0, z = 0}} }, }, - unitClass = UnitClass.SUBMARINE, + unitClass = UnitClass.MISSILE_SUBMARINE, unitType = UnitType.UNDERWATER, isVehicle = true, health = 500, @@ -2120,7 +2123,7 @@ units = { projectile = {id = ProjectileId.CRUISE_MISSILE, pos = {x = 0, y = 15, z = -3.5}, rot = {w = .707, x = -.707, y = 0, z = 0}} }, }, - unitClass = UnitClass.SUBMARINE, + unitClass = UnitClass.MISSILE_SUBMARINE, unitType = UnitType.UNDERWATER, isVehicle = true, health = 500, diff --git a/activeGameState.cpp b/activeGameState.cpp index 59141b7..21b569e 100755 --- a/activeGameState.cpp +++ b/activeGameState.cpp @@ -252,7 +252,7 @@ namespace battleship{ orderPossible = (roverSelected && transferResource); } else if(cursorState == CursorState::HACK) - orderPossible = (!ownGameObj && gameObjUnit && mainPlayer->getSelectedUnit(0)->getUnitClass() == UnitClass::ENGINEER); + orderPossible = (!ownGameObj && gameObjUnit && mainPlayer->getSelectedUnit(0)->getUnitClass() == UnitClass::CYBORG_ENGINEER); else if(controlPressed) cursorState = CursorState::ATTACK; else @@ -285,7 +285,6 @@ namespace battleship{ void ActiveGameState::update() { ConcreteGuiManager *guiManager = ConcreteGuiManager::getSingleton(); - guiManager->getText("depth")->setText(L"Depth: " + to_wstring(depth)); guiManager->getText("refineds")->setText(to_wstring(mainPlayer->getResource(ResourceType::REFINEDS))); guiManager->getText("wealth")->setText(to_wstring(mainPlayer->getResource(ResourceType::WEALTH))); guiManager->getText("research")->setText(to_wstring(mainPlayer->getResource(ResourceType::RESEARCH))); @@ -723,7 +722,8 @@ namespace battleship{ Order::TYPE type; switch(selectedUnits[0]->getUnitClass()){ - case UnitClass::ENGINEER: + case UnitClass::ROBO_ENGINEER: + case UnitClass::CYBORG_ENGINEER: case UnitClass::FREEZER: type = Order::TYPE::BUILD; break; @@ -740,7 +740,6 @@ namespace battleship{ Unit *buildStruct = GameObjectFactory::createUnit(mainPlayer, gmObjFr.getId(), gmObjFr.getPos(), gmObjFr.getRot()); issueOrder(type, vector{Order::Target(buildStruct, gmObjFr.getPos())}, shiftPressed); - mainPlayer->addUnit(buildStruct); } buildableStructSelected = false; diff --git a/freezer.cpp b/freezer.cpp index 1cd5c8c..5450a0a 100644 --- a/freezer.cpp +++ b/freezer.cpp @@ -33,7 +33,7 @@ namespace battleship{ Vehicle::build(order); - if(targ->getUnitClass() == UnitClass::ICE_SHEET && ((Structure*)targ)->getBuildStatus() >= 100) + if(targ->getUnitClass() == UnitClass::ICE_SHEET && ((Structure*)targ)->isComplete()) removeOrder(0); } } diff --git a/game.cpp b/game.cpp index 4b2256d..d9cb555 100644 --- a/game.cpp +++ b/game.cpp @@ -43,14 +43,14 @@ namespace battleship{ } void Game::update(){ - updateLuaPlayers(false); + //updateLuaPlayers(false); sol::state_view SOL_LUA_VIEW = generateView(); - for(int i = 0; i < players.size(); i++) - if(players[i]->isCpuPlayer()){ - string plStr = "game.players[" + to_string(i + 1) + "]"; - SOL_LUA_VIEW.script("executeBtNode(" + plStr + ", " + plStr + ".behaviour)"); - } + 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.script("tbl = " + plStr + ":getUnitsByClass(UnitClass.CYBORG_ENGINEER, -1)"); + } int numPlayersWithUnits = 0; @@ -76,18 +76,19 @@ namespace battleship{ FxManager::getSingleton()->update(); } - void Game::updateLuaPlayers(bool resetBehaviour){ + void Game::initLuaPlayers(bool resetBehaviour){ sol::state_view SOL_LUA_VIEW = generateView(); + vector cpuPlayers = getCpuPlayers(); - for(int i = 0; i < players.size(); i++) - SOL_LUA_VIEW["game"]["players"][i + 1] = players[i]; + for(int i = 0; i < cpuPlayers.size(); i++) + SOL_LUA_VIEW["game"]["cpuPlayers"][i + 1] = cpuPlayers[i]; if(resetBehaviour) SOL_LUA_VIEW.script_file(GameManager::getSingleton()->getPath() + "Scripts/Core/playerInit.lua"); } void Game::removeAllElements(){ - generateView().script("game.players = {}"); + generateView().script("game.cpuPlayers = {}"); while(!players.empty()){ delete players[0]; @@ -113,7 +114,7 @@ namespace battleship{ for(string f : configData::scripts) SOL_LUA_VIEW.script_file(gm->getPath() + f); - updateLuaPlayers(true); + initLuaPlayers(); guiManager->readLuaScreenScript("inGame.lua", activeState->getGuiButtons()); string gop = SOL_LUA_VIEW["gameObjPrefix"], vfxp = SOL_LUA_VIEW["vfxPrefix"]; @@ -233,4 +234,14 @@ namespace battleship{ return playersVec; } + + vector Game::getCpuPlayers(){ + vector playersVec; + + for(Player *pl : players) + if(pl->isCpuPlayer()) + playersVec.push_back(pl); + + return playersVec; + } } diff --git a/game.h b/game.h index d2bd4e5..19ffbff 100644 --- a/game.h +++ b/game.h @@ -20,13 +20,14 @@ namespace battleship{ public: static Game* getSingleton(); void update(); - void updateLuaPlayers(bool); + void initLuaPlayers(bool = false); void togglePause(); void removeAllElements(); void initTechnologies(); float calcAbilFromTech(Ability::Type, std::vector, int, int); bool isUnitUnlocked(std::vector, int); std::vector getPlayers(bool = false); + std::vector getCpuPlayers(); inline void setDebug(bool d){this->debug = d;} inline void addPlayer(Player *pl){players.push_back(pl);} inline bool isDebug(){return debug;} diff --git a/gameManager.cpp b/gameManager.cpp index 8eb1edc..4b476fd 100755 --- a/gameManager.cpp +++ b/gameManager.cpp @@ -14,6 +14,7 @@ #include "guiAppState.h" #include "defConfigs.h" #include "player.h" +#include "extractor.h" #include "factory.h" #include "engineer.h" #include "destructable.h" @@ -85,6 +86,7 @@ namespace battleship{ SOL_LUA_STATE.new_usertype( "Structure", sol::constructors(), "getPos", &GameObject::getPos, + "isComplete", &Structure::isComplete, "getBuildStatus", &Structure::getBuildStatus ); @@ -124,8 +126,13 @@ namespace battleship{ "getUnitsByClass", &Player::getUnitsByClass ); + SOL_LUA_STATE.new_usertype( + "Extractor", sol::constructors() + ); + SOL_LUA_STATE.new_usertype( "ResourceDeposit", sol::constructors(), + "getExtractor", &ResourceDeposit::getExtractor, "getPos", &GameObject::getPos ); @@ -161,6 +168,7 @@ namespace battleship{ SOL_LUA_STATE.new_usertype( "Map", "getSingleton", &Map::getSingleton, + "getMapSize", &Map::getMapSize, "getSpawnPoint", &Map::getSpawnPoint, "getNumSpawnPoints", &Map::getNumSpawnPoints ); diff --git a/gameObjectFactory.cpp b/gameObjectFactory.cpp index 5a4873d..9fded44 100644 --- a/gameObjectFactory.cpp +++ b/gameObjectFactory.cpp @@ -34,11 +34,13 @@ namespace battleship{ if(vehicle) switch((UnitClass)unitClass){ - case UnitClass::ENGINEER: + case UnitClass::ROBO_ENGINEER: + case UnitClass::CYBORG_ENGINEER: return new Engineer(player, id, pos, rot, Unit::State::STAND_GROUND); case UnitClass::RESOURCE_ROVER: return new ResourceRover(player, id, pos, rot, Unit::State::STAND_GROUND); case UnitClass::SUBMARINE: + case UnitClass::MISSILE_SUBMARINE: return new Submarine(player, id, pos, rot, Unit::State::STAND_GROUND); case UnitClass::FREEZER: return new Freezer(player, id, pos, rot, Unit::State::STAND_GROUND); @@ -47,7 +49,6 @@ namespace battleship{ } else switch((UnitClass)unitClass){ - case UnitClass::FORT: case UnitClass::LAND_FACTORY: case UnitClass::NAVAL_FACTORY: return new Factory(player, id, pos, rot, buildStatus, Unit::State::STAND_GROUND); diff --git a/inGameAppState.cpp b/inGameAppState.cpp index c852c1c..281b147 100755 --- a/inGameAppState.cpp +++ b/inGameAppState.cpp @@ -74,7 +74,7 @@ namespace battleship{ activeState = new ActiveGameState(guiState, playerId); stateManager->attachAppState(activeState); - Game::getSingleton()->updateLuaPlayers(true); + Game::getSingleton()->initLuaPlayers(true); Map::Minimap::getSingleton()->updateImage(); } diff --git a/structure.cpp b/structure.cpp index 3926dae..a054347 100644 --- a/structure.cpp +++ b/structure.cpp @@ -31,7 +31,7 @@ namespace battleship{ vector selectingPlayers = getSelectingPlayers(); bool mainPlayerSelecting = (activeState && find(selectingPlayers.begin(), selectingPlayers.end(), mainPlayer) != selectingPlayers.end()); - if(buildStatus < 100) + if(!isComplete()) destructable->displayStats(buildStatusForeground, buildStatusBackground, buildStatus, 100, mainPlayer == player && mainPlayerSelecting, Vector2(0, -10)); else{ buildStatusBackground->setVisible(false); diff --git a/unit.cpp b/unit.cpp index 82bb397..a115fc1 100755 --- a/unit.cpp +++ b/unit.cpp @@ -413,7 +413,7 @@ namespace battleship{ return destructTarg && !getWeaponsByOrder(Order::TYPE::ATTACK).empty(); } case Order::TYPE::BUILD: - return (unitClass == UnitClass::ENGINEER || unitClass == UnitClass::FREEZER); + return (unitClass == UnitClass::ROBO_ENGINEER || unitClass == UnitClass::CYBORG_ENGINEER || unitClass == UnitClass::FREEZER); case Order::TYPE::PATROL: case Order::TYPE::MOVE: return vehicle; @@ -465,6 +465,9 @@ namespace battleship{ } orders.push_back(order); + + if(order.type == Order::TYPE::BUILD && order.targets[0].unit) + player->addUnit((Unit*)order.targets[0].unit); } void Unit::halt() { diff --git a/unit.h b/unit.h index da424bf..2da7709 100755 --- a/unit.h +++ b/unit.h @@ -67,15 +67,18 @@ namespace battleship{ enum class Corner {FRONT_LEFT, FRONT_RIGHT, REAR_LEFT, REAR_RIGHT}; enum class UnitType {UNDERWATER, SEA_LEVEL, HOVER, LAND, AIR, NONE = -1}; enum class UnitClass { - WAR_MECH, + MECH, TANK, ARTILLERY, - ENGINEER, + ROBO_ENGINEER, + CYBORG_ENGINEER, TRANSPORT, RESOURCE_ROVER, CRUISER, + ANTI_SUB_CRUISER, CARRIER, SUBMARINE, + MISSILE_SUBMARINE, ICEBREAKER, FREEZER, EMP_BOAT, @@ -86,7 +89,6 @@ namespace battleship{ POINT_DEFENSE, EXTRACTOR, REFINERY, - FORT, ICE_SHEET }; diff --git a/vehicle.cpp b/vehicle.cpp index 9017dc9..9e8f074 100644 --- a/vehicle.cpp +++ b/vehicle.cpp @@ -470,12 +470,12 @@ namespace battleship{ sol::table targTable = generateView()["units"][structure->getId()]; int costRate = (int)targTable["cost"] / 100, buildRate = (int)targTable["buildTime"] / 100; - if(structure->getBuildStatus() < 100 && player->getResource(ResourceType::REFINEDS) >= costRate && getTime() - lastBuildTime > buildRate){ + if(!structure->isComplete() && player->getResource(ResourceType::REFINEDS) >= costRate && getTime() - lastBuildTime > buildRate){ structure->incrementBuildStatus(); player->updateResource(ResourceType::REFINEDS, -costRate, true); lastBuildTime = getTime(); } - else if(structure->getBuildStatus() >= 100){ + else if(structure->isComplete()){ removeOrder(0); player->incStructuresBuilt(); } From 35fb88cb9adb1b514a7acf3ad9e2ea9684284fc9 Mon Sep 17 00:00:00 2001 From: devZoGok Date: Sun, 15 Feb 2026 11:53:22 +0200 Subject: [PATCH 02/22] CPU players capture spawn points --- Assets/Scripts/Core/main.lua | 2 +- Assets/Scripts/Core/player.lua | 267 +++++++++++-------------- Assets/Scripts/Core/playerInit.lua | 53 +---- Assets/Scripts/Gui/playerSelection.lua | 2 +- game.cpp | 3 +- player.cpp | 2 +- 6 files changed, 134 insertions(+), 195 deletions(-) diff --git a/Assets/Scripts/Core/main.lua b/Assets/Scripts/Core/main.lua index 263b2ba..0bd7e18 100644 --- a/Assets/Scripts/Core/main.lua +++ b/Assets/Scripts/Core/main.lua @@ -1 +1 @@ -game = {players = {}} +game = {cpuPlayers = {}} diff --git a/Assets/Scripts/Core/player.lua b/Assets/Scripts/Core/player.lua index 6d3a94d..0af5c47 100644 --- a/Assets/Scripts/Core/player.lua +++ b/Assets/Scripts/Core/player.lua @@ -5,29 +5,28 @@ Player.behaviour = {} Player.pointDefenseTrans = {} Player.unitClassMappings = { - {UnitId.ACS_MECH, UnitId.AINC_MECH, UnitId.ER_MECH}, --WAR_MECH = 0, - {UnitId.ACS_TANK, UnitId.AINC_TANK, UnitId.ER_TANK}, --TANK = 1, - {UnitId.ACS_ARTILLERY, UnitId.AINC_ARTILLERY, UnitId.ER_ARTILLERY}, --ARTILLERY = 2, - {UnitId.ACS_ROBO_ENGINEER, UnitId.AINC_ROBO_ENGINEER, UnitId.ER_ROBO_ENGINEER}, --ENGINEER = 3, - {UnitId.ACS_CYBORG_ENGINEER, UnitId.AINC_CYBORG_ENGINEER, UnitId.ER_CYBORG_ENGINEER}, --ENGINEER = 3, - {UnitId.ACS_MECH, UnitId.AINC_MECH, UnitId.ER_MECH}, --TRANSPORT = 4, - {UnitId.ACS_CARGO_SHIP, UnitId.AINC_CARGO_SHIP, UnitId.ER_CARGO_SHIP}, --RESOURCE_ROVER = 5, - {UnitId.ACS_CRUISER, UnitId.AINC_CRUISER, UnitId.ER_CRUISER}, --CRUISER = 6, - {UnitId.ACS_ANTI_SUB_CRUISER, UnitId.AINC_ANTI_SUB_CRUISER, UnitId.ER_ANTI_SUB_CRUISER}, --CRUISER = 6, - {nil, nil, nil}, --CARRIER = 8, - {UnitId.ACS_SUBMARINE, UnitId.AINC_SUBMARINE, UnitId.ER_SUBMARINE}, --SUBMARINE = 9, - {UnitId.ACS_MISSILE_SUBMARINE, UnitId.AINC_MISSILE_SUBMARINE, UnitId.ER_MISSILE_SUBMARINE}, --SUBMARINE = 9, - {UnitId.ACS_ICEBREAKER, UnitId.AINC_ICEBREAKER, UnitId.ER_ICEBREAKER}, --ICEBREAKER = 10, - {nil, UnitId.FREEZER, nil}, --FREEZER = 11, - {nil, UnitId.EMP_BOAT, nil}, --EMP_BOAT = 12, - {UnitId.ACS_LAND_FACTORY, UnitId.AINC_LAND_FACTORY, UnitId.ER_LAND_FACTORY}, --LAND_FACTORY = 13, - {UnitId.ACS_NAVAL_FACTORY, UnitId.AINC_NAVAL_FACTORY, UnitId.ER_NAVAL_FACTORY}, --NAVAL_FACTORY = 14, - {UnitId.ACS_TRADE_CENTER, UnitId.AINC_TRADE_CENTER, UnitId.ER_TRADE_CENTER}, --TRADE_CENTER = 15, - {UnitId.ACS_LAB, UnitId.AINC_LAB, UnitId.ER_LAB}, --LAB = 16, - {UnitId.POINT_DEFENSE, UnitId.POINT_DEFENSE, UnitId.POINT_DEFENSE}, --POINT_DEFENSE = 17, - {UnitId.EXTRACTOR, UnitId.EXTRACTOR, UnitId.EXTRACTOR}, --EXTRACTOR = 18, - {UnitId.ACS_REFINERY, UnitId.AINC_REFINERY, UnitId.ER_REFINERY}, --REFINERY = 19, - --{UnitId.ACS_MECH, UnitId.AINC_MECH, UnitId.ER_MECH}, --ICE_SHEET = 20, + {UnitId.ACS_MECH, UnitId.AINC_MECH, UnitId.ER_MECH}, + {UnitId.ACS_TANK, UnitId.AINC_TANK, UnitId.ER_TANK}, + {UnitId.ACS_ARTILLERY, UnitId.AINC_ARTILLERY, UnitId.ER_ARTILLERY}, + {UnitId.ACS_ROBO_ENGINEER, UnitId.AINC_ROBO_ENGINEER, UnitId.ER_ROBO_ENGINEER}, + {UnitId.ACS_CYBORG_ENGINEER, UnitId.AINC_CYBORG_ENGINEER, UnitId.ER_CYBORG_ENGINEER}, + {UnitId.ACS_MECH, UnitId.AINC_MECH, UnitId.ER_MECH}, + {UnitId.ACS_CARGO_SHIP, UnitId.AINC_CARGO_SHIP, UnitId.ER_CARGO_SHIP}, + {UnitId.ACS_CRUISER, UnitId.AINC_CRUISER, UnitId.ER_CRUISER}, + {UnitId.ACS_ANTI_SUB_CRUISER, UnitId.AINC_ANTI_SUB_CRUISER, UnitId.ER_ANTI_SUB_CRUISER}, + {nil, nil, nil}, + {UnitId.ACS_SUBMARINE, UnitId.AINC_SUBMARINE, UnitId.ER_SUBMARINE}, + {UnitId.ACS_MISSILE_SUBMARINE, UnitId.AINC_MISSILE_SUBMARINE, UnitId.ER_MISSILE_SUBMARINE}, + {UnitId.ACS_ICEBREAKER, UnitId.AINC_ICEBREAKER, UnitId.ER_ICEBREAKER}, + {nil, UnitId.FREEZER, nil}, + {nil, UnitId.EMP_BOAT, nil}, + {UnitId.ACS_LAND_FACTORY, UnitId.AINC_LAND_FACTORY, UnitId.ER_LAND_FACTORY}, + {UnitId.ACS_NAVAL_FACTORY, UnitId.AINC_NAVAL_FACTORY, UnitId.ER_NAVAL_FACTORY}, + {UnitId.ACS_TRADE_CENTER, UnitId.AINC_TRADE_CENTER, UnitId.ER_TRADE_CENTER}, + {UnitId.ACS_LAB, UnitId.AINC_LAB, UnitId.ER_LAB}, + {UnitId.POINT_DEFENSE, UnitId.POINT_DEFENSE, UnitId.POINT_DEFENSE}, + {UnitId.EXTRACTOR, UnitId.EXTRACTOR, UnitId.EXTRACTOR}, + {UnitId.ACS_REFINERY, UnitId.AINC_REFINERY, UnitId.ER_REFINERY}, } Player.taskForceData = { @@ -60,7 +59,7 @@ end function Player:initPdSpots() numPd = 6 - radius = 40 + radius = 100 map = Map.getSingleton() mapSize = map:getMapSize() @@ -73,8 +72,6 @@ function Player:initPdSpots() self.pointDefenseTrans[#self.pointDefenseTrans + 1] = {pos = pdPos, angle = angle} end end - - print('num pd pos: ' .. #self.pointDefenseTrans) end function Player:init() @@ -111,12 +108,10 @@ function Player:getBuildableUnitSlotId(buildingUnit, buildableUnitId) bu = buildingUnit:getBuildableUnit(i - 1) if bu.buildable and bu.id == buildableUnitId then - print('returning ' .. i - 1) return i - 1 end end - print('returning nil') return nil end @@ -128,16 +123,6 @@ function Player:buildLandFactory(arguments) return factory:toStructure():isComplete() and BTNodeResult.SUCCESS or BTNodeResult.RUNNING end - engis = self:getUnitsByClass(UnitClass.CYBORG_ENGINEER, 1) - if #engis == 0 then return BTNodeResult.FAILURE end - - self:deselectUnits() - self:selectUnits({engis[1]}) - - right = (self.baseDir:getAngleBetween(Vector3:new(1, 0, 0)) > .5 * math.pi) - angle = self.baseDir:getAngleBetween(Vector3:new(0, 0, 1)) * (right and -1 or 1) - rot = Quaternion:new(angle, Vector3:new(0, 1, 0)) - factId = self.unitClassMappings[UnitClass.LAND_FACTORY + 1][self:getFaction() + 1] sp = Map.getSingleton():getSpawnPoint(self:getSpawnPointId()) self:buildStructure(self:findIdleEngineer(), factId, sp, angle) @@ -146,32 +131,28 @@ function Player:buildLandFactory(arguments) end function Player:trainEngineers(arguments) - factory = self:getUnitsByClass(UnitClass.LAND_FACTORY, 1)[1]:toFactory() - engineers = self:getUnitsByClass(UnitClass.CYBORG_ENGINEER, -1) - engineerDiffNum = self.numStartEngis - #engineers - queueDiff = engineerDiffNum - #factory:getQueue() + engis = self:getUnitsByClass(UnitClass.CYBORG_ENGINEER, -1) + + if #engis >= self.numStartEngis then return BTNodeResult.SUCCESS end + + factory = self:getUnitsByClass(UnitClass.LAND_FACTORY, -1)[1]:toFactory() + + if #factory:getQueue() > 0 then return BTNodeResult.RUNNING end engiBuildId = self.unitClassMappings[UnitClass.CYBORG_ENGINEER + 1][self:getFaction() + 1] + numBuildEngis = self.numStartEngis - #engis - for i = 1, queueDiff do + for i = 1, numBuildEngis do factory:appendToQueue(self:getBuildableUnitSlotId(factory, engiBuildId)) end - engisStartNumBuilt = (#self:getUnitsByClass(UnitClass.CYBORG_ENGINEER, -1) == self.numStartEngis) - return (engisStartNumBuilt and BTNodeResult.SUCCESS or BTNodeResult.RUNNING) + return BTNodeResult.RUNNING end function Player:buildRefinery(arguments) - refinery = self:getUnitsByClass(UnitClass.REFINERY, 1)[1] - - if refinery then - print('cpu player: ') - print(self) - print('ref1: ') - print(refinery) - print('ref2: ') - print(refinery:toStructure()) - return (refinery:toStructure():isComplete() and BTNodeResult.SUCCESS or BTNodeResult.RUNNING) + refinery = self:getUnitsByClass(UnitClass.REFINERY, 1) + if #refinery > 0 then + return (refinery[1]:toStructure():isComplete() and BTNodeResult.SUCCESS or BTNodeResult.RUNNING) end buildPos = Map.getSingleton():getSpawnPoint(self:getSpawnPointId()):add(self.baseDir:mult(20)) @@ -183,10 +164,10 @@ end -- TODO optimize deposit position check function Player:buildExtractor(arguments) - extractor = self:getUnitsByClass(UnitClass.EXTRACTOR, -1)[1] + extractor = self:getUnitsByClass(UnitClass.EXTRACTOR, -1) - if extractor then - return (extractor:toStructure():isComplete() and BTNodeResult.SUCCESS or BTNodeResult.RUNNING) + if #extractor > 0 then + return (extractor[1]:toStructure():isComplete() and BTNodeResult.SUCCESS or BTNodeResult.RUNNING) end visibleDeposits = {} @@ -231,6 +212,7 @@ function Player:buildHarvester(arguments) end factory = self:getUnitsByClass(UnitClass.LAND_FACTORY, 1)[1] + if not factory then return BTNodeResult.FAILURE end factory = factory:toFactory() @@ -262,12 +244,12 @@ function Player:startHarvesting() return BTNodeResult.SUCCESS end -function Player:buildLandDefForce(arguments) - landFactory = self:getUnitsByClass(UnitClass.LAND_FACTORY, 1)[1] +function Player:buildLandForce(arguments) + landFactory = self:getUnitsByClass(UnitClass.LAND_FACTORY, 1) - if not landFactory then return BTNodeResult.FAILURE end + if #landFactory == 0 then return BTNodeResult.FAILURE end - landFactory = landFactory:toFactory() + landFactory = landFactory[1]:toFactory() numAttackableSpawnPoints = Map.getSingleton():getNumSpawnPoints() - 1 if #landFactory:getQueue() == 0 then @@ -282,19 +264,12 @@ function Player:buildLandDefForce(arguments) end end - print('about to...') - if enoughUnits then - print('here it comes...') - return BTNodeResult.SUCCESS - end - else - return BTNodeResult.RUNNING - end + if enoughUnits then return BTNodeResult.SUCCESS end + else return BTNodeResult.RUNNING end for i = 1, numAttackableSpawnPoints do for j = 1, #self.taskForceData do for k = 1, self.taskForceData[j].numUnits do - print('queueing...') unitId = self.unitClassMappings[self.taskForceData[j].class + 1][self:getFaction() + 1] slotId = self:getBuildableUnitSlotId(landFactory, unitId) landFactory:appendToQueue(slotId) @@ -316,119 +291,117 @@ function Player:buildPointDefenseRing(arguments) return BTNodeResult.RUNNING end -function Player:formTaskForce(arguments) - if self.taskForces[arguments.tfId] then return BTNodeResult.SUCCESS end +function Player:formTaskForces(arguments) + numSpawnPoints = Map.getSingleton():getNumSpawnPoints() + + if #self.taskForces == numSpawnPoints - 1 then return BTNodeResult.SUCCESS end unitGroups = {} for i = 1, #self.taskForceData do unitGroups[i] = self:getUnitsByClass(self.taskForceData[i].class, -1) - enoughGroupUnits = (#unitGroups[i] - (arguments.tfId - 1) * self.taskForceData[i].numUnits >= self.taskForceData[i].numUnits) + enoughGroupUnits = (#unitGroups[i] >= (numSpawnPoints - 1) * self.taskForceData[i].numUnits) if not enoughGroupUnits then return BTNodeResult.FAILURE end end - self.taskForces[arguments.tfId] = {units = {}, attacking = false, clearing = false, spawnPointId = arguments.spId} + tfId = 1 - for i = 1, #self.taskForceData do - unitGroup = {} + for i = 1, numSpawnPoints do + if i - 1 == self:getSpawnPointId() then goto continue end - subArrId = (arguments.tfId - 1) * self.taskForceData[i].numUnits + 1 - table.move(unitGroups[i], subArrId, subArrId + self.taskForceData[i].numUnits, 1, unitGroup) - table.move(unitGroup, 1, #unitGroup, #self.taskForces[arguments.tfId].units + 1, self.taskForces[arguments.tfId].units) + self.taskForces[tfId] = {units = {}, moving = false, arrived = false, spawnPointId = i - 1} + + for j = 1, #self.taskForceData do + subArrId = (tfId - 1) * self.taskForceData[j].numUnits + 1 + unitTbl = table.move( + unitGroups[j], + subArrId, + subArrId + self.taskForceData[j].numUnits, + #self.taskForces[tfId].units + 1, + self.taskForces[tfId].units + ) + self.taskForces[tfId].units = unitTbl + end + + tfId = tfId + 1 + ::continue:: end return BTNodeResult.SUCCESS end -function Player:clearNearEnemies(arguments) - taskForce = self.taskForces[arguments.tfId] - - if taskForce.clearing then return BTNodeResult.SUCCESS end - - self:selectUnits(taskForce.units) - - targetUnits = {} - players = Game.getSingleton():getPlayers(false) - - for i = 1, #players do - if players[i] == self or players[i]:getTeam() == self:getTeam() then goto continue end - - plUnits = players[i]:getUnits() - targetUnits = table.move(plUnits, 1, #plUnits, #targetUnits + 1, targetUnits) - - ::continue:: - end - - targUnit = nil - - for i = 1, #targetUnits do - for j = 1, #taskForce.units do - tfUnit = taskForce.units[j] - - if tfUnit:getPos():getDistanceFrom(targetUnits[i]:getPos()) < tfUnit:getLineOfSight() then - targUnit = targetUnits[i] - break - end - end - - if targUnit then break end - end - - if not taskForce.clearing and targUnit then - self:deselectUnits() - self:selectUnits(taskForce.units) - self:issueOrder(OrderType.MOVE, Vector3:new(0, 0, 0), {Target:new(targUnit:toGameObject(), Vector3:new(0, 0, 0))}, false) - - self.taskForces[arguments.tfId].clearing = true - elseif taskForce.clearing and not targUnit then - self.taskForces[arguments.tfId].clearing = false - end - - taskForce = self.taskForces[arguments.tfId] - return taskForce.clearing and BTNodeResult.RUNNING or BTNodeResult.SUCCESS +function Player:isLandRouteToSpawnpoint(arguments) + return BTNodeResult.SUCCESS end -function Player:attackSpawnPoint(arguments) - taskForce = self.taskForces[arguments.tfId] +function Player:occupySpawnPoint(arguments) + tfId = arguments.taskForceId - if taskForce.attacking then return BTNodeResult.RUNNING end + if #self.taskForces[tfId].units > 0 then + if self.taskForces[tfId].moving then + return BTNodeResult.RUNNING + elseif self.taskForces[tfId].arrived then + return BTNodeResult.SUCCESS + end + elseif #self.taskForces[tfId].units == 0 then + return BTNodeResult.FAILURE + end - self:deselectUnits() - self:selectUnits(taskForce.units) + spawnPoint = Map.getSingleton():getSpawnPoint(self.taskForces[tfId].spawnPointId) + minDist = 10 - for i = 1, #taskForce.units do taskForce.units[i]:setState(0) end + for i = 1, #self.taskForces[tfId].units do + if self.taskForces[tfId].units[i]:getPos():getDistanceFrom(spawnPoint) < minDist then + self.taskForces[tfId].moving = false + self.taskForces[tfId].arrived = true + return BTNodeResult.SUCCESS + end + end - enemySpawnPoint = Map.getSingleton():getSpawnPoint(taskForce.spawnPointId) - self:issueOrder(OrderType.MOVE, Vector3:new(0, 0, 0), {Target:new(nil, enemySpawnPoint)}, false) - self.taskForces[arguments.tfId].attacking = true + if not self.taskForces[tfId].moving then + self.taskForces[tfId].moving = true + self:deselectUnits() + self:selectUnits(self.taskForces[tfId].units) + self:issueOrder(OrderType.MOVE, Vector3:new(0, 0, 0), {Target:new(nil, spawnPoint)}, false) + end return BTNodeResult.RUNNING end ---[[ -function Player:generateTaskforceActions() +function Player:generateTaskForceActions() + children = {} + tfId = 1 numSpawnPoints = Map.getSingleton():getNumSpawnPoints() - actionsTable = {} - taskForceId = 1 for i = 1, numSpawnPoints do if i - 1 == self:getSpawnPointId() then goto continue end - actionsTable[#actionsTable + 1] = { + arguments = {taskForceId = tfId} + children[#children + 1] = { type = BTNodeType.SEQUENCE, children = { - {type = BTNodeType.FUNCTION, func = 'formTaskForce', args = {tfId = taskForceId, spId = i - 1}}, - {type = BTNodeType.FUNCTION, func = 'clearNearEnemies', args = {tfId = taskForceId}}, - {type = BTNodeType.FUNCTION, func = 'attackSpawnPoint', args = {tfId = taskForceId}}, + { + type = BTNodeType.SELECTOR, + children = { + {type = BTNodeType.FUNCTION, func = 'isLandRouteToSpawnpoint', args = arguments}, + { + type = BTNodeType.SEQUENCE, + children = { + {type = BTNodeType.FUNCTION, func = 'boardTransports', args = arguments}, + {type = BTNodeType.FUNCTION, func = 'moveTransports', args = arguments}, + {type = BTNodeType.FUNCTION, func = 'unloadTransports', args = arguments}, + } + } + } + }, + {type = BTNodeType.FUNCTION, func = 'occupySpawnPoint', args = arguments} } } - taskForceId = taskForceId + 1 - + tfId = tfId + 1 ::continue:: end - return actionsTable + return children end -]]-- diff --git a/Assets/Scripts/Core/playerInit.lua b/Assets/Scripts/Core/playerInit.lua index 7ef2bcb..7ce1f3f 100644 --- a/Assets/Scripts/Core/playerInit.lua +++ b/Assets/Scripts/Core/playerInit.lua @@ -19,56 +19,23 @@ for i = 1, #game.cpuPlayers do } } }, - { - type = BTNodeType.SEQUENCE, - children = { - {type = BTNodeType.FUNCTION, func = 'buildLandDefForce'}, - {type = BTNodeType.FUNCTION, func = 'buildPointDefenseRing'} - } - }, - --[[ - ]]-- - --[[ - { - type = BTNodeType.SELECTOR, - children = { - {type = BTNodeType.FUNCTION, func = 'hasLandRouteToAllSpawnPoints'}, - { - type = BTNodeType.SEQUENCE, - children = { - {type = BTNodeType.FUNCTION, func = 'buildNavalFactory'}, - {type = BTNodeType.FUNCTION, func = 'buildNavalForce'} - } - }, - } - }, - { - type = BTNodeType.SELECTOR, - children = { - {type = BTNodeType.FUNCTION, func = 'hasLandRouteToSpawnPoint'}, - { - type = BTNodeType.SEQUENCE, - children = { - {type = BTNodeType.FUNCTION, func = 'boardTransports'}, - {type = BTNodeType.FUNCTION, func = 'moveTransports'} - {type = BTNodeType.FUNCTION, func = 'unloadTransports'} - } - }, - } - }, - {type = BTNodeType.FUNCTION, func = 'occupySpawnpoint'}, { type = BTNodeType.PARALLEL, children = { - {type = BTNodeType.FUNCTION, func = 'buildTaskForces'}, { - type = BTNodeType.PARALLEL, - numMinSuccesses = 2, - children = game.cpuPlayers[i]:generateTaskforceActions() + type = BTNodeType.SEQUENCE, + children = { + {type = BTNodeType.FUNCTION, func = 'buildLandForce'}, + {type = BTNodeType.FUNCTION, func = 'formTaskForces'}, + } }, + {type = BTNodeType.FUNCTION, func = 'buildPointDefenseRing'}, } }, - ]]-- + { + type = BTNodeType.PARALLEL, + children = game.cpuPlayers[i]:generateTaskForceActions() + } } } end diff --git a/Assets/Scripts/Gui/playerSelection.lua b/Assets/Scripts/Gui/playerSelection.lua index 20bc65d..397ea42 100644 --- a/Assets/Scripts/Gui/playerSelection.lua +++ b/Assets/Scripts/Gui/playerSelection.lua @@ -4,7 +4,7 @@ Size = {x = 150, y = 20} gap = 10 gapFactor = 3 -factions = {'None', "America inc.", "European Republic", "Asian Co-Prosperity Sphere"} +factions = {'None', 'Asian Co-Prosperity Sphere', 'America inc.', 'European Republic'} difficulties = {"Easy", "Medium", "Hard"} colors = {'White', 'Black', 'Red'} teams = {'0', '1', '2', '3', '4'} diff --git a/game.cpp b/game.cpp index d9cb555..a99ab19 100644 --- a/game.cpp +++ b/game.cpp @@ -6,6 +6,7 @@ #include "inGameAppState.h" #include "concreteGuiManager.h" #include "fxManager.h" +#include "factory.h" #include "defConfigs.h" #include @@ -43,13 +44,11 @@ namespace battleship{ } void Game::update(){ - //updateLuaPlayers(false); sol::state_view SOL_LUA_VIEW = generateView(); 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.script("tbl = " + plStr + ":getUnitsByClass(UnitClass.CYBORG_ENGINEER, -1)"); } int numPlayersWithUnits = 0; diff --git a/player.cpp b/player.cpp index 18cfd97..466de8c 100755 --- a/player.cpp +++ b/player.cpp @@ -116,7 +116,7 @@ namespace battleship{ break; } - if(!u->isVehicle() && ((Structure*)u)->getBuildStatus() < 100){ + if(!u->isVehicle() && ((Structure*)u)->isComplete()){ structBuilt = false; break; } From 86346c0e0222905af24bc61e70cadce2103470f8 Mon Sep 17 00:00:00 2001 From: devZoGok Date: Sun, 15 Feb 2026 18:18:38 +0200 Subject: [PATCH 03/22] added missing file --- Assets/Scripts/Gui/activeGameState.lua | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/Assets/Scripts/Gui/activeGameState.lua b/Assets/Scripts/Gui/activeGameState.lua index c633e9a..99ed904 100644 --- a/Assets/Scripts/Gui/activeGameState.lua +++ b/Assets/Scripts/Gui/activeGameState.lua @@ -11,11 +11,11 @@ function generateFactionMusic(factionId) factionTracks = {} if factionId == 0 then - factionTracks = americanTracks - elseif factionId == 1 then - factionTracks = europeanTracks - elseif factionId == 2 then factionTracks = asianTracks + elseif factionId == 1 then + factionTracks = americanTracks + elseif factionId == 2 then + factionTracks = europeanTracks end return { @@ -105,17 +105,6 @@ gui = { size = {x = s, y = s}, imagePath = resIconBasePath .. 'research.png', }, - { - guiType = GuiType.TEXT, - name = 'depth', - text = '', - pos = {x = 0, y = 400, z = 0}, - scale = {x = 0, y = 1}, - font = 'batang.ttf', - fontFirstChar = 0, - fontLastChar = 256, - color = {x = 1, y = 1, z = 1, w = 1} - }, { guiType = GuiType.BUTTON, buttonType = ButtonType.PLAYER_TRADE, From 4709581fcd64497193d9c6a0e8e81c0d4f26fcdf Mon Sep 17 00:00:00 2001 From: devZoGok Date: Sun, 15 Feb 2026 18:20:27 +0200 Subject: [PATCH 04/22] buildings cannot be placed outside of the map --- gameObjectFrameController.cpp | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/gameObjectFrameController.cpp b/gameObjectFrameController.cpp index 313e1ed..5fa6490 100644 --- a/gameObjectFrameController.cpp +++ b/gameObjectFrameController.cpp @@ -75,13 +75,25 @@ namespace battleship{ //TODO fix which unit frames light green or red void GameObjectFrameController::checkPlacement(GameObjectFrame &s){ + s.status = GameObjectFrame::PLACEABLE; + Map *map = Map::getSingleton(); + Vector3 mapSize = map->getMapSize(); + int dirMult[][2]{{1, 1}, {1, -1}, {-1, 1}, {-1, -1}}; + + 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()); + + if(!(fabs(cornerPos.x) <= .5 * mapSize.x && fabs(cornerPos.z) <=.5 * mapSize.z)){ + s.status = GameObjectFrame::NOT_PLACEABLE; + return; + } + } + MeshData meshData = map->getNodeParent()->getChild(0)->getMesh(0)->getMeshBase(); MeshData::Vertex *verts = meshData.vertices; int numVerts = 3 * meshData.numTris; - s.status = GameObjectFrame::PLACEABLE; - if(s.getMaxUnevenness() > 0) for(int i = 0; i < numVerts; i++){ float diffX = fabs(s.getPos().x - verts[i].pos->x); @@ -90,7 +102,7 @@ namespace battleship{ if(diffX < 0.5 * s.getWidth() && diffZ < 0.5 * s.getLength() && diffY > s.getMaxUnevenness()){ s.status = GameObjectFrame::NOT_PLACEABLE; - break; + return; } } @@ -132,7 +144,7 @@ namespace battleship{ if(intersects){ s.status = GameObjectFrame::NOT_PLACEABLE; - break; + return; } } } From 03f9f9cc2e632066c2f5774bee5f26abde168e37 Mon Sep 17 00:00:00 2001 From: devZoGok Date: Sun, 15 Feb 2026 18:23:09 +0200 Subject: [PATCH 05/22] calculating heuristics in a function ending pathpoint generation early for empty paths --- pathfinder.cpp | 9 +++++++++ pathfinder.h | 1 + vehicle.cpp | 8 ++++---- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/pathfinder.cpp b/pathfinder.cpp index 0d52d0d..c0b3924 100644 --- a/pathfinder.cpp +++ b/pathfinder.cpp @@ -16,6 +16,15 @@ namespace battleship{ return pathfinder; } + vector Pathfinder::calcHeuristics(vector &cells, int dest){ + vector heuristics; + + for(Map::Cell &cell : cells) + heuristics.push_back(145 * (cells[dest].pos.getDistanceFrom(cell.pos))); + + return heuristics; + } + vector Pathfinder::findPath(vector &cells, vector &heuristics, int source, int dest, Vehicle *vehicle){ const int size = cells.size(); u32 *distances = new u32[size]; diff --git a/pathfinder.h b/pathfinder.h index 13f38af..7a63c33 100644 --- a/pathfinder.h +++ b/pathfinder.h @@ -13,6 +13,7 @@ namespace battleship{ class Pathfinder{ public: static Pathfinder* getSingleton(); + std::vector calcHeuristics(std::vector&, int); std::vector findPath(std::vector&, std::vector&, int, int, Vehicle* = nullptr); inline vb01::u32 getImpassibleNodeVal(){return impassibleNodeVal;} inline void setImpassibleNodeVal(vb01::u32 val){this->impassibleNodeVal = val;} diff --git a/vehicle.cpp b/vehicle.cpp index 9e8f074..d61f979 100644 --- a/vehicle.cpp +++ b/vehicle.cpp @@ -343,12 +343,12 @@ namespace battleship{ } } - vector heuristics; + Pathfinder *pf = Pathfinder::getSingleton(); + vector heuristics = pf->calcHeuristics(cells, dest); + vector path = pf->findPath(cells, heuristics, source, dest, this); - for(Map::Cell &cell : cells) - heuristics.push_back(145 * (cells[dest].pos.getDistanceFrom(cell.pos))); + if(path.empty()) return; - vector path = Pathfinder::getSingleton()->findPath(cells, heuristics, source, dest, this); path.erase(path.begin()); bool pathTruncated = false; From 268ea14f4a77db64d976acbd60ebdb6ac9f3bc74 Mon Sep 17 00:00:00 2001 From: devZoGok Date: Mon, 16 Feb 2026 12:14:43 +0200 Subject: [PATCH 06/22] refactored path point generation exposed Pathfinder to Lua --- Assets/Scripts/Core/player.lua | 21 +++++++++++++++- gameManager.cpp | 17 +++++++++++++ map.h | 1 + pathfinder.cpp | 3 +++ vehicle.cpp | 46 +++++++++++++++++++++++----------- vehicle.h | 2 ++ 6 files changed, 75 insertions(+), 15 deletions(-) diff --git a/Assets/Scripts/Core/player.lua b/Assets/Scripts/Core/player.lua index 0af5c47..475c33e 100644 --- a/Assets/Scripts/Core/player.lua +++ b/Assets/Scripts/Core/player.lua @@ -331,8 +331,27 @@ function Player:formTaskForces(arguments) return BTNodeResult.SUCCESS end +--TODO remove heuristics +function Player:landRouteToSpawnpoint(mapPoint) + map = Map.getSingleton() + dest = map:getCellId(mapPoint, false) + 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, nil) + + for i = 1, #path do + if cells[path[i]].type == 1 then return false end + end + + return true +end + function Player:isLandRouteToSpawnpoint(arguments) - return BTNodeResult.SUCCESS + mapPoint = Map.getSingleton():getSpawnPoint(self.taskForces[arguments.taskForceId].spawnPointId) + return (self:landRouteToSpawnpoint(mapPoint) and BTNodeResult.SUCCESS or BTNodeResult.FAILURE) end function Player:occupySpawnPoint(arguments) diff --git a/gameManager.cpp b/gameManager.cpp index 4b476fd..008de60 100755 --- a/gameManager.cpp +++ b/gameManager.cpp @@ -19,6 +19,7 @@ #include "engineer.h" #include "destructable.h" #include "pointDefense.h" +#include "pathfinder.h" #include "resourceDeposit.h" using namespace std; @@ -165,13 +166,29 @@ namespace battleship{ "multVec", [](Quaternion q, Vector3 v){return q * v;} ); + SOL_LUA_STATE.new_usertype( + "Cell", + "pos", &Map::Cell::pos, + "type", &Map::Cell::type + ); + SOL_LUA_STATE.new_usertype( "Map", "getSingleton", &Map::getSingleton, + "getCell", &Map::getCell, + "getCells", &Map::getCells, + "getCellId", &Map::getCellId, "getMapSize", &Map::getMapSize, "getSpawnPoint", &Map::getSpawnPoint, "getNumSpawnPoints", &Map::getNumSpawnPoints ); + + SOL_LUA_STATE.new_usertype( + "Pathfinder", + "getSingleton", &Pathfinder::getSingleton, + "calcHeuristics", &Pathfinder::calcHeuristics, + "findPath", &Pathfinder::findPath + ); } void GameManager::initLua(string gameDir){ diff --git a/map.h b/map.h index 700ac67..c9d0b4a 100755 --- a/map.h +++ b/map.h @@ -83,6 +83,7 @@ namespace battleship{ std::vector getSurroundingCells(vb01::Vector3, int); void blockCells(Unit*); void unblockCells(Unit*); + inline Map::Cell getCell(int i){return cells[i];} inline std::string getMapName(){return mapName;} inline vb01::Node* getNodeParent(){return terrainNode;} inline vb01::Vector3 getCellSize(){return CELL_SIZE;} diff --git a/pathfinder.cpp b/pathfinder.cpp index c0b3924..b693387 100644 --- a/pathfinder.cpp +++ b/pathfinder.cpp @@ -26,6 +26,9 @@ namespace battleship{ } vector Pathfinder::findPath(vector &cells, vector &heuristics, int source, int dest, Vehicle *vehicle){ + if(heuristics.empty() || (heuristics.size() == 1 && heuristics[0] == 0.0)) + heuristics = calcHeuristics(cells, dest); + const int size = cells.size(); u32 *distances = new u32[size]; vector *paths = new vector[size]; diff --git a/vehicle.cpp b/vehicle.cpp index d61f979..ded8ccc 100644 --- a/vehicle.cpp +++ b/vehicle.cpp @@ -316,22 +316,20 @@ namespace battleship{ //TODO allow ships to attack land targets and vice versa //TODO recursively search for vacant dest cell neibourghss - void Vehicle::preparePathpoints(Order &order, Vector3 destPos, bool appendDestPos){ - removeAllPathpoints(); - + bool Vehicle::canReachTarget(Vector3 destPos, int &source, int &dest){ Map *map = Map::getSingleton(); vector &cells = map->getCells(); - int source = map->getCellId(pos); + 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; + if(type != UnitType::HOVER && !(waterVehCanMove || landVehCanMove)) return false; - int dest = map->getCellId(destPos); + dest = map->getCellId(destPos); - if(type != UnitType::UNDERWATER && type == UnitType::SEA_LEVEL && fabs(destPos.y - cells[dest].pos.y) > .1) return; + if(type != UnitType::UNDERWATER && type == UnitType::SEA_LEVEL && fabs(destPos.y - cells[dest].pos.y) > .1) return false; if(cells[dest].blockedBy){ vector surrCellIds = map->getSurroundingCells(cells[dest].pos, 1); @@ -343,14 +341,13 @@ namespace battleship{ } } - Pathfinder *pf = Pathfinder::getSingleton(); - vector heuristics = pf->calcHeuristics(cells, dest); - vector path = pf->findPath(cells, heuristics, source, dest, this); + return true; + } - if(path.empty()) return; - - path.erase(path.begin()); + 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 = 0; 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)){ @@ -361,7 +358,7 @@ namespace battleship{ break; } else if(order.type == Order::TYPE::GARRISON && type == UnitType::LAND && cells[path[i]].type != Map::Cell::LAND && path.size() - 1 != i) - return; + return false; } for(int p : path) addPathpoint(cells[p].pos); @@ -410,6 +407,27 @@ namespace battleship{ if(appendDestPos && !pathTruncated) addPathpoint(destPos); + + return true; + } + + void Vehicle::preparePathpoints(Order &order, Vector3 destPos, bool appendDestPos){ + removeAllPathpoints(); + + vector &cells = Map::getSingleton()->getCells(); + int source, dest; + + if(!canReachTarget(destPos, source, dest)) return; + + vector heurs; + Pathfinder *pf = Pathfinder::getSingleton(); + vector path = pf->findPath(cells, heurs, source, dest, this); + + if(path.empty()) return; + + path.erase(path.begin()); + + if(!truncatePath(order, path, destPos, appendDestPos)) return; } void Vehicle::removePathpoint(int i){ diff --git a/vehicle.h b/vehicle.h index 056199f..9b5bf64 100644 --- a/vehicle.h +++ b/vehicle.h @@ -25,6 +25,8 @@ namespace battleship{ vb01::s64 lastBuildTime = 0; inline int getNextPatrolPointId(int numPoints) {return patrolPointId == numPoints - 1 ? 0 : patrolPointId + 1;} + bool canReachTarget(vb01::Vector3, int&, int&); + bool truncatePath(Order&, std::vector&, vb01::Vector3, bool); void startCurrentOrder(); bool validateGarrisonOrder(Order); void enterGarrisonable(); From 580659aad580d973ce57c285e20d2430778dca0f Mon Sep 17 00:00:00 2001 From: devZoGok Date: Mon, 16 Feb 2026 16:38:04 +0200 Subject: [PATCH 07/22] refactored visible game object detection checking whether the game object frame is on the right type of a cell --- activeGameState.cpp | 72 +++++------------------------------ activeGameState.h | 1 - gameObjectFrameController.cpp | 21 +++++++++- player.cpp | 33 ++++++++++++++++ player.h | 2 + 5 files changed, 63 insertions(+), 66 deletions(-) diff --git a/activeGameState.cpp b/activeGameState.cpp index 21b569e..d58c76d 100755 --- a/activeGameState.cpp +++ b/activeGameState.cpp @@ -476,73 +476,19 @@ namespace battleship{ return (numAboveEdges > 0 && numBelowEdges > 0); } - //TODO fix fog of war for hostile units void ActiveGameState::renderUnits() { - ConcreteGuiManager *guiManager = ConcreteGuiManager::getSingleton(); - vector listboxes{}; - vector checkboxes{}; - vector sliders{}; - vector textboxes{}; - vector guiRects = guiManager->getGuiRectangles(); - vector texts{ - guiManager->getText("depth"), - guiManager->getText("refineds"), - guiManager->getText("wealth"), - guiManager->getText("research") - }; + vector friendlyUnits = mainPlayer->getFriendlyUnits(); - vector units; + for (Player *p : Game::getSingleton()->getPlayers(true)){ + vector units = p->getUnits(); - for (Player *p : Game::getSingleton()->getPlayers(true)) - for (Unit *u : p->getUnits()) - units.push_back(u); - - vector selUnits = mainPlayer->getSelectedUnits(); - - for (Unit *u : units) { - if(u->isVehicle() && ((Vehicle*)u)->getGarrisonable()) continue; - - Node *model = u->getModel(); - - if (u->getPlayer()->getTeam() == mainPlayer->getTeam()){ - if(!u->getLosLightNode()) u->initLosLight(); - - model->setVisible(true); - } - else{ - if(u->getLosLightNode()) u->destroyLosLight(); - - model->setVisible(false); - - for (int i = 0; i < units.size(); i++) - if(units[i]->getPlayer() == mainPlayer){ - Vector3 obsUnitPos = units[i]->getPos(); - obsUnitPos.y = 0; - - Vector3 compUnitPos = u->getPos(); - compUnitPos.y = 0; - float dist = compUnitPos.getDistanceFrom(obsUnitPos); - - if(dist <= units[i]->getLineOfSight()){ - model->setVisible(true); - break; - } - } + for (Unit *u : units) { + if(u->isVehicle() && ((Vehicle*)u)->getGarrisonable()) continue; + + bool unitVisible = mainPlayer->isObjectVisible((GameObject*)u, friendlyUnits); + u->getModel()->setVisible(unitVisible); } - } - } - - //TODO improve this for greater accuracy - bool ActiveGameState::isInLineOfSight(Vector3 center, float radius, Unit *u) { - bool inside = false; - - for (int i = 0; i < 4 && !inside; i++) - if (center.getDistanceFrom(u->getCorner(i)) <= radius){ - inside = true; - break; - } - - return inside; + } } void ActiveGameState::updateDragBox() { diff --git a/activeGameState.h b/activeGameState.h index 0778be1..fef0dcb 100755 --- a/activeGameState.h +++ b/activeGameState.h @@ -73,7 +73,6 @@ namespace battleship{ void updateStructureFrames(); void castRayToTerrain(); void issueOrder(Order::TYPE, std::vector, bool); - bool isInLineOfSight(vb01::Vector3, float, Unit*); void enableUnitState(Unit::State); void addUnitGui(); inline bool canSelectHoveredOnGameObj(){return gameObjHoveredOn && gameObjHoveredOn->isSelectable() && gameObjHoveredOn->getPlayer() == mainPlayer;} diff --git a/gameObjectFrameController.cpp b/gameObjectFrameController.cpp index 5fa6490..3bc5060 100644 --- a/gameObjectFrameController.cpp +++ b/gameObjectFrameController.cpp @@ -73,18 +73,35 @@ namespace battleship{ } } - //TODO fix which unit frames light green or red + //TODO include checking if frame is outside of line of sight void GameObjectFrameController::checkPlacement(GameObjectFrame &s){ s.status = GameObjectFrame::PLACEABLE; Map *map = Map::getSingleton(); Vector3 mapSize = map->getMapSize(); + Vector3 cellSize = map->getCellSize(); int dirMult[][2]{{1, 1}, {1, -1}, {-1, 1}, {-1, -1}}; + vector &cells = map->getCells(); + + sol::table tbl = generateView()["units"][s.getId() + 1]; + UnitType ut = (UnitType)tbl["unitType"]; + + //vector friendlyUnits = s.getPlayer()->getFriendlyUnits(); 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()); - if(!(fabs(cornerPos.x) <= .5 * mapSize.x && fabs(cornerPos.z) <=.5 * mapSize.z)){ + if(!(fabs(cornerPos.x) <= .5 * mapSize.x && fabs(cornerPos.z) <= .5 * mapSize.z)){ + s.status = GameObjectFrame::NOT_PLACEABLE; + return; + } + + Map::Cell cell = cells[map->getCellId(cornerPos, false)]; + 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; return; } diff --git a/player.cpp b/player.cpp index 466de8c..b705407 100755 --- a/player.cpp +++ b/player.cpp @@ -286,4 +286,37 @@ namespace battleship{ } } } + + vector Player::getFriendlyUnits(bool includeOwn){ + vector friendlyUnits; + + for (Player *pl : Game::getSingleton()->getPlayers(true)) + for (Unit *u : pl->getUnits()) + if((includeOwn && pl == this) || pl->getTeam() == getTeam()) + friendlyUnits.push_back(u); + + return friendlyUnits; + } + + //TODO improve this for greater accuracy + bool Player::isObjectVisible(GameObject *object, std::vector friendlyUnits) { + Player *objPlayer = object->getPlayer(); + + for(Unit *friendlyUnit : friendlyUnits){ + if(objPlayer == this || objPlayer->getTeam() == getTeam()) + return true; + + Vector3 obsUnitPos = object->getPos(); + obsUnitPos.y = 0; + + Vector3 compUnitPos = friendlyUnit->getPos(); + compUnitPos.y = 0; + float dist = compUnitPos.getDistanceFrom(obsUnitPos); + + if(dist <= friendlyUnit->getLineOfSight()) + return true; + } + + return false; + } } diff --git a/player.h b/player.h index d2f68b8..1ccce17 100755 --- a/player.h +++ b/player.h @@ -39,6 +39,8 @@ namespace battleship{ void deselectUnit(Unit*); std::vector getDestructables(); void updateGameStats(Unit*); + std::vector getFriendlyUnits(bool = true); + bool isObjectVisible(GameObject*, std::vector); inline int getResource(ResourceType rt){return resources[(int)rt];} inline void updateResource(ResourceType rt, int amount, bool add){resources[(int)rt] = (add ? resources[(int)rt] + amount : amount);} inline Trader* getTrader(){return trader;} From 35b22318c4b043138870b13683f1dd771d76f8db Mon Sep 17 00:00:00 2001 From: devZoGok Date: Thu, 19 Feb 2026 20:52:18 +0200 Subject: [PATCH 08/22] expanded checks to determine if a game object frame is placeable on a given spot --- Assets/Scripts/Core/player.lua | 133 +++++++++++++++++++++++------ Assets/Scripts/Core/playerInit.lua | 2 + activeGameState.cpp | 2 +- buildButton.cpp | 2 +- gameManager.cpp | 22 +++++ gameObject.cpp | 2 +- gameObjectFrame.h | 4 +- gameObjectFrameController.cpp | 17 +++- gameObjectListbox.cpp | 2 +- player.cpp | 7 +- 10 files changed, 154 insertions(+), 39 deletions(-) diff --git a/Assets/Scripts/Core/player.lua b/Assets/Scripts/Core/player.lua index 475c33e..1d4c45b 100644 --- a/Assets/Scripts/Core/player.lua +++ b/Assets/Scripts/Core/player.lua @@ -1,5 +1,6 @@ Player.numStartEngis = 3 -Player.baseDir = nil +Player.baseQuat = nil +Player.baseRadius = 180 Player.numDefWarMechs = 1 Player.behaviour = {} Player.pointDefenseTrans = {} @@ -37,7 +38,7 @@ Player.taskForceData = { Player.taskForces = {} -function Player:initBaseDir() +function Player:initBaseQuat() map = Map.getSingleton() sp = map:getSpawnPoint(self:getSpawnPointId()) minDistId = nil @@ -54,45 +55,109 @@ function Player:initBaseDir() ::continue:: end - self.baseDir = map:getSpawnPoint(minDistId):subtr(sp):norm() + dir = map:getSpawnPoint(minDistId):subtr(sp):norm() + left = (Vector3:new(1, 0, 0):getAngleBetween(dir) < .5 * math.pi) + angle = dir:getAngleBetween(Vector3:new(0, 0, 1)) * (left and 1 or -1) + + self.baseQuat = Quaternion:new(angle, Vector3:new(0, 1, 0)) end function Player:initPdSpots() - numPd = 6 - radius = 100 - map = Map.getSingleton() - mapSize = map:getMapSize() + pdId = self.unitClassMappings[UnitClass.POINT_DEFENSE + 1][self:getFaction() + 1] + pdFrame = GameObjectFrame:new(pdId, 0, self, nil, Vector3:new(0, 0, 0), Quaternion:new(1, 0, 0, 0)) + baseDir = self.baseQuat:multVec(Vector3:new(0, 0, 1)) + frameCtrl = GameObjectFrameController.getSingleton() + + numPd = 12 for i = 1, numPd do angle = (i - 1) * 2 * math.pi / numPd - rotDir = Quaternion:new(angle, Vector3:new(0, 1, 0)):multVec(self.baseDir) - pdPos = map:getSpawnPoint(self:getSpawnPointId()):add(rotDir:norm():mult(radius)) + rotDir = Quaternion:new(angle, Vector3:new(0, 1, 0)):multVec(baseDir) + pdPos = Map.getSingleton():getSpawnPoint(self:getSpawnPointId()):add(rotDir:norm():mult(self.baseRadius)) + pdFrame:placeAt(pdPos) - if math.abs(pdPos.x) < .5 * mapSize.x and math.abs(pdPos.z) < .5 * mapSize.z then + frameCtrl:checkPlacement(pdFrame) + + if pdFrame.status == 0 then self.pointDefenseTrans[#self.pointDefenseTrans + 1] = {pos = pdPos, angle = angle} end end + + pdFrame:destroy() +end + +--TODO replace this method with more robust checks to determine the possibility of building on a given spot +function Player:updatePdSpots() + pdId = self.unitClassMappings[UnitClass.POINT_DEFENSE + 1][self:getFaction() + 1] + pdFrame = GameObjectFrame:new(pdId, 0, self, nil, Vector3:new(0, 0, 0), Quaternion:new(1, 0, 0, 0)) + + gofCtrl = GameObjectFrameController.getSingleton() + delPdTrans = {} + + for i = 1, #self.pointDefenseTrans do + pdFrame:placeAt(self.pointDefenseTrans[i].pos) + gofCtrl:checkPlacement(pdFrame) + + if pdFrame.status == 1 then + table.insert(delPdTrans, i) + end + end + + for i = #delPdTrans, 1, -1 do + table.remove(self.pointDefenseTrans, i) + end + + pdFrame:destroy() end function Player:init() - self:initBaseDir() + self:initBaseQuat() self:initPdSpots() end +function Player:findSuitableSpot(buildingId, startPos) + structFrame = GameObjectFrame:new(buildingId, 0, self, nil, startPos, self.baseQuat) + numYPos = math.floor(2 * self.baseRadius / structFrame:getLength()) + numXPos = math.floor(2 * self.baseRadius / structFrame:getWidth()) + + baseDir = self.baseQuat:multVec(Vector3:new(0, 0, 1)) + baseLeft = self.baseQuat:multVec(Vector3:new(1, 0, 0)) + point = Map.getSingleton():getSpawnPoint(self:getSpawnPointId()):add(baseDir:mult(self.baseRadius)):add(baseLeft:mult(self.baseRadius)) + + frameCtrl = GameObjectFrameController.getSingleton() + frameCtrl:checkPlacement(structFrame) + + if structFrame.status == 1 then + for i = 1, numXPos do + point = point:add(baseLeft:neg():mult(i - 1)) + structFrame:placeAt(point) + frameCtrl:checkPlacement(structFrame) + + if structFrame.status == 0 then + structFrame:destroy() + return point + end + end + else + structFrame:destroy() + return startPos + end + + structFrame:destroy() + return nil +end + function Player:buildStructure(engineer, buildingId, buildPos, buildAngle) if not engineer then return BTNodeResult.FAILURE end + print(buildAngle) self:deselectUnits() self:selectUnits({engineer}) - building = GameObjectFactory.createUnit(self, buildingId, buildPos, Quaternion:new(buildAngle, Vector3:new(0, 1, 0)), 0) + building = GameObjectFactory.createUnit(self, buildingId, buildPos, Quaternion:new(-buildAngle, Vector3:new(0, 1, 0)), 0) self:issueOrder(OrderType.BUILD, Vector3:new(0, 0, 0), {Target:new(building:toGameObject(), Vector3:new(0, 0, 0))}, false) end -function Player:findSuitableSpot() - return Vector3:new(0, 0, 0) -end - function Player:findIdleEngineer() engineers = self:getUnitsByClass(UnitClass.CYBORG_ENGINEER, -1) @@ -115,7 +180,6 @@ function Player:getBuildableUnitSlotId(buildingUnit, buildableUnitId) return nil end ---TODO simplify building construction function Player:buildLandFactory(arguments) factory = self:getUnitsByClass(UnitClass.LAND_FACTORY, 1)[1] @@ -124,10 +188,14 @@ function Player:buildLandFactory(arguments) end 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()) - self:buildStructure(self:findIdleEngineer(), factId, sp, angle) + pos = self:findSuitableSpot(factId, sp:add(rd)) - return BTNodeResult.RUNNING + if pos then + self:buildStructure(self:findIdleEngineer(), factId, pos, self.baseQuat:getAngle()) + return BTNodeResult.RUNNING + else return BTNodeResult.FAILURE end end function Player:trainEngineers(arguments) @@ -150,16 +218,25 @@ function Player:trainEngineers(arguments) end function Player:buildRefinery(arguments) - refinery = self:getUnitsByClass(UnitClass.REFINERY, 1) - if #refinery > 0 then - return (refinery[1]:toStructure():isComplete() and BTNodeResult.SUCCESS or BTNodeResult.RUNNING) + refinery = self:getUnitsByClass(UnitClass.REFINERY, 1)[1] + if refinery then + return (refinery:toStructure():isComplete() and BTNodeResult.SUCCESS or BTNodeResult.RUNNING) end - buildPos = Map.getSingleton():getSpawnPoint(self:getSpawnPointId()):add(self.baseDir:mult(20)) - factId = self.unitClassMappings[UnitClass.REFINERY + 1][self:getFaction() + 1] - self:buildStructure(self:findIdleEngineer(), factId, buildPos, -.0) + refId = self.unitClassMappings[UnitClass.REFINERY + 1][self:getFaction() + 1] + factId = self.unitClassMappings[UnitClass.LAND_FACTORY + 1][self:getFaction() + 1] - return BTNodeResult.RUNNING + rd = self.baseQuat:multVec(Vector3:new(0, 0, 1)):mult(self.baseRadius - .5 * (units[factId + 1].size.z + units[refId + 1].size.z)) + sp = Map.getSingleton():getSpawnPoint(self:getSpawnPointId()) + buildPos = sp:add(rd) + + pos = self:findSuitableSpot(refId, buildPos) + + if pos then + buildAngle = (self.baseQuat:getAngle() + math.pi) + self:buildStructure(self:findIdleEngineer(), refId, buildPos, buildAngle) + return BTNodeResult.RUNNING + else return BTNodeResult.FAILURE end end -- TODO optimize deposit position check @@ -281,8 +358,10 @@ function Player:buildLandForce(arguments) end function Player:buildPointDefenseRing(arguments) + self:updatePdSpots() pointDefs = self:getUnitsByClass(UnitClass.POINT_DEFENSE, -1) - if #pointDefs == #self.pointDefenseTrans then return BTNodeResult.SUCCESS end + + if #pointDefs >= #self.pointDefenseTrans then return BTNodeResult.SUCCESS end pdId = self.unitClassMappings[UnitClass.POINT_DEFENSE + 1][self:getFaction() + 1] pdTrans = self.pointDefenseTrans[#pointDefs + 1] diff --git a/Assets/Scripts/Core/playerInit.lua b/Assets/Scripts/Core/playerInit.lua index 7ce1f3f..f25b8df 100644 --- a/Assets/Scripts/Core/playerInit.lua +++ b/Assets/Scripts/Core/playerInit.lua @@ -36,6 +36,8 @@ for i = 1, #game.cpuPlayers do type = BTNodeType.PARALLEL, children = game.cpuPlayers[i]:generateTaskForceActions() } + --[[ + ]]-- } } end diff --git a/activeGameState.cpp b/activeGameState.cpp index d58c76d..a8c2fd7 100755 --- a/activeGameState.cpp +++ b/activeGameState.cpp @@ -311,7 +311,7 @@ namespace battleship{ if(!buildableStructSelected) for(int i = 0; i < selectedUnits.size(); i++) - fc->addGameObjectFrame(GameObjectFrame(selectedUnits[i]->getId(), GameObject::Type::UNIT, selectedUnits[i], targets[0].pos)); + fc->addGameObjectFrame(GameObjectFrame(selectedUnits[i]->getId(), GameObject::Type::UNIT, mainPlayer, selectedUnits[i], targets[0].pos)); selectingDestOrient = true; fc->setRotating(true); diff --git a/buildButton.cpp b/buildButton.cpp index 6146b6f..af69db7 100644 --- a/buildButton.cpp +++ b/buildButton.cpp @@ -29,7 +29,7 @@ namespace battleship{ if(builder->getBuildableUnit(slotId).buildable){ GameObjectFrameController *ufCtr = GameObjectFrameController::getSingleton(); - ufCtr->addGameObjectFrame(GameObjectFrame(builder->getBuildableUnit(slotId).id, GameObject::Type::UNIT)); + ufCtr->addGameObjectFrame(GameObjectFrame(builder->getBuildableUnit(slotId).id, GameObject::Type::UNIT, player)); ufCtr->setPlacingOnSurface(true); activeState->setBuildableStructSelected(true); } diff --git a/gameManager.cpp b/gameManager.cpp index 008de60..7400e65 100755 --- a/gameManager.cpp +++ b/gameManager.cpp @@ -8,6 +8,7 @@ #include #include "gameManager.h" +#include "gameObjectFrameController.h" #include "gameObjectFactory.h" #include "map.h" #include "game.h" @@ -151,6 +152,7 @@ namespace battleship{ "norm", &Vector3::norm, "getAngleBetween", &Vector3::getAngleBetween, "getDistanceFrom", &Vector3::getDistanceFrom, + "neg", [](Vector3 v){return -v;}, "add", [](Vector3 v1, Vector3 v2){return v1 + v2;}, "subtr", [](Vector3 v1, Vector3 v2){return v1 - v2;}, "mult", [](Vector3 v1, float s){return v1 * s;}, @@ -163,6 +165,8 @@ namespace battleship{ "x", &Quaternion::x, "y", &Quaternion::y, "z", &Quaternion::z, + "getAngle", &Quaternion::getAngle, + "getAxis", &Quaternion::getAxis, "multVec", [](Quaternion q, Vector3 v){return q * v;} ); @@ -189,6 +193,24 @@ namespace battleship{ "calcHeuristics", &Pathfinder::calcHeuristics, "findPath", &Pathfinder::findPath ); + + SOL_LUA_STATE.new_usertype( + "GameObjectFrame", sol::constructors(), + "getOriginalUnit", &GameObjectFrame::getOriginalUnit, + "destroy", &GameObjectFrame::destroy, + "placeAt", &GameObjectFrame::placeAt, + "getDirVec", &GameObjectFrame::getDirVec, + "getLeftVec", &GameObjectFrame::getLeftVec, + "getLength", &GameObjectFrame::getLength, + "getWidth", &GameObjectFrame::getWidth, + "status", &GameObjectFrame::status + ); + + SOL_LUA_STATE.new_usertype( + "GameObjectFrameController", + "getSingleton", &GameObjectFrameController::getSingleton, + "checkPlacement", &GameObjectFrameController::checkPlacement + ); } void GameManager::initLua(string gameDir){ diff --git a/gameObject.cpp b/gameObject.cpp index e9bac8f..db193f1 100644 --- a/gameObject.cpp +++ b/gameObject.cpp @@ -153,7 +153,7 @@ namespace battleship{ model->setMaterial(mat); sol::optional colNodeOpt = gameObjTable["colorNodes"]; - if(player && colNodeOpt != sol::nullopt) + if(!(model->isWireframe() || colNodeOpt == sol::nullopt)) useColor(player->getColorMaterial()); root->getRootNode()->attachChild(model); diff --git a/gameObjectFrame.h b/gameObjectFrame.h index 5a249d2..24dfdbd 100644 --- a/gameObjectFrame.h +++ b/gameObjectFrame.h @@ -10,9 +10,9 @@ namespace battleship{ struct GameObjectFrame : public GameObject{ enum Status{PLACEABLE, NOT_PLACEABLE, PLACED}; - GameObjectFrame(int i, GameObject::Type t, Unit *ou = nullptr, vb01::Vector3 pos = vb01::Vector3::VEC_ZERO, vb01::Quaternion rot = vb01::Quaternion::QUAT_W) : + 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), - GameObject(t, i, nullptr, pos, rot) + GameObject(t, i, pl, pos, rot) { initModel(false); initProperties(); diff --git a/gameObjectFrameController.cpp b/gameObjectFrameController.cpp index 3bc5060..59905e3 100644 --- a/gameObjectFrameController.cpp +++ b/gameObjectFrameController.cpp @@ -54,7 +54,7 @@ namespace battleship{ buildDir = gameObjectFrames[1].getModel()->getPosition() - gameObjectFrames[0].getModel()->getPosition(); Vector3 pos = paintSelectRowStart + buildDir.norm() * hypothenuse * gameObjectFrames.size(); - addGameObjectFrame(GameObjectFrame(structureId, GameObject::Type::UNIT, nullptr, pos)); + addGameObjectFrame(GameObjectFrame(structureId, GameObject::Type::UNIT, gameObjectFrames[0].getPlayer(), nullptr, pos)); } } @@ -86,7 +86,8 @@ namespace battleship{ sol::table tbl = generateView()["units"][s.getId() + 1]; UnitType ut = (UnitType)tbl["unitType"]; - //vector friendlyUnits = s.getPlayer()->getFriendlyUnits(); + vector friendlyUnits = (s.getPlayer() ? s.getPlayer()->getFriendlyUnits() : vector{}); + bool withinLos = false; 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()); @@ -105,6 +106,18 @@ namespace battleship{ s.status = GameObjectFrame::NOT_PLACEABLE; return; } + + if(!withinLos) + for(Unit *fu : friendlyUnits) + if(fu->getPos().getDistanceFrom(s.getPos()) < fu->getLineOfSight()){ + withinLos = true; + break; + } + } + + if(!withinLos){ + s.status = GameObjectFrame::NOT_PLACEABLE; + return; } MeshData meshData = map->getNodeParent()->getChild(0)->getMesh(0)->getMeshBase(); diff --git a/gameObjectListbox.cpp b/gameObjectListbox.cpp index 2725daa..f82fede 100644 --- a/gameObjectListbox.cpp +++ b/gameObjectListbox.cpp @@ -18,7 +18,7 @@ namespace battleship{ void GameObjectListbox::onClose(){ GameObjectFrameController *ufCtr = GameObjectFrameController::getSingleton(); GameObject::Type type = (unitListbox ? GameObject::Type::UNIT : GameObject::Type::RESOURCE_DEPOSIT); - ufCtr->addGameObjectFrame(GameObjectFrame(gameObjIds[selectedOption], type)); + ufCtr->addGameObjectFrame(GameObjectFrame(gameObjIds[selectedOption], type, nullptr)); ufCtr->setPlacingOnSurface(true); } } diff --git a/player.cpp b/player.cpp index b705407..eb762bc 100755 --- a/player.cpp +++ b/player.cpp @@ -307,13 +307,12 @@ namespace battleship{ return true; Vector3 obsUnitPos = object->getPos(); - obsUnitPos.y = 0; + Vector2 oup2d = Vector2(obsUnitPos.x, obsUnitPos.z); Vector3 compUnitPos = friendlyUnit->getPos(); - compUnitPos.y = 0; - float dist = compUnitPos.getDistanceFrom(obsUnitPos); + Vector2 cup2d = Vector2(compUnitPos.x, compUnitPos.z); - if(dist <= friendlyUnit->getLineOfSight()) + if(cup2d.getDistanceFrom(oup2d) <= friendlyUnit->getLineOfSight()) return true; } From 63ad44b3b89d92ccb2f759e09655e791f1b9385e Mon Sep 17 00:00:00 2001 From: devZoGok Date: Thu, 26 Feb 2026 14:42:50 +0200 Subject: [PATCH 09/22] CPU players build a naval factory and task force --- Assets/Scripts/Core/player.lua | 191 ++++++++++++------ Assets/Scripts/Core/playerInit.lua | 55 +++-- Assets/Scripts/GameObjects/Units/unitData.lua | 4 +- Assets/Scripts/aiAgent.lua | 2 +- 4 files changed, 178 insertions(+), 74 deletions(-) diff --git a/Assets/Scripts/Core/player.lua b/Assets/Scripts/Core/player.lua index 1d4c45b..bd6ae26 100644 --- a/Assets/Scripts/Core/player.lua +++ b/Assets/Scripts/Core/player.lua @@ -4,6 +4,8 @@ Player.baseRadius = 180 Player.numDefWarMechs = 1 Player.behaviour = {} Player.pointDefenseTrans = {} +Player.canReachSpawnPoint = {} +Player.navalFactoryCellId = nil Player.unitClassMappings = { {UnitId.ACS_MECH, UnitId.AINC_MECH, UnitId.ER_MECH}, @@ -11,7 +13,7 @@ Player.unitClassMappings = { {UnitId.ACS_ARTILLERY, UnitId.AINC_ARTILLERY, UnitId.ER_ARTILLERY}, {UnitId.ACS_ROBO_ENGINEER, UnitId.AINC_ROBO_ENGINEER, UnitId.ER_ROBO_ENGINEER}, {UnitId.ACS_CYBORG_ENGINEER, UnitId.AINC_CYBORG_ENGINEER, UnitId.ER_CYBORG_ENGINEER}, - {UnitId.ACS_MECH, UnitId.AINC_MECH, UnitId.ER_MECH}, + {UnitId.ACS_TRANSPORT, UnitId.AINC_TRANSPORT, UnitId.ER_TRANSPORT}, {UnitId.ACS_CARGO_SHIP, UnitId.AINC_CARGO_SHIP, UnitId.ER_CARGO_SHIP}, {UnitId.ACS_CRUISER, UnitId.AINC_CRUISER, UnitId.ER_CRUISER}, {UnitId.ACS_ANTI_SUB_CRUISER, UnitId.AINC_ANTI_SUB_CRUISER, UnitId.ER_ANTI_SUB_CRUISER}, @@ -30,12 +32,18 @@ Player.unitClassMappings = { {UnitId.ACS_REFINERY, UnitId.AINC_REFINERY, UnitId.ER_REFINERY}, } -Player.taskForceData = { +Player.landTaskForceData = { {class = UnitClass.MECH, numUnits = 2}, {class = UnitClass.TANK, numUnits = 1}, {class = UnitClass.ARTILLERY, numUnits = 1} } +Player.navalTaskForceData = { + {class = UnitClass.TRANSPORT, numUnits = 1}, + --{class = UnitClass.CRUISER, numUnits = 1}, + --{class = UnitClass.ANTI_SUB_CRUISER, numUnits = 1} +} + Player.taskForces = {} function Player:initBaseQuat() @@ -110,26 +118,65 @@ function Player:updatePdSpots() pdFrame:destroy() end +--TODO remove heuristics +function Player:landRouteToSpawnpoint_(mapPoint) + map = Map.getSingleton() + dest = map:getCellId(mapPoint, false) + 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, nil) + + for i = 1, #path do + if cells[path[i]].type == 1 then + if not self.navalFactoryCellId then self.navalFactoryCellId = path[i] end + return false + end + end + + return true +end + +function Player:landRouteToSpawnpoint(arguments) + spId = self.taskForces[arguments.taskForceId].spawnPointId + 1 + return self.canReachSpawnPoint[spId] and BTNodeType.SUCCESS or BTNodeType.FAILURE +end + function Player:init() self:initBaseQuat() self:initPdSpots() + + for i = 1, Map.getSingleton():getNumSpawnPoints() do + if i - 1 == self:getSpawnPointId() then self.canReachSpawnPoint[i] = true end + + spawnPoint = Map.getSingleton():getSpawnPoint(i - 1) + self.canReachSpawnPoint[i] = self:landRouteToSpawnpoint_(spawnPoint) + end end -function Player:findSuitableSpot(buildingId, startPos) - structFrame = GameObjectFrame:new(buildingId, 0, self, nil, startPos, self.baseQuat) +function Player:findSuitableSpot(buildingId, idealPos) + 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()) baseDir = self.baseQuat:multVec(Vector3:new(0, 0, 1)) baseLeft = self.baseQuat:multVec(Vector3:new(1, 0, 0)) - point = Map.getSingleton():getSpawnPoint(self:getSpawnPointId()):add(baseDir:mult(self.baseRadius)):add(baseLeft:mult(self.baseRadius)) frameCtrl = GameObjectFrameController.getSingleton() frameCtrl:checkPlacement(structFrame) - if structFrame.status == 1 then - for i = 1, numXPos do - point = point:add(baseLeft:neg():mult(i - 1)) + if structFrame.status == 0 then + structFrame:destroy() + return idealPos + 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)) structFrame:placeAt(point) frameCtrl:checkPlacement(structFrame) @@ -138,9 +185,6 @@ function Player:findSuitableSpot(buildingId, startPos) return point end end - else - structFrame:destroy() - return startPos end structFrame:destroy() @@ -190,7 +234,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:add(rd)) + pos = self:findSuitableSpot(factId, sp) if pos then self:buildStructure(self:findIdleEngineer(), factId, pos, self.baseQuat:getAngle()) @@ -226,7 +270,7 @@ function Player:buildRefinery(arguments) refId = self.unitClassMappings[UnitClass.REFINERY + 1][self:getFaction() + 1] factId = self.unitClassMappings[UnitClass.LAND_FACTORY + 1][self:getFaction() + 1] - rd = self.baseQuat:multVec(Vector3:new(0, 0, 1)):mult(self.baseRadius - .5 * (units[factId + 1].size.z + units[refId + 1].size.z)) + rd = self.baseQuat:multVec(Vector3:new(0, 0, 1)):mult(-.5 * (units[factId + 1].size.z + units[refId + 1].size.z)) sp = Map.getSingleton():getSpawnPoint(self:getSpawnPointId()) buildPos = sp:add(rd) @@ -321,21 +365,61 @@ function Player:startHarvesting() return BTNodeResult.SUCCESS end -function Player:buildLandForce(arguments) - landFactory = self:getUnitsByClass(UnitClass.LAND_FACTORY, 1) +function Player:canReachAllSpawnPoints(arguments) + return self.navalFactoryCellId and BTNodeResult.FAILURE or BTNodeResult.SUCCESS +end - if #landFactory == 0 then return BTNodeResult.FAILURE end +function Player:buildNavalFactory(arguments) + navalFactory = self:getUnitsByClass(UnitClass.NAVAL_FACTORY, 1)[1] - landFactory = landFactory[1]:toFactory() - numAttackableSpawnPoints = Map.getSingleton():getNumSpawnPoints() - 1 + if navalFactory then + return (navalFactory:toStructure():isComplete() and BTNodeResult.SUCCESS or BTNodeResult.RUNNING) + end - if #landFactory:getQueue() == 0 then + navalFactId = self.unitClassMappings[UnitClass.NAVAL_FACTORY + 1][self:getFaction() + 1] + pos = self:findSuitableSpot(navalFactId, Map.getSingleton():getCell(self.navalFactoryCellId).pos) + + if pos then + self:buildStructure(self:findIdleEngineer(), navalFactId, pos, self.baseQuat:getAngle()) + return BTNodeResult.RUNNING + else return BTNodeResult.FAILURE end +end + +function Player:buildNavalForce(arguments) + transports = self:getUnitsByClass(UnitClass.TRANSPORT, -1) + + if #transports >= #self.taskForces then return BTNodeResult.SUCCESS end + + factory = self:getUnitsByClass(UnitClass.NAVAL_FACTORY, 1)[1]:toFactory() + + if #factory:getQueue() > 0 then return BTNodeResult.RUNNING end + + transportBuildId = self.unitClassMappings[UnitClass.TRANSPORT + 1][self:getFaction() + 1] + numBuildTransports = #self.taskForces - #transports + + for i = 1, numBuildTransports do + factory:appendToQueue(self:getBuildableUnitSlotId(factory, transportBuildId)) + end + + return BTNodeResult.RUNNING +end + +function Player:buildTaskForces(arguments) + factory = self:getUnitsByClass(arguments.factoryClass, 1) + + if #factory == 0 then return BTNodeResult.FAILURE end + + factory = factory[1]:toFactory() + numTaskForces = arguments.numTaskForces or 1 + taskForceData = arguments.taskForceData + + if #factory:getQueue() == 0 then enoughUnits = true - for i = 1, #self.taskForceData do - tfUnits = self:getUnitsByClass(self.taskForceData[i].class, -1) + for i = 1, #taskForceData do + tfUnits = self:getUnitsByClass(taskForceData[i].class, -1) - if #tfUnits < numAttackableSpawnPoints * self.taskForceData[i].numUnits then + if #tfUnits < numTaskForces * taskForceData[i].numUnits then enoughUnits = false break end @@ -344,12 +428,12 @@ function Player:buildLandForce(arguments) if enoughUnits then return BTNodeResult.SUCCESS end else return BTNodeResult.RUNNING end - for i = 1, numAttackableSpawnPoints do - for j = 1, #self.taskForceData do - for k = 1, self.taskForceData[j].numUnits do - unitId = self.unitClassMappings[self.taskForceData[j].class + 1][self:getFaction() + 1] - slotId = self:getBuildableUnitSlotId(landFactory, unitId) - landFactory:appendToQueue(slotId) + for i = 1, numTaskForces do + for j = 1, #taskForceData do + for k = 1, taskForceData[j].numUnits do + unitId = self.unitClassMappings[taskForceData[j].class + 1][self:getFaction() + 1] + slotId = self:getBuildableUnitSlotId(factory, unitId) + factory:appendToQueue(slotId) end end end @@ -370,16 +454,16 @@ function Player:buildPointDefenseRing(arguments) return BTNodeResult.RUNNING end -function Player:formTaskForces(arguments) +function Player:formLandTaskForces(arguments) numSpawnPoints = Map.getSingleton():getNumSpawnPoints() if #self.taskForces == numSpawnPoints - 1 then return BTNodeResult.SUCCESS end unitGroups = {} - for i = 1, #self.taskForceData do - unitGroups[i] = self:getUnitsByClass(self.taskForceData[i].class, -1) - enoughGroupUnits = (#unitGroups[i] >= (numSpawnPoints - 1) * self.taskForceData[i].numUnits) + for i = 1, #self.landTaskForceData do + unitGroups[i] = self:getUnitsByClass(self.landTaskForceData[i].class, -1) + enoughGroupUnits = (#unitGroups[i] >= (numSpawnPoints - 1) * self.landTaskForceData[i].numUnits) if not enoughGroupUnits then return BTNodeResult.FAILURE end end @@ -391,12 +475,12 @@ function Player:formTaskForces(arguments) self.taskForces[tfId] = {units = {}, moving = false, arrived = false, spawnPointId = i - 1} - for j = 1, #self.taskForceData do - subArrId = (tfId - 1) * self.taskForceData[j].numUnits + 1 + for j = 1, #self.landTaskForceData do + subArrId = (tfId - 1) * self.landTaskForceData[j].numUnits + 1 unitTbl = table.move( unitGroups[j], subArrId, - subArrId + self.taskForceData[j].numUnits, + subArrId + self.landTaskForceData[j].numUnits, #self.taskForces[tfId].units + 1, self.taskForces[tfId].units ) @@ -410,27 +494,16 @@ function Player:formTaskForces(arguments) return BTNodeResult.SUCCESS end ---TODO remove heuristics -function Player:landRouteToSpawnpoint(mapPoint) - map = Map.getSingleton() - dest = map:getCellId(mapPoint, false) - 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, nil) - - for i = 1, #path do - if cells[path[i]].type == 1 then return false end - end - - return true +function Player:boardTransports(arguments) + return BTNodeResult.FAILURE end -function Player:isLandRouteToSpawnpoint(arguments) - mapPoint = Map.getSingleton():getSpawnPoint(self.taskForces[arguments.taskForceId].spawnPointId) - return (self:landRouteToSpawnpoint(mapPoint) and BTNodeResult.SUCCESS or BTNodeResult.FAILURE) +function Player:moveTransports(arguments) + return BTNodeResult.FAILURE +end + +function Player:unloadTransports(arguments) + return BTNodeResult.FAILURE end function Player:occupySpawnPoint(arguments) @@ -482,18 +555,18 @@ function Player:generateTaskForceActions() { type = BTNodeType.SELECTOR, children = { - {type = BTNodeType.FUNCTION, func = 'isLandRouteToSpawnpoint', args = arguments}, + {type = BTNodeType.FUNCTION, func = self.landRouteToSpawnpoint, args = arguments}, { type = BTNodeType.SEQUENCE, children = { - {type = BTNodeType.FUNCTION, func = 'boardTransports', args = arguments}, - {type = BTNodeType.FUNCTION, func = 'moveTransports', args = arguments}, - {type = BTNodeType.FUNCTION, func = 'unloadTransports', args = arguments}, + {type = BTNodeType.FUNCTION, func = self.boardTransports, args = arguments}, + {type = BTNodeType.FUNCTION, func = self.moveTransports, args = arguments}, + {type = BTNodeType.FUNCTION, func = self.unloadTransports, args = arguments}, } } } }, - {type = BTNodeType.FUNCTION, func = 'occupySpawnPoint', args = arguments} + {type = BTNodeType.FUNCTION, func = self.occupySpawnPoint, args = arguments} } } diff --git a/Assets/Scripts/Core/playerInit.lua b/Assets/Scripts/Core/playerInit.lua index f25b8df..3462064 100644 --- a/Assets/Scripts/Core/playerInit.lua +++ b/Assets/Scripts/Core/playerInit.lua @@ -1,43 +1,74 @@ for i = 1, #game.cpuPlayers do - game.cpuPlayers[i]:init() + player = game.cpuPlayers[i] + player:init() - game.cpuPlayers[i].behaviour = { + player.behaviour = { type = BTNodeType.SEQUENCE, children = { - {type = BTNodeType.FUNCTION, func = 'buildLandFactory'}, - {type = BTNodeType.FUNCTION, func = 'trainEngineers'}, + {type = BTNodeType.FUNCTION, func = player.buildLandFactory}, + { + type = BTNodeType.FUNCTION, + func = player.buildTaskForces, + args = { + factoryClass = UnitClass.LAND_FACTORY, + taskForceData = {{class = UnitClass.CYBORG_ENGINEER, numUnits = 3}} + } + }, { type = BTNodeType.PARALLEL, children = { - {type = BTNodeType.FUNCTION, func = 'buildRefinery'}, + {type = BTNodeType.FUNCTION, func = player.buildRefinery}, { type = BTNodeType.SEQUENCE, children = { - {type = BTNodeType.FUNCTION, func = 'buildExtractor'}, - {type = BTNodeType.FUNCTION, func = 'buildHarvester'}, + {type = BTNodeType.FUNCTION, func = player.buildExtractor}, + {type = BTNodeType.FUNCTION, func = player.buildHarvester}, } } } }, + { + type = BTNodeType.SELECTOR, + children = { + {type = BTNodeType.FUNCTION, func = player.canReachAllSpawnPoints}, + { + type = BTNodeType.SEQUENCE, + children = { + {type = BTNodeType.FUNCTION, func = player.buildNavalFactory}, + { + type = BTNodeType.FUNCTION, + func = player.buildTaskForces, + args = {factoryClass = UnitClass.NAVAL_FACTORY, taskForceData = player.navalTaskForceData} + }, + } + }, + } + }, { type = BTNodeType.PARALLEL, children = { { type = BTNodeType.SEQUENCE, children = { - {type = BTNodeType.FUNCTION, func = 'buildLandForce'}, - {type = BTNodeType.FUNCTION, func = 'formTaskForces'}, + { + type = BTNodeType.FUNCTION, + func = player.buildTaskForces, + args = { + factoryClass = UnitClass.LAND_FACTORY, + numTaskForces = Map.getSingleton():getNumSpawnPoints() - 1, + taskForceData = player.landTaskForceData + } + }, + {type = BTNodeType.FUNCTION, func = player.formLandTaskForces}, } }, - {type = BTNodeType.FUNCTION, func = 'buildPointDefenseRing'}, + {type = BTNodeType.FUNCTION, func = player.buildPointDefenseRing}, } }, { type = BTNodeType.PARALLEL, children = game.cpuPlayers[i]:generateTaskForceActions() } - --[[ - ]]-- } } end diff --git a/Assets/Scripts/GameObjects/Units/unitData.lua b/Assets/Scripts/GameObjects/Units/unitData.lua index 163ed50..b67cc0e 100644 --- a/Assets/Scripts/GameObjects/Units/unitData.lua +++ b/Assets/Scripts/GameObjects/Units/unitData.lua @@ -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 = 200, + lineOfSight = 400, name = 'Engineer', basePath = PATH .. vehiclePrefix .. 'Engineers/', albedoPath = 'engineer.jpg', @@ -1006,7 +1006,7 @@ units = { health = 500, buildTime = 1000, cost = 500, - garrisonCapacity = {2, 2, 2}, + garrisonCapacity = {2, 2, 2, 2}, size = {x = 16.5, y = 15.2, z = 45.8}, hitboxOffset = {x = 0, y = 7, z = 0}, lineOfSight = 5, diff --git a/Assets/Scripts/aiAgent.lua b/Assets/Scripts/aiAgent.lua index 671e577..1db05e5 100644 --- a/Assets/Scripts/aiAgent.lua +++ b/Assets/Scripts/aiAgent.lua @@ -9,7 +9,7 @@ function executeBtNode(agent, btNode) if btNode.children[i].type == BTNodeType.FUNCTION then func = btNode.children[i].func args = btNode.children[i].args - res = agent[func](agent, args) + res = func(agent, args) else res = executeBtNode(agent, btNode.children[i]) end From 27d1cdbe0c798604e8d5d4cc246966af51a6eebb Mon Sep 17 00:00:00 2001 From: devZoGok Date: Sat, 28 Feb 2026 14:04:44 +0200 Subject: [PATCH 10/22] naval transports disembark troops on the other side of the sea bugfix for selecting garrisoned units bugfix for selecting cell id with depth check --- Assets/Scripts/Core/player.lua | 81 ++++++++++++++++++++++++++---- Assets/Scripts/Core/playerInit.lua | 2 +- activeGameState.cpp | 4 +- gameManager.cpp | 9 ++++ map.cpp | 5 +- player.cpp | 14 +++++- player.h | 1 + unit.cpp | 5 +- vehicle.cpp | 6 ++- 9 files changed, 103 insertions(+), 24 deletions(-) diff --git a/Assets/Scripts/Core/player.lua b/Assets/Scripts/Core/player.lua index bd6ae26..db1ca50 100644 --- a/Assets/Scripts/Core/player.lua +++ b/Assets/Scripts/Core/player.lua @@ -4,7 +4,7 @@ Player.baseRadius = 180 Player.numDefWarMechs = 1 Player.behaviour = {} Player.pointDefenseTrans = {} -Player.canReachSpawnPoint = {} +Player.spawnPointData = {} Player.navalFactoryCellId = nil Player.unitClassMappings = { @@ -129,19 +129,32 @@ function Player:landRouteToSpawnpoint_(mapPoint) heurs = pf:calcHeuristics(cells, dest) path = pf:findPath(cells, heurs, source, dest, nil) + embarkCellId = nil + disembarkCellId = nil + for i = 1, #path do - if cells[path[i]].type == 1 then + if map:getCell(path[i]).type == 1 then + embarkCellId = path[i - 1] if not self.navalFactoryCellId then self.navalFactoryCellId = path[i] end - return false + break; end end - return true + for i = #path, 1, -1 do + if map:getCell(path[i]).type == 1 then + disembarkCellId = path[i + 1] + break; + end + end + + if embarkCellId or disembarkCellId then + return {reachable = false, embarkCellId = embarkCellId, disembarkCellId = disembarkCellId} + else return {reachable = true} end end function Player:landRouteToSpawnpoint(arguments) spId = self.taskForces[arguments.taskForceId].spawnPointId + 1 - return self.canReachSpawnPoint[spId] and BTNodeType.SUCCESS or BTNodeType.FAILURE + return self.spawnPointData[spId].reachable == true and BTNodeResult.SUCCESS or BTNodeResult.FAILURE end function Player:init() @@ -149,10 +162,10 @@ function Player:init() self:initPdSpots() for i = 1, Map.getSingleton():getNumSpawnPoints() do - if i - 1 == self:getSpawnPointId() then self.canReachSpawnPoint[i] = true end + if i - 1 == self:getSpawnPointId() then self.spawnPointData[i] = {reachable = true} end spawnPoint = Map.getSingleton():getSpawnPoint(i - 1) - self.canReachSpawnPoint[i] = self:landRouteToSpawnpoint_(spawnPoint) + self.spawnPointData[i] = self:landRouteToSpawnpoint_(spawnPoint) end end @@ -193,7 +206,6 @@ end function Player:buildStructure(engineer, buildingId, buildPos, buildAngle) if not engineer then return BTNodeResult.FAILURE end - print(buildAngle) self:deselectUnits() self:selectUnits({engineer}) @@ -495,11 +507,57 @@ function Player:formLandTaskForces(arguments) end function Player:boardTransports(arguments) - return BTNodeResult.FAILURE + transports = self:getUnitsByClass(UnitClass.TRANSPORT, -1) + + if not transports[1] then return BTNodeResult.FAILURE end + + map = Map.getSingleton() + currTransportCell = map:getCell(map:getCellId(transports[1]:getPos(), false)) + + if transports[1]:getNumOrders() == 0 and currTransportCell.type == 1 then + self:deselectUnits() + self:selectUnits(transports) + self:issueOrder(OrderType.MOVE, Vector3:new(0, 0, 0), {Target:new(nil, map:getSpawnPoint(self:getSpawnPointId()))}, false) + elseif currTransportCell.type == 0 and not self.taskForces[arguments.taskForceId].moving then + self:deselectUnits() + self:selectUnits(transports) + self:haltUnits() + self:deselectUnits() + self:selectUnits(self.taskForces[arguments.taskForceId].units) + self:issueOrder(OrderType.GARRISON, Vector3:new(0, 0, 0), {Target:new(transports[1]:toGameObject(), Vector3:new(0, 0, 0))}, false) + self.taskForces[arguments.taskForceId].moving = true + end + + garrisonSlots = transports[1]:getGarrisonSlots() + + for i = 1, #garrisonSlots do + if not garrisonSlots[i].vehicle then + return BTNodeResult.RUNNING + end + end + + self.taskForces[arguments.taskForceId].moving = false + return BTNodeResult.SUCCESS end function Player:moveTransports(arguments) - return BTNodeResult.FAILURE + transports = self:getUnitsByClass(UnitClass.TRANSPORT, -1) + map = Map.getSingleton() + spId = self.taskForces[arguments.taskForceId].spawnPointId + + if map:getCellId(transports[1]:getPos(), false) == self.spawnPointData[spId + 1].disembarkCellId then + self:deselectUnits() + self:selectUnits(transports) + self:issueOrder(OrderType.EJECT, Vector3:new(0, 0, 0), {}, false) + return BTNodeResult.SUCCESS + elseif transports[1]:getNumOrders() > 0 then return BTNodeResult.RUNNING end + + self:deselectUnits() + self:selectUnits(transports) + targ = Target:new(nil, map:getCell(self.spawnPointData[spId + 1].disembarkCellId).pos) + self:issueOrder(OrderType.MOVE, Vector3:new(0, 0, 0), {targ}, false) + + return BTNodeResult.RUNNING end function Player:unloadTransports(arguments) @@ -561,12 +619,13 @@ function Player:generateTaskForceActions() children = { {type = BTNodeType.FUNCTION, func = self.boardTransports, args = arguments}, {type = BTNodeType.FUNCTION, func = self.moveTransports, args = arguments}, - {type = BTNodeType.FUNCTION, func = self.unloadTransports, args = arguments}, } } } }, {type = BTNodeType.FUNCTION, func = self.occupySpawnPoint, args = arguments} + --[[ + ]]-- } } diff --git a/Assets/Scripts/Core/playerInit.lua b/Assets/Scripts/Core/playerInit.lua index 3462064..b8fa9f4 100644 --- a/Assets/Scripts/Core/playerInit.lua +++ b/Assets/Scripts/Core/playerInit.lua @@ -67,7 +67,7 @@ for i = 1, #game.cpuPlayers do }, { type = BTNodeType.PARALLEL, - children = game.cpuPlayers[i]:generateTaskForceActions() + children = player:generateTaskForceActions() } } } diff --git a/activeGameState.cpp b/activeGameState.cpp index a8c2fd7..e652b40 100755 --- a/activeGameState.cpp +++ b/activeGameState.cpp @@ -751,9 +751,7 @@ namespace battleship{ CameraController::getSingleton()->setLookingAround(isPressed); break; case Bind::HALT: - if(isPressed) - for (Unit *u : mainPlayer->getSelectedUnits()) - u->halt(); + if(isPressed) mainPlayer->haltUnits(); break; //TODO reimplement submarine diving mechanic case Bind::LEFT_CONTROL: diff --git a/gameManager.cpp b/gameManager.cpp index 7400e65..652524a 100755 --- a/gameManager.cpp +++ b/gameManager.cpp @@ -59,10 +59,18 @@ namespace battleship{ "createResourceDeposit", &GameObjectFactory::createResourceDeposit ); + SOL_LUA_STATE.new_usertype( + "GarrisonSlot", + "vehicle", &Unit::GarrisonSlot::vehicle, + "category", &Unit::GarrisonSlot::category + ); + SOL_LUA_STATE.new_usertype( "Unit", sol::constructors(), "setState", &Unit::setState, "getDestructable", &Unit::getDestructable, + "getGarrisonSlots", &Unit::getGarrisonSlots, + "getNumGarrisonSlots", &Unit::getNumGarrisonSlots, "getOrder", &Unit::getOrder, "getNumOrders", &Unit::getNumOrders, "getPos", &GameObject::getPos, @@ -113,6 +121,7 @@ namespace battleship{ SOL_LUA_STATE.new_usertype( "Player", sol::constructors(), "getSelectedUnits", &Player::getSelectedUnits, + "haltUnits", &Player::haltUnits, "addUnit", &Player::addUnit, "getUnit", &Player::getUnit, "getNumUnits", &Player::getNumUnits, diff --git a/map.cpp b/map.cpp index 14187db..3cb23ed 100755 --- a/map.cpp +++ b/map.cpp @@ -747,9 +747,8 @@ namespace battleship{ return cellId; } - return cellId; - } - else return surfaceCellId; + } + else return surfaceCellId; } } diff --git a/player.cpp b/player.cpp index eb762bc..6592f60 100755 --- a/player.cpp +++ b/player.cpp @@ -4,6 +4,7 @@ #include "player.h" #include "game.h" #include "structure.h" +#include "vehicle.h" #include "projectile.h" #include "tradeOffer.h" #include "destructable.h" @@ -71,6 +72,11 @@ namespace battleship{ } } + void Player::haltUnits(){ + for (Unit *u : selectedUnits) + u->halt(); + } + int Player::getOrderLineId(Order::TYPE type, Vector3 startPos, Vector3 endPos){ Vector3 color; @@ -169,11 +175,15 @@ namespace battleship{ } void Player::selectUnits(vector selUnits){ - for(Unit *u : selUnits) - if(find(selectedUnits.begin(), selectedUnits.end(), u) == selectedUnits.end()){ + for(Unit *u : selUnits){ + bool garrisonable = (!u->isVehicle() || (u->isVehicle() && !((Vehicle*)u)->getGarrisonable())); + bool selected = (find(selectedUnits.begin(), selectedUnits.end(), u) != selectedUnits.end()); + + if(garrisonable && !selected){ selectedUnits.push_back(u); u->select(); } + } } vector Player::getUnitsById(int id, int numUnits){ diff --git a/player.h b/player.h index 1ccce17..e104312 100755 --- a/player.h +++ b/player.h @@ -22,6 +22,7 @@ namespace battleship{ Player(int, int, int, vb01::Vector3, bool = true, int = -1, std::string = ""); ~Player(); void update(); + void haltUnits(); void issueOrder(Order::TYPE, vb01::Vector3, std::vector, bool); void removeUnit(Unit*); void removeUnit(int); diff --git a/unit.cpp b/unit.cpp index a115fc1..a5bc534 100755 --- a/unit.cpp +++ b/unit.cpp @@ -493,7 +493,7 @@ namespace battleship{ Map *map = Map::getSingleton(); if(alignToSurface){ - vector res = RayCaster::cast(Vector3(p.x, 100, p.z), -Vector3::VEC_J, map->getNodeParent()->getChildren(), 0, 20); + vector res = map->raycastTerrain(Vector3(p.x, 100, p.z), -Vector3::VEC_J, true); if(res.empty() || res[0].mesh->getNode() != map->getNodeParent()->getChild(0)) model->lookAt(Vector3(dirVec.x, 0, dirVec.z).norm(), Vector3::VEC_J); @@ -514,8 +514,7 @@ namespace battleship{ if(activeState){ map->blockCells(this); - if(activeState->getPlayer()) - Map::Minimap::getSingleton()->updateImage(); + //if(activeState->getPlayer()) Map::Minimap::getSingleton()->updateImage(); } } diff --git a/vehicle.cpp b/vehicle.cpp index ded8ccc..cc02956 100644 --- a/vehicle.cpp +++ b/vehicle.cpp @@ -49,7 +49,11 @@ namespace battleship{ void Vehicle::startCurrentOrder(){ Unit::startCurrentOrder(); - if(orders[0].type == Order::TYPE::LAUNCH) return; + switch(orders[0].type){ + case Order::TYPE::LAUNCH: + case Order::TYPE::EJECT: + return; + } removeAllPathpoints(); From dbc1a055676d5598a98fb286a2143906ce054f50 Mon Sep 17 00:00:00 2001 From: devZoGok Date: Sun, 1 Mar 2026 16:03:05 +0200 Subject: [PATCH 11/22] bugfix for unloaded land force not moving towards the spawn point --- Assets/Scripts/Core/player.lua | 39 ++++++++++++++++++++-------------- gameManager.cpp | 2 ++ unit.cpp | 11 ++++++++++ unit.h | 2 ++ 4 files changed, 38 insertions(+), 16 deletions(-) diff --git a/Assets/Scripts/Core/player.lua b/Assets/Scripts/Core/player.lua index db1ca50..7c9c38c 100644 --- a/Assets/Scripts/Core/player.lua +++ b/Assets/Scripts/Core/player.lua @@ -512,20 +512,27 @@ function Player:boardTransports(arguments) if not transports[1] then return BTNodeResult.FAILURE end map = Map.getSingleton() - currTransportCell = map:getCell(map:getCellId(transports[1]:getPos(), false)) + tpPos = transports[1]:getPos() + currTransportCell = map:getCell(map:getCellId(tpPos, false)) + + spId = self.taskForces[arguments.taskForceId].spawnPointId + embarkCell = map:getCell(self.spawnPointData[spId + 1].embarkCellId) + disembarkCell = map:getCell(self.spawnPointData[spId + 1].disembarkCellId) if transports[1]:getNumOrders() == 0 and currTransportCell.type == 1 then self:deselectUnits() self:selectUnits(transports) - self:issueOrder(OrderType.MOVE, Vector3:new(0, 0, 0), {Target:new(nil, map:getSpawnPoint(self:getSpawnPointId()))}, false) - elseif currTransportCell.type == 0 and not self.taskForces[arguments.taskForceId].moving then - self:deselectUnits() - self:selectUnits(transports) - self:haltUnits() - self:deselectUnits() - self:selectUnits(self.taskForces[arguments.taskForceId].units) - self:issueOrder(OrderType.GARRISON, Vector3:new(0, 0, 0), {Target:new(transports[1]:toGameObject(), Vector3:new(0, 0, 0))}, false) - self.taskForces[arguments.taskForceId].moving = true + self:issueOrder(OrderType.MOVE, Vector3:new(0, 0, 0), {Target:new(nil, embarkCell.pos)}, false) + elseif currTransportCell.type == 0 then + closerToDisembark = (tpPos:getDistanceFrom(disembarkCell.pos) < tpPos:getDistanceFrom(embarkCell.pos)) + + if closerToDisembark then return BTNodeResult.SUCCESS + elseif not self.taskForces[arguments.taskForceId].moving then + self:deselectUnits() + self:selectUnits(self.taskForces[arguments.taskForceId].units) + self:issueOrder(OrderType.GARRISON, Vector3:new(0, 0, 0), {Target:new(transports[1]:toGameObject(), Vector3:new(0, 0, 0))}, false) + self.taskForces[arguments.taskForceId].moving = true + end end garrisonSlots = transports[1]:getGarrisonSlots() @@ -542,6 +549,9 @@ end function Player:moveTransports(arguments) transports = self:getUnitsByClass(UnitClass.TRANSPORT, -1) + + if transports[1]:isGarrisonEmpty() then return BTNodeResult.SUCCESS end + map = Map.getSingleton() spId = self.taskForces[arguments.taskForceId].spawnPointId @@ -554,16 +564,13 @@ function Player:moveTransports(arguments) self:deselectUnits() self:selectUnits(transports) - targ = Target:new(nil, map:getCell(self.spawnPointData[spId + 1].disembarkCellId).pos) - self:issueOrder(OrderType.MOVE, Vector3:new(0, 0, 0), {targ}, false) + + targPos = map:getCell(self.spawnPointData[spId + 1].disembarkCellId).pos + self:issueOrder(OrderType.MOVE, Vector3:new(0, 0, 0), {Target:new(nil, targPos)}, false) return BTNodeResult.RUNNING end -function Player:unloadTransports(arguments) - return BTNodeResult.FAILURE -end - function Player:occupySpawnPoint(arguments) tfId = arguments.taskForceId diff --git a/gameManager.cpp b/gameManager.cpp index 652524a..6a28239 100755 --- a/gameManager.cpp +++ b/gameManager.cpp @@ -70,7 +70,9 @@ namespace battleship{ "setState", &Unit::setState, "getDestructable", &Unit::getDestructable, "getGarrisonSlots", &Unit::getGarrisonSlots, + "getNumFreeGarrisonSlots", &Unit::getNumFreeGarrisonSlots, "getNumGarrisonSlots", &Unit::getNumGarrisonSlots, + "isGarrisonEmpty", &Unit::isGarrisonEmpty, "getOrder", &Unit::getOrder, "getNumOrders", &Unit::getNumOrders, "getPos", &GameObject::getPos, diff --git a/unit.cpp b/unit.cpp index a5bc534..02a550f 100755 --- a/unit.cpp +++ b/unit.cpp @@ -171,6 +171,15 @@ namespace battleship{ delete losLightNode; losLightNode = nullptr; } + + int Unit::getNumFreeGarrisonSlots(){ + int numFreeSlots = 0; + + for(GarrisonSlot &slot : garrisonSlots) + if(!slot.vehicle) numFreeSlots++; + + return numFreeSlots; + } void Unit::destroySound(){ selectionSfx->stop(); @@ -468,6 +477,8 @@ namespace battleship{ if(order.type == Order::TYPE::BUILD && order.targets[0].unit) player->addUnit((Unit*)order.targets[0].unit); + + executeOrders(); } void Unit::halt() { diff --git a/unit.h b/unit.h index 2da7709..8b494c8 100755 --- a/unit.h +++ b/unit.h @@ -118,6 +118,7 @@ namespace battleship{ bool canGarrison(Vehicle*); void initLosLight(); void destroyLosLight(); + int getNumFreeGarrisonSlots(); inline void setState(State s){state = s;} inline State getState(){return state;} inline Engineer* toEngineer(){return (Engineer*)this;} @@ -127,6 +128,7 @@ namespace battleship{ inline PointDefense* toPointDefense(){return (PointDefense*)this;} inline GameObject* toGameObject(){return (GameObject*)this;} inline int getNumGarrisonSlots(){return garrisonSlots.size();} + inline bool isGarrisonEmpty(){return getNumFreeGarrisonSlots() == getNumGarrisonSlots();} inline const std::vector& getGarrisonSlots(){return garrisonSlots;} inline float getLineOfSight() {return lineOfSight;} inline UnitType getType() {return type;} From 5bcd86d6066a90f8bd4d268e429b6c2b06d17532 Mon Sep 17 00:00:00 2001 From: devZoGok Date: Sun, 1 Mar 2026 21:21:51 +0200 Subject: [PATCH 12/22] improved CPU player behaviour land force transporting subsystem to use multiple transports --- Assets/Scripts/Core/player.lua | 116 +++++++++++------- Assets/Scripts/GameObjects/Units/unitData.lua | 4 +- gameManager.cpp | 8 ++ unit.h | 2 + 4 files changed, 87 insertions(+), 43 deletions(-) diff --git a/Assets/Scripts/Core/player.lua b/Assets/Scripts/Core/player.lua index 7c9c38c..1f34437 100644 --- a/Assets/Scripts/Core/player.lua +++ b/Assets/Scripts/Core/player.lua @@ -39,7 +39,7 @@ Player.landTaskForceData = { } Player.navalTaskForceData = { - {class = UnitClass.TRANSPORT, numUnits = 1}, + {class = UnitClass.TRANSPORT, numUnits = 2}, --{class = UnitClass.CRUISER, numUnits = 1}, --{class = UnitClass.ANTI_SUB_CRUISER, numUnits = 1} } @@ -485,7 +485,7 @@ function Player:formLandTaskForces(arguments) for i = 1, numSpawnPoints do if i - 1 == self:getSpawnPointId() then goto continue end - self.taskForces[tfId] = {units = {}, moving = false, arrived = false, spawnPointId = i - 1} + self.taskForces[tfId] = {units = {}, moving = false, arrived = false, landed = false, spawnPointId = i - 1} for j = 1, #self.landTaskForceData do subArrId = (tfId - 1) * self.landTaskForceData[j].numUnits + 1 @@ -506,69 +506,103 @@ function Player:formLandTaskForces(arguments) return BTNodeResult.SUCCESS end -function Player:boardTransports(arguments) - transports = self:getUnitsByClass(UnitClass.TRANSPORT, -1) +function Player:getTransportData(transports) + allIdle = true + allInWater = true + allInLand = true - if not transports[1] then return BTNodeResult.FAILURE end + for i = 1, #transports do + numOrders = transports[i]:getNumOrders() + currCell = map:getCell(map:getCellId(transports[i]:getPos(), false)) + + if numOrders > 0 then allIdle = false end + + if currCell.type ~= 1 then allInWater = false + elseif currCell.type ~= 0 then allInLand = false end + end + + return {allIdle = allIdle, allInWater = allInWater, allInLand = allInLand} +end + +function Player:boardTransports(arguments) + taskForce = self.taskForces[arguments.taskForceId] + + if taskForce.landed then return BTNodeResult.SUCCESS end map = Map.getSingleton() - tpPos = transports[1]:getPos() - currTransportCell = map:getCell(map:getCellId(tpPos, false)) + transports = self:getUnitsByClass(UnitClass.TRANSPORT, -1) + tpData = self:getTransportData(transports) - spId = self.taskForces[arguments.taskForceId].spawnPointId - embarkCell = map:getCell(self.spawnPointData[spId + 1].embarkCellId) - disembarkCell = map:getCell(self.spawnPointData[spId + 1].disembarkCellId) - - if transports[1]:getNumOrders() == 0 and currTransportCell.type == 1 then + if tpData.allIdle and tpData.allInWater then self:deselectUnits() self:selectUnits(transports) + + embarkCell = map:getCell(self.spawnPointData[taskForce.spawnPointId + 1].embarkCellId) self:issueOrder(OrderType.MOVE, Vector3:new(0, 0, 0), {Target:new(nil, embarkCell.pos)}, false) - elseif currTransportCell.type == 0 then - closerToDisembark = (tpPos:getDistanceFrom(disembarkCell.pos) < tpPos:getDistanceFrom(embarkCell.pos)) + end + + if tpData.allInWater and not tpData.allIdle then return BTNodeResult.RUNNING end + + if not taskForce.moving and tpData.allInLand and tpData.allIdle then + usaId = 1 + + for i = 1, #transports do + numSlots = transports[i]:getNumGarrisonSlots() + un = table.move(taskForce.units, usaId, usaId + numSlots - 1, 1, {}) + usaId = usaId + numSlots - if closerToDisembark then return BTNodeResult.SUCCESS - elseif not self.taskForces[arguments.taskForceId].moving then self:deselectUnits() - self:selectUnits(self.taskForces[arguments.taskForceId].units) - self:issueOrder(OrderType.GARRISON, Vector3:new(0, 0, 0), {Target:new(transports[1]:toGameObject(), Vector3:new(0, 0, 0))}, false) - self.taskForces[arguments.taskForceId].moving = true + self:selectUnits(un) + self:issueOrder(OrderType.GARRISON, Vector3:new(0, 0, 0), {Target:new(transports[i]:toGameObject(), Vector3:new(0, 0, 0))}, false) end + + taskForce.moving = true end - garrisonSlots = transports[1]:getGarrisonSlots() + for i = 1, #taskForce.units do + garrisoned = taskForce.units[i]:toVehicle():getGarrisonable() - for i = 1, #garrisonSlots do - if not garrisonSlots[i].vehicle then - return BTNodeResult.RUNNING - end + if i == #taskForce.units and garrisoned then + taskForce.moving = false + return BTNodeResult.SUCCESS + elseif not garrisoned then return BTNodeResult.RUNNING end end - - self.taskForces[arguments.taskForceId].moving = false - return BTNodeResult.SUCCESS end function Player:moveTransports(arguments) transports = self:getUnitsByClass(UnitClass.TRANSPORT, -1) - if transports[1]:isGarrisonEmpty() then return BTNodeResult.SUCCESS end + for i = 1, #transports do + empty = transports[i]:isGarrisonEmpty() - map = Map.getSingleton() - spId = self.taskForces[arguments.taskForceId].spawnPointId + if i == #transports and empty then return BTNodeResult.SUCCESS + elseif not empty then break end + end + + tpData = self:getTransportData(transports) + + if tpData.allInLand and tpData.allIdle then + spId = self.taskForces[arguments.taskForceId].spawnPointId + targPos = Map.getSingleton():getCell(self.spawnPointData[spId + 1].disembarkCellId).pos + + for i = 1, #transports do + nearDisembarkPoint = (transports[i]:getPos():getDistanceFrom(targPos) < 10) + + if i == #transports and nearDisembarkPoint then + self:deselectUnits() + self:selectUnits(transports) + self:issueOrder(OrderType.EJECT, Vector3:new(0, 0, 0), {}, false) + self.taskForces[arguments.taskForceId].landed = true + return BTNodeResult.SUCCESS + elseif not nearDisembarkPoint then break end + end - if map:getCellId(transports[1]:getPos(), false) == self.spawnPointData[spId + 1].disembarkCellId then self:deselectUnits() self:selectUnits(transports) - self:issueOrder(OrderType.EJECT, Vector3:new(0, 0, 0), {}, false) - return BTNodeResult.SUCCESS - elseif transports[1]:getNumOrders() > 0 then return BTNodeResult.RUNNING end + self:issueOrder(OrderType.MOVE, Vector3:new(0, 0, 0), {Target:new(nil, targPos)}, false) + end - self:deselectUnits() - self:selectUnits(transports) - - targPos = map:getCell(self.spawnPointData[spId + 1].disembarkCellId).pos - self:issueOrder(OrderType.MOVE, Vector3:new(0, 0, 0), {Target:new(nil, targPos)}, false) - - return BTNodeResult.RUNNING + if not tpData.allIdle then return BTNodeResult.RUNNING end end function Player:occupySpawnPoint(arguments) diff --git a/Assets/Scripts/GameObjects/Units/unitData.lua b/Assets/Scripts/GameObjects/Units/unitData.lua index b67cc0e..d1c924d 100644 --- a/Assets/Scripts/GameObjects/Units/unitData.lua +++ b/Assets/Scripts/GameObjects/Units/unitData.lua @@ -1006,7 +1006,7 @@ units = { health = 500, buildTime = 1000, cost = 500, - garrisonCapacity = {2, 2, 2, 2}, + garrisonCapacity = {2, 2, 2}, size = {x = 16.5, y = 15.2, z = 45.8}, hitboxOffset = {x = 0, y = 7, z = 0}, lineOfSight = 5, @@ -1016,7 +1016,7 @@ units = { meshPath = 'acsTransport.xml', albedoPath = 'transport.jpg', selectionSfx = PATH .. 'Sounds/Units/Transports/selection.ogg', - speed = .1, + speed = .6, destinationOffset = .1, anglePrecision = .1, maxTurnAngle = .1, diff --git a/gameManager.cpp b/gameManager.cpp index 6a28239..0e5bbc0 100755 --- a/gameManager.cpp +++ b/gameManager.cpp @@ -17,6 +17,7 @@ #include "player.h" #include "extractor.h" #include "factory.h" +#include "vehicle.h" #include "engineer.h" #include "destructable.h" #include "pointDefense.h" @@ -83,11 +84,18 @@ namespace battleship{ "toStructure", &Unit::toStructure, "toFactory", &Unit::toFactory, "toGameObject", &Unit::toGameObject, + "toVehicle", &Unit::toVehicle, + //"toVehicle", [](Unit *u){return (Vehicle*)u;}, "getBuildableUnit", &Unit::getBuildableUnit, "getBuildableUnits", &Unit::getBuildableUnits, "getNumBuildableUnits", &Unit::getNumBuildableUnits ); + SOL_LUA_STATE.new_usertype( + "Engineer", sol::constructors(), + "getGarrisonable", &Vehicle::getGarrisonable + ); + SOL_LUA_STATE.new_usertype( "Engineer", sol::constructors(), "getBuildableUnit", &Unit::getBuildableUnit, diff --git a/unit.h b/unit.h index 8b494c8..82acd3e 100755 --- a/unit.h +++ b/unit.h @@ -30,6 +30,7 @@ namespace battleship{ class Weapon; class Unit; class Vehicle; + class Vehicle; class Structure; class Factory; class Cruiser; @@ -121,6 +122,7 @@ namespace battleship{ int getNumFreeGarrisonSlots(); inline void setState(State s){state = s;} inline State getState(){return state;} + inline Vehicle* toVehicle(){return (Vehicle*)this;} inline Engineer* toEngineer(){return (Engineer*)this;} inline Structure* toStructure(){return (Structure*)this;} inline Factory* toFactory(){return (Factory*)this;} From ee0dfaecde69a2d4a258b9733012ffb0eb7a6392 Mon Sep 17 00:00:00 2001 From: devZoGok Date: Mon, 2 Mar 2026 15:42:08 +0200 Subject: [PATCH 13/22] transporting units overseas to multiple spawn points --- Assets/Scripts/Core/player.lua | 61 ++++++++++++++++++++-------------- factory.cpp | 4 +-- factory.h | 3 ++ gameManager.cpp | 21 +++++++----- unit.h | 18 ++-------- 5 files changed, 56 insertions(+), 51 deletions(-) diff --git a/Assets/Scripts/Core/player.lua b/Assets/Scripts/Core/player.lua index 1f34437..0d5f16b 100644 --- a/Assets/Scripts/Core/player.lua +++ b/Assets/Scripts/Core/player.lua @@ -6,6 +6,7 @@ Player.behaviour = {} Player.pointDefenseTrans = {} Player.spawnPointData = {} Player.navalFactoryCellId = nil +Player.navalRallyPoint = nil Player.unitClassMappings = { {UnitId.ACS_MECH, UnitId.AINC_MECH, UnitId.ER_MECH}, @@ -39,7 +40,7 @@ Player.landTaskForceData = { } Player.navalTaskForceData = { - {class = UnitClass.TRANSPORT, numUnits = 2}, + {class = UnitClass.TRANSPORT, numUnits = 3}, --{class = UnitClass.CRUISER, numUnits = 1}, --{class = UnitClass.ANTI_SUB_CRUISER, numUnits = 1} } @@ -250,6 +251,8 @@ function Player:buildLandFactory(arguments) if pos then self:buildStructure(self:findIdleEngineer(), factId, pos, self.baseQuat:getAngle()) + factory = self:getUnitsByClass(UnitClass.LAND_FACTORY, 1)[1]:toFactory() + factory:setRallyPoint(factory:getPos():add(factory:getDirVec():mult(40))) return BTNodeResult.RUNNING else return BTNodeResult.FAILURE end end @@ -282,9 +285,9 @@ function Player:buildRefinery(arguments) refId = self.unitClassMappings[UnitClass.REFINERY + 1][self:getFaction() + 1] factId = self.unitClassMappings[UnitClass.LAND_FACTORY + 1][self:getFaction() + 1] - rd = self.baseQuat:multVec(Vector3:new(0, 0, 1)):mult(-.5 * (units[factId + 1].size.z + units[refId + 1].size.z)) - sp = Map.getSingleton():getSpawnPoint(self:getSpawnPointId()) - buildPos = sp:add(rd) + rd = self.baseQuat:multVec(Vector3:new(0, 0, 1)):mult(-.6 * (units[factId + 1].size.z + units[refId + 1].size.z)) + fp = self:getUnitsByClass(UnitClass.LAND_FACTORY, 1)[1]:getPos() + buildPos = fp:add(rd) pos = self:findSuitableSpot(refId, buildPos) @@ -393,6 +396,7 @@ function Player:buildNavalFactory(arguments) if pos then self:buildStructure(self:findIdleEngineer(), navalFactId, pos, self.baseQuat:getAngle()) + self.navalRallyPoint = self:getUnitsByClass(UnitClass.NAVAL_FACTORY, 1)[1]:toFactory():getRallyPoint() return BTNodeResult.RUNNING else return BTNodeResult.FAILURE end end @@ -492,7 +496,7 @@ function Player:formLandTaskForces(arguments) unitTbl = table.move( unitGroups[j], subArrId, - subArrId + self.landTaskForceData[j].numUnits, + math.max(subArrId + self.landTaskForceData[j].numUnits - 1, 1), #self.taskForces[tfId].units + 1, self.taskForces[tfId].units ) @@ -525,6 +529,7 @@ function Player:getTransportData(transports) end function Player:boardTransports(arguments) + print('executing BOARD ' .. arguments.taskForceId) taskForce = self.taskForces[arguments.taskForceId] if taskForce.landed then return BTNodeResult.SUCCESS end @@ -570,6 +575,8 @@ function Player:boardTransports(arguments) end function Player:moveTransports(arguments) + if self.taskForces[arguments.taskForceId].landed then return BTNodeResult.SUCCESS end + transports = self:getUnitsByClass(UnitClass.TRANSPORT, -1) for i = 1, #transports do @@ -593,6 +600,7 @@ function Player:moveTransports(arguments) self:selectUnits(transports) self:issueOrder(OrderType.EJECT, Vector3:new(0, 0, 0), {}, false) self.taskForces[arguments.taskForceId].landed = true + self:issueOrder(OrderType.MOVE, Vector3:new(0, 0, 0), {Target:new(nil, self.navalRallyPoint)}, false) return BTNodeResult.SUCCESS elseif not nearDisembarkPoint then break end end @@ -608,6 +616,10 @@ end function Player:occupySpawnPoint(arguments) tfId = arguments.taskForceId + if not self.spawnPointData[self.taskForces[tfId].spawnPointId + 1].reachable and not self.taskForces[tfId].landed then + return BTNodeResult.FAILURE + end + if #self.taskForces[tfId].units > 0 then if self.taskForces[tfId].moving then return BTNodeResult.RUNNING @@ -640,35 +652,34 @@ function Player:occupySpawnPoint(arguments) end function Player:generateTaskForceActions() - children = {} - tfId = 1 + children = { + {type = BTNodeType.SEQUENCE, children = {}}, + {type = BTNodeType.PARALLEL, children = {}}, + } numSpawnPoints = Map.getSingleton():getNumSpawnPoints() + tfId = 1 for i = 1, numSpawnPoints do if i - 1 == self:getSpawnPointId() then goto continue end arguments = {taskForceId = tfId} - children[#children + 1] = { - type = BTNodeType.SEQUENCE, - children = { - { - type = BTNodeType.SELECTOR, - children = { - {type = BTNodeType.FUNCTION, func = self.landRouteToSpawnpoint, args = arguments}, - { - type = BTNodeType.SEQUENCE, - children = { - {type = BTNodeType.FUNCTION, func = self.boardTransports, args = arguments}, - {type = BTNodeType.FUNCTION, func = self.moveTransports, args = arguments}, - } + table.insert( + children[1].children, + { + type = BTNodeType.SELECTOR, + children = { + {type = BTNodeType.FUNCTION, func = self.landRouteToSpawnpoint, args = arguments}, + { + type = BTNodeType.SEQUENCE, + children = { + {type = BTNodeType.FUNCTION, func = self.boardTransports, args = arguments}, + {type = BTNodeType.FUNCTION, func = self.moveTransports, args = arguments}, } } - }, - {type = BTNodeType.FUNCTION, func = self.occupySpawnPoint, args = arguments} - --[[ - ]]-- + } } - } + ) + table.insert(children[2].children, {type = BTNodeType.FUNCTION, func = self.occupySpawnPoint, args = arguments}) tfId = tfId + 1 ::continue:: diff --git a/factory.cpp b/factory.cpp index aacf115..9390bdc 100644 --- a/factory.cpp +++ b/factory.cpp @@ -15,7 +15,7 @@ namespace battleship{ using namespace std; using namespace gameBase; - Factory::Factory(Player *player, int id, Vector3 pos, Quaternion rot, int buildStatus, Unit::State state) : Structure(player, id, pos, rot, buildStatus, state){} + Factory::Factory(Player *player, int id, Vector3 pos, Quaternion rot, int buildStatus, Unit::State state) : Structure(player, id, pos, rot, buildStatus, state), rallyPoint(pos + 30 * dirVec){} void Factory::update(){ Structure::update(); @@ -70,7 +70,7 @@ namespace battleship{ if(trainingStatus >= 100){ Unit *unit = GameObjectFactory::createUnit(player, unitQueue[0], pos, rot); player->addUnit(unit); - unit->receiveOrder(Order(Order::TYPE::MOVE, vector{Order::Target(nullptr, pos + 30 * dirVec)}, Vector3::VEC_ZERO), false); + unit->receiveOrder(Order(Order::TYPE::MOVE, vector{Order::Target(nullptr, rallyPoint)}, Vector3::VEC_ZERO), false); unitQueue.erase(unitQueue.begin()); trainingStatus = 0; diff --git a/factory.h b/factory.h index 586eab8..d9a2128 100644 --- a/factory.h +++ b/factory.h @@ -20,11 +20,14 @@ namespace battleship{ void update(); int getNumQueueUnitsById(int); void appendToQueue(int); + inline void setRallyPoint(vb01::Vector3 rp){this->rallyPoint = rp;} + inline vb01::Vector3 getRallyPoint(){return rallyPoint;} inline std::vector getQueue(){return unitQueue;} private: std::vector unitQueue; int maxLenQueue, trainingStatus = 0; vb01::s64 lastTrainTime = 0; + vb01::Vector3 rallyPoint; void initProperties(); void train(); diff --git a/gameManager.cpp b/gameManager.cpp index 0e5bbc0..3ec792e 100755 --- a/gameManager.cpp +++ b/gameManager.cpp @@ -76,19 +76,20 @@ namespace battleship{ "isGarrisonEmpty", &Unit::isGarrisonEmpty, "getOrder", &Unit::getOrder, "getNumOrders", &Unit::getNumOrders, + "getDirVec", &GameObject::getDirVec, + "getLeftVec", &GameObject::getLeftVec, "getPos", &GameObject::getPos, "getUnitClass", &Unit::getUnitClass, "getLineOfSight", &Unit::getLineOfSight, - "toEngineer", &Unit::toEngineer, - "toPointDefense", &Unit::toPointDefense, - "toStructure", &Unit::toStructure, - "toFactory", &Unit::toFactory, - "toGameObject", &Unit::toGameObject, - "toVehicle", &Unit::toVehicle, - //"toVehicle", [](Unit *u){return (Vehicle*)u;}, "getBuildableUnit", &Unit::getBuildableUnit, "getBuildableUnits", &Unit::getBuildableUnits, - "getNumBuildableUnits", &Unit::getNumBuildableUnits + "getNumBuildableUnits", &Unit::getNumBuildableUnits, + "toGameObject", [](Unit *u){return (GameObject*)u;}, + "toVehicle", [](Unit *u){return (Vehicle*)u;}, + "toEngineer", [](Unit *u){return (Engineer*)u;}, + "toStructure", [](Unit *u){return (Structure*)u;}, + "toFactory", [](Unit *u){return (Factory*)u;}, + "toPointDefense", [](Unit *u){return (PointDefense*)u;} ); SOL_LUA_STATE.new_usertype( @@ -121,10 +122,14 @@ namespace battleship{ "getBuildableUnit", &Unit::getBuildableUnit, "getBuildableUnits", &Unit::getBuildableUnits, "getNumBuildableUnits", &Unit::getNumBuildableUnits, + "getDirVec", &GameObject::getDirVec, + "getLeftVec", &GameObject::getLeftVec, "getPos", &GameObject::getPos, "appendToQueue", &Factory::appendToQueue, "getBuildStatus", &Structure::getBuildStatus, "getNumQueueUnitsById", &Factory::getNumQueueUnitsById, + "getRallyPoint", &Factory::getRallyPoint, + "setRallyPoint", &Factory::setRallyPoint, "getQueue", &Factory::getQueue ); diff --git a/unit.h b/unit.h index 82acd3e..0bcf425 100755 --- a/unit.h +++ b/unit.h @@ -28,16 +28,9 @@ namespace vb01{ namespace battleship{ class Player; class Weapon; - class Unit; - class Vehicle; - class Vehicle; - class Structure; - class Factory; - class Cruiser; - class Engineer; - class PointDefense; - class Projectile; class GameObject; + class Vehicle; + class Projectile; struct Order { enum class TYPE {ATTACK, BUILD, MOVE, GARRISON, EJECT, PATROL, LAUNCH, SUPPLY, LOAD, UNLOAD, HACK}; @@ -122,13 +115,6 @@ namespace battleship{ int getNumFreeGarrisonSlots(); inline void setState(State s){state = s;} inline State getState(){return state;} - inline Vehicle* toVehicle(){return (Vehicle*)this;} - inline Engineer* toEngineer(){return (Engineer*)this;} - inline Structure* toStructure(){return (Structure*)this;} - inline Factory* toFactory(){return (Factory*)this;} - inline Cruiser* toCruiser(){return (Cruiser*)this;} - inline PointDefense* toPointDefense(){return (PointDefense*)this;} - inline GameObject* toGameObject(){return (GameObject*)this;} inline int getNumGarrisonSlots(){return garrisonSlots.size();} inline bool isGarrisonEmpty(){return getNumFreeGarrisonSlots() == getNumGarrisonSlots();} inline const std::vector& getGarrisonSlots(){return garrisonSlots;} From 6669c1e8c225ca4ea23d1438ca3ed86d27aa74af Mon Sep 17 00:00:00 2001 From: devZoGok Date: Tue, 3 Mar 2026 16:26:35 +0200 Subject: [PATCH 14/22] optimized CPU player spawn point occupation implementation bugfix for navigating empty paths --- Assets/Scripts/Core/player.lua | 78 +++++++++++++++++------------- Assets/Scripts/Core/playerInit.lua | 1 + gameManager.cpp | 1 + player.cpp | 11 +++++ player.h | 1 + vehicle.cpp | 5 +- 6 files changed, 62 insertions(+), 35 deletions(-) diff --git a/Assets/Scripts/Core/player.lua b/Assets/Scripts/Core/player.lua index 0d5f16b..ead20ef 100644 --- a/Assets/Scripts/Core/player.lua +++ b/Assets/Scripts/Core/player.lua @@ -237,6 +237,24 @@ function Player:getBuildableUnitSlotId(buildingUnit, buildableUnitId) return nil end +function Player:updateTaskForces(arguments) + units = self:getUnits() + + for i = 1, #self.taskForces do + for j = #self.taskForces[i].units, 1, -1 do + for k = 1, #units do + if self.taskForces[i].units[j] == units[k] then goto continue end + end + + table.remove(self.taskForces[i].units, j) + + ::continue:: + end + end + + return BTNodeResult.SUCCESS +end + function Player:buildLandFactory(arguments) factory = self:getUnitsByClass(UnitClass.LAND_FACTORY, 1)[1] @@ -244,13 +262,17 @@ function Player:buildLandFactory(arguments) return factory:toStructure():isComplete() and BTNodeResult.SUCCESS or BTNodeResult.RUNNING end + engi = self:findIdleEngineer() + + if not engi then return BTNodeResult.FAILURE end + 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) if pos then - self:buildStructure(self:findIdleEngineer(), factId, pos, self.baseQuat:getAngle()) + self:buildStructure(engi, factId, pos, self.baseQuat:getAngle()) factory = self:getUnitsByClass(UnitClass.LAND_FACTORY, 1)[1]:toFactory() factory:setRallyPoint(factory:getPos():add(factory:getDirVec():mult(40))) return BTNodeResult.RUNNING @@ -401,25 +423,6 @@ function Player:buildNavalFactory(arguments) else return BTNodeResult.FAILURE end end -function Player:buildNavalForce(arguments) - transports = self:getUnitsByClass(UnitClass.TRANSPORT, -1) - - if #transports >= #self.taskForces then return BTNodeResult.SUCCESS end - - factory = self:getUnitsByClass(UnitClass.NAVAL_FACTORY, 1)[1]:toFactory() - - if #factory:getQueue() > 0 then return BTNodeResult.RUNNING end - - transportBuildId = self.unitClassMappings[UnitClass.TRANSPORT + 1][self:getFaction() + 1] - numBuildTransports = #self.taskForces - #transports - - for i = 1, numBuildTransports do - factory:appendToQueue(self:getBuildableUnitSlotId(factory, transportBuildId)) - end - - return BTNodeResult.RUNNING -end - function Player:buildTaskForces(arguments) factory = self:getUnitsByClass(arguments.factoryClass, 1) @@ -489,7 +492,7 @@ function Player:formLandTaskForces(arguments) for i = 1, numSpawnPoints do if i - 1 == self:getSpawnPointId() then goto continue end - self.taskForces[tfId] = {units = {}, moving = false, arrived = false, landed = false, spawnPointId = i - 1} + self.taskForces[tfId] = {units = {}, moving = false, attacking = false, arrived = false, landed = false, spawnPointId = i - 1} for j = 1, #self.landTaskForceData do subArrId = (tfId - 1) * self.landTaskForceData[j].numUnits + 1 @@ -529,7 +532,6 @@ function Player:getTransportData(transports) end function Player:boardTransports(arguments) - print('executing BOARD ' .. arguments.taskForceId) taskForce = self.taskForces[arguments.taskForceId] if taskForce.landed then return BTNodeResult.SUCCESS end @@ -621,31 +623,41 @@ function Player:occupySpawnPoint(arguments) end if #self.taskForces[tfId].units > 0 then - if self.taskForces[tfId].moving then - return BTNodeResult.RUNNING - elseif self.taskForces[tfId].arrived then + if self.taskForces[tfId].arrived then return BTNodeResult.SUCCESS end elseif #self.taskForces[tfId].units == 0 then return BTNodeResult.FAILURE end + hostileUnits = self:getHostileUnits() spawnPoint = Map.getSingleton():getSpawnPoint(self.taskForces[tfId].spawnPointId) minDist = 10 - for i = 1, #self.taskForces[tfId].units do - if self.taskForces[tfId].units[i]:getPos():getDistanceFrom(spawnPoint) < minDist then - self.taskForces[tfId].moving = false - self.taskForces[tfId].arrived = true - return BTNodeResult.SUCCESS + if #hostileUnits == 0 then + for i = 1, #self.taskForces[tfId].units do + if self.taskForces[tfId].units[i]:getPos():getDistanceFrom(spawnPoint) < minDist then + self.taskForces[tfId].moving = false + self.taskForces[tfId].arrived = true + return BTNodeResult.SUCCESS + end end - end - if not self.taskForces[tfId].moving then + if not self.taskForces[tfId].moving then + self.taskForces[tfId].moving = true + self:deselectUnits() + self:selectUnits(self.taskForces[tfId].units) + self:issueOrder(OrderType.MOVE, Vector3:new(0, 0, 0), {Target:new(nil, spawnPoint)}, false) + end + + self.taskForces[tfId].attacking = false self.taskForces[tfId].moving = true + elseif #hostileUnits > 0 and self.taskForces[tfId].units[1]:getNumOrders() == 0 then self:deselectUnits() self:selectUnits(self.taskForces[tfId].units) - self:issueOrder(OrderType.MOVE, Vector3:new(0, 0, 0), {Target:new(nil, spawnPoint)}, false) + self:issueOrder(OrderType.ATTACK, Vector3:new(0, 0, 0), {Target:new(hostileUnits[1]:toGameObject(), Vector3:new(0, 0, 0))}, false) + self.taskForces[tfId].attacking = true + self.taskForces[tfId].movign = false end return BTNodeResult.RUNNING diff --git a/Assets/Scripts/Core/playerInit.lua b/Assets/Scripts/Core/playerInit.lua index b8fa9f4..2c7a289 100644 --- a/Assets/Scripts/Core/playerInit.lua +++ b/Assets/Scripts/Core/playerInit.lua @@ -5,6 +5,7 @@ for i = 1, #game.cpuPlayers do player.behaviour = { type = BTNodeType.SEQUENCE, children = { + --{type = BTNodeType.FUNCTION, func = player.updateTaskForces}, {type = BTNodeType.FUNCTION, func = player.buildLandFactory}, { type = BTNodeType.FUNCTION, diff --git a/gameManager.cpp b/gameManager.cpp index 3ec792e..ef5311d 100755 --- a/gameManager.cpp +++ b/gameManager.cpp @@ -148,6 +148,7 @@ namespace battleship{ "getFaction", &Player::getFaction, "getTeam", &Player::getTeam, "getUnits", &Player::getUnits, + "getHostileUnits", &Player::getHostileUnits, "getUnitsById", &Player::getUnitsById, "getUnitsByClass", &Player::getUnitsByClass ); diff --git a/player.cpp b/player.cpp index 6592f60..72efd66 100755 --- a/player.cpp +++ b/player.cpp @@ -308,6 +308,17 @@ namespace battleship{ return friendlyUnits; } + vector Player::getHostileUnits(){ + vector hostileUnits, friendlyUnits = getFriendlyUnits(true); + + for (Player *pl : Game::getSingleton()->getPlayers(true)) + for (Unit *u : pl->getUnits()) + if(pl->getTeam() != getTeam() && isObjectVisible(u, friendlyUnits)) + hostileUnits.push_back(u); + + return hostileUnits; + } + //TODO improve this for greater accuracy bool Player::isObjectVisible(GameObject *object, std::vector friendlyUnits) { Player *objPlayer = object->getPlayer(); diff --git a/player.h b/player.h index e104312..3fc2f55 100755 --- a/player.h +++ b/player.h @@ -41,6 +41,7 @@ namespace battleship{ std::vector getDestructables(); void updateGameStats(Unit*); std::vector getFriendlyUnits(bool = true); + std::vector getHostileUnits(); bool isObjectVisible(GameObject*, std::vector); inline int getResource(ResourceType rt){return resources[(int)rt];} inline void updateResource(ResourceType rt, int amount, bool add){resources[(int)rt] = (add ? resources[(int)rt] + amount : amount);} diff --git a/vehicle.cpp b/vehicle.cpp index cc02956..8c5d338 100644 --- a/vehicle.cpp +++ b/vehicle.cpp @@ -238,6 +238,8 @@ namespace battleship{ } void Vehicle::navigate(float destOffset){ + if(pathPoints.empty()) return; + Vector3 hypVec = (pathPoints[0] - pos); Vector3 baseDir = getVecToPlane(pos, hypVec, upVec); float angle = baseDir.getAngleBetween(dirVec); @@ -253,8 +255,7 @@ namespace battleship{ void Vehicle::move(Order order) { navigate(0.5 * Map::getSingleton()->getCellSize().x); - if(pathPoints.empty()) - removeOrder(0); + if(pathPoints.empty()) removeOrder(0); } void Vehicle::exitGarrisonable(Vector3 exitPos){ From 6c55a255c07d36a017b81495bbd912153a3c1a24 Mon Sep 17 00:00:00 2001 From: devZoGok Date: Wed, 4 Mar 2026 19:40:05 +0200 Subject: [PATCH 15/22] improved CPU player AI resource harvesting --- Assets/Scripts/Core/player.lua | 92 ++++++++++++++---------------- Assets/Scripts/Core/playerInit.lua | 14 ++++- gameManager.cpp | 12 +++- 3 files changed, 63 insertions(+), 55 deletions(-) diff --git a/Assets/Scripts/Core/player.lua b/Assets/Scripts/Core/player.lua index ead20ef..56babb3 100644 --- a/Assets/Scripts/Core/player.lua +++ b/Assets/Scripts/Core/player.lua @@ -1,4 +1,3 @@ -Player.numStartEngis = 3 Player.baseQuat = nil Player.baseRadius = 180 Player.numDefWarMechs = 1 @@ -274,30 +273,11 @@ function Player:buildLandFactory(arguments) if pos then self:buildStructure(engi, factId, pos, self.baseQuat:getAngle()) factory = self:getUnitsByClass(UnitClass.LAND_FACTORY, 1)[1]:toFactory() - factory:setRallyPoint(factory:getPos():add(factory:getDirVec():mult(40))) + factory:setRallyPoint(factory:getPos():add(factory:getDirVec():mult(50))) return BTNodeResult.RUNNING else return BTNodeResult.FAILURE end end -function Player:trainEngineers(arguments) - engis = self:getUnitsByClass(UnitClass.CYBORG_ENGINEER, -1) - - if #engis >= self.numStartEngis then return BTNodeResult.SUCCESS end - - factory = self:getUnitsByClass(UnitClass.LAND_FACTORY, -1)[1]:toFactory() - - if #factory:getQueue() > 0 then return BTNodeResult.RUNNING end - - engiBuildId = self.unitClassMappings[UnitClass.CYBORG_ENGINEER + 1][self:getFaction() + 1] - numBuildEngis = self.numStartEngis - #engis - - for i = 1, numBuildEngis do - factory:appendToQueue(self:getBuildableUnitSlotId(factory, engiBuildId)) - end - - return BTNodeResult.RUNNING -end - function Player:buildRefinery(arguments) refinery = self:getUnitsByClass(UnitClass.REFINERY, 1)[1] if refinery then @@ -320,47 +300,42 @@ function Player:buildRefinery(arguments) else return BTNodeResult.FAILURE end end --- TODO optimize deposit position check -function Player:buildExtractor(arguments) - extractor = self:getUnitsByClass(UnitClass.EXTRACTOR, -1) - - if #extractor > 0 then - return (extractor[1]:toStructure():isComplete() and BTNodeResult.SUCCESS or BTNodeResult.RUNNING) - end - +function Player:getVisibleDeposits(nearBase) visibleDeposits = {} players = Game.getSingleton():getPlayers(true) + friendlyUnits = self:getFriendlyUnits(true) + sp = Map.getSingleton():getSpawnPoint(self:getSpawnPointId()) for i = 1, #players do deposits = players[i]:getResourceDeposits() for j = 1, #deposits do - for k = 1, #self:getUnits() do - unit = self:getUnit(k - 1) - - if unit:getPos():getDistanceFrom(deposits[j]:getPos()) <= unit:getLineOfSight() and not deposits[j]:getExtractor() then - visibleDeposits[#visibleDeposits + 1] = deposits[i] - goto continue + if self:isObjectVisible(deposits[j]:toGameObject(), friendlyUnits) then + if not nearBase or (nearBase and deposits[j]:getPos():getDistanceFrom(sp) < self.baseRadius) then + visibleDeposits[#visibleDeposits + 1] = deposits[j] end end - - ::continue:: end end - if #visibleDeposits == 0 then return BTNodeResult.FAILURE end + return visibleDeposits +end - depPos = visibleDeposits[1]:getPos() - spawnPoint = Map.getSingleton():getSpawnPoint(self:getSpawnPointId()) +function Player:buildExtractors(arguments) + extractors = self:getUnitsByClass(UnitClass.EXTRACTOR, -1) + visibleDeposits = self:getVisibleDeposits() + depId = nil for i = 1, #visibleDeposits do - if visibleDeposits[i]:getPos():getDistanceFrom(spawnPoint) < depPos:getDistanceFrom(spawnPoint) then - depPos = deposits[i]:getPos() + if not visibleDeposits[i]:getExtractor() then + depId = i + break end end - self:buildStructure(self:findIdleEngineer(), UnitId.EXTRACTOR, depPos, 0) + if not depId then return BTNodeResult.SUCCESS end + self:buildStructure(self:findIdleEngineer(), UnitId.EXTRACTOR, visibleDeposits[depId]:getPos(), 0) return BTNodeResult.RUNNING end @@ -385,21 +360,40 @@ end function Player:startHarvesting() harvesters = self:getUnitsByClass(UnitClass.RESOURCE_ROVER, -1) + harvester = nil for i = 1, #harvesters do - if harvesters[i]:getNumOrders() > 0 and harvesters[i]:getOrder(0).type == 7 then + order = (harvesters[i]:getNumOrders() > 0 and harvesters[i]:getOrder(0) or nil) + + if i == #harvesters and order.type == OrderType.SUPPLY then return BTNodeResult.SUCCESS + elseif not order or (order and order.type ~= OrderType.SUPPLY) then + harvester = harvesters[i] + break end end - extractor = self:getUnitsByClass(UnitClass.EXTRACTOR, 1)[1] - if not extractor then return BTNodeResult.FAILURE end + extractors = self:getUnitsByClass(UnitClass.EXTRACTOR, -1) + if #extractors == 0 then return BTNodeResult.FAILURE end + + for i = #extractors, 1, -1 do + if extractors[i]:toExtractor():getDeposit():getAmmount() == 0 then + table.remove(extractors, i) + end + end + + minDistId = 1 + for i = 1, #extractors do + if extractors[i]:getPos():getDistanceFrom(harvester:getPos()) < extractors[minDistId]:getPos():getDistanceFrom(harvester:getPos()) then + minDistId = i + end + end self:deselectUnits() - self:selectUnits({harvesters[1]}) - self:issueOrder(OrderType.SUPPLY, Vector3:new(0, 0, 0), {Target:new(extractor:toGameObject(), Vector3:new(0, 0, 0))}, false) + self:selectUnits({harvester}) + self:issueOrder(OrderType.SUPPLY, Vector3:new(0, 0, 0), {Target:new(extractors[minDistId]:toGameObject(), Vector3:new(0, 0, 0))}, false) - return BTNodeResult.SUCCESS + return BTNodeResult.RUNNING end function Player:canReachAllSpawnPoints(arguments) diff --git a/Assets/Scripts/Core/playerInit.lua b/Assets/Scripts/Core/playerInit.lua index 2c7a289..a6fce0c 100644 --- a/Assets/Scripts/Core/playerInit.lua +++ b/Assets/Scripts/Core/playerInit.lua @@ -12,7 +12,7 @@ for i = 1, #game.cpuPlayers do func = player.buildTaskForces, args = { factoryClass = UnitClass.LAND_FACTORY, - taskForceData = {{class = UnitClass.CYBORG_ENGINEER, numUnits = 3}} + taskForceData = {{class = UnitClass.CYBORG_ENGINEER, numUnits = 5}} } }, { @@ -22,12 +22,20 @@ for i = 1, #game.cpuPlayers do { type = BTNodeType.SEQUENCE, children = { - {type = BTNodeType.FUNCTION, func = player.buildExtractor}, - {type = BTNodeType.FUNCTION, func = player.buildHarvester}, + {type = BTNodeType.FUNCTION, func = player.buildExtractors}, + { + type = BTNodeType.FUNCTION, + func = player.buildTaskForces, + args = { + factoryClass = UnitClass.LAND_FACTORY, + taskForceData = {{class = UnitClass.RESOURCE_ROVER, numUnits = 4}} + } + }, } } } }, + {type = BTNodeType.FUNCTION, func = player.startHarvesting}, { type = BTNodeType.SELECTOR, children = { diff --git a/gameManager.cpp b/gameManager.cpp index ef5311d..ca2167f 100755 --- a/gameManager.cpp +++ b/gameManager.cpp @@ -89,6 +89,7 @@ namespace battleship{ "toEngineer", [](Unit *u){return (Engineer*)u;}, "toStructure", [](Unit *u){return (Structure*)u;}, "toFactory", [](Unit *u){return (Factory*)u;}, + "toExtractor", [](Unit *u){return (Extractor*)u;}, "toPointDefense", [](Unit *u){return (PointDefense*)u;} ); @@ -149,18 +150,23 @@ namespace battleship{ "getTeam", &Player::getTeam, "getUnits", &Player::getUnits, "getHostileUnits", &Player::getHostileUnits, + "getFriendlyUnits", &Player::getFriendlyUnits, "getUnitsById", &Player::getUnitsById, - "getUnitsByClass", &Player::getUnitsByClass + "getUnitsByClass", &Player::getUnitsByClass, + "isObjectVisible", &Player::isObjectVisible ); SOL_LUA_STATE.new_usertype( - "Extractor", sol::constructors() + "Extractor", sol::constructors(), + "getDeposit", &Extractor::getDeposit ); SOL_LUA_STATE.new_usertype( "ResourceDeposit", sol::constructors(), "getExtractor", &ResourceDeposit::getExtractor, - "getPos", &GameObject::getPos + "getAmmount", &ResourceDeposit::getAmmount, + "getPos", &GameObject::getPos, + "toGameObject", [](ResourceDeposit *rd){return (GameObject*)rd;} ); SOL_LUA_STATE.new_usertype( From a61f3eebdf200ef06134fa1ff522c88fc9b9b555 Mon Sep 17 00:00:00 2001 From: devZoGok Date: Thu, 5 Mar 2026 20:10:58 +0200 Subject: [PATCH 16/22] improvements to reform taskforces upon destruction --- Assets/Scripts/Core/player.lua | 45 ++++++++++-------------------- Assets/Scripts/Core/playerInit.lua | 1 - player.cpp | 16 +++++++++++ player.h | 1 + unit.cpp | 14 ++++++++++ 5 files changed, 46 insertions(+), 31 deletions(-) diff --git a/Assets/Scripts/Core/player.lua b/Assets/Scripts/Core/player.lua index 56babb3..c944d28 100644 --- a/Assets/Scripts/Core/player.lua +++ b/Assets/Scripts/Core/player.lua @@ -237,12 +237,12 @@ function Player:getBuildableUnitSlotId(buildingUnit, buildableUnitId) end function Player:updateTaskForces(arguments) - units = self:getUnits() + numUnits = self:getNumUnits() for i = 1, #self.taskForces do for j = #self.taskForces[i].units, 1, -1 do - for k = 1, #units do - if self.taskForces[i].units[j] == units[k] then goto continue end + for k = 1, numUnits do + if self.taskForces[i].units[j] == self:getUnit(k - 1) then goto continue end end table.remove(self.taskForces[i].units, j) @@ -339,25 +339,6 @@ function Player:buildExtractors(arguments) return BTNodeResult.RUNNING end -function Player:buildHarvester(arguments) - if #self:getUnitsByClass(UnitClass.RESOURCE_ROVER, 1) > 0 then - return BTNodeResult.SUCCESS - end - - factory = self:getUnitsByClass(UnitClass.LAND_FACTORY, 1)[1] - - if not factory then return BTNodeResult.FAILURE end - - factory = factory:toFactory() - roverSlotId = self.unitClassMappings[UnitClass.RESOURCE_ROVER + 1][self:getFaction() + 1] - - if #factory:getQueue() == 0 then - factory:appendToQueue(self:getBuildableUnitSlotId(factory, roverSlotId)) - end - - return BTNodeResult.RUNNING -end - function Player:startHarvesting() harvesters = self:getUnitsByClass(UnitClass.RESOURCE_ROVER, -1) harvester = nil @@ -365,7 +346,7 @@ function Player:startHarvesting() for i = 1, #harvesters do order = (harvesters[i]:getNumOrders() > 0 and harvesters[i]:getOrder(0) or nil) - if i == #harvesters and order.type == OrderType.SUPPLY then + if i == #harvesters and order and order.type == OrderType.SUPPLY then return BTNodeResult.SUCCESS elseif not order or (order and order.type ~= OrderType.SUPPLY) then harvester = harvesters[i] @@ -383,10 +364,13 @@ function Player:startHarvesting() end minDistId = 1 + hPos = harvester:getPos() + for i = 1, #extractors do - if extractors[i]:getPos():getDistanceFrom(harvester:getPos()) < extractors[minDistId]:getPos():getDistanceFrom(harvester:getPos()) then - minDistId = i - end + currDist = extractors[i]:getPos():getDistanceFrom(hPos) + minDist = extractors[minDistId]:getPos():getDistanceFrom(hPos) + + if currDist < minDist then minDistId = i end end self:deselectUnits() @@ -612,15 +596,16 @@ end function Player:occupySpawnPoint(arguments) tfId = arguments.taskForceId + if not self.taskForces[tfId] then return BTNodeResult.FAILURE end + if not self.spawnPointData[self.taskForces[tfId].spawnPointId + 1].reachable and not self.taskForces[tfId].landed then return BTNodeResult.FAILURE end - if #self.taskForces[tfId].units > 0 then - if self.taskForces[tfId].arrived then - return BTNodeResult.SUCCESS - end + if #self.taskForces[tfId].units > 0 and self.taskForces[tfId].arrived then + return BTNodeResult.SUCCESS elseif #self.taskForces[tfId].units == 0 then + table.remove(self.taskForces, tfId) return BTNodeResult.FAILURE end diff --git a/Assets/Scripts/Core/playerInit.lua b/Assets/Scripts/Core/playerInit.lua index a6fce0c..2d67779 100644 --- a/Assets/Scripts/Core/playerInit.lua +++ b/Assets/Scripts/Core/playerInit.lua @@ -5,7 +5,6 @@ for i = 1, #game.cpuPlayers do player.behaviour = { type = BTNodeType.SEQUENCE, children = { - --{type = BTNodeType.FUNCTION, func = player.updateTaskForces}, {type = BTNodeType.FUNCTION, func = player.buildLandFactory}, { type = BTNodeType.FUNCTION, diff --git a/player.cpp b/player.cpp index 72efd66..16558b2 100755 --- a/player.cpp +++ b/player.cpp @@ -154,6 +154,12 @@ namespace battleship{ delete units[id]; units.erase(units.begin() + id); + + if(cpuPlayer){ + sol::state_view SOL_LUA_VIEW = generateView(); + int id = getCpuPlayerId() + 1; + SOL_LUA_VIEW.script("game.cpuPlayers[" + to_string(id) + "]:updateTaskForces()"); + } } void Player::removeResourceDeposit(int id){ @@ -339,4 +345,14 @@ namespace battleship{ return false; } + + int Player::getCpuPlayerId(){ + vector cpuPlayers = Game::getSingleton()->getCpuPlayers(); + + for(int i = 0; i < cpuPlayers.size(); i++) + if(cpuPlayers[i] == this) + return i; + + return -1; + } } diff --git a/player.h b/player.h index 3fc2f55..13afc78 100755 --- a/player.h +++ b/player.h @@ -43,6 +43,7 @@ namespace battleship{ std::vector getFriendlyUnits(bool = true); std::vector getHostileUnits(); bool isObjectVisible(GameObject*, std::vector); + int getCpuPlayerId(); inline int getResource(ResourceType rt){return resources[(int)rt];} inline void updateResource(ResourceType rt, int amount, bool add){resources[(int)rt] = (add ? resources[(int)rt] + amount : amount);} inline Trader* getTrader(){return trader;} diff --git a/unit.cpp b/unit.cpp index 02a550f..70e4537 100755 --- a/unit.cpp +++ b/unit.cpp @@ -306,6 +306,20 @@ namespace battleship{ if(!currOrderStarted) startCurrentOrder(); + if(!orders[0].targets.empty() && orders[0].targets[0].unit){ + vector units; + + for(Player *pl : Game::getSingleton()->getPlayers(true)){ + vector us = pl->getUnits(); + units.insert(units.end(), us.begin(), us.end()); + } + + if(find(units.begin(), units.end(), orders[0].targets[0].unit) == units.end()){ + removeOrder(0); + return; + } + } + switch (orders[0].type) { case Order::TYPE::ATTACK: attack(orders[0]); From 0ce020069857ca1ee380355d56c4f4cea90d88b4 Mon Sep 17 00:00:00 2001 From: devZoGok Date: Fri, 6 Mar 2026 16:55:16 +0200 Subject: [PATCH 17/22] CPU players use cruisers to soften up hostile defenses vehicles only choose unblocked cells according to their type --- Assets/Scripts/Core/player.lua | 91 +++++++++++++++++++++++++++++++--- vehicle.cpp | 31 +++++++++--- 2 files changed, 107 insertions(+), 15 deletions(-) diff --git a/Assets/Scripts/Core/player.lua b/Assets/Scripts/Core/player.lua index c944d28..c32e794 100644 --- a/Assets/Scripts/Core/player.lua +++ b/Assets/Scripts/Core/player.lua @@ -15,7 +15,7 @@ Player.unitClassMappings = { {UnitId.ACS_CYBORG_ENGINEER, UnitId.AINC_CYBORG_ENGINEER, UnitId.ER_CYBORG_ENGINEER}, {UnitId.ACS_TRANSPORT, UnitId.AINC_TRANSPORT, UnitId.ER_TRANSPORT}, {UnitId.ACS_CARGO_SHIP, UnitId.AINC_CARGO_SHIP, UnitId.ER_CARGO_SHIP}, - {UnitId.ACS_CRUISER, UnitId.AINC_CRUISER, UnitId.ER_CRUISER}, + {UnitId.ACS_ASSAULT_CRUISER, UnitId.AINC_ASSAULT_CRUISER, UnitId.ER_ASSAULT_CRUISER}, {UnitId.ACS_ANTI_SUB_CRUISER, UnitId.AINC_ANTI_SUB_CRUISER, UnitId.ER_ANTI_SUB_CRUISER}, {nil, nil, nil}, {UnitId.ACS_SUBMARINE, UnitId.AINC_SUBMARINE, UnitId.ER_SUBMARINE}, @@ -39,9 +39,9 @@ Player.landTaskForceData = { } Player.navalTaskForceData = { - {class = UnitClass.TRANSPORT, numUnits = 3}, - --{class = UnitClass.CRUISER, numUnits = 1}, - --{class = UnitClass.ANTI_SUB_CRUISER, numUnits = 1} + {class = UnitClass.TRANSPORT, numUnits = 2}, + --{class = UnitClass.CRUISER, numUnits = 2}, + {class = UnitClass.ANTI_SUB_CRUISER, numUnits = 1} } Player.taskForces = {} @@ -131,9 +131,12 @@ function Player:landRouteToSpawnpoint_(mapPoint) embarkCellId = nil disembarkCellId = nil + navalSupportCellId = nil + navalRallyCellId = nil 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; @@ -142,13 +145,20 @@ function Player:landRouteToSpawnpoint_(mapPoint) for i = #path, 1, -1 do if map:getCell(path[i]).type == 1 then + navalSupportCellId = path[i] disembarkCellId = path[i + 1] break; end end if embarkCellId or disembarkCellId then - return {reachable = false, embarkCellId = embarkCellId, disembarkCellId = disembarkCellId} + return { + reachable = false, + embarkCellId = embarkCellId, + navalRallyCellId = navalRallyCellId, + navalSupportCellId = navalSupportCellId, + disembarkCellId = disembarkCellId + } else return {reachable = true} end end @@ -519,6 +529,13 @@ function Player:boardTransports(arguments) tpData = self:getTransportData(transports) if tpData.allIdle and tpData.allInWater then + self:deselectUnits() + self:selectUnits(self:getUnitsByClass(UnitClass.CRUISER, -1)) + self:selectUnits(self:getUnitsByClass(UnitClass.ANTI_SUB_CRUISER, -1)) + + rallyCell = map:getCell(self.spawnPointData[taskForce.spawnPointId + 1].navalRallyCellId) + self:issueOrder(OrderType.MOVE, Vector3:new(0, 0, 0), {Target:new(nil, rallyCell.pos)}, false) + self:deselectUnits() self:selectUnits(transports) @@ -588,11 +605,63 @@ function Player:moveTransports(arguments) self:deselectUnits() self:selectUnits(transports) self:issueOrder(OrderType.MOVE, Vector3:new(0, 0, 0), {Target:new(nil, targPos)}, false) + self:deselectUnits() + self:selectUnits(self:getUnitsByClass(UnitClass.CRUISER, -1)) + self:selectUnits(self:getUnitsByClass(UnitClass.ANTI_SUB_CRUISER, -1)) + + targPos = Map.getSingleton():getCell(self.spawnPointData[spId + 1].navalSupportCellId).pos + self:issueOrder(OrderType.MOVE, Vector3:new(0, 0, 0), {Target:new(nil, targPos)}, false) end if not tpData.allIdle then return BTNodeResult.RUNNING end end +function Player:getNearestHostileUnits(friendlyUnits, hostileUnits) +end + +function Player:clearShoreDefenses(arguments) + self:deselectUnits() + self:selectUnits(self:getUnitsByClass(UnitClass.CRUISER, -1)) + self:selectUnits(self:getUnitsByClass(UnitClass.ANTI_SUB_CRUISER, -1)) + selUnits = self:getSelectedUnits() + maxLineOfSight = nil + + for i = 1, #selUnits do + if not maxLineOfSight then maxLineOfSight = selUnits[i]:getLineOfSight() end + + if selUnits[i]:getLineOfSight() > maxLineOfSight then + maxLineOfSight = selUnits[i]:getLineOfSight() + end + end + + if not selUnits[1] then return BTNodeResult.FAILURE end + + hostileUnits = self:getHostileUnits() + order = (selUnits[1]:getNumOrders() > 0 and selUnits[1]:getOrder(0) or nil) + + for i = 1, #hostileUnits do + for j = 1, #selUnits do + if hostileUnits[i]:getPos():getDistanceFrom(selUnits[j]:getPos()) < maxLineOfSight then + + if not order or (order and (order.type ~= OrderType.ATTACK or (order.type == OrderType.ATTACK and order.targets[1].unit ~= hostileUnits[i]))) then + self:issueOrder(OrderType.ATTACK, Vector3:new(0, 0, 0), {Target:new(hostileUnits[i]:toGameObject(), hostileUnits[i]:getPos())}, false) + end + + return BTNodeResult.RUNNING + end + end + end + + map = Map.getSingleton() + supportCellId = self.spawnPointData[arguments.spawnPointId + 1].navalSupportCellId + + if map:getCellId(selUnits[1]:getPos(), true) ~= supportCellId and not order or (order and order.type ~= OrderType.MOVE) then + self:issueOrder(OrderType.MOVE, Vector3:new(0, 0, 0), {Target:new(nil, map:getCell(supportCellId).pos)}, false) + end + + return BTNodeResult.SUCCESS +end + function Player:occupySpawnPoint(arguments) tfId = arguments.taskForceId @@ -661,10 +730,16 @@ function Player:generateTaskForceActions() children = { {type = BTNodeType.FUNCTION, func = self.landRouteToSpawnpoint, args = arguments}, { - type = BTNodeType.SEQUENCE, + type = BTNodeType.PARALLEL, children = { - {type = BTNodeType.FUNCTION, func = self.boardTransports, args = arguments}, - {type = BTNodeType.FUNCTION, func = self.moveTransports, args = arguments}, + { + type = BTNodeType.SEQUENCE, + children = { + {type = BTNodeType.FUNCTION, func = self.boardTransports, args = arguments}, + {type = BTNodeType.FUNCTION, func = self.moveTransports, args = arguments}, + } + }, + {type = BTNodeType.FUNCTION, func = self.clearShoreDefenses, args = {spawnPointId = i - 1}} } } } diff --git a/vehicle.cpp b/vehicle.cpp index 8c5d338..c4085b6 100644 --- a/vehicle.cpp +++ b/vehicle.cpp @@ -339,14 +339,31 @@ namespace battleship{ if(cells[dest].blockedBy){ vector surrCellIds = map->getSurroundingCells(cells[dest].pos, 1); - for(int scid : surrCellIds) - if(!cells[scid].blockedBy){ - dest = scid; - break; - } - } + for(int scid : surrCellIds){ + if(!cells[scid].blockedBy) + switch(type){ + case UnitType::HOVER: + dest = scid; + return true; + case UnitType::LAND: + if(cells[scid].type == Map::Cell::LAND){ + dest = scid; + return true; + } + break; + case UnitType::SEA_LEVEL: + case UnitType::UNDERWATER: + if(cells[scid].type == Map::Cell::WATER){ + dest = scid; + return true; + } + break; + } + } - return true; + return false; + } + else return true; } bool Vehicle::truncatePath(Order &order, vector &path, Vector3 destPos, bool appendDestPos){ From c9f690c719a78b574d1302840269237901a24193 Mon Sep 17 00:00:00 2001 From: devZoGok Date: Sat, 7 Mar 2026 12:58:47 +0200 Subject: [PATCH 18/22] synchronised naval unit movement towards spawn points --- Assets/Scripts/Core/player.lua | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/Assets/Scripts/Core/player.lua b/Assets/Scripts/Core/player.lua index c32e794..a647245 100644 --- a/Assets/Scripts/Core/player.lua +++ b/Assets/Scripts/Core/player.lua @@ -593,10 +593,13 @@ function Player:moveTransports(arguments) nearDisembarkPoint = (transports[i]:getPos():getDistanceFrom(targPos) < 10) if i == #transports and nearDisembarkPoint then + self.taskForces[arguments.taskForceId].landed = true + self:deselectUnits() self:selectUnits(transports) self:issueOrder(OrderType.EJECT, Vector3:new(0, 0, 0), {}, false) - self.taskForces[arguments.taskForceId].landed = true + self:selectUnits(self:getUnitsByClass(UnitClass.CRUISER, -1)) + self:selectUnits(self:getUnitsByClass(UnitClass.ANTI_SUB_CRUISER, -1)) self:issueOrder(OrderType.MOVE, Vector3:new(0, 0, 0), {Target:new(nil, self.navalRallyPoint)}, false) return BTNodeResult.SUCCESS elseif not nearDisembarkPoint then break end @@ -620,6 +623,8 @@ function Player:getNearestHostileUnits(friendlyUnits, hostileUnits) end function Player:clearShoreDefenses(arguments) + if self.taskForces[arguments.taskForceId].landed then return BTNodeResult.SUCCESS end + self:deselectUnits() self:selectUnits(self:getUnitsByClass(UnitClass.CRUISER, -1)) self:selectUnits(self:getUnitsByClass(UnitClass.ANTI_SUB_CRUISER, -1)) @@ -652,8 +657,9 @@ function Player:clearShoreDefenses(arguments) end end + spawnPointId = self.taskForces[arguments.taskForceId].spawnPointId + 1 + supportCellId = self.spawnPointData[spawnPointId].navalSupportCellId map = Map.getSingleton() - supportCellId = self.spawnPointData[arguments.spawnPointId + 1].navalSupportCellId if map:getCellId(selUnits[1]:getPos(), true) ~= supportCellId and not order or (order and order.type ~= OrderType.MOVE) then self:issueOrder(OrderType.MOVE, Vector3:new(0, 0, 0), {Target:new(nil, map:getCell(supportCellId).pos)}, false) @@ -730,16 +736,16 @@ function Player:generateTaskForceActions() children = { {type = BTNodeType.FUNCTION, func = self.landRouteToSpawnpoint, args = arguments}, { - type = BTNodeType.PARALLEL, + type = BTNodeType.SEQUENCE, children = { + {type = BTNodeType.FUNCTION, func = self.boardTransports, args = arguments}, { - type = BTNodeType.SEQUENCE, + type = BTNodeType.PARALLEL, children = { - {type = BTNodeType.FUNCTION, func = self.boardTransports, args = arguments}, {type = BTNodeType.FUNCTION, func = self.moveTransports, args = arguments}, + {type = BTNodeType.FUNCTION, func = self.clearShoreDefenses, args = arguments} } }, - {type = BTNodeType.FUNCTION, func = self.clearShoreDefenses, args = {spawnPointId = i - 1}} } } } From 3df38f8edac6020d570356c8b9c8b8cd79fab154 Mon Sep 17 00:00:00 2001 From: devZoGok Date: Sat, 7 Mar 2026 18:32:26 +0200 Subject: [PATCH 19/22] CPU players reform empty task forces --- Assets/Scripts/Core/player.lua | 59 ++++++++++++++++++++---------- Assets/Scripts/Core/playerInit.lua | 4 +- 2 files changed, 42 insertions(+), 21 deletions(-) diff --git a/Assets/Scripts/Core/player.lua b/Assets/Scripts/Core/player.lua index a647245..2b7fd58 100644 --- a/Assets/Scripts/Core/player.lua +++ b/Assets/Scripts/Core/player.lua @@ -172,10 +172,18 @@ function Player:init() self:initPdSpots() for i = 1, Map.getSingleton():getNumSpawnPoints() do - if i - 1 == self:getSpawnPointId() then self.spawnPointData[i] = {reachable = true} end + taskForce = {units = {}, moving = false, attacking = false, arrived = false, landed = false, spawnPointId = i - 1} + if i - 1 == self:getSpawnPointId() then + taskForce.arrived = true + taskForce.landed = true + self.spawnPointData[i] = {reachable = true} + end + + self.taskForces[i] = taskForce spawnPoint = Map.getSingleton():getSpawnPoint(i - 1) self.spawnPointData[i] = self:landRouteToSpawnpoint_(spawnPoint) + end end @@ -259,6 +267,13 @@ function Player:updateTaskForces(arguments) ::continue:: end + + if #self.taskForces[i].units == 0 then + self.taskForces[i].arrived = false + self.taskForces[i].moving = false + self.taskForces[i].attacking = false + self.taskForces[i].landed = false + end end return BTNodeResult.SUCCESS @@ -283,7 +298,7 @@ function Player:buildLandFactory(arguments) if pos then self:buildStructure(engi, factId, pos, self.baseQuat:getAngle()) factory = self:getUnitsByClass(UnitClass.LAND_FACTORY, 1)[1]:toFactory() - factory:setRallyPoint(factory:getPos():add(factory:getDirVec():mult(50))) + factory:setRallyPoint(factory:getPos():add(factory:getDirVec():mult(100))) return BTNodeResult.RUNNING else return BTNodeResult.FAILURE end end @@ -411,6 +426,16 @@ function Player:buildNavalFactory(arguments) else return BTNodeResult.FAILURE end end +function Player:getNumEmptyTaskForces() + numEmpty = 0 + + for i = 1, #self.taskForces do + if #self.taskForces[i].units == 0 then numEmpty = numEmpty + 1 end + end + + return numEmpty +end + function Player:buildTaskForces(arguments) factory = self:getUnitsByClass(arguments.factoryClass, 1) @@ -464,34 +489,35 @@ end function Player:formLandTaskForces(arguments) numSpawnPoints = Map.getSingleton():getNumSpawnPoints() - if #self.taskForces == numSpawnPoints - 1 then return BTNodeResult.SUCCESS end + for i = 1, #self.taskForces do + if i == #self.taskForces and #self.taskForces[i].units > 0 then return BTNodeResult.SUCCESS + else break end + end unitGroups = {} for i = 1, #self.landTaskForceData do unitGroups[i] = self:getUnitsByClass(self.landTaskForceData[i].class, -1) - enoughGroupUnits = (#unitGroups[i] >= (numSpawnPoints - 1) * self.landTaskForceData[i].numUnits) + enoughGroupUnits = (#unitGroups[i] >= #self.taskForces * self.landTaskForceData[i].numUnits) if not enoughGroupUnits then return BTNodeResult.FAILURE end end tfId = 1 - for i = 1, numSpawnPoints do - if i - 1 == self:getSpawnPointId() then goto continue end - - self.taskForces[tfId] = {units = {}, moving = false, attacking = false, arrived = false, landed = false, spawnPointId = i - 1} + for i = 1, #self.taskForces do + if #self.taskForces[i].units > 0 then goto continue end for j = 1, #self.landTaskForceData do - subArrId = (tfId - 1) * self.landTaskForceData[j].numUnits + 1 + subArrId = #unitGroups[j] - (tfId * self.landTaskForceData[j].numUnits) + 1 unitTbl = table.move( unitGroups[j], subArrId, - math.max(subArrId + self.landTaskForceData[j].numUnits - 1, 1), - #self.taskForces[tfId].units + 1, - self.taskForces[tfId].units + subArrId + self.landTaskForceData[j].numUnits - 1, + #self.taskForces[i].units + 1, + self.taskForces[i].units ) - self.taskForces[tfId].units = unitTbl + self.taskForces[i].units = unitTbl end tfId = tfId + 1 @@ -671,8 +697,6 @@ end function Player:occupySpawnPoint(arguments) tfId = arguments.taskForceId - if not self.taskForces[tfId] then return BTNodeResult.FAILURE end - if not self.spawnPointData[self.taskForces[tfId].spawnPointId + 1].reachable and not self.taskForces[tfId].landed then return BTNodeResult.FAILURE end @@ -680,7 +704,6 @@ function Player:occupySpawnPoint(arguments) if #self.taskForces[tfId].units > 0 and self.taskForces[tfId].arrived then return BTNodeResult.SUCCESS elseif #self.taskForces[tfId].units == 0 then - table.remove(self.taskForces, tfId) return BTNodeResult.FAILURE end @@ -726,9 +749,7 @@ function Player:generateTaskForceActions() tfId = 1 for i = 1, numSpawnPoints do - if i - 1 == self:getSpawnPointId() then goto continue end - - arguments = {taskForceId = tfId} + arguments = {taskForceId = i} table.insert( children[1].children, { diff --git a/Assets/Scripts/Core/playerInit.lua b/Assets/Scripts/Core/playerInit.lua index 2d67779..9f95c8f 100644 --- a/Assets/Scripts/Core/playerInit.lua +++ b/Assets/Scripts/Core/playerInit.lua @@ -11,7 +11,7 @@ for i = 1, #game.cpuPlayers do func = player.buildTaskForces, args = { factoryClass = UnitClass.LAND_FACTORY, - taskForceData = {{class = UnitClass.CYBORG_ENGINEER, numUnits = 5}} + taskForceData = {{class = UnitClass.CYBORG_ENGINEER, numUnits = 4}} } }, { @@ -63,7 +63,7 @@ for i = 1, #game.cpuPlayers do func = player.buildTaskForces, args = { factoryClass = UnitClass.LAND_FACTORY, - numTaskForces = Map.getSingleton():getNumSpawnPoints() - 1, + numTaskForces = player:getNumEmptyTaskForces(), taskForceData = player.landTaskForceData } }, From adc5f329f05cb1ba5bb3bab0aff55cebc68a3013 Mon Sep 17 00:00:00 2001 From: devZoGok Date: Sun, 8 Mar 2026 11:40:18 +0200 Subject: [PATCH 20/22] bugfix unpausing crashes --- activeGameState.cpp | 5 +++-- game.cpp | 5 ++--- game.h | 2 +- inGameAppState.cpp | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/activeGameState.cpp b/activeGameState.cpp index e652b40..4d26463 100755 --- a/activeGameState.cpp +++ b/activeGameState.cpp @@ -478,14 +478,15 @@ namespace battleship{ void ActiveGameState::renderUnits() { vector friendlyUnits = mainPlayer->getFriendlyUnits(); + Game *game = Game::getSingleton(); - for (Player *p : Game::getSingleton()->getPlayers(true)){ + for (Player *p : game->getPlayers(true)){ vector units = p->getUnits(); for (Unit *u : units) { if(u->isVehicle() && ((Vehicle*)u)->getGarrisonable()) continue; - bool unitVisible = mainPlayer->isObjectVisible((GameObject*)u, friendlyUnits); + bool unitVisible = (game->isDebug() || mainPlayer->isObjectVisible((GameObject*)u, friendlyUnits)); u->getModel()->setVisible(unitVisible); } } diff --git a/game.cpp b/game.cpp index a99ab19..10715c1 100644 --- a/game.cpp +++ b/game.cpp @@ -75,15 +75,14 @@ namespace battleship{ FxManager::getSingleton()->update(); } - void Game::initLuaPlayers(bool resetBehaviour){ + void Game::initLuaPlayers(){ sol::state_view SOL_LUA_VIEW = generateView(); vector cpuPlayers = getCpuPlayers(); for(int i = 0; i < cpuPlayers.size(); i++) SOL_LUA_VIEW["game"]["cpuPlayers"][i + 1] = cpuPlayers[i]; - if(resetBehaviour) - SOL_LUA_VIEW.script_file(GameManager::getSingleton()->getPath() + "Scripts/Core/playerInit.lua"); + SOL_LUA_VIEW.script_file(GameManager::getSingleton()->getPath() + "Scripts/Core/playerInit.lua"); } void Game::removeAllElements(){ diff --git a/game.h b/game.h index 19ffbff..1661f3f 100644 --- a/game.h +++ b/game.h @@ -20,7 +20,7 @@ namespace battleship{ public: static Game* getSingleton(); void update(); - void initLuaPlayers(bool = false); + void initLuaPlayers(); void togglePause(); void removeAllElements(); void initTechnologies(); diff --git a/inGameAppState.cpp b/inGameAppState.cpp index 281b147..23b23de 100755 --- a/inGameAppState.cpp +++ b/inGameAppState.cpp @@ -74,8 +74,8 @@ namespace battleship{ activeState = new ActiveGameState(guiState, playerId); stateManager->attachAppState(activeState); - Game::getSingleton()->initLuaPlayers(true); - Map::Minimap::getSingleton()->updateImage(); + Game::getSingleton()->initLuaPlayers(); + //Map::Minimap::getSingleton()->updateImage(); } void InGameAppState::onDettached() { From 2b850ca060a780fd5604d0a563cd82d74cff1071 Mon Sep 17 00:00:00 2001 From: devZoGok Date: Sun, 15 Mar 2026 17:40:28 +0200 Subject: [PATCH 21/22] conflict resolutions --- Assets/Scripts/Core/player.lua | 6 +++--- Assets/Scripts/Core/playerInit.lua | 2 +- Assets/Scripts/GameObjects/Units/unitData.lua | 20 +++++++++---------- cameraController.cpp | 3 +-- defConfigs.h | 2 +- 5 files changed, 16 insertions(+), 17 deletions(-) diff --git a/Assets/Scripts/Core/player.lua b/Assets/Scripts/Core/player.lua index 2b7fd58..6c22f47 100644 --- a/Assets/Scripts/Core/player.lua +++ b/Assets/Scripts/Core/player.lua @@ -33,8 +33,8 @@ Player.unitClassMappings = { } Player.landTaskForceData = { - {class = UnitClass.MECH, numUnits = 2}, - {class = UnitClass.TANK, numUnits = 1}, + {class = UnitClass.MECH, numUnits = 3}, + {class = UnitClass.TANK, numUnits = 2}, {class = UnitClass.ARTILLERY, numUnits = 1} } @@ -127,7 +127,7 @@ function Player:landRouteToSpawnpoint_(mapPoint) pf = Pathfinder.getSingleton() cells = map:getCells() heurs = pf:calcHeuristics(cells, dest) - path = pf:findPath(cells, heurs, source, dest, nil) + path = pf:findPath(cells, heurs, source, dest, -1) embarkCellId = nil disembarkCellId = nil diff --git a/Assets/Scripts/Core/playerInit.lua b/Assets/Scripts/Core/playerInit.lua index 9f95c8f..5071a07 100644 --- a/Assets/Scripts/Core/playerInit.lua +++ b/Assets/Scripts/Core/playerInit.lua @@ -11,7 +11,7 @@ for i = 1, #game.cpuPlayers do func = player.buildTaskForces, args = { factoryClass = UnitClass.LAND_FACTORY, - taskForceData = {{class = UnitClass.CYBORG_ENGINEER, numUnits = 4}} + taskForceData = {{class = UnitClass.CYBORG_ENGINEER, numUnits = 5}} } }, { diff --git a/Assets/Scripts/GameObjects/Units/unitData.lua b/Assets/Scripts/GameObjects/Units/unitData.lua index d1c924d..76b23db 100644 --- a/Assets/Scripts/GameObjects/Units/unitData.lua +++ b/Assets/Scripts/GameObjects/Units/unitData.lua @@ -178,7 +178,7 @@ units = { maxUnevenness = .5, isVehicle = true, health = 500, - buildTime = 1000, + buildTime = 600, cost = 500, size = {x = 6, y = 9.22, z = 2.42}, hitboxOffset = {x = 0, y = 4.5, z = 0}, @@ -189,7 +189,7 @@ units = { albedoPath = 'mech.jpg', colorNodes = {'WarRobotTemplate.001'}, selectionSfx = PATH .. 'Sounds/Units/WarMechs/selection.ogg', - speed = .3, + speed = .4, destinationOffset = .1, anglePrecision = .1, maxTurnAngle = .1, @@ -346,7 +346,7 @@ units = { { orderType = OrderType.ATTACK, rateOfFire = 1000, - damage = 200, + damage = 900, maxRange = 50, maxFireAngle = .1, nodes = {{name = 'TankASHead', rotationSpeed = .05, vertical = false}, {name = 'barell', rotationSpeed = .05, angleConstraints = {min = 0, max = .349}, vertical = true}}, @@ -392,7 +392,7 @@ units = { albedoPath = 'tank.jpg', colorNodes = {'TankASHead'}, selectionSfx = PATH .. 'Sounds/Units/Tanks/selection.ogg', - speed = .4, + speed = .5, destinationOffset = .1, anglePrecision = .1, maxTurnAngle = .1, @@ -556,7 +556,7 @@ units = { alignToSurface = true, isVehicle = true, health = 500, - buildTime = 1000, + buildTime = 1500, cost = 500, size = {x = 9.99, y = 15.4, z = 14.}, hitboxOffset = {x = 0, y = 7.69, z = .71}, @@ -567,7 +567,7 @@ units = { albedoPath = 'artillery.jpg', colorNodes = {'Asia_Mech.002'}, selectionSfx = PATH .. 'Sounds/Units/Tanks/selection.ogg', - speed = .3, + speed = .35, destinationOffset = .1, anglePrecision = .1, maxTurnAngle = .1, @@ -766,7 +766,7 @@ units = { colorNodes = {'Engineer_Asia.001'}, meshPath = 'acsCyborgEngineer.xml', selectionSfx = PATH .. 'Sounds/Units/Engineers/selection.ogg', - speed = .5, + speed = 1, destinationOffset = .1, anglePrecision = .1, maxTurnAngle = .1, @@ -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 = 200, + lineOfSight = 600, name = 'Engineer', basePath = PATH .. vehiclePrefix .. 'Engineers/', albedoPath = 'engineer.jpg', @@ -1380,7 +1380,7 @@ units = { meshPath = 'heavyAssaultCruiser.xml', albedoPath = 'cruiser.jpg', selectionSfx = PATH .. 'Sounds/Units/Cruisers/selection.ogg', - speed = .1, + speed = .4, destinationOffset = .1, anglePrecision = .1, maxTurnAngle = .1, @@ -1692,7 +1692,7 @@ units = { meshPath = 'acsAntiSubCruiser.xml', albedoPath = 'cruiser.jpg', selectionSfx = PATH .. 'Sounds/Units/Cruisers/selection.ogg', - speed = .3, + speed = .6, destinationOffset = .1, anglePrecision = .1, maxTurnAngle = .1, diff --git a/cameraController.cpp b/cameraController.cpp index 37a5403..fa6751e 100644 --- a/cameraController.cpp +++ b/cameraController.cpp @@ -45,8 +45,7 @@ namespace battleship{ } void CameraController::orientCamera(Vector3 rotAxis, double str){ - float minStr = .001; - Quaternion rotQuat = Quaternion(.5 * (fabs(str) > minStr ? str : 0), rotAxis); + Quaternion rotQuat = Quaternion(.025 * str, rotAxis); Camera *cam = Root::getSingleton()->getCamera(); Vector3 dir = rotQuat * cam->getDirection(), up = rotQuat * cam->getUp(); cam->lookAt(dir, up); diff --git a/defConfigs.h b/defConfigs.h index b0a5fc1..e11c178 100755 --- a/defConfigs.h +++ b/defConfigs.h @@ -21,7 +21,7 @@ namespace battleship{ const std::string DEFAULT_TEXTURE = "Textures/defaultTexture.jpg"; const double camPanSpeed = .5, CAMERA_DISTANCE = 100, CAMERA_ZOOM_INCREMENT = 1, cellLength = 7, cellWidth = 7, cellDepth = 7, DIST_FROM_RAY = 15; - const int maxNumGroups = 10, NUM_MAX_ZOOMS = 75, NUM_SUBDIVS = 100; + const int maxNumGroups = 10, NUM_MAX_ZOOMS = 300, NUM_SUBDIVS = 100; const vb01::u32 IMPASS_NODE_VAL = 65535; const static int numAppStates = 5; From 73625f349f6eb7f4fe93fae43b02330f43fa769a Mon Sep 17 00:00:00 2001 From: devZoGok Date: Sun, 15 Mar 2026 19:59:34 +0200 Subject: [PATCH 22/22] map update --- Assets/Models/Maps/2lakes/2lakes.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Assets/Models/Maps/2lakes/2lakes.lua b/Assets/Models/Maps/2lakes/2lakes.lua index cf6d5fd..eca8726 100644 --- a/Assets/Models/Maps/2lakes/2lakes.lua +++ b/Assets/Models/Maps/2lakes/2lakes.lua @@ -6,18 +6,18 @@ metadata = { impassibleNodeValue = 65535, spawnPoints = { {x = -105.929001, y = -0.110725, z = -404.756104}, - {x = 17.326164, y = 0.000046, z = 390.545441}, + {x = 17.326164, y = 0.000046, z = 360.545441}, }, choosablePlayers = { { units = { - {id = 10, pos = {x = -20.000000, y = 0.000000, z = -20.000000}, rot = {w = 1.000000, x = 0.000000, y = 0.000000, z = 0.000000}}, + {id = 10, pos = {x = -105.929001, y = -0.110725, z = -404.756104}, rot = {w = 1.000000, x = 0.000000, y = 0.000000, z = 0.000000}}, }, resourceDeposits = {} }, { units = { - {id = UnitId.ACS_TANK, pos = {x = -20.000000, y = 0.000000, z = -20.000000}, rot = {w = 1.000000, x = 0.000000, y = 0.000000, z = 0.000000}}, + {id = UnitId.ACS_CYBORG_ENGINEER, pos = {x = -20.215218, y = 0.000000, z = 365.251648}, rot = {w = 1.000000, x = 0.000000, y = 0.000000, z = 0.000000}}, }, resourceDeposits = {} }