Player.numStartEngis = 3 Player.baseDir = nil Player.numDefWarMechs = 1 Player.behaviour = {} 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.taskForces = {} --TODO simplify building construction function Player:buildFort(arguments) forts = self:getUnitsByClass(UnitClass.FORT, 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]}) map = Map.getSingleton() sp = map:getSpawnPoint(self:getSpawnPointId()) minDistId = nil for i = 0, map:getNumSpawnPoints() - 1 do if i == self:getSpawnPointId() then goto continue end if minDistId == nil then minDistId = i end if map:getSpawnPoint(i):getDistanceFrom(sp) < map:getSpawnPoint(minDistId):getDistanceFrom(sp) then minDistId = i end ::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, UnitId.FORT, 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.FORT, 1)[1]:toFactory() engineers = self:getUnitsByClass(UnitClass.ENGINEER, -1) engineerDiffNum = self.numStartEngis - #engineers queueDiff = engineerDiffNum - #fort:getQueue() for i = 1, queueDiff do fort:appendToQueue(0) end engisStartNumBuilt = (#self:getUnitsByClass(UnitClass.ENGINEER, -1) == self.numStartEngis) return (engisStartNumBuilt and BTNodeResult.SUCCESS or BTNodeResult.RUNNING) end function Player:findIdleEngineer() engineers = self:getUnitsByClass(UnitClass.ENGINEER, -1) for i = 1, #engineers do if engineers[i]:getNumOrders() == 0 then return engineers[i] end end return nil end function Player:buildStructure(engineer, buildingId, buildPos, buildAngle) if not engineer then return BTNodeResult.FAILURE end 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 -- TODO dispatch another engineer if the previous is destroyed before completion function Player:buildLandFactory(arguments) buildPos = Map.getSingleton():getSpawnPoint(self:getSpawnPointId()):add(self.baseDir:mult(20)) return self:buildStructure(self:findIdleEngineer(), UnitId.LAND_FACTORY, buildPos, .6) end function Player:buildRefinery(arguments) buildPos = Map.getSingleton():getSpawnPoint(self:getSpawnPointId()):add(self.baseDir:mult(20)) return self:buildStructure(self:findIdleEngineer(), UnitId.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() end end return self:buildStructure(self:findIdleEngineer(), UnitId.EXTRACTOR, depPos, 0) end function Player:buildHarvester(arguments) if self:getUnitsByClass(UnitClass.RESOURCE_ROVER, 1)[1] then return BTNodeResult.SUCCESS end factory = self:getUnitsByClass(UnitClass.LAND_FACTORY, 1)[1] if not factory then return BTNodeResult.FAILURE end factory = factory:toFactory() if #factory:getQueue() == 0 then factory:appendToQueue(3) end return BTNodeResult.RUNNING end function Player:startHarvesting() harvesters = self:getUnitsByClass(UnitClass.RESOURCE_ROVER, -1) for i = 1, #harvesters do if harvesters[i]:getNumOrders() > 0 and harvesters[i]:getOrder(0).type == 7 then return BTNodeResult.SUCCESS end end extractor = self:getUnitsByClass(UnitClass.EXTRACTOR, 1)[1] if not extractor then return BTNodeResult.FAILURE 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) return BTNodeResult.SUCCESS end function Player:buildTaskForces(arguments) landFactory = self:getUnitsByClass(UnitClass.LAND_FACTORY, 1)[1] if not landFactory then return BTNodeResult.FAILURE end landFactory = landFactory:toFactory() numAttackableSpawnPoints = Map.getSingleton():getNumSpawnPoints() - 1 if #landFactory:getQueue() == 0 then enoughUnits = true for i = 1, #self.taskForceData do tfUnits = self:getUnitsByClass(self.taskForceData[i].class, -1) if #tfUnits < numAttackableSpawnPoints * self.taskForceData[i].numUnits then enoughUnits = false break end end if enoughUnits then return BTNodeResult.SUCCESS end 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) end end end return BTNodeResult.RUNNING end function Player:formTaskForce(arguments) if self.taskForces[arguments.tfId] 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) if not enoughGroupUnits then return BTNodeResult.FAILURE end end self.taskForces[arguments.tfId] = {units = {}, attacking = false, clearing = false, spawnPointId = arguments.spId} for i = 1, #self.taskForceData do unitGroup = {} 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) 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 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() numSpawnPoints = Map.getSingleton():getNumSpawnPoints() actionsTable = {} 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}}, } } taskForceId = taskForceId + 1 ::continue:: end return actionsTable end