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 = {} } 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 416c70d..6c22f47 100644 --- a/Assets/Scripts/Core/player.lua +++ b/Assets/Scripts/Core/player.lua @@ -1,106 +1,52 @@ -Player.numStartEngis = 3 -Player.baseDir = nil +Player.baseQuat = nil +Player.baseRadius = 180 Player.numDefWarMechs = 1 Player.behaviour = {} +Player.pointDefenseTrans = {} +Player.spawnPointData = {} +Player.navalFactoryCellId = nil +Player.navalRallyPoint = nil -Player.taskForceData = { - {class = UnitClass.WAR_MECH, buildId = 0, numUnits = 2}, - {class = UnitClass.TANK, buildId = 1, numUnits = 1}, - {class = UnitClass.ARTILLERY, buildId = 2, numUnits = 1} +Player.unitClassMappings = { + {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_TRANSPORT, UnitId.AINC_TRANSPORT, UnitId.ER_TRANSPORT}, + {UnitId.ACS_CARGO_SHIP, UnitId.AINC_CARGO_SHIP, UnitId.ER_CARGO_SHIP}, + {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}, + {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.landTaskForceData = { + {class = UnitClass.MECH, numUnits = 3}, + {class = UnitClass.TANK, numUnits = 2}, + {class = UnitClass.ARTILLERY, numUnits = 1} +} + +Player.navalTaskForceData = { + {class = UnitClass.TRANSPORT, numUnits = 2}, + --{class = UnitClass.CRUISER, numUnits = 2}, + {class = UnitClass.ANTI_SUB_CRUISER, 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:initBaseQuat() map = Map.getSingleton() sp = map:getSpawnPoint(self:getSpawnPointId()) minDistId = nil @@ -117,32 +63,177 @@ function Player:buildFort(arguments) ::continue:: 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) + 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) - return BTNodeResult.RUNNING + self.baseQuat = Quaternion:new(angle, Vector3:new(0, 1, 0)) 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() + 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() - engiBuildId = self:getBuildableUnitIdFromClass(fort, UnitClass.ENGINEER) + numPd = 12 - for i = 1, queueDiff do fort:appendToQueue(engiBuildId) end + for i = 1, numPd do + angle = (i - 1) * 2 * math.pi / numPd + 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) - engisStartNumBuilt = (#self:getUnitsByClass(UnitClass.ENGINEER, -1) == self.numStartEngis) - return (engisStartNumBuilt and BTNodeResult.SUCCESS or BTNodeResult.RUNNING) + 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 + +--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, -1) + + 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; + end + end + + 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, + navalRallyCellId = navalRallyCellId, + navalSupportCellId = navalSupportCellId, + disembarkCellId = disembarkCellId + } + else return {reachable = true} end +end + +function Player:landRouteToSpawnpoint(arguments) + spId = self.taskForces[arguments.taskForceId].spawnPointId + 1 + return self.spawnPointData[spId].reachable == true and BTNodeResult.SUCCESS or BTNodeResult.FAILURE +end + +function Player:init() + self:initBaseQuat() + self:initPdSpots() + + for i = 1, Map.getSingleton():getNumSpawnPoints() do + 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 + +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)) + + frameCtrl = GameObjectFrameController.getSingleton() + frameCtrl:checkPlacement(structFrame) + + 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) + + if structFrame.status == 0 then + structFrame:destroy() + return point + end + end + end + + structFrame:destroy() + return nil +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: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,116 +242,230 @@ 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) - end - - building = self:getUnitsByClass(unitClass, 1)[1] - return (building:toStructure():getBuildStatus() == 100 and BTNodeResult.SUCCESS or BTNodeResult.RUNNING) -end - -function Player:buildRefinery(arguments) - buildPos = Map.getSingleton():getSpawnPoint(self:getSpawnPointId()):add(self.baseDir:mult(20)) - return self:buildStructure(self:findIdleEngineer(), self:getFactionUnitIdFromClass(UnitClass.REFINERY), buildPos, -.1) -end - --- TODO optimize deposit position check -function Player:buildExtractor(arguments) - deposits = {} - players = Game.getSingleton():getPlayers(true) - - for i = 1, #players do - deps = players[i]:getResourceDeposits() - table.move(deps, 1, #deps, #deposits + 1, deposits) - end - - if #deposits == 0 then return BTNodeResult.FAILURE end - - depPos = deposits[1]:getPos() - spawnPoint = Map.getSingleton():getSpawnPoint(self:getSpawnPointId()) - - for i = 1, #deposits do - if deposits[i]:getPos():getDistanceFrom(spawnPoint) < depPos:getDistanceFrom(spawnPoint) then - depPos = deposits[i]:getPos() + if bu.buildable and bu.id == buildableUnitId then + return i - 1 end end - return self:buildStructure(self:findIdleEngineer(), UnitId.EXTRACTOR, depPos, 0) + return nil end -function Player:buildHarvester(arguments) - if self:getUnitsByClass(UnitClass.RESOURCE_ROVER, 1)[1] then - return BTNodeResult.SUCCESS +function Player:updateTaskForces(arguments) + numUnits = self:getNumUnits() + + for i = 1, #self.taskForces do + for j = #self.taskForces[i].units, 1, -1 do + 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) + + ::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 +end + +function Player:buildLandFactory(arguments) factory = self:getUnitsByClass(UnitClass.LAND_FACTORY, 1)[1] - if not factory then return BTNodeResult.FAILURE end - factory = factory:toFactory() + if factory then + return factory:toStructure():isComplete() and BTNodeResult.SUCCESS or BTNodeResult.RUNNING + end - if #factory:getQueue() == 0 then factory:appendToQueue(3) 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(engi, factId, pos, self.baseQuat:getAngle()) + factory = self:getUnitsByClass(UnitClass.LAND_FACTORY, 1)[1]:toFactory() + factory:setRallyPoint(factory:getPos():add(factory:getDirVec():mult(100))) + return BTNodeResult.RUNNING + else return BTNodeResult.FAILURE end +end + +function Player:buildRefinery(arguments) + refinery = self:getUnitsByClass(UnitClass.REFINERY, 1)[1] + if refinery then + return (refinery:toStructure():isComplete() and BTNodeResult.SUCCESS or BTNodeResult.RUNNING) + end + + 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(-.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) + + 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 + +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 + 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 + end + end + + return visibleDeposits +end + +function Player:buildExtractors(arguments) + extractors = self:getUnitsByClass(UnitClass.EXTRACTOR, -1) + visibleDeposits = self:getVisibleDeposits() + depId = nil + + for i = 1, #visibleDeposits do + if not visibleDeposits[i]:getExtractor() then + depId = i + break + end + end + + if not depId then return BTNodeResult.SUCCESS end + + self:buildStructure(self:findIdleEngineer(), UnitId.EXTRACTOR, visibleDeposits[depId]:getPos(), 0) return BTNodeResult.RUNNING 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 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 + hPos = harvester:getPos() + + for i = 1, #extractors do + currDist = extractors[i]:getPos():getDistanceFrom(hPos) + minDist = extractors[minDistId]:getPos():getDistanceFrom(hPos) + + if currDist < minDist 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) + return self.navalFactoryCellId and BTNodeResult.FAILURE or BTNodeResult.SUCCESS +end + +function Player:buildNavalFactory(arguments) + navalFactory = self:getUnitsByClass(UnitClass.NAVAL_FACTORY, 1)[1] + + if navalFactory then + return (navalFactory:toStructure():isComplete() and BTNodeResult.SUCCESS or BTNodeResult.RUNNING) + end + + navalFactId = self.unitClassMappings[UnitClass.NAVAL_FACTORY + 1][self:getFaction() + 1] + pos = self:findSuitableSpot(navalFactId, Map.getSingleton():getCell(self.navalFactoryCellId).pos) + + 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 + +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) - landFactory = self:getUnitsByClass(UnitClass.LAND_FACTORY, 1)[1] + factory = self:getUnitsByClass(arguments.factoryClass, 1) - if not landFactory then return BTNodeResult.FAILURE end + if #factory == 0 then return BTNodeResult.FAILURE end - landFactory = landFactory:toFactory() - numAttackableSpawnPoints = Map.getSingleton():getNumSpawnPoints() - 1 + factory = factory[1]:toFactory() + numTaskForces = arguments.numTaskForces or 1 + taskForceData = arguments.taskForceData - if #landFactory:getQueue() == 0 then + 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 end if enoughUnits then return BTNodeResult.SUCCESS end - 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) + 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 @@ -268,117 +473,310 @@ function Player:buildTaskForces(arguments) return BTNodeResult.RUNNING end -function Player:formTaskForce(arguments) - if self.taskForces[arguments.tfId] then return BTNodeResult.SUCCESS end +function Player:buildPointDefenseRing(arguments) + self:updatePdSpots() + 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:formLandTaskForces(arguments) + numSpawnPoints = Map.getSingleton():getNumSpawnPoints() + + 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.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) + for i = 1, #self.landTaskForceData do + unitGroups[i] = self:getUnitsByClass(self.landTaskForceData[i].class, -1) + enoughGroupUnits = (#unitGroups[i] >= #self.taskForces * self.landTaskForceData[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, #self.taskForces do + if #self.taskForces[i].units > 0 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) + for j = 1, #self.landTaskForceData do + subArrId = #unitGroups[j] - (tfId * self.landTaskForceData[j].numUnits) + 1 + unitTbl = table.move( + unitGroups[j], + subArrId, + subArrId + self.landTaskForceData[j].numUnits - 1, + #self.taskForces[i].units + 1, + self.taskForces[i].units + ) + self.taskForces[i].units = unitTbl + end + + tfId = tfId + 1 + ::continue:: end return BTNodeResult.SUCCESS end -function Player:clearNearEnemies(arguments) - taskForce = self.taskForces[arguments.tfId] +function Player:getTransportData(transports) + allIdle = true + allInWater = true + allInLand = true - if taskForce.clearing then return BTNodeResult.SUCCESS end + for i = 1, #transports do + numOrders = transports[i]:getNumOrders() + currCell = map:getCell(map:getCellId(transports[i]:getPos(), false)) - self:selectUnits(taskForce.units) + if numOrders > 0 then allIdle = false end - 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:: + if currCell.type ~= 1 then allInWater = false + elseif currCell.type ~= 0 then allInLand = false end end - targUnit = nil + return {allIdle = allIdle, allInWater = allInWater, allInLand = allInLand} +end - for i = 1, #targetUnits do - for j = 1, #taskForce.units do - tfUnit = taskForce.units[j] +function Player:boardTransports(arguments) + taskForce = self.taskForces[arguments.taskForceId] - if tfUnit:getPos():getDistanceFrom(targetUnits[i]:getPos()) < tfUnit:getLineOfSight() then - targUnit = targetUnits[i] - break + if taskForce.landed then return BTNodeResult.SUCCESS end + + map = Map.getSingleton() + transports = self:getUnitsByClass(UnitClass.TRANSPORT, -1) + 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) + + embarkCell = map:getCell(self.spawnPointData[taskForce.spawnPointId + 1].embarkCellId) + self:issueOrder(OrderType.MOVE, Vector3:new(0, 0, 0), {Target:new(nil, embarkCell.pos)}, false) + 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 + + self:deselectUnits() + 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 + + for i = 1, #taskForce.units do + garrisoned = taskForce.units[i]:toVehicle():getGarrisonable() + + if i == #taskForce.units and garrisoned then + taskForce.moving = false + return BTNodeResult.SUCCESS + elseif not garrisoned then return BTNodeResult.RUNNING end + end +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 + empty = transports[i]:isGarrisonEmpty() + + 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.taskForces[arguments.taskForceId].landed = true + + self:deselectUnits() + self:selectUnits(transports) + self:issueOrder(OrderType.EJECT, Vector3:new(0, 0, 0), {}, false) + 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 + end + + 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) + 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)) + 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 + + spawnPointId = self.taskForces[arguments.taskForceId].spawnPointId + 1 + supportCellId = self.spawnPointData[spawnPointId].navalSupportCellId + map = Map.getSingleton() + + 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 + + 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 and self.taskForces[tfId].arrived then + return BTNodeResult.SUCCESS + elseif #self.taskForces[tfId].units == 0 then + return BTNodeResult.FAILURE + end + + hostileUnits = self:getHostileUnits() + spawnPoint = Map.getSingleton():getSpawnPoint(self.taskForces[tfId].spawnPointId) + minDist = 10 + + 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 - if targUnit then break end - end + 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 - if not taskForce.clearing and targUnit then + 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(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 + self:selectUnits(self.taskForces[tfId].units) + 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 - taskForce = self.taskForces[arguments.tfId] - return taskForce.clearing and BTNodeResult.RUNNING or BTNodeResult.SUCCESS -end - -function Player:attackSpawnPoint(arguments) - taskForce = self.taskForces[arguments.tfId] - - if taskForce.attacking then return BTNodeResult.RUNNING end - - self:deselectUnits() - self:selectUnits(taskForce.units) - - for i = 1, #taskForce.units do taskForce.units[i]:setState(0) 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 - return BTNodeResult.RUNNING end -function Player:generateTaskforceActions() +function Player:generateTaskForceActions() + children = { + {type = BTNodeType.SEQUENCE, children = {}}, + {type = BTNodeType.PARALLEL, children = {}}, + } numSpawnPoints = Map.getSingleton():getNumSpawnPoints() - actionsTable = {} + tfId = 1 - taskForceId = 1 for i = 1, numSpawnPoints do - if i - 1 == self:getSpawnPointId() then goto continue end - - actionsTable[#actionsTable + 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}}, + arguments = {taskForceId = i} + 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.PARALLEL, + children = { + {type = BTNodeType.FUNCTION, func = self.moveTransports, args = arguments}, + {type = BTNodeType.FUNCTION, func = self.clearShoreDefenses, args = arguments} + } + }, + } + } + } } - } - - taskForceId = taskForceId + 1 + ) + table.insert(children[2].children, {type = BTNodeType.FUNCTION, func = self.occupySpawnPoint, args = arguments}) + 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 9f001df..5071a07 100644 --- a/Assets/Scripts/Core/playerInit.lua +++ b/Assets/Scripts/Core/playerInit.lua @@ -1,41 +1,82 @@ -for i = 1, #game.players do - game.players[i].behaviour = { +for i = 1, #game.cpuPlayers do + player = game.cpuPlayers[i] + player:init() + + player.behaviour = { type = BTNodeType.SEQUENCE, children = { - {type = BTNodeType.FUNCTION, func = 'buildFort'}, - {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 = 5}} + } + }, { type = BTNodeType.PARALLEL, children = { + {type = BTNodeType.FUNCTION, func = player.buildRefinery}, { 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 = 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 = { + {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.FUNCTION, func = 'buildTaskForces'}, { - type = BTNodeType.PARALLEL, - numMinSuccesses = 2, - children = game.players[i]:generateTaskforceActions() + type = BTNodeType.SEQUENCE, + children = { + { + type = BTNodeType.FUNCTION, + func = player.buildTaskForces, + args = { + factoryClass = UnitClass.LAND_FACTORY, + numTaskForces = player:getNumEmptyTaskForces(), + taskForceData = player.landTaskForceData + } + }, + {type = BTNodeType.FUNCTION, func = player.formLandTaskForces}, + } }, + {type = BTNodeType.FUNCTION, func = player.buildPointDefenseRing}, } }, - --[[ - ]]-- + { + type = BTNodeType.PARALLEL, + children = player:generateTaskForceActions() + } } } end diff --git a/Assets/Scripts/GameObjects/Units/unitData.lua b/Assets/Scripts/GameObjects/Units/unitData.lua index ac10fed..76b23db 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,13 +172,13 @@ units = { }, } }, - unitClass = UnitClass.WAR_MECH, + unitClass = UnitClass.MECH, unitType = UnitType.LAND, armor = {ArmorType.MECHANIC}, 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}, @@ -186,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, @@ -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, @@ -343,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}}, @@ -389,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, @@ -553,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}, @@ -564,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, @@ -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, @@ -756,14 +759,14 @@ 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', colorNodes = {'Engineer_Asia.001'}, meshPath = 'acsCyborgEngineer.xml', selectionSfx = PATH .. 'Sounds/Units/Engineers/selection.ogg', - speed = .5, + speed = 1, destinationOffset = .1, anglePrecision = .1, maxTurnAngle = .1, @@ -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, @@ -837,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', @@ -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, @@ -1013,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, @@ -1219,7 +1222,7 @@ units = { armor = {ArmorType.CAST}, maxUnevenness = .5, alignToSurface = true, - capacity = 3000, + capacity = 1000, loadRate = 1, loadSpeed = 5, isVehicle = true, @@ -1377,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, @@ -1673,7 +1676,7 @@ units = { hitFx = {} }, }, - unitClass = UnitClass.CRUISER, + unitClass = UnitClass.ANTI_SUB_CRUISER, unitType = UnitType.SEA_LEVEL, armor = {ArmorType.STEEL}, isVehicle = true, @@ -1689,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, @@ -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/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, 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/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 diff --git a/activeGameState.cpp b/activeGameState.cpp index 59141b7..4d26463 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))); @@ -312,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); @@ -477,73 +476,20 @@ 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(); + Game *game = Game::getSingleton(); - vector units; + for (Player *p : game->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 = (game->isDebug() || 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() { @@ -723,7 +669,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 +687,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; @@ -806,9 +752,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/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/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/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; 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/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..10715c1 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,14 +44,12 @@ namespace battleship{ } void Game::update(){ - 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)"); + } int numPlayersWithUnits = 0; @@ -76,18 +75,18 @@ namespace battleship{ FxManager::getSingleton()->update(); } - void Game::updateLuaPlayers(bool resetBehaviour){ + void Game::initLuaPlayers(){ 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"); + 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 +112,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 +232,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..1661f3f 100644 --- a/game.h +++ b/game.h @@ -20,13 +20,14 @@ namespace battleship{ public: static Game* getSingleton(); void update(); - void updateLuaPlayers(bool); + void initLuaPlayers(); 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..ca2167f 100755 --- a/gameManager.cpp +++ b/gameManager.cpp @@ -8,16 +8,20 @@ #include #include "gameManager.h" +#include "gameObjectFrameController.h" #include "gameObjectFactory.h" #include "map.h" #include "game.h" #include "guiAppState.h" #include "defConfigs.h" #include "player.h" +#include "extractor.h" #include "factory.h" +#include "vehicle.h" #include "engineer.h" #include "destructable.h" #include "pointDefense.h" +#include "pathfinder.h" #include "resourceDeposit.h" using namespace std; @@ -56,23 +60,42 @@ 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, + "getNumFreeGarrisonSlots", &Unit::getNumFreeGarrisonSlots, + "getNumGarrisonSlots", &Unit::getNumGarrisonSlots, + "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, "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;}, + "toExtractor", [](Unit *u){return (Extractor*)u;}, + "toPointDefense", [](Unit *u){return (PointDefense*)u;} + ); + + SOL_LUA_STATE.new_usertype( + "Engineer", sol::constructors(), + "getGarrisonable", &Vehicle::getGarrisonable ); SOL_LUA_STATE.new_usertype( @@ -85,6 +108,7 @@ namespace battleship{ SOL_LUA_STATE.new_usertype( "Structure", sol::constructors(), "getPos", &GameObject::getPos, + "isComplete", &Structure::isComplete, "getBuildStatus", &Structure::getBuildStatus ); @@ -99,16 +123,21 @@ 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 ); SOL_LUA_STATE.new_usertype( "Player", sol::constructors(), "getSelectedUnits", &Player::getSelectedUnits, + "haltUnits", &Player::haltUnits, "addUnit", &Player::addUnit, "getUnit", &Player::getUnit, "getNumUnits", &Player::getNumUnits, @@ -120,13 +149,24 @@ namespace battleship{ "getFaction", &Player::getFaction, "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(), + "getDeposit", &Extractor::getDeposit ); SOL_LUA_STATE.new_usertype( "ResourceDeposit", sol::constructors(), - "getPos", &GameObject::getPos + "getExtractor", &ResourceDeposit::getExtractor, + "getAmmount", &ResourceDeposit::getAmmount, + "getPos", &GameObject::getPos, + "toGameObject", [](ResourceDeposit *rd){return (GameObject*)rd;} ); SOL_LUA_STATE.new_usertype( @@ -143,6 +183,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;}, @@ -155,15 +196,52 @@ 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;} ); + 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 + ); + + 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/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/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 313e1ed..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)); } } @@ -73,15 +73,57 @@ 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() ? 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()); + + 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; + } + + 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(); 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 +132,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 +174,7 @@ namespace battleship{ if(intersects){ s.status = GameObjectFrame::NOT_PLACEABLE; - break; + return; } } } 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/inGameAppState.cpp b/inGameAppState.cpp index c852c1c..23b23de 100755 --- a/inGameAppState.cpp +++ b/inGameAppState.cpp @@ -74,8 +74,8 @@ namespace battleship{ activeState = new ActiveGameState(guiState, playerId); stateManager->attachAppState(activeState); - Game::getSingleton()->updateLuaPlayers(true); - Map::Minimap::getSingleton()->updateImage(); + Game::getSingleton()->initLuaPlayers(); + //Map::Minimap::getSingleton()->updateImage(); } void InGameAppState::onDettached() { 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/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 0d52d0d..b693387 100644 --- a/pathfinder.cpp +++ b/pathfinder.cpp @@ -16,7 +16,19 @@ 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){ + 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/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/player.cpp b/player.cpp index 18cfd97..16558b2 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; @@ -116,7 +122,7 @@ namespace battleship{ break; } - if(!u->isVehicle() && ((Structure*)u)->getBuildStatus() < 100){ + if(!u->isVehicle() && ((Structure*)u)->isComplete()){ structBuilt = false; break; } @@ -148,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){ @@ -169,11 +181,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){ @@ -286,4 +302,57 @@ 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; + } + + 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(); + + for(Unit *friendlyUnit : friendlyUnits){ + if(objPlayer == this || objPlayer->getTeam() == getTeam()) + return true; + + Vector3 obsUnitPos = object->getPos(); + Vector2 oup2d = Vector2(obsUnitPos.x, obsUnitPos.z); + + Vector3 compUnitPos = friendlyUnit->getPos(); + Vector2 cup2d = Vector2(compUnitPos.x, compUnitPos.z); + + if(cup2d.getDistanceFrom(oup2d) <= friendlyUnit->getLineOfSight()) + return true; + } + + 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 d2f68b8..13afc78 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); @@ -39,6 +40,10 @@ namespace battleship{ void deselectUnit(Unit*); std::vector getDestructables(); void updateGameStats(Unit*); + 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/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..70e4537 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(); @@ -297,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]); @@ -413,7 +436,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 +488,11 @@ namespace battleship{ } orders.push_back(order); + + if(order.type == Order::TYPE::BUILD && order.targets[0].unit) + player->addUnit((Unit*)order.targets[0].unit); + + executeOrders(); } void Unit::halt() { @@ -490,7 +518,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); @@ -511,8 +539,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/unit.h b/unit.h index da424bf..0bcf425 100755 --- a/unit.h +++ b/unit.h @@ -28,15 +28,9 @@ namespace vb01{ namespace battleship{ class Player; class Weapon; - class Unit; - 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}; @@ -67,15 +61,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 +83,6 @@ namespace battleship{ POINT_DEFENSE, EXTRACTOR, REFINERY, - FORT, ICE_SHEET }; @@ -116,15 +112,11 @@ 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;} - 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;} inline float getLineOfSight() {return lineOfSight;} inline UnitType getType() {return type;} diff --git a/vehicle.cpp b/vehicle.cpp index 9017dc9..c4085b6 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(); @@ -234,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); @@ -249,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){ @@ -316,41 +321,55 @@ 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); - 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 false; } + else return true; + } - vector heuristics; - - for(Map::Cell &cell : cells) - heuristics.push_back(145 * (cells[dest].pos.getDistanceFrom(cell.pos))); - - vector path = Pathfinder::getSingleton()->findPath(cells, heuristics, source, dest, this); - 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 +380,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 +429,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){ @@ -470,12 +510,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(); } 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();