mirror of
https://github.com/veden/Rampant.git
synced 2025-09-16 09:16:43 +02:00
FACTO-258: Consolidated most libraries
This commit is contained in:
@@ -63,6 +63,8 @@ Version: 3.2.0
|
|||||||
- Reduced corpse and particle variation to optimize increased entity times due to high entity counts
|
- Reduced corpse and particle variation to optimize increased entity times due to high entity counts
|
||||||
- Removed unneeded iterators
|
- Removed unneeded iterators
|
||||||
- Moved map properties directly into chunk object
|
- Moved map properties directly into chunk object
|
||||||
|
Framework:
|
||||||
|
- Consolidated most libraries
|
||||||
|
|
||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
Version: 3.1.2
|
Version: 3.1.2
|
||||||
|
88
control.lua
88
control.lua
@@ -18,24 +18,15 @@
|
|||||||
local ChunkPropertyUtils = require("libs/ChunkPropertyUtils")
|
local ChunkPropertyUtils = require("libs/ChunkPropertyUtils")
|
||||||
local UnitUtils = require("libs/UnitUtils")
|
local UnitUtils = require("libs/UnitUtils")
|
||||||
local BaseUtils = require("libs/BaseUtils")
|
local BaseUtils = require("libs/BaseUtils")
|
||||||
local MapUtils = require("libs/MapUtils")
|
|
||||||
local MathUtils = require("libs/MathUtils")
|
local MathUtils = require("libs/MathUtils")
|
||||||
local UnitGroupUtils = require("libs/UnitGroupUtils")
|
local Processor = require("libs/Processor")
|
||||||
local ChunkProcessor = require("libs/ChunkProcessor")
|
|
||||||
local MapProcessor = require("libs/MapProcessor")
|
|
||||||
local Constants = require("libs/Constants")
|
local Constants = require("libs/Constants")
|
||||||
local PheromoneUtils = require("libs/PheromoneUtils")
|
local MapUtils = require("libs/MapUtils")
|
||||||
local SquadDefense = require("libs/SquadDefense")
|
local Squad = require("libs/Squad")
|
||||||
local SquadAttack = require("libs/SquadAttack")
|
|
||||||
local AiAttackWave = require("libs/AIAttackWave")
|
|
||||||
local AiPlanning = require("libs/AIPlanning")
|
|
||||||
local MovementUtils = require("libs/MovementUtils")
|
|
||||||
local tests = require("tests")
|
local tests = require("tests")
|
||||||
local ChunkUtils = require("libs/ChunkUtils")
|
local ChunkUtils = require("libs/ChunkUtils")
|
||||||
local Upgrade = require("libs/Upgrade")
|
local Upgrade = require("libs/Upgrade")
|
||||||
local AiPredicates = require("libs/AIPredicates")
|
local Utils = require("libs/Utils")
|
||||||
local stringUtils = require("libs/StringUtils")
|
|
||||||
local queryUtils = require("libs/QueryUtils")
|
|
||||||
|
|
||||||
-- Constants
|
-- Constants
|
||||||
|
|
||||||
@@ -68,18 +59,18 @@ local SETTLE_CLOUD_WARMUP = Constants.SETTLE_CLOUD_WARMUP
|
|||||||
|
|
||||||
-- imported functions
|
-- imported functions
|
||||||
|
|
||||||
local isMember = stringUtils.isMember
|
local isMember = Utils.isMember
|
||||||
local split = stringUtils.split
|
local split = Utils.split
|
||||||
|
|
||||||
local planning = AiPlanning.planning
|
local planning = BaseUtils.planning
|
||||||
|
|
||||||
local addBasesToAllEnemyStructures = ChunkUtils.addBasesToAllEnemyStructures
|
local addBasesToAllEnemyStructures = ChunkUtils.addBasesToAllEnemyStructures
|
||||||
|
|
||||||
local setPointAreaInQuery = queryUtils.setPointAreaInQuery
|
local setPointAreaInQuery = Utils.setPointAreaInQuery
|
||||||
|
|
||||||
local nextMap = MapUtils.nextMap
|
local nextMap = MapUtils.nextMap
|
||||||
|
|
||||||
local processClouds = MapProcessor.processClouds
|
local processClouds = Processor.processClouds
|
||||||
|
|
||||||
local distortPosition = MathUtils.distortPosition
|
local distortPosition = MathUtils.distortPosition
|
||||||
local linearInterpolation = MathUtils.linearInterpolation
|
local linearInterpolation = MathUtils.linearInterpolation
|
||||||
@@ -88,26 +79,26 @@ local prepMap = Upgrade.prepMap
|
|||||||
|
|
||||||
local findBaseInitialAlignment = BaseUtils.findBaseInitialAlignment
|
local findBaseInitialAlignment = BaseUtils.findBaseInitialAlignment
|
||||||
|
|
||||||
local processBaseAIs = AiPlanning.processBaseAIs
|
local processBaseAIs = BaseUtils.processBaseAIs
|
||||||
|
|
||||||
local registerEnemyBaseStructure = ChunkUtils.registerEnemyBaseStructure
|
local registerEnemyBaseStructure = ChunkUtils.registerEnemyBaseStructure
|
||||||
|
|
||||||
local queueGeneratedChunk = MapUtils.queueGeneratedChunk
|
local queueGeneratedChunk = MapUtils.queueGeneratedChunk
|
||||||
local isRampantSetting = stringUtils.isRampantSetting
|
local isRampantSetting = Utils.isRampantSetting
|
||||||
|
|
||||||
local processPendingUpgrades = ChunkProcessor.processPendingUpgrades
|
local processPendingUpgrades = Processor.processPendingUpgrades
|
||||||
local canMigrate = AiPredicates.canMigrate
|
local canMigrate = BaseUtils.canMigrate
|
||||||
|
|
||||||
local squadDispatch = SquadAttack.squadDispatch
|
local squadDispatch = Squad.squadDispatch
|
||||||
|
|
||||||
local cleanUpMapTables = MapProcessor.cleanUpMapTables
|
local cleanUpMapTables = Processor.cleanUpMapTables
|
||||||
|
|
||||||
local positionToChunkXY = MapUtils.positionToChunkXY
|
local positionToChunkXY = MapUtils.positionToChunkXY
|
||||||
|
|
||||||
local processVengence = MapProcessor.processVengence
|
local processVengence = Processor.processVengence
|
||||||
local processAttackWaves = MapProcessor.processAttackWaves
|
local processAttackWaves = Processor.processAttackWaves
|
||||||
|
|
||||||
local disperseVictoryScent = PheromoneUtils.disperseVictoryScent
|
local disperseVictoryScent = MapUtils.disperseVictoryScent
|
||||||
|
|
||||||
local getChunkByPosition = MapUtils.getChunkByPosition
|
local getChunkByPosition = MapUtils.getChunkByPosition
|
||||||
|
|
||||||
@@ -116,31 +107,31 @@ local getChunkByXY = MapUtils.getChunkByXY
|
|||||||
|
|
||||||
local entityForPassScan = ChunkUtils.entityForPassScan
|
local entityForPassScan = ChunkUtils.entityForPassScan
|
||||||
|
|
||||||
local processPendingChunks = ChunkProcessor.processPendingChunks
|
local processPendingChunks = Processor.processPendingChunks
|
||||||
local processScanChunks = ChunkProcessor.processScanChunks
|
local processScanChunks = Processor.processScanChunks
|
||||||
|
|
||||||
local processMap = MapProcessor.processMap
|
local processMap = Processor.processMap
|
||||||
local processPlayers = MapProcessor.processPlayers
|
local processPlayers = Processor.processPlayers
|
||||||
local scanEnemyMap = MapProcessor.scanEnemyMap
|
local scanEnemyMap = Processor.scanEnemyMap
|
||||||
local scanPlayerMap = MapProcessor.scanPlayerMap
|
local scanPlayerMap = Processor.scanPlayerMap
|
||||||
local scanResourceMap = MapProcessor.scanResourceMap
|
local scanResourceMap = Processor.scanResourceMap
|
||||||
|
|
||||||
local processNests = MapProcessor.processNests
|
local processNests = Processor.processNests
|
||||||
|
|
||||||
local rallyUnits = AiAttackWave.rallyUnits
|
local rallyUnits = Squad.rallyUnits
|
||||||
|
|
||||||
local recycleBases = BaseUtils.recycleBases
|
local recycleBases = BaseUtils.recycleBases
|
||||||
|
|
||||||
local deathScent = PheromoneUtils.deathScent
|
local deathScent = MapUtils.deathScent
|
||||||
local victoryScent = PheromoneUtils.victoryScent
|
local victoryScent = MapUtils.victoryScent
|
||||||
|
|
||||||
local createSquad = UnitGroupUtils.createSquad
|
local createSquad = Squad.createSquad
|
||||||
|
|
||||||
local createBase = BaseUtils.createBase
|
local createBase = BaseUtils.createBase
|
||||||
local findNearbyBaseByPosition = ChunkPropertyUtils.findNearbyBaseByPosition
|
local findNearbyBaseByPosition = ChunkPropertyUtils.findNearbyBaseByPosition
|
||||||
local findNearbyBase = ChunkPropertyUtils.findNearbyBase
|
local findNearbyBase = ChunkPropertyUtils.findNearbyBase
|
||||||
|
|
||||||
local processActiveNests = MapProcessor.processActiveNests
|
local processActiveNests = Processor.processActiveNests
|
||||||
|
|
||||||
local removeDrainPylons = ChunkPropertyUtils.removeDrainPylons
|
local removeDrainPylons = ChunkPropertyUtils.removeDrainPylons
|
||||||
local getDrainPylonPair = ChunkPropertyUtils.getDrainPylonPair
|
local getDrainPylonPair = ChunkPropertyUtils.getDrainPylonPair
|
||||||
@@ -151,7 +142,7 @@ local isDrained = ChunkPropertyUtils.isDrained
|
|||||||
local setDrainedTick = ChunkPropertyUtils.setDrainedTick
|
local setDrainedTick = ChunkPropertyUtils.setDrainedTick
|
||||||
local getCombinedDeathGeneratorRating = ChunkPropertyUtils.getCombinedDeathGeneratorRating
|
local getCombinedDeathGeneratorRating = ChunkPropertyUtils.getCombinedDeathGeneratorRating
|
||||||
|
|
||||||
local retreatUnits = SquadDefense.retreatUnits
|
local retreatUnits = Squad.retreatUnits
|
||||||
|
|
||||||
local accountPlayerEntity = ChunkUtils.accountPlayerEntity
|
local accountPlayerEntity = ChunkUtils.accountPlayerEntity
|
||||||
local unregisterEnemyBaseStructure = ChunkUtils.unregisterEnemyBaseStructure
|
local unregisterEnemyBaseStructure = ChunkUtils.unregisterEnemyBaseStructure
|
||||||
@@ -160,7 +151,7 @@ local makeImmortalEntity = ChunkUtils.makeImmortalEntity
|
|||||||
local registerResource = ChunkUtils.registerResource
|
local registerResource = ChunkUtils.registerResource
|
||||||
local unregisterResource = ChunkUtils.unregisterResource
|
local unregisterResource = ChunkUtils.unregisterResource
|
||||||
|
|
||||||
local cleanSquads = SquadAttack.cleanSquads
|
local cleanSquads = Squad.cleanSquads
|
||||||
|
|
||||||
local queueUpgrade = BaseUtils.queueUpgrade
|
local queueUpgrade = BaseUtils.queueUpgrade
|
||||||
|
|
||||||
@@ -221,20 +212,13 @@ local function hookEvents()
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function initializeLibraries()
|
local function initializeLibraries()
|
||||||
MapProcessor.init(Universe)
|
|
||||||
AiPlanning.init(Universe)
|
|
||||||
AiAttackWave.init(Universe)
|
|
||||||
AiPredicates.init(Universe)
|
|
||||||
BaseUtils.init(Universe)
|
BaseUtils.init(Universe)
|
||||||
ChunkProcessor.init(Universe)
|
Squad.init(Universe)
|
||||||
|
BaseUtils.init(Universe)
|
||||||
|
Processor.init(Universe)
|
||||||
ChunkPropertyUtils.init(Universe)
|
ChunkPropertyUtils.init(Universe)
|
||||||
ChunkUtils.init(Universe)
|
ChunkUtils.init(Universe)
|
||||||
MapUtils.init(Universe)
|
MapUtils.init(Universe)
|
||||||
MovementUtils.init(Universe)
|
|
||||||
PheromoneUtils.init(Universe)
|
|
||||||
SquadAttack.init(Universe)
|
|
||||||
SquadDefense.init(Universe)
|
|
||||||
UnitGroupUtils.init(Universe)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function onLoad()
|
local function onLoad()
|
||||||
|
@@ -1,418 +0,0 @@
|
|||||||
-- Copyright (C) 2022 veden
|
|
||||||
|
|
||||||
-- This program is free software: you can redistribute it and/or modify
|
|
||||||
-- it under the terms of the GNU General Public License as published by
|
|
||||||
-- the Free Software Foundation, either version 3 of the License, or
|
|
||||||
-- (at your option) any later version.
|
|
||||||
|
|
||||||
-- This program is distributed in the hope that it will be useful,
|
|
||||||
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
-- GNU General Public License for more details.
|
|
||||||
|
|
||||||
-- You should have received a copy of the GNU General Public License
|
|
||||||
-- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
|
|
||||||
if (aiAttackWaveG) then
|
|
||||||
return aiAttackWaveG
|
|
||||||
end
|
|
||||||
local aiAttackWave = {}
|
|
||||||
|
|
||||||
-- imports
|
|
||||||
|
|
||||||
local Universe
|
|
||||||
|
|
||||||
--
|
|
||||||
|
|
||||||
local constants = require("Constants")
|
|
||||||
local mapUtils = require("MapUtils")
|
|
||||||
local chunkPropertyUtils = require("ChunkPropertyUtils")
|
|
||||||
local unitGroupUtils = require("UnitGroupUtils")
|
|
||||||
local movementUtils = require("MovementUtils")
|
|
||||||
local mathUtils = require("MathUtils")
|
|
||||||
local baseUtils = require("libs/BaseUtils")
|
|
||||||
|
|
||||||
-- constants
|
|
||||||
|
|
||||||
local BASE_PHEROMONE = constants.BASE_PHEROMONE
|
|
||||||
local PLAYER_PHEROMONE = constants.PLAYER_PHEROMONE
|
|
||||||
local RESOURCE_PHEROMONE = constants.RESOURCE_PHEROMONE
|
|
||||||
|
|
||||||
local AI_SQUAD_COST = constants.AI_SQUAD_COST
|
|
||||||
local AI_SETTLER_COST = constants.AI_SETTLER_COST
|
|
||||||
local AI_VENGENCE_SQUAD_COST = constants.AI_VENGENCE_SQUAD_COST
|
|
||||||
|
|
||||||
local COOLDOWN_RALLY = constants.COOLDOWN_RALLY
|
|
||||||
|
|
||||||
local CHUNK_ALL_DIRECTIONS = constants.CHUNK_ALL_DIRECTIONS
|
|
||||||
|
|
||||||
local CHUNK_SIZE = constants.CHUNK_SIZE
|
|
||||||
|
|
||||||
local RALLY_CRY_DISTANCE = constants.RALLY_CRY_DISTANCE
|
|
||||||
|
|
||||||
local BASE_AI_STATE_SIEGE = constants.BASE_AI_STATE_SIEGE
|
|
||||||
local BASE_AI_STATE_ONSLAUGHT = constants.BASE_AI_STATE_ONSLAUGHT
|
|
||||||
local BASE_AI_STATE_RAIDING = constants.BASE_AI_STATE_RAIDING
|
|
||||||
local BASE_AI_STATE_AGGRESSIVE = constants.BASE_AI_STATE_AGGRESSIVE
|
|
||||||
|
|
||||||
-- imported functions
|
|
||||||
|
|
||||||
local calculateKamikazeSettlerThreshold = unitGroupUtils.calculateKamikazeSettlerThreshold
|
|
||||||
local calculateKamikazeSquadThreshold = unitGroupUtils.calculateKamikazeSquadThreshold
|
|
||||||
|
|
||||||
local positionFromDirectionAndChunk = mapUtils.positionFromDirectionAndChunk
|
|
||||||
|
|
||||||
local getPassable = chunkPropertyUtils.getPassable
|
|
||||||
local isActiveRaidNest = chunkPropertyUtils.isActiveRaidNest
|
|
||||||
local isActiveNest = chunkPropertyUtils.isActiveNest
|
|
||||||
local getRallyTick = chunkPropertyUtils.getRallyTick
|
|
||||||
local setRallyTick = chunkPropertyUtils.setRallyTick
|
|
||||||
|
|
||||||
local gaussianRandomRangeRG = mathUtils.gaussianRandomRangeRG
|
|
||||||
|
|
||||||
local getNeighborChunks = mapUtils.getNeighborChunks
|
|
||||||
local getChunkByXY = mapUtils.getChunkByXY
|
|
||||||
local scoreNeighborsForFormation = movementUtils.scoreNeighborsForFormation
|
|
||||||
local scoreNeighborsForResource = movementUtils.scoreNeighborsForResource
|
|
||||||
local createSquad = unitGroupUtils.createSquad
|
|
||||||
|
|
||||||
local modifyBaseUnitPoints = baseUtils.modifyBaseUnitPoints
|
|
||||||
|
|
||||||
local mCeil = math.ceil
|
|
||||||
|
|
||||||
-- module code
|
|
||||||
|
|
||||||
local function settlerWaveScaling()
|
|
||||||
return mCeil(gaussianRandomRangeRG(Universe.settlerWaveSize,
|
|
||||||
Universe.settlerWaveDeviation,
|
|
||||||
Universe.expansionMinSize,
|
|
||||||
Universe.expansionMaxSize,
|
|
||||||
Universe.random))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function attackWaveScaling()
|
|
||||||
return mCeil(gaussianRandomRangeRG(Universe.attackWaveSize,
|
|
||||||
Universe.attackWaveDeviation,
|
|
||||||
1,
|
|
||||||
Universe.attackWaveUpperBound,
|
|
||||||
Universe.random))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function attackWaveValidCandidate(chunk, base)
|
|
||||||
if isActiveNest(chunk) then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
if (base.stateAI == BASE_AI_STATE_RAIDING) or
|
|
||||||
(base.stateAI == BASE_AI_STATE_SIEGE) or
|
|
||||||
(base.stateAI == BASE_AI_STATE_ONSLAUGHT)
|
|
||||||
then
|
|
||||||
return isActiveRaidNest(chunk)
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local function scoreSettlerLocation(map, neighborChunk)
|
|
||||||
return neighborChunk[RESOURCE_PHEROMONE] + -neighborChunk[PLAYER_PHEROMONE]
|
|
||||||
end
|
|
||||||
|
|
||||||
local function scoreSiegeSettlerLocation(map, neighborChunk)
|
|
||||||
return (neighborChunk[RESOURCE_PHEROMONE] + neighborChunk[BASE_PHEROMONE]) + -neighborChunk[PLAYER_PHEROMONE]
|
|
||||||
end
|
|
||||||
|
|
||||||
local function scoreUnitGroupLocation(map, neighborChunk)
|
|
||||||
return neighborChunk[PLAYER_PHEROMONE] + neighborChunk[BASE_PHEROMONE]
|
|
||||||
end
|
|
||||||
|
|
||||||
local function validSiegeSettlerLocation(map, neighborChunk)
|
|
||||||
return (getPassable(neighborChunk) == CHUNK_ALL_DIRECTIONS) and
|
|
||||||
(not neighborChunk.nestCount)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function validSettlerLocation(map, chunk, neighborChunk)
|
|
||||||
local chunkResource = chunk[RESOURCE_PHEROMONE]
|
|
||||||
return (getPassable(neighborChunk) == CHUNK_ALL_DIRECTIONS) and
|
|
||||||
(not neighborChunk.nestCount) and
|
|
||||||
(neighborChunk[RESOURCE_PHEROMONE] >= chunkResource)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function validUnitGroupLocation(map, neighborChunk)
|
|
||||||
return getPassable(neighborChunk) == CHUNK_ALL_DIRECTIONS and
|
|
||||||
(not neighborChunk.nestCount)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function visitPattern(o, cX, cY, distance)
|
|
||||||
local startX
|
|
||||||
local endX
|
|
||||||
local stepX
|
|
||||||
local startY
|
|
||||||
local endY
|
|
||||||
local stepY
|
|
||||||
if (o == 0) then
|
|
||||||
startX = cX - distance
|
|
||||||
endX = cX + distance
|
|
||||||
stepX = 32
|
|
||||||
startY = cY - distance
|
|
||||||
endY = cY + distance
|
|
||||||
stepY = 32
|
|
||||||
elseif (o == 1) then
|
|
||||||
startX = cX + distance
|
|
||||||
endX = cX - distance
|
|
||||||
stepX = -32
|
|
||||||
startY = cY + distance
|
|
||||||
endY = cY - distance
|
|
||||||
stepY = -32
|
|
||||||
elseif (o == 2) then
|
|
||||||
startX = cX - distance
|
|
||||||
endX = cX + distance
|
|
||||||
stepX = 32
|
|
||||||
startY = cY + distance
|
|
||||||
endY = cY - distance
|
|
||||||
stepY = -32
|
|
||||||
elseif (o == 3) then
|
|
||||||
startX = cX + distance
|
|
||||||
endX = cX - distance
|
|
||||||
stepX = -32
|
|
||||||
startY = cY - distance
|
|
||||||
endY = cY + distance
|
|
||||||
stepY = 32
|
|
||||||
end
|
|
||||||
return startX, endX, stepX, startY, endY, stepY
|
|
||||||
end
|
|
||||||
|
|
||||||
function aiAttackWave.rallyUnits(chunk, tick, base)
|
|
||||||
if ((tick - getRallyTick(chunk) > COOLDOWN_RALLY) and (base.unitPoints >= AI_VENGENCE_SQUAD_COST)) then
|
|
||||||
setRallyTick(chunk, tick)
|
|
||||||
local cX = chunk.x
|
|
||||||
local cY = chunk.y
|
|
||||||
local startX, endX, stepX, startY, endY, stepY = visitPattern(tick % 4, cX, cY, RALLY_CRY_DISTANCE)
|
|
||||||
local vengenceQueue = Universe.vengenceQueue
|
|
||||||
local map = chunk.map
|
|
||||||
for x=startX, endX, stepX do
|
|
||||||
for y=startY, endY, stepY do
|
|
||||||
if (x ~= cX) and (y ~= cY) then
|
|
||||||
local rallyChunk = getChunkByXY(map, x, y)
|
|
||||||
if (rallyChunk ~= -1) and rallyChunk.nestCount then
|
|
||||||
vengenceQueue[rallyChunk.id] = rallyChunk
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function aiAttackWave.formSettlers(chunk, base)
|
|
||||||
if (Universe.builderCount < Universe.AI_MAX_BUILDER_COUNT)
|
|
||||||
and (base.sentExpansionGroups < base.maxExpansionGroups)
|
|
||||||
and ((base.unitPoints - AI_SETTLER_COST) > 0)
|
|
||||||
and (Universe.random() < Universe.formSquadThreshold)
|
|
||||||
then
|
|
||||||
local map = chunk.map
|
|
||||||
local surface = map.surface
|
|
||||||
local squadPath, squadDirection
|
|
||||||
if (base.stateAI == BASE_AI_STATE_SIEGE) then
|
|
||||||
squadPath, squadDirection = scoreNeighborsForFormation(getNeighborChunks(map, chunk.x, chunk.y),
|
|
||||||
validSiegeSettlerLocation,
|
|
||||||
scoreSiegeSettlerLocation,
|
|
||||||
map)
|
|
||||||
else
|
|
||||||
squadPath, squadDirection = scoreNeighborsForResource(chunk,
|
|
||||||
getNeighborChunks(map, chunk.x, chunk.y),
|
|
||||||
validSettlerLocation,
|
|
||||||
scoreSettlerLocation,
|
|
||||||
map)
|
|
||||||
end
|
|
||||||
|
|
||||||
if (squadPath ~= -1) then
|
|
||||||
local squadPosition = surface.find_non_colliding_position("biter-spawner",
|
|
||||||
positionFromDirectionAndChunk(squadDirection,
|
|
||||||
chunk,
|
|
||||||
0.98),
|
|
||||||
CHUNK_SIZE,
|
|
||||||
4,
|
|
||||||
true)
|
|
||||||
if squadPosition then
|
|
||||||
local squad = createSquad(squadPosition, map, nil, true, base)
|
|
||||||
|
|
||||||
local scaledWaveSize = settlerWaveScaling()
|
|
||||||
Universe.formGroupCommand.group = squad.group
|
|
||||||
Universe.formCommand.unit_count = scaledWaveSize
|
|
||||||
local foundUnits = surface.set_multi_command(Universe.formCommand)
|
|
||||||
if (foundUnits > 0) then
|
|
||||||
base.sentExpansionGroups = base.sentExpansionGroups + 1
|
|
||||||
|
|
||||||
squad.base = base
|
|
||||||
local kamikazeThreshold = calculateKamikazeSettlerThreshold(foundUnits)
|
|
||||||
if base.stateAI == BASE_AI_STATE_SIEGE then
|
|
||||||
kamikazeThreshold = kamikazeThreshold * 2.5
|
|
||||||
end
|
|
||||||
squad.kamikaze = Universe.random() < kamikazeThreshold
|
|
||||||
|
|
||||||
Universe.builderCount = Universe.builderCount + 1
|
|
||||||
modifyBaseUnitPoints(base, -AI_SETTLER_COST, "Settler", squadPosition.x, squadPosition.y)
|
|
||||||
Universe.groupNumberToSquad[squad.groupNumber] = squad
|
|
||||||
else
|
|
||||||
if (squad.group.valid) then
|
|
||||||
squad.group.destroy()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function aiAttackWave.formVengenceSquad(chunk, base)
|
|
||||||
if (Universe.squadCount < Universe.AI_MAX_SQUAD_COUNT)
|
|
||||||
and ((base.unitPoints - AI_VENGENCE_SQUAD_COST) > 0)
|
|
||||||
and (Universe.random() < Universe.formSquadThreshold)
|
|
||||||
then
|
|
||||||
if (chunk[BASE_PHEROMONE] < 0.0001) or (chunk[PLAYER_PHEROMONE] < 0.0001) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local map = chunk.map
|
|
||||||
|
|
||||||
local surface = map.surface
|
|
||||||
local squadPath, squadDirection = scoreNeighborsForFormation(getNeighborChunks(map, chunk.x, chunk.y),
|
|
||||||
validUnitGroupLocation,
|
|
||||||
scoreUnitGroupLocation,
|
|
||||||
map)
|
|
||||||
if (squadPath ~= -1) then
|
|
||||||
local squadPosition = surface.find_non_colliding_position("biter-spawner",
|
|
||||||
positionFromDirectionAndChunk(squadDirection,
|
|
||||||
chunk,
|
|
||||||
0.98),
|
|
||||||
CHUNK_SIZE,
|
|
||||||
4,
|
|
||||||
true)
|
|
||||||
if squadPosition then
|
|
||||||
local squad = createSquad(squadPosition, map, nil, false, base)
|
|
||||||
|
|
||||||
squad.rabid = Universe.random() < 0.03
|
|
||||||
|
|
||||||
local scaledWaveSize = attackWaveScaling()
|
|
||||||
Universe.formGroupCommand.group = squad.group
|
|
||||||
Universe.formCommand.unit_count = scaledWaveSize
|
|
||||||
local foundUnits = surface.set_multi_command(Universe.formCommand)
|
|
||||||
if (foundUnits > 0) then
|
|
||||||
squad.base = base
|
|
||||||
squad.kamikaze = Universe.random() < calculateKamikazeSquadThreshold(foundUnits)
|
|
||||||
Universe.groupNumberToSquad[squad.groupNumber] = squad
|
|
||||||
Universe.squadCount = Universe.squadCount + 1
|
|
||||||
modifyBaseUnitPoints(base, -AI_VENGENCE_SQUAD_COST, "Vengence", squadPosition.x, squadPosition.y)
|
|
||||||
else
|
|
||||||
if (squad.group.valid) then
|
|
||||||
squad.group.destroy()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function aiAttackWave.formVengenceSettler(chunk, base)
|
|
||||||
if (Universe.builderCount < Universe.AI_MAX_BUILDER_COUNT)
|
|
||||||
and (base.sentExpansionGroups < base.maxExpansionGroups)
|
|
||||||
and ((base.unitPoints - AI_VENGENCE_SQUAD_COST) > 0)
|
|
||||||
and (Universe.random() < Universe.formSquadThreshold)
|
|
||||||
then
|
|
||||||
local map = chunk.map
|
|
||||||
local surface = map.surface
|
|
||||||
local squadPath, squadDirection = scoreNeighborsForFormation(getNeighborChunks(map, chunk.x, chunk.y),
|
|
||||||
validUnitGroupLocation,
|
|
||||||
scoreUnitGroupLocation,
|
|
||||||
map)
|
|
||||||
if (squadPath ~= -1) then
|
|
||||||
local squadPosition = surface.find_non_colliding_position("biter-spawner",
|
|
||||||
positionFromDirectionAndChunk(squadDirection,
|
|
||||||
chunk,
|
|
||||||
0.98),
|
|
||||||
CHUNK_SIZE,
|
|
||||||
4,
|
|
||||||
true)
|
|
||||||
if squadPosition then
|
|
||||||
local squad = createSquad(squadPosition, map, nil, true, base)
|
|
||||||
|
|
||||||
squad.rabid = Universe.random() < 0.03
|
|
||||||
|
|
||||||
local scaledWaveSize = settlerWaveScaling()
|
|
||||||
Universe.formGroupCommand.group = squad.group
|
|
||||||
Universe.formCommand.unit_count = scaledWaveSize
|
|
||||||
local foundUnits = surface.set_multi_command(Universe.formCommand)
|
|
||||||
if (foundUnits > 0) then
|
|
||||||
base.sentExpansionGroups = base.sentExpansionGroups + 1
|
|
||||||
|
|
||||||
squad.base = base
|
|
||||||
squad.kamikaze = Universe.random() < calculateKamikazeSettlerThreshold(foundUnits)
|
|
||||||
Universe.groupNumberToSquad[squad.groupNumber] = squad
|
|
||||||
Universe.builderCount = Universe.builderCount + 1
|
|
||||||
modifyBaseUnitPoints(base, -AI_VENGENCE_SQUAD_COST, "Vengence Settlers", squadPosition.x, squadPosition.y)
|
|
||||||
else
|
|
||||||
if (squad.group.valid) then
|
|
||||||
squad.group.destroy()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function aiAttackWave.formSquads(chunk, base)
|
|
||||||
if (Universe.squadCount < Universe.AI_MAX_SQUAD_COUNT)
|
|
||||||
and attackWaveValidCandidate(chunk, base)
|
|
||||||
and ((base.unitPoints - AI_SQUAD_COST) > 0)
|
|
||||||
and (Universe.random() < Universe.formSquadThreshold)
|
|
||||||
then
|
|
||||||
if (chunk[BASE_PHEROMONE] < 0.0001) or (chunk[PLAYER_PHEROMONE] < 0.0001) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local map = chunk.map
|
|
||||||
local surface = map.surface
|
|
||||||
local squadPath, squadDirection = scoreNeighborsForFormation(getNeighborChunks(map, chunk.x, chunk.y),
|
|
||||||
validUnitGroupLocation,
|
|
||||||
scoreUnitGroupLocation,
|
|
||||||
map)
|
|
||||||
if (squadPath ~= -1) then
|
|
||||||
local squadPosition = surface.find_non_colliding_position("biter-spawner",
|
|
||||||
positionFromDirectionAndChunk(squadDirection,
|
|
||||||
chunk,
|
|
||||||
0.98),
|
|
||||||
CHUNK_SIZE,
|
|
||||||
4,
|
|
||||||
true)
|
|
||||||
if squadPosition then
|
|
||||||
local squad = createSquad(squadPosition, map, nil, false, base)
|
|
||||||
|
|
||||||
squad.rabid = Universe.random() < 0.03
|
|
||||||
|
|
||||||
local scaledWaveSize = attackWaveScaling()
|
|
||||||
Universe.formGroupCommand.group = squad.group
|
|
||||||
Universe.formCommand.unit_count = scaledWaveSize
|
|
||||||
local foundUnits = surface.set_multi_command(Universe.formCommand)
|
|
||||||
if (foundUnits > 0) then
|
|
||||||
squad.base = base
|
|
||||||
squad.kamikaze = Universe.random() < calculateKamikazeSquadThreshold(foundUnits)
|
|
||||||
Universe.squadCount = Universe.squadCount + 1
|
|
||||||
Universe.groupNumberToSquad[squad.groupNumber] = squad
|
|
||||||
if (base.stateAI == BASE_AI_STATE_AGGRESSIVE) then
|
|
||||||
base.sentAggressiveGroups = base.sentAggressiveGroups + 1
|
|
||||||
end
|
|
||||||
modifyBaseUnitPoints(base, -AI_SQUAD_COST, "Squad", squadPosition.x, squadPosition.y)
|
|
||||||
else
|
|
||||||
if (squad.group.valid) then
|
|
||||||
squad.group.destroy()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function aiAttackWave.init(universe)
|
|
||||||
Universe = universe
|
|
||||||
end
|
|
||||||
|
|
||||||
aiAttackWaveG = aiAttackWave
|
|
||||||
return aiAttackWave
|
|
@@ -1,594 +0,0 @@
|
|||||||
-- Copyright (C) 2022 veden
|
|
||||||
|
|
||||||
-- This program is free software: you can redistribute it and/or modify
|
|
||||||
-- it under the terms of the GNU General Public License as published by
|
|
||||||
-- the Free Software Foundation, either version 3 of the License, or
|
|
||||||
-- (at your option) any later version.
|
|
||||||
|
|
||||||
-- This program is distributed in the hope that it will be useful,
|
|
||||||
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
-- GNU General Public License for more details.
|
|
||||||
|
|
||||||
-- You should have received a copy of the GNU General Public License
|
|
||||||
-- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
|
|
||||||
if aiPlanningG then
|
|
||||||
return aiPlanningG
|
|
||||||
end
|
|
||||||
local aiPlanning = {}
|
|
||||||
|
|
||||||
--
|
|
||||||
|
|
||||||
local universe
|
|
||||||
|
|
||||||
-- imports
|
|
||||||
|
|
||||||
local constants = require("Constants")
|
|
||||||
local mathUtils = require("MathUtils")
|
|
||||||
local baseUtils = require("BaseUtils")
|
|
||||||
|
|
||||||
-- constants
|
|
||||||
|
|
||||||
local UNIT_DEATH_POINT_COST = constants.UNIT_DEATH_POINT_COST
|
|
||||||
|
|
||||||
local BASE_PROCESS_INTERVAL = constants.BASE_PROCESS_INTERVAL
|
|
||||||
|
|
||||||
local BASE_GENERATION_STATE_ACTIVE = constants.BASE_GENERATION_STATE_ACTIVE
|
|
||||||
local BASE_GENERATION_STATE_DORMANT = constants.BASE_GENERATION_STATE_DORMANT
|
|
||||||
|
|
||||||
local BASE_GENERATION_MIN_STATE_DURATION = constants.BASE_GENERATION_MIN_STATE_DURATION
|
|
||||||
local BASE_GENERATION_MAX_STATE_DURATION = constants.BASE_GENERATION_MAX_STATE_DURATION
|
|
||||||
|
|
||||||
local TEMPERAMENT_RANGE_MAX = constants.TEMPERAMENT_RANGE_MAX
|
|
||||||
local TEMPERAMENT_RANGE_MIN = constants.TEMPERAMENT_RANGE_MIN
|
|
||||||
local TEMPERAMENT_DIVIDER = constants.TEMPERAMENT_DIVIDER
|
|
||||||
local NO_RETREAT_BASE_PERCENT = constants.NO_RETREAT_BASE_PERCENT
|
|
||||||
local NO_RETREAT_EVOLUTION_BONUS_MAX = constants.NO_RETREAT_EVOLUTION_BONUS_MAX
|
|
||||||
|
|
||||||
local AI_UNIT_REFUND = constants.AI_UNIT_REFUND
|
|
||||||
|
|
||||||
local AI_MAX_POINTS = constants.AI_MAX_POINTS
|
|
||||||
|
|
||||||
local BASE_RALLY_CHANCE = constants.BASE_RALLY_CHANCE
|
|
||||||
local BONUS_RALLY_CHANCE = constants.BONUS_RALLY_CHANCE
|
|
||||||
|
|
||||||
local RETREAT_MOVEMENT_PHEROMONE_LEVEL_MIN = constants.RETREAT_MOVEMENT_PHEROMONE_LEVEL_MIN
|
|
||||||
local RETREAT_MOVEMENT_PHEROMONE_LEVEL_MAX = constants.RETREAT_MOVEMENT_PHEROMONE_LEVEL_MAX
|
|
||||||
local MINIMUM_AI_POINTS = constants.MINIMUM_AI_POINTS
|
|
||||||
|
|
||||||
local ACTIVE_NESTS_PER_AGGRESSIVE_GROUPS = constants.ACTIVE_NESTS_PER_AGGRESSIVE_GROUPS
|
|
||||||
local ALL_NESTS_PER_EXPANSION_GROUPS = constants.ALL_NESTS_PER_EXPANSION_GROUPS
|
|
||||||
|
|
||||||
local BASE_AI_STATE_PEACEFUL = constants.BASE_AI_STATE_PEACEFUL
|
|
||||||
local BASE_AI_STATE_AGGRESSIVE = constants.BASE_AI_STATE_AGGRESSIVE
|
|
||||||
local BASE_AI_STATE_RAIDING = constants.BASE_AI_STATE_RAIDING
|
|
||||||
local BASE_AI_STATE_MIGRATING = constants.BASE_AI_STATE_MIGRATING
|
|
||||||
local BASE_AI_STATE_ONSLAUGHT = constants.BASE_AI_STATE_ONSLAUGHT
|
|
||||||
local BASE_AI_STATE_SIEGE = constants.BASE_AI_STATE_SIEGE
|
|
||||||
local AI_POINT_GENERATOR_AMOUNT = constants.AI_POINT_GENERATOR_AMOUNT
|
|
||||||
|
|
||||||
local BASE_AI_MIN_STATE_DURATION = constants.BASE_AI_MIN_STATE_DURATION
|
|
||||||
local BASE_AI_MAX_STATE_DURATION = constants.BASE_AI_MAX_STATE_DURATION
|
|
||||||
|
|
||||||
|
|
||||||
-- imported functions
|
|
||||||
|
|
||||||
local randomTickEvent = mathUtils.randomTickEvent
|
|
||||||
local randomTickDuration = mathUtils.randomTickDuration
|
|
||||||
|
|
||||||
local upgradeBaseBasedOnDamage = baseUtils.upgradeBaseBasedOnDamage
|
|
||||||
local modifyBaseUnitPoints = baseUtils.modifyBaseUnitPoints
|
|
||||||
local modifyBaseSpecialPoints = baseUtils.modifyBaseSpecialPoints
|
|
||||||
|
|
||||||
local linearInterpolation = mathUtils.linearInterpolation
|
|
||||||
|
|
||||||
local mFloor = math.floor
|
|
||||||
|
|
||||||
local mMax = math.max
|
|
||||||
local mMin = math.min
|
|
||||||
|
|
||||||
local mCeil = math.ceil
|
|
||||||
|
|
||||||
-- module code
|
|
||||||
|
|
||||||
local function getTimeStringFromTick(tick)
|
|
||||||
|
|
||||||
local tickToSeconds = tick / 60
|
|
||||||
|
|
||||||
local days = mFloor(tickToSeconds / 86400)
|
|
||||||
local hours = mFloor((tickToSeconds % 86400) / 3600)
|
|
||||||
local minutes = mFloor((tickToSeconds % 3600) / 60)
|
|
||||||
local seconds = mFloor(tickToSeconds % 60)
|
|
||||||
return days .. "d " .. hours .. "h " .. minutes .. "m " .. seconds .. "s"
|
|
||||||
end
|
|
||||||
|
|
||||||
function aiPlanning.planning(evolutionLevel)
|
|
||||||
universe.evolutionLevel = evolutionLevel
|
|
||||||
local maxPoints = mMax(AI_MAX_POINTS * evolutionLevel, MINIMUM_AI_POINTS)
|
|
||||||
universe.maxPoints = maxPoints
|
|
||||||
|
|
||||||
local maxOverflowPoints = maxPoints * 3
|
|
||||||
universe.maxOverflowPoints = maxOverflowPoints
|
|
||||||
|
|
||||||
local attackWaveMaxSize = universe.attackWaveMaxSize
|
|
||||||
universe.retreatThreshold = linearInterpolation(evolutionLevel,
|
|
||||||
RETREAT_MOVEMENT_PHEROMONE_LEVEL_MIN,
|
|
||||||
RETREAT_MOVEMENT_PHEROMONE_LEVEL_MAX)
|
|
||||||
universe.rallyThreshold = BASE_RALLY_CHANCE + (evolutionLevel * BONUS_RALLY_CHANCE)
|
|
||||||
universe.formSquadThreshold = mMax((0.35 * evolutionLevel), 0.1)
|
|
||||||
|
|
||||||
universe.attackWaveSize = attackWaveMaxSize * (evolutionLevel ^ 1.4)
|
|
||||||
universe.attackWaveDeviation = (universe.attackWaveSize * 0.333)
|
|
||||||
universe.attackWaveUpperBound = universe.attackWaveSize + (universe.attackWaveSize * 0.35)
|
|
||||||
|
|
||||||
if (universe.attackWaveSize < 1) then
|
|
||||||
universe.attackWaveSize = 2
|
|
||||||
universe.attackWaveDeviation = 1
|
|
||||||
universe.attackWaveUpperBound = 3
|
|
||||||
end
|
|
||||||
|
|
||||||
universe.settlerWaveSize = linearInterpolation(evolutionLevel ^ 1.66667,
|
|
||||||
universe.expansionMinSize,
|
|
||||||
universe.expansionMaxSize)
|
|
||||||
universe.settlerWaveDeviation = (universe.settlerWaveSize * 0.33)
|
|
||||||
|
|
||||||
universe.settlerCooldown = randomTickDuration(universe.random,
|
|
||||||
universe.expansionMinTime,
|
|
||||||
mFloor(linearInterpolation(evolutionLevel ^ 1.66667,
|
|
||||||
universe.expansionMaxTime,
|
|
||||||
universe.expansionMinTime)))
|
|
||||||
|
|
||||||
universe.unitRefundAmount = AI_UNIT_REFUND * evolutionLevel
|
|
||||||
universe.kamikazeThreshold = NO_RETREAT_BASE_PERCENT + (evolutionLevel * NO_RETREAT_EVOLUTION_BONUS_MAX)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function processBase(base, tick)
|
|
||||||
|
|
||||||
base.maxAggressiveGroups = mCeil(base.activeNests / ACTIVE_NESTS_PER_AGGRESSIVE_GROUPS)
|
|
||||||
base.maxExpansionGroups = mCeil((base.activeNests + base.activeRaidNests) / ALL_NESTS_PER_EXPANSION_GROUPS)
|
|
||||||
|
|
||||||
if (base.stateAI == BASE_AI_STATE_MIGRATING or base.stateAI == BASE_AI_STATE_SIEGE)
|
|
||||||
and base.resetExpensionGroupsTick <= tick
|
|
||||||
then
|
|
||||||
base.resetExpensionGroupsTick = tick + universe.settlerCooldown
|
|
||||||
base.sentExpansionGroups = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
local points = (AI_POINT_GENERATOR_AMOUNT * universe.random()) +
|
|
||||||
(base.activeNests * 0.144) +
|
|
||||||
(AI_POINT_GENERATOR_AMOUNT * mMax(universe.evolutionLevel ^ 2.5, 0.1))
|
|
||||||
|
|
||||||
if (base.temperament == 0) or (base.temperament == 1) then
|
|
||||||
points = points + 24
|
|
||||||
elseif (base.temperament < 0.20) or (base.temperament > 0.80) then
|
|
||||||
points = points + 14.4
|
|
||||||
elseif (base.temperament < 0.35) or (base.temperament > 0.65) then
|
|
||||||
points = points + 9.6
|
|
||||||
elseif (base.temperament < 0.45) or (base.temperament > 0.55) then
|
|
||||||
points = points + 4.8
|
|
||||||
end
|
|
||||||
|
|
||||||
if (base.stateAI == BASE_AI_STATE_ONSLAUGHT) then
|
|
||||||
points = points * 2
|
|
||||||
end
|
|
||||||
|
|
||||||
points = points * universe.aiPointsScaler
|
|
||||||
|
|
||||||
local currentPoints = base.unitPoints
|
|
||||||
|
|
||||||
if (currentPoints <= 0) then
|
|
||||||
currentPoints = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
if (currentPoints < universe.maxPoints) then
|
|
||||||
modifyBaseUnitPoints(base, points, "Logic Cycle", base.x, base.y)
|
|
||||||
end
|
|
||||||
|
|
||||||
if (base.points < universe.maxPoints) then
|
|
||||||
modifyBaseSpecialPoints(base, (points * 0.75), "Logic Cycle", base.x, base.y)
|
|
||||||
end
|
|
||||||
|
|
||||||
if universe.NEW_ENEMIES then
|
|
||||||
local deathThreshold = 0
|
|
||||||
local evolutionLevel = universe.evolutionLevel
|
|
||||||
if (evolutionLevel < universe.minimumAdaptationEvolution) then
|
|
||||||
base.deathEvents = 0
|
|
||||||
elseif (evolutionLevel < 0.5) then
|
|
||||||
deathThreshold = 17000
|
|
||||||
elseif (evolutionLevel < 0.7) then
|
|
||||||
deathThreshold = 34000
|
|
||||||
elseif (evolutionLevel < 0.9) then
|
|
||||||
deathThreshold = 60000
|
|
||||||
else
|
|
||||||
deathThreshold = 100000
|
|
||||||
end
|
|
||||||
|
|
||||||
deathThreshold = universe.adaptationModifier * deathThreshold
|
|
||||||
if ((base.deathEvents > deathThreshold) and (universe.random() > 0.95)) then
|
|
||||||
if (base.mutations < universe.MAX_BASE_MUTATIONS) then
|
|
||||||
if upgradeBaseBasedOnDamage(base) then
|
|
||||||
base.mutations = base.mutations + 1
|
|
||||||
end
|
|
||||||
elseif (base.mutations == universe.MAX_BASE_MUTATIONS) then
|
|
||||||
local roll = universe.random()
|
|
||||||
if (roll < 0.001) then
|
|
||||||
base.mutations = 0
|
|
||||||
if (universe.printBaseAdaptation) then
|
|
||||||
game.print({"description.rampant--adaptationResetDebugMessage",
|
|
||||||
base.x,
|
|
||||||
base.y,
|
|
||||||
base.mutations,
|
|
||||||
universe.MAX_BASE_MUTATIONS})
|
|
||||||
end
|
|
||||||
elseif (roll > 0.999) then
|
|
||||||
base.mutations = base.mutations + 1
|
|
||||||
if (universe.printBaseAdaptation) then
|
|
||||||
game.print({"description.rampant--adaptationFrozenDebugMessage",
|
|
||||||
base.x,
|
|
||||||
base.y})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
base.damagedBy = {}
|
|
||||||
base.deathEvents = 0
|
|
||||||
end
|
|
||||||
else
|
|
||||||
base.damagedBy = {}
|
|
||||||
base.deathEvents = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
if (base.stateGenerationTick <= tick) then
|
|
||||||
local roll = universe.random()
|
|
||||||
if (roll < 0.85) then
|
|
||||||
base.stateGeneration = BASE_GENERATION_STATE_ACTIVE
|
|
||||||
else
|
|
||||||
base.stateGeneration = BASE_GENERATION_STATE_DORMANT
|
|
||||||
end
|
|
||||||
base.stateGenerationTick = randomTickEvent(universe.random,
|
|
||||||
tick,
|
|
||||||
BASE_GENERATION_MIN_STATE_DURATION,
|
|
||||||
BASE_GENERATION_MAX_STATE_DURATION)
|
|
||||||
end
|
|
||||||
|
|
||||||
base.tick = tick
|
|
||||||
end
|
|
||||||
|
|
||||||
local function temperamentPlanner(base, evolutionLevel)
|
|
||||||
local destroyPlayerBuildings = base.destroyPlayerBuildings
|
|
||||||
local lostEnemyUnits = base.lostEnemyUnits
|
|
||||||
local lostEnemyBuilding = base.lostEnemyBuilding
|
|
||||||
local rocketLaunched = base.rocketLaunched
|
|
||||||
local builtEnemyBuilding = base.builtEnemyBuilding
|
|
||||||
local ionCannonBlasts = base.ionCannonBlasts
|
|
||||||
local artilleryBlasts = base.artilleryBlasts
|
|
||||||
local activeNests = base.activeNests
|
|
||||||
local activeRaidNests = base.activeRaidNests
|
|
||||||
|
|
||||||
local currentTemperament = base.temperamentScore
|
|
||||||
local delta = 0
|
|
||||||
|
|
||||||
if activeNests > 0 then
|
|
||||||
local val = (5.76 * activeNests)
|
|
||||||
delta = delta + val
|
|
||||||
else
|
|
||||||
delta = delta - 5.553792
|
|
||||||
end
|
|
||||||
|
|
||||||
if destroyPlayerBuildings > 0 then
|
|
||||||
if currentTemperament > 0 then
|
|
||||||
delta = delta - (5.553792 * destroyPlayerBuildings)
|
|
||||||
else
|
|
||||||
delta = delta + (5.553792 * destroyPlayerBuildings)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if activeRaidNests > 0 then
|
|
||||||
local val = (0.2304 * activeRaidNests)
|
|
||||||
delta = delta - val
|
|
||||||
else
|
|
||||||
delta = delta - 3.84
|
|
||||||
end
|
|
||||||
|
|
||||||
if lostEnemyUnits > 0 then
|
|
||||||
local multipler
|
|
||||||
if evolutionLevel < 0.3 then
|
|
||||||
multipler = 0.083328
|
|
||||||
elseif evolutionLevel < 0.5 then
|
|
||||||
multipler = 0.041472
|
|
||||||
elseif evolutionLevel < 0.7 then
|
|
||||||
multipler = 0.020736
|
|
||||||
elseif evolutionLevel < 0.9 then
|
|
||||||
multipler = 0.010368
|
|
||||||
elseif evolutionLevel < 0.9 then
|
|
||||||
multipler = 0.005184
|
|
||||||
else
|
|
||||||
multipler = 0.002592
|
|
||||||
end
|
|
||||||
local val = (multipler * lostEnemyUnits)
|
|
||||||
if (currentTemperament > 0) then
|
|
||||||
delta = delta - val
|
|
||||||
else
|
|
||||||
delta = delta + val
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if lostEnemyBuilding > 0 then
|
|
||||||
local val = (0.576 * lostEnemyBuilding)
|
|
||||||
if (currentTemperament > 0) then
|
|
||||||
delta = delta - val
|
|
||||||
else
|
|
||||||
delta = delta + val
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if builtEnemyBuilding > 0 then
|
|
||||||
local val = (0.261952 * builtEnemyBuilding)
|
|
||||||
if (currentTemperament > 0) then
|
|
||||||
delta = delta - val
|
|
||||||
else
|
|
||||||
delta = delta + val
|
|
||||||
end
|
|
||||||
else
|
|
||||||
delta = delta - 2.777088
|
|
||||||
end
|
|
||||||
|
|
||||||
if (rocketLaunched > 0) then
|
|
||||||
local val = (27.76 * rocketLaunched)
|
|
||||||
delta = delta + val
|
|
||||||
end
|
|
||||||
|
|
||||||
if (ionCannonBlasts > 0) then
|
|
||||||
local val = (13.924864 * ionCannonBlasts)
|
|
||||||
delta = delta + val
|
|
||||||
end
|
|
||||||
|
|
||||||
if (artilleryBlasts > 0) then
|
|
||||||
local val = (13.924864 * artilleryBlasts)
|
|
||||||
delta = delta + val
|
|
||||||
end
|
|
||||||
|
|
||||||
delta = delta * universe.temperamentRateModifier
|
|
||||||
base.temperamentScore = mMin(TEMPERAMENT_RANGE_MAX, mMax(TEMPERAMENT_RANGE_MIN, currentTemperament + delta))
|
|
||||||
base.temperament = ((base.temperamentScore + TEMPERAMENT_RANGE_MAX) * TEMPERAMENT_DIVIDER)
|
|
||||||
|
|
||||||
if universe.debugTemperament then
|
|
||||||
local strConsole = "Rampant Stats:\nbaseId:".. base.id .. ", aN:" .. base.activeNests ..
|
|
||||||
", aRN:" .. base.activeRaidNests .. ", dPB:" ..
|
|
||||||
base.destroyPlayerBuildings .. ", lEU:" .. base.lostEnemyUnits .. ", lEB:" ..
|
|
||||||
base.lostEnemyBuilding .. ", rL:" .. base.rocketLaunched .. ", bEB:" ..
|
|
||||||
base.builtEnemyBuilding .. ", iCB:" .. base.ionCannonBlasts .. ", aB:" ..
|
|
||||||
base.artilleryBlasts .. ", temp:" .. base.temperament .. ", tempScore:" .. base.temperamentScore ..
|
|
||||||
", points:" .. base.points .. ", unitPoints:" .. base.unitPoints .. ", state:" ..
|
|
||||||
constants.STATE_ENGLISH[base.stateAI] .. ", surface:" .. base.map.surface.index .. " [" ..
|
|
||||||
base.map.surface.name .. "]" .. ", aS:" .. universe.squadCount .. ", aB:" .. universe.builderCount ..
|
|
||||||
", atkSize:" .. universe.attackWaveSize .. ", stlSize:" .. universe.settlerWaveSize ..
|
|
||||||
", formGroup:" .. universe.formSquadThreshold .. ", sAgg:".. base.sentAggressiveGroups ..
|
|
||||||
", mAgg:" .. base.maxAggressiveGroups .. ", baseState:" .. base.stateGeneration .. ", sE:"
|
|
||||||
.. base.sentExpansionGroups .. ", mE:" .. base.maxExpansionGroups
|
|
||||||
game.print(strConsole)
|
|
||||||
print(strConsole)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function processState(base, tick)
|
|
||||||
|
|
||||||
if (base.stateAITick > tick) or not universe.awake then
|
|
||||||
if (not universe.awake) and (tick >= universe.initialPeaceTime) then
|
|
||||||
universe.awake = true
|
|
||||||
if universe.printAwakenMessage then
|
|
||||||
game.print({"description.rampant--planetHasAwoken"})
|
|
||||||
end
|
|
||||||
else
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local roll = universe.random()
|
|
||||||
if (base.temperament < 0.05) then -- 0 - 0.05
|
|
||||||
if universe.enabledMigration then
|
|
||||||
if (roll < 0.30) then
|
|
||||||
base.stateAI = BASE_AI_STATE_MIGRATING
|
|
||||||
elseif (roll < 0.50) and universe.raidAIToggle then
|
|
||||||
base.stateAI = BASE_AI_STATE_RAIDING
|
|
||||||
elseif universe.siegeAIToggle then
|
|
||||||
base.stateAI = BASE_AI_STATE_SIEGE
|
|
||||||
else
|
|
||||||
base.stateAI = BASE_AI_STATE_MIGRATING
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if universe.raidAIToggle then
|
|
||||||
if (roll < 0.70) then
|
|
||||||
base.stateAI = BASE_AI_STATE_RAIDING
|
|
||||||
else
|
|
||||||
base.stateAI = BASE_AI_STATE_AGGRESSIVE
|
|
||||||
end
|
|
||||||
else
|
|
||||||
base.stateAI = BASE_AI_STATE_AGGRESSIVE
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elseif (base.temperament < 0.20) then -- 0.05 - 0.2
|
|
||||||
if (universe.enabledMigration) then
|
|
||||||
if (roll < 0.4) then
|
|
||||||
base.stateAI = BASE_AI_STATE_MIGRATING
|
|
||||||
elseif (roll < 0.55) and universe.raidAIToggle then
|
|
||||||
base.stateAI = BASE_AI_STATE_RAIDING
|
|
||||||
elseif universe.siegeAIToggle then
|
|
||||||
base.stateAI = BASE_AI_STATE_SIEGE
|
|
||||||
else
|
|
||||||
base.stateAI = BASE_AI_STATE_MIGRATING
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if universe.raidAIToggle then
|
|
||||||
if (roll < 0.40) then
|
|
||||||
base.stateAI = BASE_AI_STATE_AGGRESSIVE
|
|
||||||
else
|
|
||||||
base.stateAI = BASE_AI_STATE_RAIDING
|
|
||||||
end
|
|
||||||
else
|
|
||||||
base.stateAI = BASE_AI_STATE_AGGRESSIVE
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elseif (base.temperament < 0.4) then -- 0.2 - 0.4
|
|
||||||
if (universe.enabledMigration) then
|
|
||||||
if (roll < 0.2) and universe.raidAIToggle then
|
|
||||||
base.stateAI = BASE_AI_STATE_RAIDING
|
|
||||||
elseif (roll < 0.2) then
|
|
||||||
base.stateAI = BASE_AI_STATE_AGGRESSIVE
|
|
||||||
elseif (roll < 0.8) then
|
|
||||||
base.stateAI = BASE_AI_STATE_MIGRATING
|
|
||||||
elseif universe.peacefulAIToggle then
|
|
||||||
base.stateAI = BASE_AI_STATE_PEACEFUL
|
|
||||||
else
|
|
||||||
base.stateAI = BASE_AI_STATE_MIGRATING
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if (roll < 0.3) then
|
|
||||||
base.stateAI = BASE_AI_STATE_AGGRESSIVE
|
|
||||||
elseif (roll < 0.6) and universe.raidAIToggle then
|
|
||||||
base.stateAI = BASE_AI_STATE_RAIDING
|
|
||||||
elseif (roll < 0.6) then
|
|
||||||
base.stateAI = BASE_AI_STATE_AGGRESSIVE
|
|
||||||
elseif universe.peacefulAIToggle then
|
|
||||||
base.stateAI = BASE_AI_STATE_PEACEFUL
|
|
||||||
else
|
|
||||||
base.stateAI = BASE_AI_STATE_AGGRESSIVE
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elseif (base.temperament < 0.6) then -- 0.4 - 0.6
|
|
||||||
if (roll < 0.4) then
|
|
||||||
base.stateAI = BASE_AI_STATE_AGGRESSIVE
|
|
||||||
elseif (roll < 0.5) and universe.raidAIToggle then
|
|
||||||
base.stateAI = BASE_AI_STATE_RAIDING
|
|
||||||
elseif (roll < 0.75) and universe.peacefulAIToggle then
|
|
||||||
base.stateAI = BASE_AI_STATE_PEACEFUL
|
|
||||||
else
|
|
||||||
if universe.enabledMigration then
|
|
||||||
base.stateAI = BASE_AI_STATE_MIGRATING
|
|
||||||
else
|
|
||||||
base.stateAI = BASE_AI_STATE_AGGRESSIVE
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elseif (base.temperament < 0.8) then -- 0.6 - 0.8
|
|
||||||
if (roll < 0.4) then
|
|
||||||
base.stateAI = BASE_AI_STATE_AGGRESSIVE
|
|
||||||
elseif (roll < 0.6) then
|
|
||||||
base.stateAI = BASE_AI_STATE_ONSLAUGHT
|
|
||||||
elseif (roll < 0.8) then
|
|
||||||
base.stateAI = BASE_AI_STATE_RAIDING
|
|
||||||
elseif universe.peacefulAIToggle then
|
|
||||||
base.stateAI = BASE_AI_STATE_PEACEFUL
|
|
||||||
else
|
|
||||||
base.stateAI = BASE_AI_STATE_AGGRESSIVE
|
|
||||||
end
|
|
||||||
elseif (base.temperament < 0.95) then -- 0.8 - 0.95
|
|
||||||
if (universe.enabledMigration and universe.raidAIToggle) then
|
|
||||||
if (roll < 0.20) and universe.siegeAIToggle then
|
|
||||||
base.stateAI = BASE_AI_STATE_SIEGE
|
|
||||||
elseif (roll < 0.45) then
|
|
||||||
base.stateAI = BASE_AI_STATE_RAIDING
|
|
||||||
elseif (roll < 0.85) then
|
|
||||||
base.stateAI = BASE_AI_STATE_ONSLAUGHT
|
|
||||||
else
|
|
||||||
base.stateAI = BASE_AI_STATE_AGGRESSIVE
|
|
||||||
end
|
|
||||||
elseif (universe.enabledMigration) then
|
|
||||||
if (roll < 0.20) and universe.siegeAIToggle then
|
|
||||||
base.stateAI = BASE_AI_STATE_SIEGE
|
|
||||||
elseif (roll < 0.75) then
|
|
||||||
base.stateAI = BASE_AI_STATE_ONSLAUGHT
|
|
||||||
else
|
|
||||||
base.stateAI = BASE_AI_STATE_AGGRESSIVE
|
|
||||||
end
|
|
||||||
elseif (universe.raidAIToggle) then
|
|
||||||
if (roll < 0.45) then
|
|
||||||
base.stateAI = BASE_AI_STATE_ONSLAUGHT
|
|
||||||
elseif (roll < 0.75) then
|
|
||||||
base.stateAI = BASE_AI_STATE_RAIDING
|
|
||||||
else
|
|
||||||
base.stateAI = BASE_AI_STATE_AGGRESSIVE
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if (roll < 0.65) then
|
|
||||||
base.stateAI = BASE_AI_STATE_ONSLAUGHT
|
|
||||||
else
|
|
||||||
base.stateAI = BASE_AI_STATE_AGGRESSIVE
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if (universe.enabledMigration and universe.raidAIToggle) then
|
|
||||||
if (roll < 0.30) and universe.siegeAIToggle then
|
|
||||||
base.stateAI = BASE_AI_STATE_SIEGE
|
|
||||||
elseif (roll < 0.65) then
|
|
||||||
base.stateAI = BASE_AI_STATE_RAIDING
|
|
||||||
else
|
|
||||||
base.stateAI = BASE_AI_STATE_ONSLAUGHT
|
|
||||||
end
|
|
||||||
elseif (universe.enabledMigration) then
|
|
||||||
if (roll < 0.30) and universe.siegeAIToggle then
|
|
||||||
base.stateAI = BASE_AI_STATE_SIEGE
|
|
||||||
else
|
|
||||||
base.stateAI = BASE_AI_STATE_ONSLAUGHT
|
|
||||||
end
|
|
||||||
elseif (universe.raidAIToggle) then
|
|
||||||
if (roll < 0.45) then
|
|
||||||
base.stateAI = BASE_AI_STATE_ONSLAUGHT
|
|
||||||
else
|
|
||||||
base.stateAI = BASE_AI_STATE_RAIDING
|
|
||||||
end
|
|
||||||
else
|
|
||||||
base.stateAI = BASE_AI_STATE_ONSLAUGHT
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local remainingUnits = base.lostEnemyUnits % 20
|
|
||||||
if remainingUnits ~= 0 then
|
|
||||||
modifyBaseUnitPoints(base, -(remainingUnits*UNIT_DEATH_POINT_COST), remainingUnits.." Units Lost")
|
|
||||||
end
|
|
||||||
|
|
||||||
base.destroyPlayerBuildings = 0
|
|
||||||
base.lostEnemyUnits = 0
|
|
||||||
base.lostEnemyBuilding = 0
|
|
||||||
base.rocketLaunched = 0
|
|
||||||
base.builtEnemyBuilding = 0
|
|
||||||
base.ionCannonBlasts = 0
|
|
||||||
base.artilleryBlasts = 0
|
|
||||||
|
|
||||||
base.stateAITick = randomTickEvent(universe.random, tick, BASE_AI_MIN_STATE_DURATION, BASE_AI_MAX_STATE_DURATION)
|
|
||||||
|
|
||||||
if universe.printAIStateChanges then
|
|
||||||
game.print(base.id .. ": AI is now: " .. constants.STATE_ENGLISH[base.stateAI] .. ", Next state change is in "
|
|
||||||
.. string.format("%.2f", (base.stateAITick - tick) / (60*60)) .. " minutes @ " ..
|
|
||||||
getTimeStringFromTick(base.stateAITick) .. " playtime")
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
function aiPlanning.processBaseAIs(tick)
|
|
||||||
local baseId = universe.processBaseAIIterator
|
|
||||||
local base
|
|
||||||
if not baseId then
|
|
||||||
baseId, base = next(universe.bases, nil)
|
|
||||||
else
|
|
||||||
base = universe.bases[baseId]
|
|
||||||
end
|
|
||||||
if not baseId then
|
|
||||||
universe.processBaseAIIterator = nil
|
|
||||||
return
|
|
||||||
else
|
|
||||||
universe.processBaseAIIterator = next(universe.bases, baseId)
|
|
||||||
if (tick - base.tick) <= BASE_PROCESS_INTERVAL then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
temperamentPlanner(base, universe.evolutionLevel)
|
|
||||||
processState(base, tick)
|
|
||||||
processBase(base, tick)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function aiPlanning.init(universeGlobal)
|
|
||||||
universe = universeGlobal
|
|
||||||
end
|
|
||||||
|
|
||||||
aiPlanningG = aiPlanning
|
|
||||||
return aiPlanning
|
|
@@ -1,91 +0,0 @@
|
|||||||
-- Copyright (C) 2022 veden
|
|
||||||
|
|
||||||
-- This program is free software: you can redistribute it and/or modify
|
|
||||||
-- it under the terms of the GNU General Public License as published by
|
|
||||||
-- the Free Software Foundation, either version 3 of the License, or
|
|
||||||
-- (at your option) any later version.
|
|
||||||
|
|
||||||
-- This program is distributed in the hope that it will be useful,
|
|
||||||
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
-- GNU General Public License for more details.
|
|
||||||
|
|
||||||
-- You should have received a copy of the GNU General Public License
|
|
||||||
-- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
|
|
||||||
if (aiPredicatesG) then
|
|
||||||
return aiPredicatesG
|
|
||||||
end
|
|
||||||
local aiPredicates = {}
|
|
||||||
|
|
||||||
--
|
|
||||||
|
|
||||||
local universe
|
|
||||||
|
|
||||||
-- imports
|
|
||||||
|
|
||||||
local constants = require("Constants")
|
|
||||||
|
|
||||||
-- constants
|
|
||||||
|
|
||||||
local BASE_AI_STATE_RAIDING = constants.BASE_AI_STATE_RAIDING
|
|
||||||
local BASE_AI_STATE_AGGRESSIVE = constants.BASE_AI_STATE_AGGRESSIVE
|
|
||||||
local BASE_AI_STATE_MIGRATING = constants.BASE_AI_STATE_MIGRATING
|
|
||||||
local BASE_AI_STATE_SIEGE = constants.BASE_AI_STATE_SIEGE
|
|
||||||
local BASE_AI_STATE_ONSLAUGHT = constants.BASE_AI_STATE_ONSLAUGHT
|
|
||||||
|
|
||||||
-- imported functions
|
|
||||||
|
|
||||||
-- module code
|
|
||||||
|
|
||||||
function aiPredicates.canAttack(map, base)
|
|
||||||
local isAggressive = ((base.stateAI == BASE_AI_STATE_AGGRESSIVE)
|
|
||||||
and (base.sentAggressiveGroups < base.maxAggressiveGroups))
|
|
||||||
local isRaiding = (base.stateAI == BASE_AI_STATE_RAIDING)
|
|
||||||
local isOnslaught = (base.stateAI == BASE_AI_STATE_ONSLAUGHT)
|
|
||||||
local isRaidSieging = universe.raidAIToggle
|
|
||||||
and (base.stateAI == BASE_AI_STATE_SIEGE)
|
|
||||||
and (base.sentExpansionGroups >= base.maxExpansionGroups)
|
|
||||||
local goodAI = isAggressive or isRaiding or isOnslaught or isRaidSieging
|
|
||||||
if not goodAI then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
local surface = map.surface
|
|
||||||
if surface.peaceful_mode then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
local nocturalMode = universe.aiNocturnalMode
|
|
||||||
local noctural = (not nocturalMode) or (nocturalMode and surface.darkness > 0.65)
|
|
||||||
if not noctural then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function aiPredicates.canMigrate(map, base)
|
|
||||||
local badAIState = (base.stateAI ~= BASE_AI_STATE_MIGRATING) and (base.stateAI ~= BASE_AI_STATE_SIEGE)
|
|
||||||
if badAIState then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
if not universe.expansion then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
local surface = map.surface
|
|
||||||
if surface.peaceful_mode then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
local nocturalMode = universe.aiNocturnalMode
|
|
||||||
local noctural = (not nocturalMode) or (nocturalMode and surface.darkness > 0.65)
|
|
||||||
if not noctural then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function aiPredicates.init(universeGlobal)
|
|
||||||
universe = universeGlobal
|
|
||||||
end
|
|
||||||
|
|
||||||
aiPredicatesG = aiPredicates
|
|
||||||
return aiPredicates
|
|
@@ -25,57 +25,99 @@ local Universe
|
|||||||
|
|
||||||
-- imports
|
-- imports
|
||||||
|
|
||||||
local stringUtils = require("StringUtils")
|
local Utils = require("Utils")
|
||||||
local mathUtils = require("MathUtils")
|
local MathUtils = require("MathUtils")
|
||||||
local constants = require("Constants")
|
local Constants = require("Constants")
|
||||||
local chunkPropertyUtils = require("ChunkPropertyUtils")
|
local MapUtils = require("MapUtils")
|
||||||
local mapUtils = require("MapUtils")
|
|
||||||
local queryUtils = require("QueryUtils")
|
|
||||||
|
|
||||||
-- constants
|
-- Constants
|
||||||
|
|
||||||
local TIERS = constants.TIERS
|
local TIERS = Constants.TIERS
|
||||||
local EVO_TO_TIER_MAPPING = constants.EVO_TO_TIER_MAPPING
|
local EVO_TO_TIER_MAPPING = Constants.EVO_TO_TIER_MAPPING
|
||||||
local PROXY_ENTITY_LOOKUP = constants.PROXY_ENTITY_LOOKUP
|
local PROXY_ENTITY_LOOKUP = Constants.PROXY_ENTITY_LOOKUP
|
||||||
local BUILDING_HIVE_TYPE_LOOKUP = constants.BUILDING_HIVE_TYPE_LOOKUP
|
local BUILDING_HIVE_TYPE_LOOKUP = Constants.BUILDING_HIVE_TYPE_LOOKUP
|
||||||
local COST_LOOKUP = constants.COST_LOOKUP
|
local COST_LOOKUP = Constants.COST_LOOKUP
|
||||||
local UPGRADE_LOOKUP = constants.UPGRADE_LOOKUP
|
local UPGRADE_LOOKUP = Constants.UPGRADE_LOOKUP
|
||||||
|
|
||||||
local ENEMY_ALIGNMENT_LOOKUP = constants.ENEMY_ALIGNMENT_LOOKUP
|
local ENEMY_ALIGNMENT_LOOKUP = Constants.ENEMY_ALIGNMENT_LOOKUP
|
||||||
|
|
||||||
local EVOLUTION_TABLE_ALIGNMENT = constants.EVOLUTION_TABLE_ALIGNMENT
|
local EVOLUTION_TABLE_ALIGNMENT = Constants.EVOLUTION_TABLE_ALIGNMENT
|
||||||
local BUILDING_EVOLVE_LOOKUP = constants.BUILDING_EVOLVE_LOOKUP
|
local BUILDING_EVOLVE_LOOKUP = Constants.BUILDING_EVOLVE_LOOKUP
|
||||||
|
|
||||||
local MINIMUM_BUILDING_COST = constants.MINIMUM_BUILDING_COST
|
local BASE_AI_STATE_RAIDING = Constants.BASE_AI_STATE_RAIDING
|
||||||
local FACTION_MUTATION_MAPPING = constants.FACTION_MUTATION_MAPPING
|
local BASE_AI_STATE_AGGRESSIVE = Constants.BASE_AI_STATE_AGGRESSIVE
|
||||||
|
local BASE_AI_STATE_MIGRATING = Constants.BASE_AI_STATE_MIGRATING
|
||||||
|
local BASE_AI_STATE_SIEGE = Constants.BASE_AI_STATE_SIEGE
|
||||||
|
local BASE_AI_STATE_ONSLAUGHT = Constants.BASE_AI_STATE_ONSLAUGHT
|
||||||
|
|
||||||
local MAGIC_MAXIMUM_NUMBER = constants.MAGIC_MAXIMUM_NUMBER
|
local MINIMUM_BUILDING_COST = Constants.MINIMUM_BUILDING_COST
|
||||||
|
local FACTION_MUTATION_MAPPING = Constants.FACTION_MUTATION_MAPPING
|
||||||
|
|
||||||
local FACTIONS_BY_DAMAGE_TYPE = constants.FACTIONS_BY_DAMAGE_TYPE
|
local MAGIC_MAXIMUM_NUMBER = Constants.MAGIC_MAXIMUM_NUMBER
|
||||||
|
|
||||||
local BASE_GENERATION_STATE_ACTIVE = constants.BASE_GENERATION_STATE_ACTIVE
|
local FACTIONS_BY_DAMAGE_TYPE = Constants.FACTIONS_BY_DAMAGE_TYPE
|
||||||
|
|
||||||
local BASE_DISTANCE_THRESHOLD = constants.BASE_DISTANCE_THRESHOLD
|
local BASE_GENERATION_STATE_ACTIVE = Constants.BASE_GENERATION_STATE_ACTIVE
|
||||||
local BASE_DISTANCE_LEVEL_BONUS = constants.BASE_DISTANCE_LEVEL_BONUS
|
|
||||||
local BASE_DISTANCE_TO_EVO_INDEX = constants.BASE_DISTANCE_TO_EVO_INDEX
|
|
||||||
|
|
||||||
local CHUNK_SIZE = constants.CHUNK_SIZE
|
local BASE_DISTANCE_THRESHOLD = Constants.BASE_DISTANCE_THRESHOLD
|
||||||
|
local BASE_DISTANCE_LEVEL_BONUS = Constants.BASE_DISTANCE_LEVEL_BONUS
|
||||||
|
local BASE_DISTANCE_TO_EVO_INDEX = Constants.BASE_DISTANCE_TO_EVO_INDEX
|
||||||
|
|
||||||
local BASE_AI_STATE_PEACEFUL = constants.BASE_AI_STATE_PEACEFUL
|
local CHUNK_SIZE = Constants.CHUNK_SIZE
|
||||||
|
|
||||||
|
local BASE_AI_STATE_PEACEFUL = Constants.BASE_AI_STATE_PEACEFUL
|
||||||
|
|
||||||
|
local BASE_RALLY_CHANCE = Constants.BASE_RALLY_CHANCE
|
||||||
|
local BONUS_RALLY_CHANCE = Constants.BONUS_RALLY_CHANCE
|
||||||
|
|
||||||
|
local RETREAT_MOVEMENT_PHEROMONE_LEVEL_MIN = Constants.RETREAT_MOVEMENT_PHEROMONE_LEVEL_MIN
|
||||||
|
local RETREAT_MOVEMENT_PHEROMONE_LEVEL_MAX = Constants.RETREAT_MOVEMENT_PHEROMONE_LEVEL_MAX
|
||||||
|
local MINIMUM_AI_POINTS = Constants.MINIMUM_AI_POINTS
|
||||||
|
|
||||||
|
local AI_POINT_GENERATOR_AMOUNT = Constants.AI_POINT_GENERATOR_AMOUNT
|
||||||
|
|
||||||
|
local BASE_AI_MIN_STATE_DURATION = Constants.BASE_AI_MIN_STATE_DURATION
|
||||||
|
local BASE_AI_MAX_STATE_DURATION = Constants.BASE_AI_MAX_STATE_DURATION
|
||||||
|
local NO_RETREAT_BASE_PERCENT = Constants.NO_RETREAT_BASE_PERCENT
|
||||||
|
local NO_RETREAT_EVOLUTION_BONUS_MAX = Constants.NO_RETREAT_EVOLUTION_BONUS_MAX
|
||||||
|
|
||||||
|
local AI_UNIT_REFUND = Constants.AI_UNIT_REFUND
|
||||||
|
local AI_MAX_POINTS = Constants.AI_MAX_POINTS
|
||||||
|
local BASE_GENERATION_MIN_STATE_DURATION = Constants.BASE_GENERATION_MIN_STATE_DURATION
|
||||||
|
local BASE_GENERATION_MAX_STATE_DURATION = Constants.BASE_GENERATION_MAX_STATE_DURATION
|
||||||
|
local BASE_GENERATION_STATE_DORMANT = Constants.BASE_GENERATION_STATE_DORMANT
|
||||||
|
local STATE_ENGLISH = Constants.STATE_ENGLISH
|
||||||
|
|
||||||
|
local ACTIVE_NESTS_PER_AGGRESSIVE_GROUPS = Constants.ACTIVE_NESTS_PER_AGGRESSIVE_GROUPS
|
||||||
|
local ALL_NESTS_PER_EXPANSION_GROUPS = Constants.ALL_NESTS_PER_EXPANSION_GROUPS
|
||||||
|
local TEMPERAMENT_RANGE_MAX = Constants.TEMPERAMENT_RANGE_MAX
|
||||||
|
local TEMPERAMENT_RANGE_MIN = Constants.TEMPERAMENT_RANGE_MIN
|
||||||
|
local TEMPERAMENT_DIVIDER = Constants.TEMPERAMENT_DIVIDER
|
||||||
|
local UNIT_DEATH_POINT_COST = Constants.UNIT_DEATH_POINT_COST
|
||||||
|
local BASE_PROCESS_INTERVAL = Constants.BASE_PROCESS_INTERVAL
|
||||||
|
|
||||||
-- imported functions
|
-- imported functions
|
||||||
|
|
||||||
local isMember = stringUtils.isMember
|
local getTimeStringFromTick = Utils.getTimeStringFromTick
|
||||||
|
|
||||||
local setPositionXYInQuery = queryUtils.setPositionXYInQuery
|
local randomTickEvent = MathUtils.randomTickEvent
|
||||||
|
local linearInterpolation = MathUtils.linearInterpolation
|
||||||
|
local randomTickDuration = MathUtils.randomTickDuration
|
||||||
|
|
||||||
local euclideanDistancePoints = mathUtils.euclideanDistancePoints
|
local isMember = Utils.isMember
|
||||||
|
|
||||||
local getChunkByPosition = mapUtils.getChunkByPosition
|
local setPositionXYInQuery = Utils.setPositionXYInQuery
|
||||||
|
|
||||||
local gaussianRandomRangeRG = mathUtils.gaussianRandomRangeRG
|
local euclideanDistancePoints = MathUtils.euclideanDistancePoints
|
||||||
|
|
||||||
|
local getChunkByPosition = MapUtils.getChunkByPosition
|
||||||
|
|
||||||
|
local gaussianRandomRangeRG = MathUtils.gaussianRandomRangeRG
|
||||||
|
|
||||||
local mFloor = math.floor
|
local mFloor = math.floor
|
||||||
|
local mCeil = math.ceil
|
||||||
|
|
||||||
|
local tableSize = table_size
|
||||||
|
|
||||||
local tableRemove = table.remove
|
local tableRemove = table.remove
|
||||||
local mMin = math.min
|
local mMin = math.min
|
||||||
@@ -303,6 +345,50 @@ function BaseUtils.recycleBases()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function BaseUtils.canAttack(base)
|
||||||
|
local isAggressive = ((base.stateAI == BASE_AI_STATE_AGGRESSIVE)
|
||||||
|
and (base.sentAggressiveGroups < base.maxAggressiveGroups))
|
||||||
|
local isRaiding = (base.stateAI == BASE_AI_STATE_RAIDING)
|
||||||
|
local isOnslaught = (base.stateAI == BASE_AI_STATE_ONSLAUGHT)
|
||||||
|
local isRaidSieging = Universe.raidAIToggle
|
||||||
|
and (base.stateAI == BASE_AI_STATE_SIEGE)
|
||||||
|
and (base.sentExpansionGroups >= base.maxExpansionGroups)
|
||||||
|
local goodAI = isAggressive or isRaiding or isOnslaught or isRaidSieging
|
||||||
|
if not goodAI then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
local surface = base.map.surface
|
||||||
|
if surface.peaceful_mode then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
local nocturalMode = Universe.aiNocturnalMode
|
||||||
|
local noctural = (not nocturalMode) or (nocturalMode and surface.darkness > 0.65)
|
||||||
|
if not noctural then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function BaseUtils.canMigrate(base)
|
||||||
|
local badAIState = (base.stateAI ~= BASE_AI_STATE_MIGRATING) and (base.stateAI ~= BASE_AI_STATE_SIEGE)
|
||||||
|
if badAIState then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
if not Universe.expansion then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
local surface = base.map.surface
|
||||||
|
if surface.peaceful_mode then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
local nocturalMode = Universe.aiNocturnalMode
|
||||||
|
local noctural = (not nocturalMode) or (nocturalMode and surface.darkness > 0.65)
|
||||||
|
if not noctural then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
function BaseUtils.queueUpgrade(entity, base, disPos, evolve, register, timeDelay)
|
function BaseUtils.queueUpgrade(entity, base, disPos, evolve, register, timeDelay)
|
||||||
Universe.pendingUpgrades[entity.unit_number] = {
|
Universe.pendingUpgrades[entity.unit_number] = {
|
||||||
["position"] = disPos,
|
["position"] = disPos,
|
||||||
@@ -402,7 +488,7 @@ local function pickMutationFromDamageType(damageType, roll, base)
|
|||||||
Universe.MAX_BASE_MUTATIONS})
|
Universe.MAX_BASE_MUTATIONS})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local alignmentCount = table_size(base.alignmentHistory)
|
local alignmentCount = tableSize(base.alignmentHistory)
|
||||||
while (alignmentCount > Universe.MAX_BASE_ALIGNMENT_HISTORY) do
|
while (alignmentCount > Universe.MAX_BASE_ALIGNMENT_HISTORY) do
|
||||||
tableRemove(base.alignmentHistory, 1)
|
tableRemove(base.alignmentHistory, 1)
|
||||||
alignmentCount = alignmentCount - 1
|
alignmentCount = alignmentCount - 1
|
||||||
@@ -604,6 +690,488 @@ function BaseUtils.modifyBaseSpecialPoints(base, points, tag, x, y)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function BaseUtils.planning(evolutionLevel)
|
||||||
|
Universe.evolutionLevel = evolutionLevel
|
||||||
|
local maxPoints = mMax(AI_MAX_POINTS * evolutionLevel, MINIMUM_AI_POINTS)
|
||||||
|
Universe.maxPoints = maxPoints
|
||||||
|
|
||||||
|
local maxOverflowPoints = maxPoints * 3
|
||||||
|
Universe.maxOverflowPoints = maxOverflowPoints
|
||||||
|
|
||||||
|
local attackWaveMaxSize = Universe.attackWaveMaxSize
|
||||||
|
Universe.retreatThreshold = linearInterpolation(evolutionLevel,
|
||||||
|
RETREAT_MOVEMENT_PHEROMONE_LEVEL_MIN,
|
||||||
|
RETREAT_MOVEMENT_PHEROMONE_LEVEL_MAX)
|
||||||
|
Universe.rallyThreshold = BASE_RALLY_CHANCE + (evolutionLevel * BONUS_RALLY_CHANCE)
|
||||||
|
Universe.formSquadThreshold = mMax((0.35 * evolutionLevel), 0.1)
|
||||||
|
|
||||||
|
Universe.attackWaveSize = attackWaveMaxSize * (evolutionLevel ^ 1.4)
|
||||||
|
Universe.attackWaveDeviation = (Universe.attackWaveSize * 0.333)
|
||||||
|
Universe.attackWaveUpperBound = Universe.attackWaveSize + (Universe.attackWaveSize * 0.35)
|
||||||
|
|
||||||
|
if (Universe.attackWaveSize < 1) then
|
||||||
|
Universe.attackWaveSize = 2
|
||||||
|
Universe.attackWaveDeviation = 1
|
||||||
|
Universe.attackWaveUpperBound = 3
|
||||||
|
end
|
||||||
|
|
||||||
|
Universe.settlerWaveSize = linearInterpolation(evolutionLevel ^ 1.66667,
|
||||||
|
Universe.expansionMinSize,
|
||||||
|
Universe.expansionMaxSize)
|
||||||
|
Universe.settlerWaveDeviation = (Universe.settlerWaveSize * 0.33)
|
||||||
|
|
||||||
|
Universe.settlerCooldown = randomTickDuration(Universe.random,
|
||||||
|
Universe.expansionMinTime,
|
||||||
|
mFloor(linearInterpolation(evolutionLevel ^ 1.66667,
|
||||||
|
Universe.expansionMaxTime,
|
||||||
|
Universe.expansionMinTime)))
|
||||||
|
|
||||||
|
Universe.unitRefundAmount = AI_UNIT_REFUND * evolutionLevel
|
||||||
|
Universe.kamikazeThreshold = NO_RETREAT_BASE_PERCENT + (evolutionLevel * NO_RETREAT_EVOLUTION_BONUS_MAX)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function processBase(base, tick)
|
||||||
|
|
||||||
|
base.maxAggressiveGroups = mCeil(base.activeNests / ACTIVE_NESTS_PER_AGGRESSIVE_GROUPS)
|
||||||
|
base.maxExpansionGroups = mCeil((base.activeNests + base.activeRaidNests) / ALL_NESTS_PER_EXPANSION_GROUPS)
|
||||||
|
|
||||||
|
if (base.stateAI == BASE_AI_STATE_MIGRATING or base.stateAI == BASE_AI_STATE_SIEGE)
|
||||||
|
and base.resetExpensionGroupsTick <= tick
|
||||||
|
then
|
||||||
|
base.resetExpensionGroupsTick = tick + Universe.settlerCooldown
|
||||||
|
base.sentExpansionGroups = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
local points = (AI_POINT_GENERATOR_AMOUNT * Universe.random()) +
|
||||||
|
(base.activeNests * 0.144) +
|
||||||
|
(AI_POINT_GENERATOR_AMOUNT * mMax(Universe.evolutionLevel ^ 2.5, 0.1))
|
||||||
|
|
||||||
|
if (base.temperament == 0) or (base.temperament == 1) then
|
||||||
|
points = points + 24
|
||||||
|
elseif (base.temperament < 0.20) or (base.temperament > 0.80) then
|
||||||
|
points = points + 14.4
|
||||||
|
elseif (base.temperament < 0.35) or (base.temperament > 0.65) then
|
||||||
|
points = points + 9.6
|
||||||
|
elseif (base.temperament < 0.45) or (base.temperament > 0.55) then
|
||||||
|
points = points + 4.8
|
||||||
|
end
|
||||||
|
|
||||||
|
if (base.stateAI == BASE_AI_STATE_ONSLAUGHT) then
|
||||||
|
points = points * 2
|
||||||
|
end
|
||||||
|
|
||||||
|
points = points * Universe.aiPointsScaler
|
||||||
|
|
||||||
|
local currentPoints = base.unitPoints
|
||||||
|
|
||||||
|
if (currentPoints <= 0) then
|
||||||
|
currentPoints = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
if (currentPoints < Universe.maxPoints) then
|
||||||
|
BaseUtils.modifyBaseUnitPoints(base, points, "Logic Cycle", base.x, base.y)
|
||||||
|
end
|
||||||
|
|
||||||
|
if (base.points < Universe.maxPoints) then
|
||||||
|
BaseUtils.modifyBaseSpecialPoints(base, (points * 0.75), "Logic Cycle", base.x, base.y)
|
||||||
|
end
|
||||||
|
|
||||||
|
if Universe.NEW_ENEMIES then
|
||||||
|
local deathThreshold = 0
|
||||||
|
local evolutionLevel = Universe.evolutionLevel
|
||||||
|
if (evolutionLevel < Universe.minimumAdaptationEvolution) then
|
||||||
|
base.deathEvents = 0
|
||||||
|
elseif (evolutionLevel < 0.5) then
|
||||||
|
deathThreshold = 17000
|
||||||
|
elseif (evolutionLevel < 0.7) then
|
||||||
|
deathThreshold = 34000
|
||||||
|
elseif (evolutionLevel < 0.9) then
|
||||||
|
deathThreshold = 60000
|
||||||
|
else
|
||||||
|
deathThreshold = 100000
|
||||||
|
end
|
||||||
|
|
||||||
|
deathThreshold = Universe.adaptationModifier * deathThreshold
|
||||||
|
if ((base.deathEvents > deathThreshold) and (Universe.random() > 0.95)) then
|
||||||
|
if (base.mutations < Universe.MAX_BASE_MUTATIONS) then
|
||||||
|
if BaseUtils.upgradeBaseBasedOnDamage(base) then
|
||||||
|
base.mutations = base.mutations + 1
|
||||||
|
end
|
||||||
|
elseif (base.mutations == Universe.MAX_BASE_MUTATIONS) then
|
||||||
|
local roll = Universe.random()
|
||||||
|
if (roll < 0.001) then
|
||||||
|
base.mutations = 0
|
||||||
|
if (Universe.printBaseAdaptation) then
|
||||||
|
game.print({"description.rampant--adaptationResetDebugMessage",
|
||||||
|
base.x,
|
||||||
|
base.y,
|
||||||
|
base.mutations,
|
||||||
|
Universe.MAX_BASE_MUTATIONS})
|
||||||
|
end
|
||||||
|
elseif (roll > 0.999) then
|
||||||
|
base.mutations = base.mutations + 1
|
||||||
|
if (Universe.printBaseAdaptation) then
|
||||||
|
game.print({"description.rampant--adaptationFrozenDebugMessage",
|
||||||
|
base.x,
|
||||||
|
base.y})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
base.damagedBy = {}
|
||||||
|
base.deathEvents = 0
|
||||||
|
end
|
||||||
|
else
|
||||||
|
base.damagedBy = {}
|
||||||
|
base.deathEvents = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
if (base.stateGenerationTick <= tick) then
|
||||||
|
local roll = Universe.random()
|
||||||
|
if (roll < 0.85) then
|
||||||
|
base.stateGeneration = BASE_GENERATION_STATE_ACTIVE
|
||||||
|
else
|
||||||
|
base.stateGeneration = BASE_GENERATION_STATE_DORMANT
|
||||||
|
end
|
||||||
|
base.stateGenerationTick = randomTickEvent(Universe.random,
|
||||||
|
tick,
|
||||||
|
BASE_GENERATION_MIN_STATE_DURATION,
|
||||||
|
BASE_GENERATION_MAX_STATE_DURATION)
|
||||||
|
end
|
||||||
|
|
||||||
|
base.tick = tick
|
||||||
|
end
|
||||||
|
|
||||||
|
local function temperamentPlanner(base, evolutionLevel)
|
||||||
|
local destroyPlayerBuildings = base.destroyPlayerBuildings
|
||||||
|
local lostEnemyUnits = base.lostEnemyUnits
|
||||||
|
local lostEnemyBuilding = base.lostEnemyBuilding
|
||||||
|
local rocketLaunched = base.rocketLaunched
|
||||||
|
local builtEnemyBuilding = base.builtEnemyBuilding
|
||||||
|
local ionCannonBlasts = base.ionCannonBlasts
|
||||||
|
local artilleryBlasts = base.artilleryBlasts
|
||||||
|
local activeNests = base.activeNests
|
||||||
|
local activeRaidNests = base.activeRaidNests
|
||||||
|
|
||||||
|
local currentTemperament = base.temperamentScore
|
||||||
|
local delta = 0
|
||||||
|
|
||||||
|
if activeNests > 0 then
|
||||||
|
local val = (5.76 * activeNests)
|
||||||
|
delta = delta + val
|
||||||
|
else
|
||||||
|
delta = delta - 5.553792
|
||||||
|
end
|
||||||
|
|
||||||
|
if destroyPlayerBuildings > 0 then
|
||||||
|
if currentTemperament > 0 then
|
||||||
|
delta = delta - (5.553792 * destroyPlayerBuildings)
|
||||||
|
else
|
||||||
|
delta = delta + (5.553792 * destroyPlayerBuildings)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if activeRaidNests > 0 then
|
||||||
|
local val = (0.2304 * activeRaidNests)
|
||||||
|
delta = delta - val
|
||||||
|
else
|
||||||
|
delta = delta - 3.84
|
||||||
|
end
|
||||||
|
|
||||||
|
if lostEnemyUnits > 0 then
|
||||||
|
local multipler
|
||||||
|
if evolutionLevel < 0.3 then
|
||||||
|
multipler = 0.083328
|
||||||
|
elseif evolutionLevel < 0.5 then
|
||||||
|
multipler = 0.041472
|
||||||
|
elseif evolutionLevel < 0.7 then
|
||||||
|
multipler = 0.020736
|
||||||
|
elseif evolutionLevel < 0.9 then
|
||||||
|
multipler = 0.010368
|
||||||
|
elseif evolutionLevel < 0.9 then
|
||||||
|
multipler = 0.005184
|
||||||
|
else
|
||||||
|
multipler = 0.002592
|
||||||
|
end
|
||||||
|
local val = (multipler * lostEnemyUnits)
|
||||||
|
if (currentTemperament > 0) then
|
||||||
|
delta = delta - val
|
||||||
|
else
|
||||||
|
delta = delta + val
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if lostEnemyBuilding > 0 then
|
||||||
|
local val = (0.576 * lostEnemyBuilding)
|
||||||
|
if (currentTemperament > 0) then
|
||||||
|
delta = delta - val
|
||||||
|
else
|
||||||
|
delta = delta + val
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if builtEnemyBuilding > 0 then
|
||||||
|
local val = (0.261952 * builtEnemyBuilding)
|
||||||
|
if (currentTemperament > 0) then
|
||||||
|
delta = delta - val
|
||||||
|
else
|
||||||
|
delta = delta + val
|
||||||
|
end
|
||||||
|
else
|
||||||
|
delta = delta - 2.777088
|
||||||
|
end
|
||||||
|
|
||||||
|
if (rocketLaunched > 0) then
|
||||||
|
local val = (27.76 * rocketLaunched)
|
||||||
|
delta = delta + val
|
||||||
|
end
|
||||||
|
|
||||||
|
if (ionCannonBlasts > 0) then
|
||||||
|
local val = (13.924864 * ionCannonBlasts)
|
||||||
|
delta = delta + val
|
||||||
|
end
|
||||||
|
|
||||||
|
if (artilleryBlasts > 0) then
|
||||||
|
local val = (13.924864 * artilleryBlasts)
|
||||||
|
delta = delta + val
|
||||||
|
end
|
||||||
|
|
||||||
|
delta = delta * Universe.temperamentRateModifier
|
||||||
|
base.temperamentScore = mMin(TEMPERAMENT_RANGE_MAX, mMax(TEMPERAMENT_RANGE_MIN, currentTemperament + delta))
|
||||||
|
base.temperament = ((base.temperamentScore + TEMPERAMENT_RANGE_MAX) * TEMPERAMENT_DIVIDER)
|
||||||
|
|
||||||
|
if Universe.debugTemperament then
|
||||||
|
local strConsole = "Rampant Stats:\nbaseId:".. base.id .. ", aN:" .. base.activeNests ..
|
||||||
|
", aRN:" .. base.activeRaidNests .. ", dPB:" ..
|
||||||
|
base.destroyPlayerBuildings .. ", lEU:" .. base.lostEnemyUnits .. ", lEB:" ..
|
||||||
|
base.lostEnemyBuilding .. ", rL:" .. base.rocketLaunched .. ", bEB:" ..
|
||||||
|
base.builtEnemyBuilding .. ", iCB:" .. base.ionCannonBlasts .. ", aB:" ..
|
||||||
|
base.artilleryBlasts .. ", temp:" .. base.temperament .. ", tempScore:" .. base.temperamentScore ..
|
||||||
|
", points:" .. base.points .. ", unitPoints:" .. base.unitPoints .. ", state:" ..
|
||||||
|
STATE_ENGLISH[base.stateAI] .. ", surface:" .. base.map.surface.index .. " [" ..
|
||||||
|
base.map.surface.name .. "]" .. ", aS:" .. Universe.squadCount .. ", aB:" .. Universe.builderCount ..
|
||||||
|
", atkSize:" .. Universe.attackWaveSize .. ", stlSize:" .. Universe.settlerWaveSize ..
|
||||||
|
", formGroup:" .. Universe.formSquadThreshold .. ", sAgg:".. base.sentAggressiveGroups ..
|
||||||
|
", mAgg:" .. base.maxAggressiveGroups .. ", baseState:" .. base.stateGeneration .. ", sE:"
|
||||||
|
.. base.sentExpansionGroups .. ", mE:" .. base.maxExpansionGroups
|
||||||
|
game.print(strConsole)
|
||||||
|
print(strConsole)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function processState(base, tick)
|
||||||
|
|
||||||
|
if (base.stateAITick > tick) or not Universe.awake then
|
||||||
|
if (not Universe.awake) and (tick >= Universe.initialPeaceTime) then
|
||||||
|
Universe.awake = true
|
||||||
|
if Universe.printAwakenMessage then
|
||||||
|
game.print({"description.rampant--planetHasAwoken"})
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local roll = Universe.random()
|
||||||
|
if (base.temperament < 0.05) then -- 0 - 0.05
|
||||||
|
if Universe.enabledMigration then
|
||||||
|
if (roll < 0.30) then
|
||||||
|
base.stateAI = BASE_AI_STATE_MIGRATING
|
||||||
|
elseif (roll < 0.50) and Universe.raidAIToggle then
|
||||||
|
base.stateAI = BASE_AI_STATE_RAIDING
|
||||||
|
elseif Universe.siegeAIToggle then
|
||||||
|
base.stateAI = BASE_AI_STATE_SIEGE
|
||||||
|
else
|
||||||
|
base.stateAI = BASE_AI_STATE_MIGRATING
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if Universe.raidAIToggle then
|
||||||
|
if (roll < 0.70) then
|
||||||
|
base.stateAI = BASE_AI_STATE_RAIDING
|
||||||
|
else
|
||||||
|
base.stateAI = BASE_AI_STATE_AGGRESSIVE
|
||||||
|
end
|
||||||
|
else
|
||||||
|
base.stateAI = BASE_AI_STATE_AGGRESSIVE
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif (base.temperament < 0.20) then -- 0.05 - 0.2
|
||||||
|
if (Universe.enabledMigration) then
|
||||||
|
if (roll < 0.4) then
|
||||||
|
base.stateAI = BASE_AI_STATE_MIGRATING
|
||||||
|
elseif (roll < 0.55) and Universe.raidAIToggle then
|
||||||
|
base.stateAI = BASE_AI_STATE_RAIDING
|
||||||
|
elseif Universe.siegeAIToggle then
|
||||||
|
base.stateAI = BASE_AI_STATE_SIEGE
|
||||||
|
else
|
||||||
|
base.stateAI = BASE_AI_STATE_MIGRATING
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if Universe.raidAIToggle then
|
||||||
|
if (roll < 0.40) then
|
||||||
|
base.stateAI = BASE_AI_STATE_AGGRESSIVE
|
||||||
|
else
|
||||||
|
base.stateAI = BASE_AI_STATE_RAIDING
|
||||||
|
end
|
||||||
|
else
|
||||||
|
base.stateAI = BASE_AI_STATE_AGGRESSIVE
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif (base.temperament < 0.4) then -- 0.2 - 0.4
|
||||||
|
if (Universe.enabledMigration) then
|
||||||
|
if (roll < 0.2) and Universe.raidAIToggle then
|
||||||
|
base.stateAI = BASE_AI_STATE_RAIDING
|
||||||
|
elseif (roll < 0.2) then
|
||||||
|
base.stateAI = BASE_AI_STATE_AGGRESSIVE
|
||||||
|
elseif (roll < 0.8) then
|
||||||
|
base.stateAI = BASE_AI_STATE_MIGRATING
|
||||||
|
elseif Universe.peacefulAIToggle then
|
||||||
|
base.stateAI = BASE_AI_STATE_PEACEFUL
|
||||||
|
else
|
||||||
|
base.stateAI = BASE_AI_STATE_MIGRATING
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if (roll < 0.3) then
|
||||||
|
base.stateAI = BASE_AI_STATE_AGGRESSIVE
|
||||||
|
elseif (roll < 0.6) and Universe.raidAIToggle then
|
||||||
|
base.stateAI = BASE_AI_STATE_RAIDING
|
||||||
|
elseif (roll < 0.6) then
|
||||||
|
base.stateAI = BASE_AI_STATE_AGGRESSIVE
|
||||||
|
elseif Universe.peacefulAIToggle then
|
||||||
|
base.stateAI = BASE_AI_STATE_PEACEFUL
|
||||||
|
else
|
||||||
|
base.stateAI = BASE_AI_STATE_AGGRESSIVE
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif (base.temperament < 0.6) then -- 0.4 - 0.6
|
||||||
|
if (roll < 0.4) then
|
||||||
|
base.stateAI = BASE_AI_STATE_AGGRESSIVE
|
||||||
|
elseif (roll < 0.5) and Universe.raidAIToggle then
|
||||||
|
base.stateAI = BASE_AI_STATE_RAIDING
|
||||||
|
elseif (roll < 0.75) and Universe.peacefulAIToggle then
|
||||||
|
base.stateAI = BASE_AI_STATE_PEACEFUL
|
||||||
|
else
|
||||||
|
if Universe.enabledMigration then
|
||||||
|
base.stateAI = BASE_AI_STATE_MIGRATING
|
||||||
|
else
|
||||||
|
base.stateAI = BASE_AI_STATE_AGGRESSIVE
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif (base.temperament < 0.8) then -- 0.6 - 0.8
|
||||||
|
if (roll < 0.4) then
|
||||||
|
base.stateAI = BASE_AI_STATE_AGGRESSIVE
|
||||||
|
elseif (roll < 0.6) then
|
||||||
|
base.stateAI = BASE_AI_STATE_ONSLAUGHT
|
||||||
|
elseif (roll < 0.8) then
|
||||||
|
base.stateAI = BASE_AI_STATE_RAIDING
|
||||||
|
elseif Universe.peacefulAIToggle then
|
||||||
|
base.stateAI = BASE_AI_STATE_PEACEFUL
|
||||||
|
else
|
||||||
|
base.stateAI = BASE_AI_STATE_AGGRESSIVE
|
||||||
|
end
|
||||||
|
elseif (base.temperament < 0.95) then -- 0.8 - 0.95
|
||||||
|
if (Universe.enabledMigration and Universe.raidAIToggle) then
|
||||||
|
if (roll < 0.20) and Universe.siegeAIToggle then
|
||||||
|
base.stateAI = BASE_AI_STATE_SIEGE
|
||||||
|
elseif (roll < 0.45) then
|
||||||
|
base.stateAI = BASE_AI_STATE_RAIDING
|
||||||
|
elseif (roll < 0.85) then
|
||||||
|
base.stateAI = BASE_AI_STATE_ONSLAUGHT
|
||||||
|
else
|
||||||
|
base.stateAI = BASE_AI_STATE_AGGRESSIVE
|
||||||
|
end
|
||||||
|
elseif (Universe.enabledMigration) then
|
||||||
|
if (roll < 0.20) and Universe.siegeAIToggle then
|
||||||
|
base.stateAI = BASE_AI_STATE_SIEGE
|
||||||
|
elseif (roll < 0.75) then
|
||||||
|
base.stateAI = BASE_AI_STATE_ONSLAUGHT
|
||||||
|
else
|
||||||
|
base.stateAI = BASE_AI_STATE_AGGRESSIVE
|
||||||
|
end
|
||||||
|
elseif (Universe.raidAIToggle) then
|
||||||
|
if (roll < 0.45) then
|
||||||
|
base.stateAI = BASE_AI_STATE_ONSLAUGHT
|
||||||
|
elseif (roll < 0.75) then
|
||||||
|
base.stateAI = BASE_AI_STATE_RAIDING
|
||||||
|
else
|
||||||
|
base.stateAI = BASE_AI_STATE_AGGRESSIVE
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if (roll < 0.65) then
|
||||||
|
base.stateAI = BASE_AI_STATE_ONSLAUGHT
|
||||||
|
else
|
||||||
|
base.stateAI = BASE_AI_STATE_AGGRESSIVE
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if (Universe.enabledMigration and Universe.raidAIToggle) then
|
||||||
|
if (roll < 0.30) and Universe.siegeAIToggle then
|
||||||
|
base.stateAI = BASE_AI_STATE_SIEGE
|
||||||
|
elseif (roll < 0.65) then
|
||||||
|
base.stateAI = BASE_AI_STATE_RAIDING
|
||||||
|
else
|
||||||
|
base.stateAI = BASE_AI_STATE_ONSLAUGHT
|
||||||
|
end
|
||||||
|
elseif (Universe.enabledMigration) then
|
||||||
|
if (roll < 0.30) and Universe.siegeAIToggle then
|
||||||
|
base.stateAI = BASE_AI_STATE_SIEGE
|
||||||
|
else
|
||||||
|
base.stateAI = BASE_AI_STATE_ONSLAUGHT
|
||||||
|
end
|
||||||
|
elseif (Universe.raidAIToggle) then
|
||||||
|
if (roll < 0.45) then
|
||||||
|
base.stateAI = BASE_AI_STATE_ONSLAUGHT
|
||||||
|
else
|
||||||
|
base.stateAI = BASE_AI_STATE_RAIDING
|
||||||
|
end
|
||||||
|
else
|
||||||
|
base.stateAI = BASE_AI_STATE_ONSLAUGHT
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local remainingUnits = base.lostEnemyUnits % 20
|
||||||
|
if remainingUnits ~= 0 then
|
||||||
|
BaseUtils.modifyBaseUnitPoints(base, -(remainingUnits*UNIT_DEATH_POINT_COST), remainingUnits.." Units Lost")
|
||||||
|
end
|
||||||
|
|
||||||
|
base.destroyPlayerBuildings = 0
|
||||||
|
base.lostEnemyUnits = 0
|
||||||
|
base.lostEnemyBuilding = 0
|
||||||
|
base.rocketLaunched = 0
|
||||||
|
base.builtEnemyBuilding = 0
|
||||||
|
base.ionCannonBlasts = 0
|
||||||
|
base.artilleryBlasts = 0
|
||||||
|
|
||||||
|
base.stateAITick = randomTickEvent(Universe.random, tick, BASE_AI_MIN_STATE_DURATION, BASE_AI_MAX_STATE_DURATION)
|
||||||
|
|
||||||
|
if Universe.printAIStateChanges then
|
||||||
|
game.print(base.id .. ": AI is now: " .. STATE_ENGLISH[base.stateAI] .. ", Next state change is in "
|
||||||
|
.. string.format("%.2f", (base.stateAITick - tick) / (60*60)) .. " minutes @ " ..
|
||||||
|
getTimeStringFromTick(base.stateAITick) .. " playtime")
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function BaseUtils.processBaseAIs(tick)
|
||||||
|
local baseId = Universe.processBaseAIIterator
|
||||||
|
local base
|
||||||
|
if not baseId then
|
||||||
|
baseId, base = next(Universe.bases, nil)
|
||||||
|
else
|
||||||
|
base = Universe.bases[baseId]
|
||||||
|
end
|
||||||
|
if not baseId then
|
||||||
|
Universe.processBaseAIIterator = nil
|
||||||
|
return
|
||||||
|
else
|
||||||
|
Universe.processBaseAIIterator = next(Universe.bases, baseId)
|
||||||
|
if (tick - base.tick) <= BASE_PROCESS_INTERVAL then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
temperamentPlanner(base, Universe.evolutionLevel)
|
||||||
|
processState(base, tick)
|
||||||
|
processBase(base, tick)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function BaseUtils.init(universe)
|
function BaseUtils.init(universe)
|
||||||
Universe = universe
|
Universe = universe
|
||||||
end
|
end
|
||||||
|
@@ -1,241 +0,0 @@
|
|||||||
-- Copyright (C) 2022 veden
|
|
||||||
|
|
||||||
-- This program is free software: you can redistribute it and/or modify
|
|
||||||
-- it under the terms of the GNU General Public License as published by
|
|
||||||
-- the Free Software Foundation, either version 3 of the License, or
|
|
||||||
-- (at your option) any later version.
|
|
||||||
|
|
||||||
-- This program is distributed in the hope that it will be useful,
|
|
||||||
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
-- GNU General Public License for more details.
|
|
||||||
|
|
||||||
-- You should have received a copy of the GNU General Public License
|
|
||||||
-- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
|
|
||||||
if (ChunkProcessorG) then
|
|
||||||
return ChunkProcessorG
|
|
||||||
end
|
|
||||||
local ChunkProcessor = {}
|
|
||||||
|
|
||||||
--
|
|
||||||
|
|
||||||
local Universe
|
|
||||||
|
|
||||||
-- imports
|
|
||||||
|
|
||||||
local ChunkUtils = require("ChunkUtils")
|
|
||||||
local QueryUtils = require("QueryUtils")
|
|
||||||
local MapUtils = require("MapUtils")
|
|
||||||
local MathUtils = require("MathUtils")
|
|
||||||
local Constants = require("Constants")
|
|
||||||
local BaseUtils = require("BaseUtils")
|
|
||||||
|
|
||||||
-- Constants
|
|
||||||
|
|
||||||
local PROXY_ENTITY_LOOKUP = Constants.PROXY_ENTITY_LOOKUP
|
|
||||||
local BASE_DISTANCE_TO_EVO_INDEX = Constants.BASE_DISTANCE_TO_EVO_INDEX
|
|
||||||
|
|
||||||
local BUILDING_SPACE_LOOKUP = Constants.BUILDING_SPACE_LOOKUP
|
|
||||||
|
|
||||||
-- imported functions
|
|
||||||
|
|
||||||
local findInsertionPoint = MapUtils.findInsertionPoint
|
|
||||||
local removeChunkFromMap = MapUtils.removeChunkFromMap
|
|
||||||
local setPositionInQuery = QueryUtils.setPositionInQuery
|
|
||||||
local registerEnemyBaseStructure = ChunkUtils.registerEnemyBaseStructure
|
|
||||||
local unregisterEnemyBaseStructure = ChunkUtils.unregisterEnemyBaseStructure
|
|
||||||
local euclideanDistancePoints = MathUtils.euclideanDistancePoints
|
|
||||||
|
|
||||||
local findEntityUpgrade = BaseUtils.findEntityUpgrade
|
|
||||||
|
|
||||||
local createChunk = ChunkUtils.createChunk
|
|
||||||
local initialScan = ChunkUtils.initialScan
|
|
||||||
local chunkPassScan = ChunkUtils.chunkPassScan
|
|
||||||
|
|
||||||
local mMin = math.min
|
|
||||||
local mMax = math.max
|
|
||||||
local next = next
|
|
||||||
local table_size = table_size
|
|
||||||
|
|
||||||
local tInsert = table.insert
|
|
||||||
|
|
||||||
-- module code
|
|
||||||
|
|
||||||
function ChunkProcessor.processPendingChunks(tick, flush)
|
|
||||||
local pendingChunks = Universe.pendingChunks
|
|
||||||
local eventId, event = next(pendingChunks, nil)
|
|
||||||
|
|
||||||
if not eventId then
|
|
||||||
if (tableSize(pendingChunks) == 0) then
|
|
||||||
-- this is needed as the next command remembers the max length a table has been
|
|
||||||
Universe.pendingChunks = {}
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local endCount = 1
|
|
||||||
if flush then
|
|
||||||
endCount = tableSize(pendingChunks)
|
|
||||||
end
|
|
||||||
for _=1,endCount do
|
|
||||||
if not flush and (event.tick > tick) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local newEventId, newEvent = next(pendingChunks, eventId)
|
|
||||||
pendingChunks[eventId] = nil
|
|
||||||
local map = event.map
|
|
||||||
if not map.surface.valid then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local topLeft = event.area.left_top
|
|
||||||
local x = topLeft.x
|
|
||||||
local y = topLeft.y
|
|
||||||
|
|
||||||
if not map[x] then
|
|
||||||
map[x] = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
if map[x][y] then
|
|
||||||
local oldChunk = map[x][y]
|
|
||||||
local chunk = initialScan(oldChunk, map, tick)
|
|
||||||
if (chunk == -1) then
|
|
||||||
removeChunkFromMap(map, oldChunk)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
local initialChunk = createChunk(map, x, y)
|
|
||||||
map[x][y] = initialChunk
|
|
||||||
Universe.chunkIdToChunk[initialChunk.id] = initialChunk
|
|
||||||
local chunk = initialScan(initialChunk, map, tick)
|
|
||||||
if (chunk ~= -1) then
|
|
||||||
tInsert(
|
|
||||||
map.processQueue,
|
|
||||||
findInsertionPoint(map.processQueue, chunk),
|
|
||||||
chunk
|
|
||||||
)
|
|
||||||
else
|
|
||||||
map[x][y] = nil
|
|
||||||
Universe.chunkIdToChunk[initialChunk.id] = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
eventId = newEventId
|
|
||||||
event = newEvent
|
|
||||||
if not eventId then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function ChunkProcessor.processPendingUpgrades(tick)
|
|
||||||
local entityId, entityData = next(Universe.pendingUpgrades, nil)
|
|
||||||
if not entityId then
|
|
||||||
if tableSize(Universe.pendingUpgrades) == 0 then
|
|
||||||
Universe.pendingUpgrades = {}
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local entity = entityData.entity
|
|
||||||
if not entity.valid then
|
|
||||||
Universe.pendingUpgrades[entityId] = nil
|
|
||||||
end
|
|
||||||
if entityData.delayTLL and tick < entityData.delayTLL then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
Universe.pendingUpgrades[entityId] = nil
|
|
||||||
local base = entityData.base
|
|
||||||
local map = base.map
|
|
||||||
local baseAlignment = base.alignment
|
|
||||||
local position = entityData.position or entity.position
|
|
||||||
|
|
||||||
local pickedBaseAlignment
|
|
||||||
if baseAlignment[2] then
|
|
||||||
if Universe.random() < 0.75 then
|
|
||||||
pickedBaseAlignment = baseAlignment[2]
|
|
||||||
else
|
|
||||||
pickedBaseAlignment = baseAlignment[1]
|
|
||||||
end
|
|
||||||
else
|
|
||||||
pickedBaseAlignment = baseAlignment[1]
|
|
||||||
end
|
|
||||||
|
|
||||||
local currentEvo = entity.prototype.build_base_evolution_requirement or 0
|
|
||||||
|
|
||||||
local distance = mMin(1, euclideanDistancePoints(position.x, position.y, 0, 0) * BASE_DISTANCE_TO_EVO_INDEX)
|
|
||||||
local evoIndex = mMax(distance, Universe.evolutionLevel)
|
|
||||||
|
|
||||||
local name = findEntityUpgrade(pickedBaseAlignment,
|
|
||||||
currentEvo,
|
|
||||||
evoIndex,
|
|
||||||
entity,
|
|
||||||
map,
|
|
||||||
entityData.evolve)
|
|
||||||
|
|
||||||
local entityName = entity.name
|
|
||||||
if not name and PROXY_ENTITY_LOOKUP[entityName] then
|
|
||||||
entity.destroy()
|
|
||||||
return
|
|
||||||
elseif (name == entityName) or not name then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local surface = entity.surface
|
|
||||||
local query = Universe.ppuUpgradeEntityQuery
|
|
||||||
query.name = name
|
|
||||||
|
|
||||||
unregisterEnemyBaseStructure(map, entity, nil, true)
|
|
||||||
entity.destroy()
|
|
||||||
local foundPosition = surface.find_non_colliding_position(BUILDING_SPACE_LOOKUP[name],
|
|
||||||
position,
|
|
||||||
2,
|
|
||||||
1,
|
|
||||||
true)
|
|
||||||
setPositionInQuery(query, foundPosition or position)
|
|
||||||
|
|
||||||
local createdEntity = surface.create_entity({
|
|
||||||
name = query.name,
|
|
||||||
position = query.position
|
|
||||||
})
|
|
||||||
if createdEntity and createdEntity.valid then
|
|
||||||
if entityData.register then
|
|
||||||
registerEnemyBaseStructure(map, createdEntity, base, tick, true)
|
|
||||||
end
|
|
||||||
if not entityData.evolve and Universe.printBaseUpgrades then
|
|
||||||
surface.print("["..base.id.."]:"..surface.name.." Upgrading ".. entityName .. " to " .. name .. " [gps=".. position.x ..",".. position.y .."]")
|
|
||||||
end
|
|
||||||
if remote.interfaces["kr-creep"] then
|
|
||||||
remote.call("kr-creep", "spawn_creep_at_position", surface, foundPosition or position, false, createdEntity.name)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function ChunkProcessor.processScanChunks()
|
|
||||||
local chunkId, chunk = next(Universe.chunkToPassScan, nil)
|
|
||||||
if not chunkId then
|
|
||||||
if (tableSize(Universe.chunkToPassScan) == 0) then
|
|
||||||
-- this is needed as the next command remembers the max length a table has been
|
|
||||||
Universe.chunkToPassScan = {}
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
Universe.chunkToPassScan[chunkId] = nil
|
|
||||||
local map = chunk.map
|
|
||||||
if not map.surface.valid then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if (chunkPassScan(chunk, map) == -1) then
|
|
||||||
removeChunkFromMap(map, chunk)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function ChunkProcessor.init(universe)
|
|
||||||
Universe = universe
|
|
||||||
end
|
|
||||||
|
|
||||||
ChunkProcessorG = ChunkProcessor
|
|
||||||
return ChunkProcessor
|
|
@@ -31,7 +31,7 @@ local Constants = require("Constants")
|
|||||||
local MapUtils = require("MapUtils")
|
local MapUtils = require("MapUtils")
|
||||||
local ChunkPropertyUtils = require("ChunkPropertyUtils")
|
local ChunkPropertyUtils = require("ChunkPropertyUtils")
|
||||||
local MathUtils = require("MathUtils")
|
local MathUtils = require("MathUtils")
|
||||||
local QueryUtils = require("QueryUtils")
|
local Utils = require("Utils")
|
||||||
|
|
||||||
-- Constants
|
-- Constants
|
||||||
|
|
||||||
@@ -73,9 +73,9 @@ local GENERATOR_PHEROMONE_LEVEL_6 = Constants.GENERATOR_PHEROMONE_LEVEL_6
|
|||||||
local removeBaseResourceChunk = ChunkPropertyUtils.removeBaseResourceChunk
|
local removeBaseResourceChunk = ChunkPropertyUtils.removeBaseResourceChunk
|
||||||
local addBaseResourceChunk = ChunkPropertyUtils.addBaseResourceChunk
|
local addBaseResourceChunk = ChunkPropertyUtils.addBaseResourceChunk
|
||||||
|
|
||||||
local setAreaInQueryChunkSize = QueryUtils.setAreaInQueryChunkSize
|
local setAreaInQueryChunkSize = Utils.setAreaInQueryChunkSize
|
||||||
local setAreaXInQuery = QueryUtils.setAreaXInQuery
|
local setAreaXInQuery = Utils.setAreaXInQuery
|
||||||
local setAreaYInQuery = QueryUtils.setAreaYInQuery
|
local setAreaYInQuery = Utils.setAreaYInQuery
|
||||||
|
|
||||||
local setPlayerBaseGenerator = ChunkPropertyUtils.setPlayerBaseGenerator
|
local setPlayerBaseGenerator = ChunkPropertyUtils.setPlayerBaseGenerator
|
||||||
local addPlayerBaseGenerator = ChunkPropertyUtils.addPlayerBaseGenerator
|
local addPlayerBaseGenerator = ChunkPropertyUtils.addPlayerBaseGenerator
|
||||||
|
@@ -28,9 +28,24 @@ local NeighborChunks
|
|||||||
|
|
||||||
local Constants = require("Constants")
|
local Constants = require("Constants")
|
||||||
local ChunkPropertyUtils = require("ChunkPropertyUtils")
|
local ChunkPropertyUtils = require("ChunkPropertyUtils")
|
||||||
|
local MathUtils = require("MathUtils")
|
||||||
|
|
||||||
-- Constants
|
-- Constants
|
||||||
|
|
||||||
|
local MAGIC_MAXIMUM_NUMBER = Constants.MAGIC_MAXIMUM_NUMBER
|
||||||
|
local ENEMY_PHEROMONE_MULTIPLER = Constants.ENEMY_PHEROMONE_MULTIPLER
|
||||||
|
local BASE_PHEROMONE = Constants.BASE_PHEROMONE
|
||||||
|
local PLAYER_PHEROMONE = Constants.PLAYER_PHEROMONE
|
||||||
|
local RESOURCE_PHEROMONE = Constants.RESOURCE_PHEROMONE
|
||||||
|
local ENEMY_PHEROMONE = Constants.ENEMY_PHEROMONE
|
||||||
|
|
||||||
|
local CHUNK_TICK = Constants.CHUNK_TICK
|
||||||
|
|
||||||
|
local VICTORY_SCENT = Constants.VICTORY_SCENT
|
||||||
|
local VICTORY_SCENT_MULTIPLER = Constants.VICTORY_SCENT_MULTIPLER
|
||||||
|
local VICTORY_SCENT_BOUND = Constants.VICTORY_SCENT_BOUND
|
||||||
|
local DEATH_PHEROMONE_GENERATOR_AMOUNT = Constants.DEATH_PHEROMONE_GENERATOR_AMOUNT
|
||||||
|
local TEN_DEATH_PHEROMONE_GENERATOR_AMOUNT = Constants.TEN_DEATH_PHEROMONE_GENERATOR_AMOUNT
|
||||||
local CHUNK_NORTH_SOUTH = Constants.CHUNK_NORTH_SOUTH
|
local CHUNK_NORTH_SOUTH = Constants.CHUNK_NORTH_SOUTH
|
||||||
local CHUNK_EAST_WEST = Constants.CHUNK_EAST_WEST
|
local CHUNK_EAST_WEST = Constants.CHUNK_EAST_WEST
|
||||||
local CHUNK_IMPASSABLE = Constants.CHUNK_IMPASSABLE
|
local CHUNK_IMPASSABLE = Constants.CHUNK_IMPASSABLE
|
||||||
@@ -42,6 +57,19 @@ local CHUNK_SIZE_DIVIDER = Constants.CHUNK_SIZE_DIVIDER
|
|||||||
|
|
||||||
-- imported functions
|
-- imported functions
|
||||||
|
|
||||||
|
local addVictoryGenerator = ChunkPropertyUtils.addVictoryGenerator
|
||||||
|
local addPermanentDeathGenerator = ChunkPropertyUtils.addPermanentDeathGenerator
|
||||||
|
local addDeathGenerator = ChunkPropertyUtils.addDeathGenerator
|
||||||
|
local getCombinedDeathGenerator = ChunkPropertyUtils.getCombinedDeathGenerator
|
||||||
|
local getCombinedDeathGeneratorRating = ChunkPropertyUtils.getCombinedDeathGeneratorRating
|
||||||
|
local getEnemyStructureCount = ChunkPropertyUtils.getEnemyStructureCount
|
||||||
|
local setDeathGenerator = ChunkPropertyUtils.setDeathGenerator
|
||||||
|
local decayPlayerGenerator = ChunkPropertyUtils.decayPlayerGenerator
|
||||||
|
local getPathRating = ChunkPropertyUtils.getPathRating
|
||||||
|
local linearInterpolation = MathUtils.linearInterpolation
|
||||||
|
local decayDeathGenerator = ChunkPropertyUtils.decayDeathGenerator
|
||||||
|
|
||||||
|
local mMax = math.max
|
||||||
local mFloor = math.floor
|
local mFloor = math.floor
|
||||||
local getPassable = ChunkPropertyUtils.getPassable
|
local getPassable = ChunkPropertyUtils.getPassable
|
||||||
local tRemove = table.remove
|
local tRemove = table.remove
|
||||||
@@ -355,6 +383,124 @@ function MapUtils.positionFromDirectionAndFlat(direction, startPosition, multipl
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function MapUtils.victoryScent(chunk, entityType)
|
||||||
|
local value = VICTORY_SCENT[entityType]
|
||||||
|
if value then
|
||||||
|
addVictoryGenerator(chunk, value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function MapUtils.disperseVictoryScent()
|
||||||
|
local chunkToVictory = Universe.chunkToVictory
|
||||||
|
local chunkId, pheromonePack = next(chunkToVictory, nil)
|
||||||
|
if not chunkId then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
chunkToVictory[chunkId] = nil
|
||||||
|
local chunk = pheromonePack.chunk
|
||||||
|
local map = chunk.map
|
||||||
|
if not map.surface.valid then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local chunkX = chunk.x
|
||||||
|
local chunkY = chunk.y
|
||||||
|
local i = 1
|
||||||
|
for x=chunkX - VICTORY_SCENT_BOUND, chunkX + VICTORY_SCENT_BOUND,32 do
|
||||||
|
for y = chunkY - VICTORY_SCENT_BOUND, chunkY + VICTORY_SCENT_BOUND,32 do
|
||||||
|
local c = MapUtils.getChunkByXY(map, x, y)
|
||||||
|
if (c ~= -1) then
|
||||||
|
local amount = pheromonePack.v * VICTORY_SCENT_MULTIPLER[i]
|
||||||
|
addDeathGenerator(c, amount)
|
||||||
|
addPermanentDeathGenerator(c, amount)
|
||||||
|
end
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function MapUtils.deathScent(chunk, structure)
|
||||||
|
local amount = -DEATH_PHEROMONE_GENERATOR_AMOUNT
|
||||||
|
if structure then
|
||||||
|
amount = -TEN_DEATH_PHEROMONE_GENERATOR_AMOUNT
|
||||||
|
end
|
||||||
|
addDeathGenerator(chunk, amount)
|
||||||
|
addPermanentDeathGenerator(chunk, amount)
|
||||||
|
end
|
||||||
|
|
||||||
|
function MapUtils.processPheromone(map, chunk, tick, player)
|
||||||
|
if chunk[CHUNK_TICK] > tick then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
chunk[CHUNK_TICK] = tick
|
||||||
|
|
||||||
|
local chunkPlayer = chunk[PLAYER_PHEROMONE]
|
||||||
|
local chunkBase = -MAGIC_MAXIMUM_NUMBER
|
||||||
|
local chunkDeath = getCombinedDeathGenerator(chunk)
|
||||||
|
local chunkResource = -MAGIC_MAXIMUM_NUMBER
|
||||||
|
local chunkEnemy = chunk[ENEMY_PHEROMONE]
|
||||||
|
|
||||||
|
local chunkCount = 1
|
||||||
|
|
||||||
|
local enemyStructureCount = getEnemyStructureCount(chunk)
|
||||||
|
|
||||||
|
local tempNeighbors = MapUtils.getNeighborChunks(map, chunk.x, chunk.y)
|
||||||
|
for i=1,8 do
|
||||||
|
local tempPheromone
|
||||||
|
local neighbor = tempNeighbors[i]
|
||||||
|
if (neighbor ~= -1) then
|
||||||
|
if MapUtils.canMoveChunkDirection(map, i, chunk, neighbor) then
|
||||||
|
chunkCount = chunkCount + 1
|
||||||
|
chunkPlayer = chunkPlayer + neighbor[PLAYER_PHEROMONE]
|
||||||
|
chunkEnemy = chunkEnemy + neighbor[ENEMY_PHEROMONE]
|
||||||
|
chunkDeath = chunkDeath + getCombinedDeathGenerator(neighbor)
|
||||||
|
tempPheromone = neighbor[BASE_PHEROMONE]
|
||||||
|
if chunkBase < tempPheromone then
|
||||||
|
chunkBase = tempPheromone
|
||||||
|
end
|
||||||
|
tempPheromone = neighbor[RESOURCE_PHEROMONE]
|
||||||
|
if chunkResource < tempPheromone then
|
||||||
|
chunkResource = tempPheromone
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
setDeathGenerator(chunk, (chunkDeath / chunkCount) * 0.75)
|
||||||
|
|
||||||
|
if not player then
|
||||||
|
decayDeathGenerator(chunk)
|
||||||
|
end
|
||||||
|
|
||||||
|
decayPlayerGenerator(chunk)
|
||||||
|
|
||||||
|
local chunkDeathRating = getCombinedDeathGeneratorRating(chunk) * getPathRating(chunk)
|
||||||
|
|
||||||
|
chunk[PLAYER_PHEROMONE] = chunkDeathRating * mMax(
|
||||||
|
chunk.playerGenerator or 0,
|
||||||
|
(chunkPlayer / chunkCount) * 0.98
|
||||||
|
)
|
||||||
|
|
||||||
|
chunk[BASE_PHEROMONE] = chunkDeathRating * mMax(
|
||||||
|
chunk.playerBaseGenerator or 0,
|
||||||
|
chunkBase * 0.9
|
||||||
|
)
|
||||||
|
|
||||||
|
chunk[ENEMY_PHEROMONE] = chunkDeathRating * mMax(
|
||||||
|
enemyStructureCount * ENEMY_PHEROMONE_MULTIPLER,
|
||||||
|
(chunkEnemy / chunkCount) * 0.9
|
||||||
|
)
|
||||||
|
|
||||||
|
local resourcePheromoneGenerator = chunk.resourceGenerator or 0
|
||||||
|
if (resourcePheromoneGenerator > 0) then
|
||||||
|
chunkResource = linearInterpolation(resourcePheromoneGenerator, 15000, 20000)
|
||||||
|
end
|
||||||
|
if enemyStructureCount ~= 0 then
|
||||||
|
chunkResource = chunkResource * 0.0001
|
||||||
|
end
|
||||||
|
chunk[RESOURCE_PHEROMONE] = chunkDeathRating * chunkResource * 0.9
|
||||||
|
end
|
||||||
|
|
||||||
function MapUtils.init(universe)
|
function MapUtils.init(universe)
|
||||||
Universe = universe
|
Universe = universe
|
||||||
NeighborChunks = universe.neighbors
|
NeighborChunks = universe.neighbors
|
||||||
|
@@ -1,308 +0,0 @@
|
|||||||
-- Copyright (C) 2022 veden
|
|
||||||
|
|
||||||
-- This program is free software: you can redistribute it and/or modify
|
|
||||||
-- it under the terms of the GNU General Public License as published by
|
|
||||||
-- the Free Software Foundation, either version 3 of the License, or
|
|
||||||
-- (at your option) any later version.
|
|
||||||
|
|
||||||
-- This program is distributed in the hope that it will be useful,
|
|
||||||
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
-- GNU General Public License for more details.
|
|
||||||
|
|
||||||
-- You should have received a copy of the GNU General Public License
|
|
||||||
-- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
|
|
||||||
if MovementUtilsG then
|
|
||||||
return MovementUtilsG
|
|
||||||
end
|
|
||||||
local MovementUtils = {}
|
|
||||||
|
|
||||||
--
|
|
||||||
|
|
||||||
local Universe
|
|
||||||
|
|
||||||
-- imports
|
|
||||||
|
|
||||||
local Constants = require("Constants")
|
|
||||||
local MapUtils = require("MapUtils")
|
|
||||||
local MathUtils = require("MathUtils")
|
|
||||||
local UnitGroupUtils = require("UnitGroupUtils")
|
|
||||||
|
|
||||||
-- Constants
|
|
||||||
|
|
||||||
local MAGIC_MAXIMUM_NUMBER = Constants.MAGIC_MAXIMUM_NUMBER
|
|
||||||
|
|
||||||
local SQUAD_SETTLING = Constants.SQUAD_SETTLING
|
|
||||||
|
|
||||||
-- imported functions
|
|
||||||
|
|
||||||
local calculateSettlerMaxDistance = UnitGroupUtils.calculateSettlerMaxDistance
|
|
||||||
|
|
||||||
local canMoveChunkDirection = MapUtils.canMoveChunkDirection
|
|
||||||
local getNeighborChunks = MapUtils.getNeighborChunks
|
|
||||||
|
|
||||||
local tableRemove = table.remove
|
|
||||||
local tableInsert = table.insert
|
|
||||||
|
|
||||||
local distortPosition = MathUtils.distortPosition
|
|
||||||
|
|
||||||
-- module code
|
|
||||||
|
|
||||||
function MovementUtils.findMovementPosition(surface, position)
|
|
||||||
local pos = position
|
|
||||||
pos = surface.find_non_colliding_position("behemoth-biter", pos, 10, 2, false)
|
|
||||||
return pos
|
|
||||||
end
|
|
||||||
|
|
||||||
function MovementUtils.findMovementPositionEntity(entityName, surface, position)
|
|
||||||
local pos = position
|
|
||||||
pos = surface.find_non_colliding_position(entityName, pos, 5, 4, true)
|
|
||||||
return pos
|
|
||||||
end
|
|
||||||
|
|
||||||
function MovementUtils.findMovementPositionDistort(surface, position)
|
|
||||||
local pos = position
|
|
||||||
pos = surface.find_non_colliding_position("behemoth-biter", pos, 10, 2, false)
|
|
||||||
return distortPosition(pos, 8)
|
|
||||||
end
|
|
||||||
|
|
||||||
function MovementUtils.addMovementPenalty(squad, chunk)
|
|
||||||
if (chunk == -1) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local penalties = squad.penalties
|
|
||||||
local penaltyCount = #penalties
|
|
||||||
for i=1,penaltyCount do
|
|
||||||
local penalty = penalties[i]
|
|
||||||
if (penalty.c.id == chunk.id) then
|
|
||||||
penalty.v = penalty.v + 1
|
|
||||||
if penalty.v >= 15 then
|
|
||||||
if Universe.enabledMigration and
|
|
||||||
(Universe.builderCount < Universe.AI_MAX_BUILDER_COUNT) then
|
|
||||||
squad.settler = true
|
|
||||||
squad.originPosition.x = squad.group.position.x
|
|
||||||
squad.originPosition.y = squad.group.position.y
|
|
||||||
squad.maxDistance = calculateSettlerMaxDistance()
|
|
||||||
|
|
||||||
squad.status = SQUAD_SETTLING
|
|
||||||
else
|
|
||||||
squad.group.destroy()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if (penaltyCount == 10) then
|
|
||||||
tableRemove(penalties, 10)
|
|
||||||
end
|
|
||||||
tableInsert(penalties,
|
|
||||||
1,
|
|
||||||
{ v = 1,
|
|
||||||
c = chunk })
|
|
||||||
end
|
|
||||||
|
|
||||||
--[[
|
|
||||||
Expects all neighbors adjacent to a chunk
|
|
||||||
--]]
|
|
||||||
function MovementUtils.scoreNeighborsForAttack(map, chunk, neighborDirectionChunks, scoreFunction)
|
|
||||||
local highestChunk = -1
|
|
||||||
local highestScore = -MAGIC_MAXIMUM_NUMBER
|
|
||||||
local highestDirection
|
|
||||||
|
|
||||||
for x=1,8 do
|
|
||||||
local neighborChunk = neighborDirectionChunks[x]
|
|
||||||
if (neighborChunk ~= -1) then
|
|
||||||
if (chunk == -1) or canMoveChunkDirection(map, x, chunk, neighborChunk) then
|
|
||||||
local score = scoreFunction(map, neighborChunk)
|
|
||||||
if (score > highestScore) then
|
|
||||||
highestScore = score
|
|
||||||
highestChunk = neighborChunk
|
|
||||||
highestDirection = x
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local nextHighestChunk = -1
|
|
||||||
local nextHighestScore = highestScore
|
|
||||||
local nextHighestDirection
|
|
||||||
|
|
||||||
if (highestChunk ~= -1) then
|
|
||||||
neighborDirectionChunks = getNeighborChunks(map, highestChunk.x, highestChunk.y)
|
|
||||||
for x=1,8 do
|
|
||||||
local neighborChunk = neighborDirectionChunks[x]
|
|
||||||
if ((neighborChunk ~= -1) and ((chunk == -1) or (neighborChunk.id ~= chunk.id)) and
|
|
||||||
canMoveChunkDirection(map, x, highestChunk, neighborChunk)) then
|
|
||||||
local score = scoreFunction(map, neighborChunk)
|
|
||||||
if (score > nextHighestScore) then
|
|
||||||
nextHighestScore = score
|
|
||||||
nextHighestChunk = neighborChunk
|
|
||||||
nextHighestDirection = x
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return highestChunk, highestDirection, nextHighestChunk, nextHighestDirection
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--[[
|
|
||||||
Expects all neighbors adjacent to a chunk
|
|
||||||
--]]
|
|
||||||
function MovementUtils.scoreNeighborsForSettling(map, chunk, neighborDirectionChunks, scoreFunction)
|
|
||||||
local highestChunk = -1
|
|
||||||
local highestScore = -MAGIC_MAXIMUM_NUMBER
|
|
||||||
local highestDirection = 0
|
|
||||||
|
|
||||||
for x=1,8 do
|
|
||||||
local neighborChunk = neighborDirectionChunks[x]
|
|
||||||
if (neighborChunk ~= -1) then
|
|
||||||
if (chunk == -1) or canMoveChunkDirection(map, x, chunk, neighborChunk) then
|
|
||||||
local score = scoreFunction(map, neighborChunk)
|
|
||||||
if (score > highestScore) then
|
|
||||||
highestScore = score
|
|
||||||
highestChunk = neighborChunk
|
|
||||||
highestDirection = x
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if (chunk ~= -1) and (scoreFunction(map, chunk) > highestScore) then
|
|
||||||
return chunk, 0, -1, 0
|
|
||||||
end
|
|
||||||
|
|
||||||
local nextHighestChunk = -1
|
|
||||||
local nextHighestScore = highestScore
|
|
||||||
local nextHighestDirection = 0
|
|
||||||
|
|
||||||
if (highestChunk ~= -1) then
|
|
||||||
neighborDirectionChunks = getNeighborChunks(map, highestChunk.x, highestChunk.y)
|
|
||||||
for x=1,8 do
|
|
||||||
local neighborChunk = neighborDirectionChunks[x]
|
|
||||||
if ((neighborChunk ~= -1) and ((chunk == -1) or (neighborChunk.id ~= chunk.id)) and
|
|
||||||
canMoveChunkDirection(map, x, highestChunk, neighborChunk)) then
|
|
||||||
local score = scoreFunction(map, neighborChunk)
|
|
||||||
if (score > nextHighestScore) then
|
|
||||||
nextHighestScore = score
|
|
||||||
nextHighestChunk = neighborChunk
|
|
||||||
nextHighestDirection = x
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return highestChunk, highestDirection, nextHighestChunk, nextHighestDirection
|
|
||||||
end
|
|
||||||
|
|
||||||
--[[
|
|
||||||
Expects all neighbors adjacent to a chunk
|
|
||||||
--]]
|
|
||||||
function MovementUtils.scoreNeighborsForResource(chunk, neighborDirectionChunks, validFunction, scoreFunction, map)
|
|
||||||
local highestChunk = -1
|
|
||||||
local highestScore = -MAGIC_MAXIMUM_NUMBER
|
|
||||||
local highestDirection
|
|
||||||
for x=1,8 do
|
|
||||||
local neighborChunk = neighborDirectionChunks[x]
|
|
||||||
if (neighborChunk ~= -1) and
|
|
||||||
canMoveChunkDirection(map, x, chunk, neighborChunk) and
|
|
||||||
validFunction(map, chunk, neighborChunk)
|
|
||||||
then
|
|
||||||
local score = scoreFunction(map, neighborChunk)
|
|
||||||
if (score > highestScore) then
|
|
||||||
highestScore = score
|
|
||||||
highestChunk = neighborChunk
|
|
||||||
highestDirection = x
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if (chunk ~= -1) and (scoreFunction(map, chunk) > highestScore) then
|
|
||||||
return -1, -1
|
|
||||||
end
|
|
||||||
|
|
||||||
return highestChunk, highestDirection
|
|
||||||
end
|
|
||||||
|
|
||||||
--[[
|
|
||||||
Expects all neighbors adjacent to a chunk
|
|
||||||
--]]
|
|
||||||
function MovementUtils.scoreNeighborsForRetreat(chunk, neighborDirectionChunks, scoreFunction, map)
|
|
||||||
local highestChunk = -1
|
|
||||||
local highestScore = -MAGIC_MAXIMUM_NUMBER
|
|
||||||
local highestDirection
|
|
||||||
|
|
||||||
for x=1,8 do
|
|
||||||
local neighborChunk = neighborDirectionChunks[x]
|
|
||||||
if (neighborChunk ~= -1) then
|
|
||||||
if (chunk == -1) or canMoveChunkDirection(map, x, chunk, neighborChunk) then
|
|
||||||
local score = scoreFunction(map, neighborChunk)
|
|
||||||
if (score > highestScore) then
|
|
||||||
highestScore = score
|
|
||||||
highestChunk = neighborChunk
|
|
||||||
highestDirection = x
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local nextHighestChunk = -1
|
|
||||||
local nextHighestScore = highestScore
|
|
||||||
local nextHighestDirection
|
|
||||||
|
|
||||||
if (highestChunk ~= -1) then
|
|
||||||
neighborDirectionChunks = getNeighborChunks(map, highestChunk.x, highestChunk.y)
|
|
||||||
for x=1,8 do
|
|
||||||
local neighborChunk = neighborDirectionChunks[x]
|
|
||||||
|
|
||||||
if ((neighborChunk ~= -1) and ((chunk == -1) or (neighborChunk.id ~= chunk.id)) and
|
|
||||||
canMoveChunkDirection(map, x, highestChunk, neighborChunk)) then
|
|
||||||
local score = scoreFunction(map, neighborChunk)
|
|
||||||
if (score > nextHighestScore) then
|
|
||||||
nextHighestScore = score
|
|
||||||
nextHighestChunk = neighborChunk
|
|
||||||
nextHighestDirection = x
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if (nextHighestChunk == nil) then
|
|
||||||
nextHighestChunk = -1
|
|
||||||
end
|
|
||||||
|
|
||||||
return highestChunk, highestDirection, nextHighestChunk, nextHighestDirection
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--[[
|
|
||||||
Expects all neighbors adjacent to a chunk
|
|
||||||
--]]
|
|
||||||
function MovementUtils.scoreNeighborsForFormation(neighborChunks, validFunction, scoreFunction, map)
|
|
||||||
local highestChunk = -1
|
|
||||||
local highestScore = -MAGIC_MAXIMUM_NUMBER
|
|
||||||
local highestDirection
|
|
||||||
for x=1,8 do
|
|
||||||
local neighborChunk = neighborChunks[x]
|
|
||||||
if (neighborChunk ~= -1) and validFunction(map, neighborChunk) then
|
|
||||||
local score = scoreFunction(map, neighborChunk)
|
|
||||||
if (score > highestScore) then
|
|
||||||
highestScore = score
|
|
||||||
highestChunk = neighborChunk
|
|
||||||
highestDirection = x
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return highestChunk, highestDirection
|
|
||||||
end
|
|
||||||
|
|
||||||
function MovementUtils.init(universe)
|
|
||||||
Universe = universe
|
|
||||||
end
|
|
||||||
|
|
||||||
MovementUtilsG = MovementUtils
|
|
||||||
return MovementUtils
|
|
@@ -1,204 +0,0 @@
|
|||||||
-- Copyright (C) 2022 veden
|
|
||||||
|
|
||||||
-- This program is free software: you can redistribute it and/or modify
|
|
||||||
-- it under the terms of the GNU General Public License as published by
|
|
||||||
-- the Free Software Foundation, either version 3 of the License, or
|
|
||||||
-- (at your option) any later version.
|
|
||||||
|
|
||||||
-- This program is distributed in the hope that it will be useful,
|
|
||||||
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
-- GNU General Public License for more details.
|
|
||||||
|
|
||||||
-- You should have received a copy of the GNU General Public License
|
|
||||||
-- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
|
|
||||||
if PheromoneUtilsG then
|
|
||||||
return PheromoneUtilsG
|
|
||||||
end
|
|
||||||
local PheromoneUtils = {}
|
|
||||||
|
|
||||||
--
|
|
||||||
|
|
||||||
local Universe
|
|
||||||
|
|
||||||
-- imports
|
|
||||||
local MathUtils = require("MathUtils")
|
|
||||||
local MapUtils = require("MapUtils")
|
|
||||||
local Constants = require("Constants")
|
|
||||||
local ChunkPropertyUtils = require("ChunkPropertyUtils")
|
|
||||||
|
|
||||||
-- Constants
|
|
||||||
|
|
||||||
local CHUNK_TICK = Constants.CHUNK_TICK
|
|
||||||
|
|
||||||
local ENEMY_PHEROMONE_MULTIPLER = Constants.ENEMY_PHEROMONE_MULTIPLER
|
|
||||||
local VICTORY_SCENT_MULTIPLER = Constants.VICTORY_SCENT_MULTIPLER
|
|
||||||
local VICTORY_SCENT_BOUND = Constants.VICTORY_SCENT_BOUND
|
|
||||||
|
|
||||||
local MAGIC_MAXIMUM_NUMBER = Constants.MAGIC_MAXIMUM_NUMBER
|
|
||||||
|
|
||||||
local BASE_PHEROMONE = Constants.BASE_PHEROMONE
|
|
||||||
local PLAYER_PHEROMONE = Constants.PLAYER_PHEROMONE
|
|
||||||
local RESOURCE_PHEROMONE = Constants.RESOURCE_PHEROMONE
|
|
||||||
local ENEMY_PHEROMONE = Constants.ENEMY_PHEROMONE
|
|
||||||
|
|
||||||
local VICTORY_SCENT = Constants.VICTORY_SCENT
|
|
||||||
|
|
||||||
local DEATH_PHEROMONE_GENERATOR_AMOUNT = Constants.DEATH_PHEROMONE_GENERATOR_AMOUNT
|
|
||||||
local TEN_DEATH_PHEROMONE_GENERATOR_AMOUNT = Constants.TEN_DEATH_PHEROMONE_GENERATOR_AMOUNT
|
|
||||||
|
|
||||||
-- imported functions
|
|
||||||
|
|
||||||
local decayPlayerGenerator = ChunkPropertyUtils.decayPlayerGenerator
|
|
||||||
local addVictoryGenerator = ChunkPropertyUtils.addVictoryGenerator
|
|
||||||
local getCombinedDeathGenerator = ChunkPropertyUtils.getCombinedDeathGenerator
|
|
||||||
local getCombinedDeathGeneratorRating = ChunkPropertyUtils.getCombinedDeathGeneratorRating
|
|
||||||
local setDeathGenerator = ChunkPropertyUtils.setDeathGenerator
|
|
||||||
|
|
||||||
local addPermanentDeathGenerator = ChunkPropertyUtils.addPermanentDeathGenerator
|
|
||||||
|
|
||||||
local canMoveChunkDirection = MapUtils.canMoveChunkDirection
|
|
||||||
local getNeighborChunks = MapUtils.getNeighborChunks
|
|
||||||
local getChunkById = MapUtils.getChunkById
|
|
||||||
|
|
||||||
local getEnemyStructureCount = ChunkPropertyUtils.getEnemyStructureCount
|
|
||||||
local getPathRating = ChunkPropertyUtils.getPathRating
|
|
||||||
local addDeathGenerator = ChunkPropertyUtils.addDeathGenerator
|
|
||||||
|
|
||||||
local decayDeathGenerator = ChunkPropertyUtils.decayDeathGenerator
|
|
||||||
|
|
||||||
local linearInterpolation = MathUtils.linearInterpolation
|
|
||||||
|
|
||||||
local getChunkByXY = MapUtils.getChunkByXY
|
|
||||||
|
|
||||||
local next = next
|
|
||||||
local mMax = math.max
|
|
||||||
|
|
||||||
-- module code
|
|
||||||
|
|
||||||
function PheromoneUtils.victoryScent(chunk, entityType)
|
|
||||||
local value = VICTORY_SCENT[entityType]
|
|
||||||
if value then
|
|
||||||
addVictoryGenerator(chunk, value)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function PheromoneUtils.disperseVictoryScent()
|
|
||||||
local chunkToVictory = Universe.chunkToVictory
|
|
||||||
local chunkId, pheromonePack = next(chunkToVictory, nil)
|
|
||||||
if not chunkId then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
chunkToVictory[chunkId] = nil
|
|
||||||
local chunk = pheromonePack.chunk
|
|
||||||
local map = chunk.map
|
|
||||||
if not map.surface.valid then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local chunkX = chunk.x
|
|
||||||
local chunkY = chunk.y
|
|
||||||
local i = 1
|
|
||||||
for x=chunkX - VICTORY_SCENT_BOUND, chunkX + VICTORY_SCENT_BOUND,32 do
|
|
||||||
for y = chunkY - VICTORY_SCENT_BOUND, chunkY + VICTORY_SCENT_BOUND,32 do
|
|
||||||
local c = getChunkByXY(map, x, y)
|
|
||||||
if (c ~= -1) then
|
|
||||||
local amount = pheromonePack.v * VICTORY_SCENT_MULTIPLER[i]
|
|
||||||
addDeathGenerator(c, amount)
|
|
||||||
addPermanentDeathGenerator(c, amount)
|
|
||||||
end
|
|
||||||
i = i + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function PheromoneUtils.deathScent(chunk, structure)
|
|
||||||
local amount = -DEATH_PHEROMONE_GENERATOR_AMOUNT
|
|
||||||
if structure then
|
|
||||||
amount = -TEN_DEATH_PHEROMONE_GENERATOR_AMOUNT
|
|
||||||
end
|
|
||||||
addDeathGenerator(chunk, amount)
|
|
||||||
addPermanentDeathGenerator(chunk, amount)
|
|
||||||
end
|
|
||||||
|
|
||||||
function PheromoneUtils.processPheromone(map, chunk, tick, player)
|
|
||||||
if chunk[CHUNK_TICK] > tick then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
chunk[CHUNK_TICK] = tick
|
|
||||||
|
|
||||||
local chunkPlayer = chunk[PLAYER_PHEROMONE]
|
|
||||||
local chunkBase = -MAGIC_MAXIMUM_NUMBER
|
|
||||||
local chunkDeath = getCombinedDeathGenerator(chunk)
|
|
||||||
local chunkResource = -MAGIC_MAXIMUM_NUMBER
|
|
||||||
local chunkEnemy = chunk[ENEMY_PHEROMONE]
|
|
||||||
|
|
||||||
local chunkCount = 1
|
|
||||||
|
|
||||||
local enemyStructureCount = getEnemyStructureCount(chunk)
|
|
||||||
|
|
||||||
local tempNeighbors = getNeighborChunks(map, chunk.x, chunk.y)
|
|
||||||
for i=1,8 do
|
|
||||||
local tempPheromone
|
|
||||||
local neighbor = tempNeighbors[i]
|
|
||||||
if (neighbor ~= -1) then
|
|
||||||
if canMoveChunkDirection(map, i, chunk, neighbor) then
|
|
||||||
chunkCount = chunkCount + 1
|
|
||||||
chunkPlayer = chunkPlayer + neighbor[PLAYER_PHEROMONE]
|
|
||||||
chunkEnemy = chunkEnemy + neighbor[ENEMY_PHEROMONE]
|
|
||||||
chunkDeath = chunkDeath + getCombinedDeathGenerator(neighbor)
|
|
||||||
tempPheromone = neighbor[BASE_PHEROMONE]
|
|
||||||
if chunkBase < tempPheromone then
|
|
||||||
chunkBase = tempPheromone
|
|
||||||
end
|
|
||||||
tempPheromone = neighbor[RESOURCE_PHEROMONE]
|
|
||||||
if chunkResource < tempPheromone then
|
|
||||||
chunkResource = tempPheromone
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
setDeathGenerator(chunk, (chunkDeath / chunkCount) * 0.75)
|
|
||||||
|
|
||||||
if not player then
|
|
||||||
decayDeathGenerator(chunk)
|
|
||||||
end
|
|
||||||
|
|
||||||
decayPlayerGenerator(chunk)
|
|
||||||
|
|
||||||
local chunkDeathRating = getCombinedDeathGeneratorRating(chunk) * getPathRating(chunk)
|
|
||||||
|
|
||||||
chunk[PLAYER_PHEROMONE] = chunkDeathRating * mMax(
|
|
||||||
chunk.playerGenerator or 0,
|
|
||||||
(chunkPlayer / chunkCount) * 0.98
|
|
||||||
)
|
|
||||||
|
|
||||||
chunk[BASE_PHEROMONE] = chunkDeathRating * mMax(
|
|
||||||
chunk.playerBaseGenerator or 0,
|
|
||||||
chunkBase * 0.9
|
|
||||||
)
|
|
||||||
|
|
||||||
chunk[ENEMY_PHEROMONE] = chunkDeathRating * mMax(
|
|
||||||
enemyStructureCount * ENEMY_PHEROMONE_MULTIPLER,
|
|
||||||
(chunkEnemy / chunkCount) * 0.9
|
|
||||||
)
|
|
||||||
|
|
||||||
local resourcePheromoneGenerator = chunk.resourceGenerator or 0
|
|
||||||
if (resourcePheromoneGenerator > 0) then
|
|
||||||
chunkResource = linearInterpolation(resourcePheromoneGenerator, 15000, 20000)
|
|
||||||
end
|
|
||||||
if enemyStructureCount ~= 0 then
|
|
||||||
chunkResource = chunkResource * 0.0001
|
|
||||||
end
|
|
||||||
chunk[RESOURCE_PHEROMONE] = chunkDeathRating * chunkResource * 0.9
|
|
||||||
end
|
|
||||||
|
|
||||||
function PheromoneUtils.init(universe)
|
|
||||||
Universe = universe
|
|
||||||
end
|
|
||||||
|
|
||||||
PheromoneUtilsG = PheromoneUtils
|
|
||||||
return PheromoneUtils
|
|
@@ -1,37 +0,0 @@
|
|||||||
-- Copyright (C) 2022 veden
|
|
||||||
|
|
||||||
-- This program is free software: you can redistribute it and/or modify
|
|
||||||
-- it under the terms of the GNU General Public License as published by
|
|
||||||
-- the Free Software Foundation, either version 3 of the License, or
|
|
||||||
-- (at your option) any later version.
|
|
||||||
|
|
||||||
-- This program is distributed in the hope that it will be useful,
|
|
||||||
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
-- GNU General Public License for more details.
|
|
||||||
|
|
||||||
-- You should have received a copy of the GNU General Public License
|
|
||||||
-- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
|
|
||||||
if playerUtilsG then
|
|
||||||
return playerUtilsG
|
|
||||||
end
|
|
||||||
local playerUtils = {}
|
|
||||||
|
|
||||||
-- imports
|
|
||||||
|
|
||||||
-- imported functions
|
|
||||||
|
|
||||||
-- module code
|
|
||||||
|
|
||||||
function playerUtils.validPlayer(player)
|
|
||||||
if player and player.valid then
|
|
||||||
local char = player.character
|
|
||||||
return char and char.valid
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
playerUtilsG = playerUtils
|
|
||||||
return playerUtils
|
|
@@ -14,10 +14,10 @@
|
|||||||
-- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
-- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
if MapProcessorG then
|
if ProcessorG then
|
||||||
return MapProcessorG
|
return ProcessorG
|
||||||
end
|
end
|
||||||
local MapProcessor = {}
|
local Processor = {}
|
||||||
|
|
||||||
--
|
--
|
||||||
|
|
||||||
@@ -25,21 +25,18 @@ local Universe
|
|||||||
|
|
||||||
-- imports
|
-- imports
|
||||||
|
|
||||||
local QueryUtils = require("QueryUtils")
|
local Utils = require("Utils")
|
||||||
local PheromoneUtils = require("PheromoneUtils")
|
|
||||||
local AiAttackWave = require("AIAttackWave")
|
|
||||||
local AiPredicates = require("AIPredicates")
|
|
||||||
local Constants = require("Constants")
|
|
||||||
local MapUtils = require("MapUtils")
|
local MapUtils = require("MapUtils")
|
||||||
local PlayerUtils = require("PlayerUtils")
|
local Squad = require("Squad")
|
||||||
|
local Constants = require("Constants")
|
||||||
local ChunkUtils = require("ChunkUtils")
|
local ChunkUtils = require("ChunkUtils")
|
||||||
local ChunkPropertyUtils = require("ChunkPropertyUtils")
|
local ChunkPropertyUtils = require("ChunkPropertyUtils")
|
||||||
local BaseUtils = require("BaseUtils")
|
local BaseUtils = require("BaseUtils")
|
||||||
|
local MathUtils = require("MathUtils")
|
||||||
|
|
||||||
-- Constants
|
-- Constants
|
||||||
|
|
||||||
local PLAYER_PHEROMONE_GENERATOR_AMOUNT = Constants.PLAYER_PHEROMONE_GENERATOR_AMOUNT
|
local PLAYER_PHEROMONE_GENERATOR_AMOUNT = Constants.PLAYER_PHEROMONE_GENERATOR_AMOUNT
|
||||||
local DURATION_ACTIVE_NEST = Constants.DURATION_ACTIVE_NEST
|
|
||||||
|
|
||||||
local PROCESS_QUEUE_SIZE = Constants.PROCESS_QUEUE_SIZE
|
local PROCESS_QUEUE_SIZE = Constants.PROCESS_QUEUE_SIZE
|
||||||
local RESOURCE_QUEUE_SIZE = Constants.RESOURCE_QUEUE_SIZE
|
local RESOURCE_QUEUE_SIZE = Constants.RESOURCE_QUEUE_SIZE
|
||||||
@@ -60,33 +57,49 @@ local BASE_AI_STATE_MIGRATING = Constants.BASE_AI_STATE_MIGRATING
|
|||||||
local COOLDOWN_DRAIN = Constants.COOLDOWN_DRAIN
|
local COOLDOWN_DRAIN = Constants.COOLDOWN_DRAIN
|
||||||
local COOLDOWN_RALLY = Constants.COOLDOWN_RALLY
|
local COOLDOWN_RALLY = Constants.COOLDOWN_RALLY
|
||||||
local COOLDOWN_RETREAT = Constants.COOLDOWN_RETREAT
|
local COOLDOWN_RETREAT = Constants.COOLDOWN_RETREAT
|
||||||
|
local PROXY_ENTITY_LOOKUP = Constants.PROXY_ENTITY_LOOKUP
|
||||||
|
local BASE_DISTANCE_TO_EVO_INDEX = Constants.BASE_DISTANCE_TO_EVO_INDEX
|
||||||
|
|
||||||
|
local BUILDING_SPACE_LOOKUP = Constants.BUILDING_SPACE_LOOKUP
|
||||||
|
|
||||||
-- imported functions
|
-- imported functions
|
||||||
|
|
||||||
local setPositionInQuery = QueryUtils.setPositionInQuery
|
local findInsertionPoint = MapUtils.findInsertionPoint
|
||||||
|
|
||||||
|
local createChunk = ChunkUtils.createChunk
|
||||||
|
local initialScan = ChunkUtils.initialScan
|
||||||
|
local euclideanDistancePoints = MathUtils.euclideanDistancePoints
|
||||||
|
|
||||||
|
local removeChunkFromMap = MapUtils.removeChunkFromMap
|
||||||
|
local chunkPassScan = ChunkUtils.chunkPassScan
|
||||||
|
local findEntityUpgrade = BaseUtils.findEntityUpgrade
|
||||||
|
|
||||||
|
local unregisterEnemyBaseStructure = ChunkUtils.unregisterEnemyBaseStructure
|
||||||
|
local registerEnemyBaseStructure = ChunkUtils.registerEnemyBaseStructure
|
||||||
|
local setPositionInQuery = Utils.setPositionInQuery
|
||||||
|
|
||||||
local addPlayerGenerator = ChunkPropertyUtils.addPlayerGenerator
|
local addPlayerGenerator = ChunkPropertyUtils.addPlayerGenerator
|
||||||
local findNearbyBase = ChunkPropertyUtils.findNearbyBase
|
local findNearbyBase = ChunkPropertyUtils.findNearbyBase
|
||||||
|
|
||||||
local removeChunkToNest = MapUtils.removeChunkToNest
|
local removeChunkToNest = MapUtils.removeChunkToNest
|
||||||
|
|
||||||
local processPheromone = PheromoneUtils.processPheromone
|
local processPheromone = MapUtils.processPheromone
|
||||||
|
|
||||||
local getCombinedDeathGeneratorRating = ChunkPropertyUtils.getCombinedDeathGeneratorRating
|
local getCombinedDeathGeneratorRating = ChunkPropertyUtils.getCombinedDeathGeneratorRating
|
||||||
local processBaseMutation = BaseUtils.processBaseMutation
|
local processBaseMutation = BaseUtils.processBaseMutation
|
||||||
|
|
||||||
local processNestActiveness = ChunkPropertyUtils.processNestActiveness
|
local processNestActiveness = ChunkPropertyUtils.processNestActiveness
|
||||||
|
|
||||||
local formSquads = AiAttackWave.formSquads
|
local formSquads = Squad.formSquads
|
||||||
local formVengenceSquad = AiAttackWave.formVengenceSquad
|
local formVengenceSquad = Squad.formVengenceSquad
|
||||||
local formVengenceSettler = AiAttackWave.formVengenceSettler
|
local formVengenceSettler = Squad.formVengenceSettler
|
||||||
local formSettlers = AiAttackWave.formSettlers
|
local formSettlers = Squad.formSettlers
|
||||||
|
|
||||||
local getChunkByPosition = MapUtils.getChunkByPosition
|
local getChunkByPosition = MapUtils.getChunkByPosition
|
||||||
local getChunkByXY = MapUtils.getChunkByXY
|
local getChunkByXY = MapUtils.getChunkByXY
|
||||||
local getChunkById = MapUtils.getChunkById
|
local getChunkById = MapUtils.getChunkById
|
||||||
|
|
||||||
local validPlayer = PlayerUtils.validPlayer
|
local validPlayer = Utils.validPlayer
|
||||||
|
|
||||||
local mapScanEnemyChunk = ChunkUtils.mapScanEnemyChunk
|
local mapScanEnemyChunk = ChunkUtils.mapScanEnemyChunk
|
||||||
local mapScanPlayerChunk = ChunkUtils.mapScanPlayerChunk
|
local mapScanPlayerChunk = ChunkUtils.mapScanPlayerChunk
|
||||||
@@ -94,13 +107,14 @@ local mapScanResourceChunk = ChunkUtils.mapScanResourceChunk
|
|||||||
|
|
||||||
local getEnemyStructureCount = ChunkPropertyUtils.getEnemyStructureCount
|
local getEnemyStructureCount = ChunkPropertyUtils.getEnemyStructureCount
|
||||||
|
|
||||||
local canAttack = AiPredicates.canAttack
|
local canAttack = BaseUtils.canAttack
|
||||||
local canMigrate = AiPredicates.canMigrate
|
local canMigrate = BaseUtils.canMigrate
|
||||||
|
|
||||||
local tableSize = table_size
|
local tableSize = table_size
|
||||||
|
|
||||||
local mMin = math.min
|
local mMin = math.min
|
||||||
local mMax = math.max
|
local mMax = math.max
|
||||||
|
local tableInsert = table.insert
|
||||||
|
|
||||||
local next = next
|
local next = next
|
||||||
|
|
||||||
@@ -113,7 +127,7 @@ local next = next
|
|||||||
In theory, this might be fine as smaller bases have less surface to attack and need to have
|
In theory, this might be fine as smaller bases have less surface to attack and need to have
|
||||||
pheromone dissipate at a faster rate.
|
pheromone dissipate at a faster rate.
|
||||||
--]]
|
--]]
|
||||||
function MapProcessor.processMap(map, tick)
|
function Processor.processMap(map, tick)
|
||||||
local processQueue = map.processQueue
|
local processQueue = map.processQueue
|
||||||
local processQueueLength = #processQueue
|
local processQueueLength = #processQueue
|
||||||
|
|
||||||
@@ -156,7 +170,7 @@ end
|
|||||||
vs
|
vs
|
||||||
the slower passive version processing the entire map in multiple passes.
|
the slower passive version processing the entire map in multiple passes.
|
||||||
--]]
|
--]]
|
||||||
function MapProcessor.processPlayers(players, tick)
|
function Processor.processPlayers(players, tick)
|
||||||
-- put down player pheromone for player hunters
|
-- put down player pheromone for player hunters
|
||||||
-- randomize player order to ensure a single player isn't singled out
|
-- randomize player order to ensure a single player isn't singled out
|
||||||
-- not looping everyone because the cost is high enough already in multiplayer
|
-- not looping everyone because the cost is high enough already in multiplayer
|
||||||
@@ -229,7 +243,7 @@ local function processCleanUp(chunks, tick, duration)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function MapProcessor.cleanUpMapTables(tick)
|
function Processor.cleanUpMapTables(tick)
|
||||||
local retreats = Universe.chunkToRetreats
|
local retreats = Universe.chunkToRetreats
|
||||||
local rallys = Universe.chunkToRallys
|
local rallys = Universe.chunkToRallys
|
||||||
local drained = Universe.chunkToDrained
|
local drained = Universe.chunkToDrained
|
||||||
@@ -246,7 +260,7 @@ end
|
|||||||
--[[
|
--[[
|
||||||
Passive scan to find entities that have been generated outside the factorio event system
|
Passive scan to find entities that have been generated outside the factorio event system
|
||||||
--]]
|
--]]
|
||||||
function MapProcessor.scanPlayerMap(map, tick)
|
function Processor.scanPlayerMap(map, tick)
|
||||||
if (map.nextProcessMap == tick) or (map.nextPlayerScan == tick) or
|
if (map.nextProcessMap == tick) or (map.nextPlayerScan == tick) or
|
||||||
(map.nextEnemyScan == tick) or (map.nextChunkProcess == tick)
|
(map.nextEnemyScan == tick) or (map.nextChunkProcess == tick)
|
||||||
then
|
then
|
||||||
@@ -274,7 +288,7 @@ function MapProcessor.scanPlayerMap(map, tick)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function MapProcessor.scanEnemyMap(map, tick)
|
function Processor.scanEnemyMap(map, tick)
|
||||||
local index = map.scanEnemyIndex
|
local index = map.scanEnemyIndex
|
||||||
|
|
||||||
local processQueue = map.processQueue
|
local processQueue = map.processQueue
|
||||||
@@ -297,7 +311,7 @@ function MapProcessor.scanEnemyMap(map, tick)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function MapProcessor.scanResourceMap(map, tick)
|
function Processor.scanResourceMap(map, tick)
|
||||||
local index = map.scanResourceIndex
|
local index = map.scanResourceIndex
|
||||||
|
|
||||||
local processQueue = map.processQueue
|
local processQueue = map.processQueue
|
||||||
@@ -320,7 +334,7 @@ function MapProcessor.scanResourceMap(map, tick)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function MapProcessor.processVengence()
|
function Processor.processVengence()
|
||||||
local vengenceQueue = Universe.vengenceQueue
|
local vengenceQueue = Universe.vengenceQueue
|
||||||
local chunkId, chunk = next(vengenceQueue, nil)
|
local chunkId, chunk = next(vengenceQueue, nil)
|
||||||
if not chunkId then
|
if not chunkId then
|
||||||
@@ -343,7 +357,7 @@ function MapProcessor.processVengence()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function MapProcessor.processNests(tick)
|
function Processor.processNests(tick)
|
||||||
local chunkId = Universe.processNestIterator
|
local chunkId = Universe.processNestIterator
|
||||||
local chunkPack
|
local chunkPack
|
||||||
if not chunkId then
|
if not chunkId then
|
||||||
@@ -383,7 +397,7 @@ local function processSpawnersBody(iterator, chunks)
|
|||||||
Universe[iterator] = nil
|
Universe[iterator] = nil
|
||||||
else
|
else
|
||||||
Universe[iterator] = next(chunks, chunkId)
|
Universe[iterator] = next(chunks, chunkId)
|
||||||
local map = chunkPack.map
|
local map = chunkPack.map -- error
|
||||||
if not map.surface.valid then
|
if not map.surface.valid then
|
||||||
if (iterator == "processMigrationIterator") then
|
if (iterator == "processMigrationIterator") then
|
||||||
removeChunkToNest(chunkId)
|
removeChunkToNest(chunkId)
|
||||||
@@ -411,8 +425,8 @@ local function processSpawnersBody(iterator, chunks)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local migrate = canMigrate(map, base)
|
local migrate = canMigrate(base)
|
||||||
local attack = canAttack(map, base)
|
local attack = canAttack(base)
|
||||||
if migrate then
|
if migrate then
|
||||||
formSettlers(chunk, base)
|
formSettlers(chunk, base)
|
||||||
end
|
end
|
||||||
@@ -422,7 +436,7 @@ local function processSpawnersBody(iterator, chunks)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function MapProcessor.processAttackWaves()
|
function Processor.processAttackWaves()
|
||||||
processSpawnersBody("processActiveSpawnerIterator",
|
processSpawnersBody("processActiveSpawnerIterator",
|
||||||
Universe.chunkToActiveNest)
|
Universe.chunkToActiveNest)
|
||||||
processSpawnersBody("processActiveRaidSpawnerIterator",
|
processSpawnersBody("processActiveRaidSpawnerIterator",
|
||||||
@@ -431,7 +445,7 @@ function MapProcessor.processAttackWaves()
|
|||||||
Universe.chunkToNests)
|
Universe.chunkToNests)
|
||||||
end
|
end
|
||||||
|
|
||||||
function MapProcessor.processClouds(tick)
|
function Processor.processClouds(tick)
|
||||||
local eventId, builderPack = next(Universe.settlePurpleCloud, nil)
|
local eventId, builderPack = next(Universe.settlePurpleCloud, nil)
|
||||||
if builderPack and (builderPack.tick <= tick) then
|
if builderPack and (builderPack.tick <= tick) then
|
||||||
Universe.settlePurpleCloud[eventId] = nil
|
Universe.settlePurpleCloud[eventId] = nil
|
||||||
@@ -446,9 +460,180 @@ function MapProcessor.processClouds(tick)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function MapProcessor.init(universe)
|
|
||||||
|
function Processor.processPendingChunks(tick, flush)
|
||||||
|
local pendingChunks = Universe.pendingChunks
|
||||||
|
local eventId, event = next(pendingChunks, nil)
|
||||||
|
|
||||||
|
if not eventId then
|
||||||
|
if (tableSize(pendingChunks) == 0) then
|
||||||
|
-- this is needed as the next command remembers the max length a table has been
|
||||||
|
Universe.pendingChunks = {}
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local endCount = 1
|
||||||
|
if flush then
|
||||||
|
endCount = tableSize(pendingChunks)
|
||||||
|
end
|
||||||
|
for _=1,endCount do
|
||||||
|
if not flush and (event.tick > tick) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local newEventId, newEvent = next(pendingChunks, eventId)
|
||||||
|
pendingChunks[eventId] = nil
|
||||||
|
local map = event.map
|
||||||
|
if not map.surface.valid then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local topLeft = event.area.left_top
|
||||||
|
local x = topLeft.x
|
||||||
|
local y = topLeft.y
|
||||||
|
|
||||||
|
if not map[x] then
|
||||||
|
map[x] = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
if map[x][y] then
|
||||||
|
local oldChunk = map[x][y]
|
||||||
|
local chunk = initialScan(oldChunk, map, tick)
|
||||||
|
if (chunk == -1) then
|
||||||
|
removeChunkFromMap(map, oldChunk)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local initialChunk = createChunk(map, x, y)
|
||||||
|
map[x][y] = initialChunk
|
||||||
|
Universe.chunkIdToChunk[initialChunk.id] = initialChunk
|
||||||
|
local chunk = initialScan(initialChunk, map, tick)
|
||||||
|
if (chunk ~= -1) then
|
||||||
|
tableInsert(
|
||||||
|
map.processQueue,
|
||||||
|
findInsertionPoint(map.processQueue, chunk),
|
||||||
|
chunk
|
||||||
|
)
|
||||||
|
else
|
||||||
|
map[x][y] = nil
|
||||||
|
Universe.chunkIdToChunk[initialChunk.id] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
eventId = newEventId
|
||||||
|
event = newEvent
|
||||||
|
if not eventId then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Processor.processPendingUpgrades(tick)
|
||||||
|
local entityId, entityData = next(Universe.pendingUpgrades, nil)
|
||||||
|
if not entityId then
|
||||||
|
if tableSize(Universe.pendingUpgrades) == 0 then
|
||||||
|
Universe.pendingUpgrades = {}
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local entity = entityData.entity
|
||||||
|
if not entity.valid then
|
||||||
|
Universe.pendingUpgrades[entityId] = nil
|
||||||
|
end
|
||||||
|
if entityData.delayTLL and tick < entityData.delayTLL then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
Universe.pendingUpgrades[entityId] = nil
|
||||||
|
local base = entityData.base
|
||||||
|
local map = base.map
|
||||||
|
local baseAlignment = base.alignment
|
||||||
|
local position = entityData.position or entity.position
|
||||||
|
|
||||||
|
local pickedBaseAlignment
|
||||||
|
if baseAlignment[2] then
|
||||||
|
if Universe.random() < 0.75 then
|
||||||
|
pickedBaseAlignment = baseAlignment[2]
|
||||||
|
else
|
||||||
|
pickedBaseAlignment = baseAlignment[1]
|
||||||
|
end
|
||||||
|
else
|
||||||
|
pickedBaseAlignment = baseAlignment[1]
|
||||||
|
end
|
||||||
|
|
||||||
|
local currentEvo = entity.prototype.build_base_evolution_requirement or 0
|
||||||
|
|
||||||
|
local distance = mMin(1, euclideanDistancePoints(position.x, position.y, 0, 0) * BASE_DISTANCE_TO_EVO_INDEX)
|
||||||
|
local evoIndex = mMax(distance, Universe.evolutionLevel)
|
||||||
|
|
||||||
|
local name = findEntityUpgrade(pickedBaseAlignment,
|
||||||
|
currentEvo,
|
||||||
|
evoIndex,
|
||||||
|
entity,
|
||||||
|
map,
|
||||||
|
entityData.evolve)
|
||||||
|
|
||||||
|
local entityName = entity.name
|
||||||
|
if not name and PROXY_ENTITY_LOOKUP[entityName] then
|
||||||
|
entity.destroy()
|
||||||
|
return
|
||||||
|
elseif (name == entityName) or not name then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local surface = entity.surface
|
||||||
|
local query = Universe.ppuUpgradeEntityQuery
|
||||||
|
query.name = name
|
||||||
|
|
||||||
|
unregisterEnemyBaseStructure(map, entity, nil, true)
|
||||||
|
entity.destroy()
|
||||||
|
local foundPosition = surface.find_non_colliding_position(BUILDING_SPACE_LOOKUP[name],
|
||||||
|
position,
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
true)
|
||||||
|
setPositionInQuery(query, foundPosition or position)
|
||||||
|
|
||||||
|
local createdEntity = surface.create_entity({
|
||||||
|
name = query.name,
|
||||||
|
position = query.position
|
||||||
|
})
|
||||||
|
if createdEntity and createdEntity.valid then
|
||||||
|
if entityData.register then
|
||||||
|
registerEnemyBaseStructure(map, createdEntity, base, tick, true)
|
||||||
|
end
|
||||||
|
if not entityData.evolve and Universe.printBaseUpgrades then
|
||||||
|
surface.print("["..base.id.."]:"..surface.name.." Upgrading ".. entityName .. " to " .. name .. " [gps=".. position.x ..",".. position.y .."]")
|
||||||
|
end
|
||||||
|
if remote.interfaces["kr-creep"] then
|
||||||
|
remote.call("kr-creep", "spawn_creep_at_position", surface, foundPosition or position, false, createdEntity.name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Processor.processScanChunks()
|
||||||
|
local chunkId, chunk = next(Universe.chunkToPassScan, nil)
|
||||||
|
if not chunkId then
|
||||||
|
if (tableSize(Universe.chunkToPassScan) == 0) then
|
||||||
|
-- this is needed as the next command remembers the max length a table has been
|
||||||
|
Universe.chunkToPassScan = {}
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
Universe.chunkToPassScan[chunkId] = nil
|
||||||
|
local map = chunk.map
|
||||||
|
if not map.surface.valid then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if (chunkPassScan(chunk, map) == -1) then
|
||||||
|
removeChunkFromMap(map, chunk)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Processor.init(universe)
|
||||||
Universe = universe
|
Universe = universe
|
||||||
end
|
end
|
||||||
|
|
||||||
MapProcessorG = MapProcessor
|
ProcessorG = Processor
|
||||||
return MapProcessor
|
return Processor
|
@@ -1,81 +0,0 @@
|
|||||||
-- Copyright (C) 2022 veden
|
|
||||||
|
|
||||||
-- This program is free software: you can redistribute it and/or modify
|
|
||||||
-- it under the terms of the GNU General Public License as published by
|
|
||||||
-- the Free Software Foundation, either version 3 of the License, or
|
|
||||||
-- (at your option) any later version.
|
|
||||||
|
|
||||||
-- This program is distributed in the hope that it will be useful,
|
|
||||||
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
-- GNU General Public License for more details.
|
|
||||||
|
|
||||||
-- You should have received a copy of the GNU General Public License
|
|
||||||
-- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
|
|
||||||
if QueryUtilsG then
|
|
||||||
return QueryUtilsG
|
|
||||||
end
|
|
||||||
local QueryUtils = {}
|
|
||||||
|
|
||||||
local Constants = require("Constants")
|
|
||||||
|
|
||||||
local CHUNK_SIZE = Constants.CHUNK_SIZE
|
|
||||||
|
|
||||||
function QueryUtils.setPositionInQuery(query, position)
|
|
||||||
local point = query.position
|
|
||||||
point[1] = position.x
|
|
||||||
point[2] = position.y
|
|
||||||
end
|
|
||||||
|
|
||||||
function QueryUtils.setPositionInCommand(cmd, position)
|
|
||||||
local point = cmd.destination
|
|
||||||
point[1] = position.x
|
|
||||||
point[2] = position.y
|
|
||||||
end
|
|
||||||
|
|
||||||
function QueryUtils.setPositionXYInQuery(query, x, y)
|
|
||||||
local point = query.position
|
|
||||||
point[1] = x
|
|
||||||
point[2] = y
|
|
||||||
end
|
|
||||||
|
|
||||||
function QueryUtils.setAreaInQuery(query, topLeftPosition, size)
|
|
||||||
local area = query.area
|
|
||||||
area[1][1] = topLeftPosition.x
|
|
||||||
area[1][2] = topLeftPosition.y
|
|
||||||
area[2][1] = topLeftPosition.x + size
|
|
||||||
area[2][2] = topLeftPosition.y + size
|
|
||||||
end
|
|
||||||
|
|
||||||
function QueryUtils.setAreaInQueryChunkSize(query, topLeftPosition)
|
|
||||||
local area = query.area
|
|
||||||
area[1][1] = topLeftPosition.x
|
|
||||||
area[1][2] = topLeftPosition.y
|
|
||||||
area[2][1] = topLeftPosition.x + CHUNK_SIZE
|
|
||||||
area[2][2] = topLeftPosition.y + CHUNK_SIZE
|
|
||||||
end
|
|
||||||
|
|
||||||
function QueryUtils.setPointAreaInQuery(query, position, size)
|
|
||||||
local area = query.area
|
|
||||||
area[1][1] = position.x - size
|
|
||||||
area[1][2] = position.y - size
|
|
||||||
area[2][1] = position.x + size
|
|
||||||
area[2][2] = position.y + size
|
|
||||||
end
|
|
||||||
|
|
||||||
function QueryUtils.setAreaYInQuery(query, y1, y2)
|
|
||||||
local area = query.area
|
|
||||||
area[1][2] = y1
|
|
||||||
area[2][2] = y2
|
|
||||||
end
|
|
||||||
|
|
||||||
function QueryUtils.setAreaXInQuery(query, x1, x2)
|
|
||||||
local area = query.area
|
|
||||||
area[1][1] = x1
|
|
||||||
area[2][1] = x2
|
|
||||||
end
|
|
||||||
|
|
||||||
QueryUtilsG = QueryUtils
|
|
||||||
return QueryUtils
|
|
1270
libs/Squad.lua
Normal file
1270
libs/Squad.lua
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,480 +0,0 @@
|
|||||||
-- Copyright (C) 2022 veden
|
|
||||||
|
|
||||||
-- This program is free software: you can redistribute it and/or modify
|
|
||||||
-- it under the terms of the GNU General Public License as published by
|
|
||||||
-- the Free Software Foundation, either version 3 of the License, or
|
|
||||||
-- (at your option) any later version.
|
|
||||||
|
|
||||||
-- This program is distributed in the hope that it will be useful,
|
|
||||||
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
-- GNU General Public License for more details.
|
|
||||||
|
|
||||||
-- You should have received a copy of the GNU General Public License
|
|
||||||
-- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
|
|
||||||
if SquadAttackG then
|
|
||||||
return SquadAttackG
|
|
||||||
end
|
|
||||||
local SquadAttack = {}
|
|
||||||
|
|
||||||
--
|
|
||||||
|
|
||||||
local Universe
|
|
||||||
|
|
||||||
-- imports
|
|
||||||
|
|
||||||
local Constants = require("Constants")
|
|
||||||
local MapUtils = require("MapUtils")
|
|
||||||
local MovementUtils = require("MovementUtils")
|
|
||||||
local MathUtils = require("MathUtils")
|
|
||||||
local ChunkPropertyUtils = require("ChunkPropertyUtils")
|
|
||||||
local QueryUtils = require("QueryUtils")
|
|
||||||
|
|
||||||
-- Constants
|
|
||||||
|
|
||||||
local PLAYER_PHEROMONE_GENERATOR_THRESHOLD = Constants.PLAYER_PHEROMONE_GENERATOR_THRESHOLD
|
|
||||||
local COMMAND_TIMEOUT = Constants.COMMAND_TIMEOUT
|
|
||||||
local PLAYER_PHEROMONE = Constants.PLAYER_PHEROMONE
|
|
||||||
local BASE_PHEROMONE = Constants.BASE_PHEROMONE
|
|
||||||
local ENEMY_PHEROMONE = Constants.ENEMY_PHEROMONE
|
|
||||||
local RESOURCE_PHEROMONE = Constants.RESOURCE_PHEROMONE
|
|
||||||
|
|
||||||
local FIVE_DEATH_PHEROMONE_GENERATOR_AMOUNT = Constants.FIVE_DEATH_PHEROMONE_GENERATOR_AMOUNT
|
|
||||||
|
|
||||||
local SQUAD_BUILDING = Constants.SQUAD_BUILDING
|
|
||||||
|
|
||||||
local SQUAD_RAIDING = Constants.SQUAD_RAIDING
|
|
||||||
local SQUAD_SETTLING = Constants.SQUAD_SETTLING
|
|
||||||
local SQUAD_GUARDING = Constants.SQUAD_GUARDING
|
|
||||||
local SQUAD_RETREATING = Constants.SQUAD_RETREATING
|
|
||||||
|
|
||||||
local BASE_AI_STATE_SIEGE = Constants.BASE_AI_STATE_SIEGE
|
|
||||||
local BASE_AI_STATE_AGGRESSIVE = Constants.BASE_AI_STATE_AGGRESSIVE
|
|
||||||
|
|
||||||
local PLAYER_PHEROMONE_MULTIPLER = Constants.PLAYER_PHEROMONE_MULTIPLER
|
|
||||||
|
|
||||||
local DEFINES_DISTRACTION_NONE = defines.distraction.none
|
|
||||||
local DEFINES_DISTRACTION_BY_ENEMY = defines.distraction.by_enemy
|
|
||||||
local DEFINES_DISTRACTION_BY_ANYTHING = defines.distraction.by_anything
|
|
||||||
|
|
||||||
-- imported functions
|
|
||||||
|
|
||||||
local tableSize = table_size
|
|
||||||
local setPositionInCommand = QueryUtils.setPositionInCommand
|
|
||||||
|
|
||||||
local euclideanDistancePoints = MathUtils.euclideanDistancePoints
|
|
||||||
|
|
||||||
local findMovementPosition = MovementUtils.findMovementPosition
|
|
||||||
|
|
||||||
local removeSquadFromChunk = ChunkPropertyUtils.removeSquadFromChunk
|
|
||||||
local addDeathGenerator = ChunkPropertyUtils.addDeathGenerator
|
|
||||||
|
|
||||||
local getNeighborChunks = MapUtils.getNeighborChunks
|
|
||||||
local addSquadToChunk = ChunkPropertyUtils.addSquadToChunk
|
|
||||||
local getChunkByXY = MapUtils.getChunkByXY
|
|
||||||
local positionToChunkXY = MapUtils.positionToChunkXY
|
|
||||||
local addMovementPenalty = MovementUtils.addMovementPenalty
|
|
||||||
local positionFromDirectionAndFlat = MapUtils.positionFromDirectionAndFlat
|
|
||||||
|
|
||||||
local euclideanDistanceNamed = MathUtils.euclideanDistanceNamed
|
|
||||||
|
|
||||||
local scoreNeighborsForAttack = MovementUtils.scoreNeighborsForAttack
|
|
||||||
local scoreNeighborsForSettling = MovementUtils.scoreNeighborsForSettling
|
|
||||||
|
|
||||||
-- module code
|
|
||||||
local function scoreResourceLocation(map, neighborChunk)
|
|
||||||
return neighborChunk[RESOURCE_PHEROMONE]
|
|
||||||
- (neighborChunk[PLAYER_PHEROMONE] * PLAYER_PHEROMONE_MULTIPLER)
|
|
||||||
- neighborChunk[ENEMY_PHEROMONE]
|
|
||||||
end
|
|
||||||
|
|
||||||
local function scoreSiegeLocation(map, neighborChunk)
|
|
||||||
local settle = neighborChunk[BASE_PHEROMONE]
|
|
||||||
+ neighborChunk[RESOURCE_PHEROMONE] * 0.5
|
|
||||||
+ (neighborChunk[PLAYER_PHEROMONE] * PLAYER_PHEROMONE_MULTIPLER)
|
|
||||||
|
|
||||||
return settle - neighborChunk[ENEMY_PHEROMONE]
|
|
||||||
end
|
|
||||||
|
|
||||||
local function scoreAttackLocation(map, neighborChunk)
|
|
||||||
local damage = neighborChunk[BASE_PHEROMONE] +
|
|
||||||
(neighborChunk[PLAYER_PHEROMONE] * PLAYER_PHEROMONE_MULTIPLER)
|
|
||||||
return damage
|
|
||||||
end
|
|
||||||
|
|
||||||
local function settleMove(map, squad)
|
|
||||||
local group = squad.group
|
|
||||||
local targetPosition = {x=0,y=0}
|
|
||||||
|
|
||||||
local groupPosition = group.position
|
|
||||||
local x, y = positionToChunkXY(groupPosition)
|
|
||||||
local chunk = getChunkByXY(map, x, y)
|
|
||||||
local scoreFunction = scoreResourceLocation
|
|
||||||
if (squad.type == BASE_AI_STATE_SIEGE) then
|
|
||||||
scoreFunction = scoreSiegeLocation
|
|
||||||
end
|
|
||||||
local squadChunk = squad.chunk
|
|
||||||
if squadChunk ~= -1 then
|
|
||||||
addDeathGenerator(squadChunk, -FIVE_DEATH_PHEROMONE_GENERATOR_AMOUNT)
|
|
||||||
end
|
|
||||||
if chunk ~= -1 then
|
|
||||||
addSquadToChunk(chunk, squad)
|
|
||||||
addMovementPenalty(squad, chunk)
|
|
||||||
if not squad.group.valid then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local distance = euclideanDistancePoints(groupPosition.x,
|
|
||||||
groupPosition.y,
|
|
||||||
squad.originPosition.x,
|
|
||||||
squad.originPosition.y)
|
|
||||||
local cmd
|
|
||||||
local position
|
|
||||||
local surface = map.surface
|
|
||||||
|
|
||||||
if (chunk ~= -1) and
|
|
||||||
(
|
|
||||||
(distance >= squad.maxDistance) or
|
|
||||||
(
|
|
||||||
chunk.resourceGenerator and (not chunk.nestCount) and (not chunk.hiveCount)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
then
|
|
||||||
position = findMovementPosition(surface, groupPosition)
|
|
||||||
|
|
||||||
if not position then
|
|
||||||
position = groupPosition
|
|
||||||
end
|
|
||||||
|
|
||||||
cmd = Universe.settleCommand
|
|
||||||
if squad.kamikaze then
|
|
||||||
cmd.distraction = DEFINES_DISTRACTION_NONE
|
|
||||||
else
|
|
||||||
cmd.distraction = DEFINES_DISTRACTION_BY_ENEMY
|
|
||||||
end
|
|
||||||
|
|
||||||
setPositionInCommand(cmd, position)
|
|
||||||
|
|
||||||
squad.status = SQUAD_BUILDING
|
|
||||||
|
|
||||||
group.set_command(cmd)
|
|
||||||
else
|
|
||||||
local attackChunk,
|
|
||||||
attackDirection,
|
|
||||||
nextAttackChunk,
|
|
||||||
nextAttackDirection = scoreNeighborsForSettling(map,
|
|
||||||
chunk,
|
|
||||||
getNeighborChunks(map, x, y),
|
|
||||||
scoreFunction)
|
|
||||||
|
|
||||||
if (attackChunk == -1) then
|
|
||||||
cmd = Universe.wanderCommand
|
|
||||||
group.set_command(cmd)
|
|
||||||
return
|
|
||||||
elseif (attackDirection ~= 0) then
|
|
||||||
local attackPlayerThreshold = Universe.attackPlayerThreshold
|
|
||||||
|
|
||||||
if (nextAttackChunk ~= -1) then
|
|
||||||
if (not nextAttackChunk.playerBaseGenerator)
|
|
||||||
and ((nextAttackChunk.playerGenerator or 0) < PLAYER_PHEROMONE_GENERATOR_THRESHOLD)
|
|
||||||
then
|
|
||||||
attackChunk = nextAttackChunk
|
|
||||||
position = findMovementPosition(
|
|
||||||
surface,
|
|
||||||
positionFromDirectionAndFlat(
|
|
||||||
nextAttackDirection,
|
|
||||||
positionFromDirectionAndFlat(
|
|
||||||
attackDirection,
|
|
||||||
groupPosition
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else
|
|
||||||
position = groupPosition
|
|
||||||
end
|
|
||||||
else
|
|
||||||
position = findMovementPosition(
|
|
||||||
surface,
|
|
||||||
positionFromDirectionAndFlat(
|
|
||||||
attackDirection,
|
|
||||||
groupPosition
|
|
||||||
)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
if position then
|
|
||||||
targetPosition.x = position.x
|
|
||||||
targetPosition.y = position.y
|
|
||||||
if nextAttackChunk ~= -1 then
|
|
||||||
addDeathGenerator(nextAttackChunk, -FIVE_DEATH_PHEROMONE_GENERATOR_AMOUNT)
|
|
||||||
else
|
|
||||||
addDeathGenerator(attackChunk, -FIVE_DEATH_PHEROMONE_GENERATOR_AMOUNT)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
cmd = Universe.wanderCommand
|
|
||||||
group.set_command(cmd)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if (nextAttackChunk ~= -1)
|
|
||||||
and (
|
|
||||||
nextAttackChunk.playerBaseGenerator
|
|
||||||
or ((nextAttackChunk.playerBaseGenerator or 0) >= PLAYER_PHEROMONE_GENERATOR_THRESHOLD)
|
|
||||||
)
|
|
||||||
then
|
|
||||||
cmd = Universe.settleCommand
|
|
||||||
squad.status = SQUAD_BUILDING
|
|
||||||
if squad.kamikaze then
|
|
||||||
cmd.distraction = DEFINES_DISTRACTION_NONE
|
|
||||||
else
|
|
||||||
cmd.distraction = DEFINES_DISTRACTION_BY_ENEMY
|
|
||||||
end
|
|
||||||
elseif attackChunk.playerBaseGenerator
|
|
||||||
or (attackChunk[PLAYER_PHEROMONE] >= attackPlayerThreshold)
|
|
||||||
then
|
|
||||||
cmd = Universe.attackCommand
|
|
||||||
|
|
||||||
if not squad.rabid then
|
|
||||||
squad.frenzy = true
|
|
||||||
squad.frenzyPosition.x = groupPosition.x
|
|
||||||
squad.frenzyPosition.y = groupPosition.y
|
|
||||||
end
|
|
||||||
else
|
|
||||||
cmd = Universe.moveCommand
|
|
||||||
if squad.rabid or squad.kamikaze then
|
|
||||||
cmd.distraction = DEFINES_DISTRACTION_NONE
|
|
||||||
else
|
|
||||||
cmd.distraction = DEFINES_DISTRACTION_BY_ENEMY
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
cmd = Universe.settleCommand
|
|
||||||
targetPosition.x = groupPosition.x
|
|
||||||
targetPosition.y = groupPosition.y
|
|
||||||
|
|
||||||
if squad.kamikaze then
|
|
||||||
cmd.distraction = DEFINES_DISTRACTION_NONE
|
|
||||||
else
|
|
||||||
cmd.distraction = DEFINES_DISTRACTION_BY_ENEMY
|
|
||||||
end
|
|
||||||
|
|
||||||
squad.status = SQUAD_BUILDING
|
|
||||||
end
|
|
||||||
|
|
||||||
setPositionInCommand(cmd, targetPosition)
|
|
||||||
|
|
||||||
group.set_command(cmd)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function attackMove(map, squad)
|
|
||||||
local targetPosition = {0,0}
|
|
||||||
|
|
||||||
local group = squad.group
|
|
||||||
|
|
||||||
local surface = map.surface
|
|
||||||
local position
|
|
||||||
local groupPosition = group.position
|
|
||||||
local x, y = positionToChunkXY(groupPosition)
|
|
||||||
local chunk = getChunkByXY(map, x, y)
|
|
||||||
local attackScorer = scoreAttackLocation
|
|
||||||
local squadChunk = squad.chunk
|
|
||||||
if squadChunk ~= -1 then
|
|
||||||
addDeathGenerator(squadChunk, -FIVE_DEATH_PHEROMONE_GENERATOR_AMOUNT)
|
|
||||||
end
|
|
||||||
if chunk ~= -1 then
|
|
||||||
addSquadToChunk(chunk, squad)
|
|
||||||
addMovementPenalty(squad, chunk)
|
|
||||||
if not squad.group.valid then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
squad.frenzy = (squad.frenzy and (euclideanDistanceNamed(groupPosition, squad.frenzyPosition) < 100))
|
|
||||||
local attackChunk, attackDirection,
|
|
||||||
nextAttackChunk, nextAttackDirection = scoreNeighborsForAttack(map,
|
|
||||||
chunk,
|
|
||||||
getNeighborChunks(map, x, y),
|
|
||||||
attackScorer)
|
|
||||||
local cmd
|
|
||||||
if (attackChunk == -1) then
|
|
||||||
cmd = Universe.wanderCommand
|
|
||||||
group.set_command(cmd)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if (nextAttackChunk ~= -1) then
|
|
||||||
attackChunk = nextAttackChunk
|
|
||||||
position = findMovementPosition(
|
|
||||||
surface,
|
|
||||||
positionFromDirectionAndFlat(
|
|
||||||
nextAttackDirection,
|
|
||||||
positionFromDirectionAndFlat(
|
|
||||||
attackDirection,
|
|
||||||
groupPosition
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else
|
|
||||||
position = findMovementPosition(
|
|
||||||
surface,
|
|
||||||
positionFromDirectionAndFlat(
|
|
||||||
attackDirection,
|
|
||||||
groupPosition
|
|
||||||
)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
if not position then
|
|
||||||
cmd = Universe.wanderCommand
|
|
||||||
group.set_command(cmd)
|
|
||||||
return
|
|
||||||
else
|
|
||||||
targetPosition.x = position.x
|
|
||||||
targetPosition.y = position.y
|
|
||||||
if (nextAttackChunk ~= -1) then
|
|
||||||
addDeathGenerator(nextAttackChunk, -FIVE_DEATH_PHEROMONE_GENERATOR_AMOUNT)
|
|
||||||
else
|
|
||||||
addDeathGenerator(attackChunk, -FIVE_DEATH_PHEROMONE_GENERATOR_AMOUNT)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if attackChunk.playerBaseGenerator and
|
|
||||||
(attackChunk[PLAYER_PHEROMONE] >= Universe.attackPlayerThreshold)
|
|
||||||
then
|
|
||||||
cmd = Universe.attackCommand
|
|
||||||
|
|
||||||
if not squad.rabid then
|
|
||||||
squad.frenzy = true
|
|
||||||
squad.frenzyPosition.x = groupPosition.x
|
|
||||||
squad.frenzyPosition.y = groupPosition.y
|
|
||||||
end
|
|
||||||
else
|
|
||||||
cmd = Universe.moveCommand
|
|
||||||
if squad.rabid or squad.frenzy then
|
|
||||||
cmd.distraction = DEFINES_DISTRACTION_BY_ANYTHING
|
|
||||||
else
|
|
||||||
cmd.distraction = DEFINES_DISTRACTION_BY_ENEMY
|
|
||||||
end
|
|
||||||
end
|
|
||||||
setPositionInCommand(cmd, targetPosition)
|
|
||||||
|
|
||||||
group.set_command(cmd)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function buildMove(map, squad)
|
|
||||||
local group = squad.group
|
|
||||||
local groupPosition = group.position
|
|
||||||
local newGroupPosition = findMovementPosition(map.surface, groupPosition)
|
|
||||||
|
|
||||||
if not newGroupPosition then
|
|
||||||
setPositionInCommand(Universe.settleCommand, groupPosition)
|
|
||||||
else
|
|
||||||
setPositionInCommand(Universe.settleCommand, newGroupPosition)
|
|
||||||
end
|
|
||||||
|
|
||||||
group.set_command(Universe.compoundSettleCommand)
|
|
||||||
end
|
|
||||||
|
|
||||||
function SquadAttack.cleanSquads(tick)
|
|
||||||
local squads = Universe.groupNumberToSquad
|
|
||||||
local groupId = Universe.squadIterator
|
|
||||||
local squad
|
|
||||||
if not groupId then
|
|
||||||
groupId, squad = next(squads, groupId)
|
|
||||||
else
|
|
||||||
squad = squads[groupId]
|
|
||||||
end
|
|
||||||
if not groupId then
|
|
||||||
Universe.squadIterator = nil
|
|
||||||
if (tableSize(squads) == 0) then
|
|
||||||
-- this is needed as the next command remembers the max length a table has been
|
|
||||||
Universe.groupNumberToSquad = {}
|
|
||||||
end
|
|
||||||
else
|
|
||||||
Universe.squadIterator = next(squads, groupId)
|
|
||||||
local group = squad.group
|
|
||||||
if not group.valid then
|
|
||||||
if squad.chunk ~= -1 then
|
|
||||||
addDeathGenerator(squad.chunk, -FIVE_DEATH_PHEROMONE_GENERATOR_AMOUNT)
|
|
||||||
end
|
|
||||||
removeSquadFromChunk(squad)
|
|
||||||
if squad.settlers then
|
|
||||||
Universe.builderCount = Universe.builderCount - 1
|
|
||||||
if Universe.builderCount < 0 then
|
|
||||||
Universe.builderCount = 0
|
|
||||||
end
|
|
||||||
else
|
|
||||||
Universe.squadCount = Universe.squadCount - 1
|
|
||||||
if Universe.squadCount < 0 then
|
|
||||||
Universe.squadCount = 0
|
|
||||||
end
|
|
||||||
if squad.type == BASE_AI_STATE_AGGRESSIVE then
|
|
||||||
local base = squad.base
|
|
||||||
base.sentAggressiveGroups = base.sentAggressiveGroups - 1
|
|
||||||
if base.sentAggressiveGroups < 0 then
|
|
||||||
base.sentAggressiveGroups = 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
squads[groupId] = nil
|
|
||||||
elseif (group.state == 4) then
|
|
||||||
squad.wanders = 0
|
|
||||||
SquadAttack.squadDispatch(squad.map, squad, tick)
|
|
||||||
elseif (squad.commandTick and (squad.commandTick < tick)) then
|
|
||||||
if squad.wanders > 5 then
|
|
||||||
squad.group.destroy()
|
|
||||||
else
|
|
||||||
squad.wanders = squad.wanders + 1
|
|
||||||
local cmd = Universe.wander2Command
|
|
||||||
squad.commandTick = tick + COMMAND_TIMEOUT
|
|
||||||
group.set_command(cmd)
|
|
||||||
group.start_moving()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function SquadAttack.squadDispatch(map, squad, tick)
|
|
||||||
local group = squad.group
|
|
||||||
if group and group.valid then
|
|
||||||
local status = squad.status
|
|
||||||
if (status == SQUAD_RAIDING) then
|
|
||||||
squad.commandTick = tick + COMMAND_TIMEOUT
|
|
||||||
attackMove(map, squad)
|
|
||||||
elseif (status == SQUAD_SETTLING) then
|
|
||||||
squad.commandTick = tick + COMMAND_TIMEOUT
|
|
||||||
settleMove(map, squad)
|
|
||||||
elseif (status == SQUAD_RETREATING) then
|
|
||||||
squad.commandTick = tick + COMMAND_TIMEOUT
|
|
||||||
if squad.settlers then
|
|
||||||
squad.status = SQUAD_SETTLING
|
|
||||||
settleMove(map, squad)
|
|
||||||
else
|
|
||||||
squad.status = SQUAD_RAIDING
|
|
||||||
attackMove(map, squad)
|
|
||||||
end
|
|
||||||
elseif (status == SQUAD_BUILDING) then
|
|
||||||
squad.commandTick = tick + COMMAND_TIMEOUT
|
|
||||||
removeSquadFromChunk(squad)
|
|
||||||
buildMove(map, squad)
|
|
||||||
elseif (status == SQUAD_GUARDING) then
|
|
||||||
squad.commandTick = tick + COMMAND_TIMEOUT
|
|
||||||
if squad.settlers then
|
|
||||||
squad.status = SQUAD_SETTLING
|
|
||||||
settleMove(map, squad)
|
|
||||||
else
|
|
||||||
squad.status = SQUAD_RAIDING
|
|
||||||
attackMove(map, squad)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function SquadAttack.init(universe)
|
|
||||||
Universe = universe
|
|
||||||
end
|
|
||||||
|
|
||||||
SquadAttackG = SquadAttack
|
|
||||||
return SquadAttack
|
|
@@ -1,169 +0,0 @@
|
|||||||
-- Copyright (C) 2022 veden
|
|
||||||
|
|
||||||
-- This program is free software: you can redistribute it and/or modify
|
|
||||||
-- it under the terms of the GNU General Public License as published by
|
|
||||||
-- the Free Software Foundation, either version 3 of the License, or
|
|
||||||
-- (at your option) any later version.
|
|
||||||
|
|
||||||
-- This program is distributed in the hope that it will be useful,
|
|
||||||
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
-- GNU General Public License for more details.
|
|
||||||
|
|
||||||
-- You should have received a copy of the GNU General Public License
|
|
||||||
-- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
|
|
||||||
if AiDefenseG then
|
|
||||||
return AiDefenseG
|
|
||||||
end
|
|
||||||
local AiDefense = {}
|
|
||||||
|
|
||||||
--
|
|
||||||
|
|
||||||
local Universe
|
|
||||||
|
|
||||||
-- imports
|
|
||||||
|
|
||||||
local Constants = require("Constants")
|
|
||||||
local MapUtils = require("MapUtils")
|
|
||||||
local UnitGroupUtils = require("UnitGroupUtils")
|
|
||||||
local MovementUtils = require("MovementUtils")
|
|
||||||
local ChunkPropertyUtils = require("ChunkPropertyUtils")
|
|
||||||
|
|
||||||
-- Constants
|
|
||||||
|
|
||||||
local PLAYER_PHEROMONE = Constants.PLAYER_PHEROMONE
|
|
||||||
local BASE_PHEROMONE = Constants.BASE_PHEROMONE
|
|
||||||
|
|
||||||
local PLAYER_PHEROMONE_MULTIPLER = Constants.PLAYER_PHEROMONE_MULTIPLER
|
|
||||||
|
|
||||||
local SQUAD_RETREATING = Constants.SQUAD_RETREATING
|
|
||||||
|
|
||||||
local COOLDOWN_RETREAT = Constants.COOLDOWN_RETREAT
|
|
||||||
|
|
||||||
-- imported functions
|
|
||||||
|
|
||||||
local findNearbyBase = ChunkPropertyUtils.findNearbyBase
|
|
||||||
|
|
||||||
local addSquadToChunk = ChunkPropertyUtils.addSquadToChunk
|
|
||||||
|
|
||||||
local positionFromDirectionAndFlat = MapUtils.positionFromDirectionAndFlat
|
|
||||||
local getNeighborChunks = MapUtils.getNeighborChunks
|
|
||||||
local findNearbyRetreatingSquad = UnitGroupUtils.findNearbyRetreatingSquad
|
|
||||||
local createSquad = UnitGroupUtils.createSquad
|
|
||||||
local scoreNeighborsForRetreat = MovementUtils.scoreNeighborsForRetreat
|
|
||||||
local findMovementPosition = MovementUtils.findMovementPosition
|
|
||||||
|
|
||||||
local getRetreatTick = ChunkPropertyUtils.getRetreatTick
|
|
||||||
local setRetreatTick = ChunkPropertyUtils.setRetreatTick
|
|
||||||
local getEnemyStructureCount = ChunkPropertyUtils.getEnemyStructureCount
|
|
||||||
|
|
||||||
-- module code
|
|
||||||
|
|
||||||
local function scoreRetreatLocation(map, neighborChunk)
|
|
||||||
return (-neighborChunk[BASE_PHEROMONE] +
|
|
||||||
-(neighborChunk[PLAYER_PHEROMONE] * PLAYER_PHEROMONE_MULTIPLER) +
|
|
||||||
-((neighborChunk.playerBaseGenerator or 0) * 1000))
|
|
||||||
end
|
|
||||||
|
|
||||||
function AiDefense.retreatUnits(chunk, cause, map, tick, radius)
|
|
||||||
if (tick - getRetreatTick(chunk) > COOLDOWN_RETREAT) and (getEnemyStructureCount(chunk) == 0) then
|
|
||||||
|
|
||||||
setRetreatTick(chunk, tick)
|
|
||||||
local exitPath,exitDirection,
|
|
||||||
nextExitPath,nextExitDirection = scoreNeighborsForRetreat(chunk,
|
|
||||||
getNeighborChunks(map,
|
|
||||||
chunk.x,
|
|
||||||
chunk.y),
|
|
||||||
scoreRetreatLocation,
|
|
||||||
map)
|
|
||||||
local position = {
|
|
||||||
x = chunk.x + 16,
|
|
||||||
y = chunk.y + 16
|
|
||||||
}
|
|
||||||
local retreatPosition
|
|
||||||
local surface = map.surface
|
|
||||||
if (exitPath == -1) then
|
|
||||||
return
|
|
||||||
elseif (nextExitPath ~= -1) then
|
|
||||||
retreatPosition = findMovementPosition(
|
|
||||||
surface,
|
|
||||||
positionFromDirectionAndFlat(
|
|
||||||
nextExitDirection,
|
|
||||||
positionFromDirectionAndFlat(
|
|
||||||
exitDirection,
|
|
||||||
position
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
exitPath = nextExitPath
|
|
||||||
else
|
|
||||||
retreatPosition = findMovementPosition(
|
|
||||||
surface,
|
|
||||||
positionFromDirectionAndFlat(
|
|
||||||
exitDirection,
|
|
||||||
position
|
|
||||||
)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
if retreatPosition then
|
|
||||||
position.x = retreatPosition.x
|
|
||||||
position.y = retreatPosition.y
|
|
||||||
else
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local newSquad = findNearbyRetreatingSquad(map, exitPath)
|
|
||||||
local created = false
|
|
||||||
|
|
||||||
if not newSquad then
|
|
||||||
if (Universe.squadCount < Universe.AI_MAX_SQUAD_COUNT) then
|
|
||||||
created = true
|
|
||||||
local base = findNearbyBase(chunk)
|
|
||||||
if not base then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
newSquad = createSquad(position, map, nil, false, base)
|
|
||||||
else
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Universe.fleeCommand.from = cause
|
|
||||||
Universe.retreatCommand.group = newSquad.group
|
|
||||||
|
|
||||||
Universe.formRetreatCommand.unit_search_distance = radius
|
|
||||||
|
|
||||||
local foundUnits = surface.set_multi_command(Universe.formRetreatCommand)
|
|
||||||
|
|
||||||
if (foundUnits == 0) then
|
|
||||||
if created then
|
|
||||||
newSquad.group.destroy()
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if created then
|
|
||||||
Universe.groupNumberToSquad[newSquad.groupNumber] = newSquad
|
|
||||||
Universe.squadCount = Universe.squadCount + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
newSquad.status = SQUAD_RETREATING
|
|
||||||
|
|
||||||
addSquadToChunk(chunk, newSquad)
|
|
||||||
|
|
||||||
newSquad.frenzy = true
|
|
||||||
local squadPosition = newSquad.group.position
|
|
||||||
newSquad.frenzyPosition.x = squadPosition.x
|
|
||||||
newSquad.frenzyPosition.y = squadPosition.y
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function AiDefense.init(universe)
|
|
||||||
Universe = universe
|
|
||||||
end
|
|
||||||
|
|
||||||
AiDefenseG = AiDefense
|
|
||||||
return AiDefense
|
|
@@ -1,60 +0,0 @@
|
|||||||
-- Copyright (C) 2022 veden
|
|
||||||
|
|
||||||
-- This program is free software: you can redistribute it and/or modify
|
|
||||||
-- it under the terms of the GNU General Public License as published by
|
|
||||||
-- the Free Software Foundation, either version 3 of the License, or
|
|
||||||
-- (at your option) any later version.
|
|
||||||
|
|
||||||
-- This program is distributed in the hope that it will be useful,
|
|
||||||
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
-- GNU General Public License for more details.
|
|
||||||
|
|
||||||
-- You should have received a copy of the GNU General Public License
|
|
||||||
-- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
|
|
||||||
if stringUtilsG then
|
|
||||||
return stringUtilsG
|
|
||||||
end
|
|
||||||
local stringUtils = {}
|
|
||||||
|
|
||||||
local sSub = string.sub
|
|
||||||
local sGMatch = string.gmatch
|
|
||||||
|
|
||||||
function stringUtils.isRampantSetting(str)
|
|
||||||
return sSub(str, 1, #"rampant--") == "rampant--"
|
|
||||||
end
|
|
||||||
|
|
||||||
function stringUtils.split(str)
|
|
||||||
local result = {}
|
|
||||||
for i in sGMatch(str, "[a-zA-Z-]+") do
|
|
||||||
result[#result+1] = i
|
|
||||||
end
|
|
||||||
return result
|
|
||||||
end
|
|
||||||
|
|
||||||
function stringUtils.isMember(str, set)
|
|
||||||
for _,s in pairs(set) do
|
|
||||||
if str == s then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
function stringUtils.intersection(set1, set2)
|
|
||||||
local result = {}
|
|
||||||
for s1 in pairs(set1) do
|
|
||||||
for s2 in pairs(set2) do
|
|
||||||
if s1 == s2 then
|
|
||||||
result[#result+1] = s1
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return result
|
|
||||||
end
|
|
||||||
|
|
||||||
stringUtilsG = stringUtils
|
|
||||||
return stringUtils
|
|
@@ -1,178 +0,0 @@
|
|||||||
-- Copyright (C) 2022 veden
|
|
||||||
|
|
||||||
-- This program is free software: you can redistribute it and/or modify
|
|
||||||
-- it under the terms of the GNU General Public License as published by
|
|
||||||
-- the Free Software Foundation, either version 3 of the License, or
|
|
||||||
-- (at your option) any later version.
|
|
||||||
|
|
||||||
-- This program is distributed in the hope that it will be useful,
|
|
||||||
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
-- GNU General Public License for more details.
|
|
||||||
|
|
||||||
-- You should have received a copy of the GNU General Public License
|
|
||||||
-- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
|
|
||||||
if UnitGroupUtilsG then
|
|
||||||
return UnitGroupUtilsG
|
|
||||||
end
|
|
||||||
local UnitGroupUtils = {}
|
|
||||||
|
|
||||||
--
|
|
||||||
|
|
||||||
local Universe
|
|
||||||
|
|
||||||
-- imports
|
|
||||||
|
|
||||||
local MapUtils = require("MapUtils")
|
|
||||||
local Constants = require("Constants")
|
|
||||||
local ChunkPropertyUtils = require("ChunkPropertyUtils")
|
|
||||||
local MathUtils = require("MathUtils")
|
|
||||||
|
|
||||||
-- Constants
|
|
||||||
|
|
||||||
local MINIMUM_EXPANSION_DISTANCE = Constants.MINIMUM_EXPANSION_DISTANCE
|
|
||||||
local SQUAD_RETREATING = Constants.SQUAD_RETREATING
|
|
||||||
local SQUAD_GUARDING = Constants.SQUAD_GUARDING
|
|
||||||
|
|
||||||
-- imported functions
|
|
||||||
|
|
||||||
local gaussianRandomRangeRG = MathUtils.gaussianRandomRangeRG
|
|
||||||
|
|
||||||
local getNeighborChunks = MapUtils.getNeighborChunks
|
|
||||||
|
|
||||||
-- module code
|
|
||||||
|
|
||||||
function UnitGroupUtils.findNearbyRetreatingSquad(map, chunk)
|
|
||||||
|
|
||||||
if chunk.squads then
|
|
||||||
for _,squad in pairs(chunk.squads) do
|
|
||||||
local unitGroup = squad.group
|
|
||||||
if (squad.status == SQUAD_RETREATING) and unitGroup and unitGroup.valid then
|
|
||||||
return squad
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local neighbors = getNeighborChunks(map, chunk.x, chunk.y)
|
|
||||||
|
|
||||||
for i=1,#neighbors do
|
|
||||||
local neighbor = neighbors[i]
|
|
||||||
if (neighbor ~= -1) and neighbor.squads then
|
|
||||||
for _,squad in pairs(neighbor.squads) do
|
|
||||||
local unitGroup = squad.group
|
|
||||||
if (squad.status == SQUAD_RETREATING) and unitGroup and unitGroup.valid then
|
|
||||||
return squad
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
function UnitGroupUtils.findNearbySquad(map, chunk)
|
|
||||||
|
|
||||||
if chunk.squads then
|
|
||||||
for _,squad in pairs(chunk.squads) do
|
|
||||||
local unitGroup = squad.group
|
|
||||||
if unitGroup and unitGroup.valid then
|
|
||||||
return squad
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local neighbors = getNeighborChunks(map, chunk.x, chunk.y)
|
|
||||||
|
|
||||||
for i=1,#neighbors do
|
|
||||||
local neighbor = neighbors[i]
|
|
||||||
if (neighbor ~= -1) and neighbor.squads then
|
|
||||||
for _,squad in pairs(neighbor.squads) do
|
|
||||||
local unitGroup = squad.group
|
|
||||||
if unitGroup and unitGroup.valid then
|
|
||||||
return squad
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
function UnitGroupUtils.calculateSettlerMaxDistance()
|
|
||||||
local targetDistance
|
|
||||||
local distanceRoll = Universe.random()
|
|
||||||
if distanceRoll < 0.05 then
|
|
||||||
return 0
|
|
||||||
elseif distanceRoll < 0.30 then
|
|
||||||
targetDistance = Universe.expansionLowTargetDistance
|
|
||||||
elseif distanceRoll < 0.70 then
|
|
||||||
targetDistance = Universe.expansionMediumTargetDistance
|
|
||||||
elseif distanceRoll < 0.95 then
|
|
||||||
targetDistance = Universe.expansionHighTargetDistance
|
|
||||||
else
|
|
||||||
return Universe.expansionMaxDistance
|
|
||||||
end
|
|
||||||
return gaussianRandomRangeRG(targetDistance,
|
|
||||||
Universe.expansionDistanceDeviation,
|
|
||||||
MINIMUM_EXPANSION_DISTANCE,
|
|
||||||
Universe.expansionMaxDistance,
|
|
||||||
Universe.random)
|
|
||||||
end
|
|
||||||
|
|
||||||
function UnitGroupUtils.createSquad(position, map, group, settlers, base)
|
|
||||||
local unitGroup = group or map.surface.create_unit_group({position=position})
|
|
||||||
|
|
||||||
local squad = {
|
|
||||||
group = unitGroup,
|
|
||||||
status = SQUAD_GUARDING,
|
|
||||||
rabid = false,
|
|
||||||
penalties = {},
|
|
||||||
base = base,
|
|
||||||
type = base.stateAI,
|
|
||||||
frenzy = false,
|
|
||||||
map = map,
|
|
||||||
wanders = 0,
|
|
||||||
settlers = settlers or false,
|
|
||||||
kamikaze = false,
|
|
||||||
frenzyPosition = {x = 0,
|
|
||||||
y = 0},
|
|
||||||
maxDistance = 0,
|
|
||||||
groupNumber = unitGroup.group_number,
|
|
||||||
originPosition = {x = 0,
|
|
||||||
y = 0},
|
|
||||||
commandTick = nil,
|
|
||||||
chunk = -1
|
|
||||||
}
|
|
||||||
|
|
||||||
if settlers then
|
|
||||||
squad.maxDistance = UnitGroupUtils.calculateSettlerMaxDistance()
|
|
||||||
end
|
|
||||||
|
|
||||||
if position then
|
|
||||||
squad.originPosition.x = position.x
|
|
||||||
squad.originPosition.y = position.y
|
|
||||||
elseif group then
|
|
||||||
squad.originPosition.x = group.position.x
|
|
||||||
squad.originPosition.y = group.position.y
|
|
||||||
end
|
|
||||||
|
|
||||||
return squad
|
|
||||||
end
|
|
||||||
|
|
||||||
function UnitGroupUtils.calculateKamikazeSquadThreshold(memberCount)
|
|
||||||
local threshold = (memberCount / Universe.attackWaveMaxSize) * 0.2 + (Universe.evolutionLevel * 0.2)
|
|
||||||
return threshold
|
|
||||||
end
|
|
||||||
|
|
||||||
function UnitGroupUtils.calculateKamikazeSettlerThreshold(memberCount)
|
|
||||||
local threshold = (memberCount / Universe.expansionMaxSize) * 0.2 + (Universe.evolutionLevel * 0.2)
|
|
||||||
return threshold
|
|
||||||
end
|
|
||||||
|
|
||||||
function UnitGroupUtils.init(universe)
|
|
||||||
Universe = universe
|
|
||||||
end
|
|
||||||
|
|
||||||
UnitGroupUtilsG = UnitGroupUtils
|
|
||||||
return UnitGroupUtils
|
|
@@ -19,7 +19,7 @@ local Upgrade = {}
|
|||||||
-- imports
|
-- imports
|
||||||
|
|
||||||
local Constants = require("libs/Constants")
|
local Constants = require("libs/Constants")
|
||||||
local ChunkProcessor = require("libs/ChunkProcessor")
|
local Processor = require("libs/Processor")
|
||||||
local ChunkPropertyUtils = require("libs/ChunkPropertyUtils")
|
local ChunkPropertyUtils = require("libs/ChunkPropertyUtils")
|
||||||
local MapUtils = require("libs/MapUtils")
|
local MapUtils = require("libs/MapUtils")
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ local TICKS_A_MINUTE = Constants.TICKS_A_MINUTE
|
|||||||
local addBaseResourceChunk = ChunkPropertyUtils.addBaseResourceChunk
|
local addBaseResourceChunk = ChunkPropertyUtils.addBaseResourceChunk
|
||||||
local sFind = string.find
|
local sFind = string.find
|
||||||
local queueGeneratedChunk = MapUtils.queueGeneratedChunk
|
local queueGeneratedChunk = MapUtils.queueGeneratedChunk
|
||||||
local processPendingChunks = ChunkProcessor.processPendingChunks
|
local processPendingChunks = Processor.processPendingChunks
|
||||||
|
|
||||||
-- module code
|
-- module code
|
||||||
|
|
||||||
|
146
libs/Utils.lua
Normal file
146
libs/Utils.lua
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
-- Copyright (C) 2022 veden
|
||||||
|
|
||||||
|
-- This program is free software: you can redistribute it and/or modify
|
||||||
|
-- it under the terms of the GNU General Public License as published by
|
||||||
|
-- the Free Software Foundation, either version 3 of the License, or
|
||||||
|
-- (at your option) any later version.
|
||||||
|
|
||||||
|
-- This program is distributed in the hope that it will be useful,
|
||||||
|
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
-- GNU General Public License for more details.
|
||||||
|
|
||||||
|
-- You should have received a copy of the GNU General Public License
|
||||||
|
-- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
if UtilsG then
|
||||||
|
return UtilsG
|
||||||
|
end
|
||||||
|
local Utils = {}
|
||||||
|
|
||||||
|
--
|
||||||
|
|
||||||
|
local Constants = require("Constants")
|
||||||
|
|
||||||
|
--
|
||||||
|
|
||||||
|
local CHUNK_SIZE = Constants.CHUNK_SIZE
|
||||||
|
|
||||||
|
--
|
||||||
|
|
||||||
|
local mFloor = math.floor
|
||||||
|
local sSub = string.sub
|
||||||
|
local sGMatch = string.gmatch
|
||||||
|
|
||||||
|
--
|
||||||
|
|
||||||
|
function Utils.isRampantSetting(str)
|
||||||
|
return sSub(str, 1, #"rampant--") == "rampant--"
|
||||||
|
end
|
||||||
|
|
||||||
|
function Utils.split(str)
|
||||||
|
local result = {}
|
||||||
|
for i in sGMatch(str, "[a-zA-Z-]+") do
|
||||||
|
result[#result+1] = i
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
function Utils.isMember(str, set)
|
||||||
|
for _,s in pairs(set) do
|
||||||
|
if str == s then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function Utils.intersection(set1, set2)
|
||||||
|
local result = {}
|
||||||
|
for s1 in pairs(set1) do
|
||||||
|
for s2 in pairs(set2) do
|
||||||
|
if s1 == s2 then
|
||||||
|
result[#result+1] = s1
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
function Utils.setPositionInQuery(query, position)
|
||||||
|
local point = query.position
|
||||||
|
point[1] = position.x
|
||||||
|
point[2] = position.y
|
||||||
|
end
|
||||||
|
|
||||||
|
function Utils.setPositionInCommand(cmd, position)
|
||||||
|
local point = cmd.destination
|
||||||
|
point[1] = position.x
|
||||||
|
point[2] = position.y
|
||||||
|
end
|
||||||
|
|
||||||
|
function Utils.setPositionXYInQuery(query, x, y)
|
||||||
|
local point = query.position
|
||||||
|
point[1] = x
|
||||||
|
point[2] = y
|
||||||
|
end
|
||||||
|
|
||||||
|
function Utils.setAreaInQuery(query, topLeftPosition, size)
|
||||||
|
local area = query.area
|
||||||
|
area[1][1] = topLeftPosition.x
|
||||||
|
area[1][2] = topLeftPosition.y
|
||||||
|
area[2][1] = topLeftPosition.x + size
|
||||||
|
area[2][2] = topLeftPosition.y + size
|
||||||
|
end
|
||||||
|
|
||||||
|
function Utils.setAreaInQueryChunkSize(query, topLeftPosition)
|
||||||
|
local area = query.area
|
||||||
|
area[1][1] = topLeftPosition.x
|
||||||
|
area[1][2] = topLeftPosition.y
|
||||||
|
area[2][1] = topLeftPosition.x + CHUNK_SIZE
|
||||||
|
area[2][2] = topLeftPosition.y + CHUNK_SIZE
|
||||||
|
end
|
||||||
|
|
||||||
|
function Utils.setPointAreaInQuery(query, position, size)
|
||||||
|
local area = query.area
|
||||||
|
area[1][1] = position.x - size
|
||||||
|
area[1][2] = position.y - size
|
||||||
|
area[2][1] = position.x + size
|
||||||
|
area[2][2] = position.y + size
|
||||||
|
end
|
||||||
|
|
||||||
|
function Utils.setAreaYInQuery(query, y1, y2)
|
||||||
|
local area = query.area
|
||||||
|
area[1][2] = y1
|
||||||
|
area[2][2] = y2
|
||||||
|
end
|
||||||
|
|
||||||
|
function Utils.setAreaXInQuery(query, x1, x2)
|
||||||
|
local area = query.area
|
||||||
|
area[1][1] = x1
|
||||||
|
area[2][1] = x2
|
||||||
|
end
|
||||||
|
|
||||||
|
function Utils.validPlayer(player)
|
||||||
|
if player and player.valid then
|
||||||
|
local char = player.character
|
||||||
|
return char and char.valid
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function Utils.getTimeStringFromTick(tick)
|
||||||
|
|
||||||
|
local tickToSeconds = tick / 60
|
||||||
|
|
||||||
|
local days = mFloor(tickToSeconds / 86400)
|
||||||
|
local hours = mFloor((tickToSeconds % 86400) / 3600)
|
||||||
|
local minutes = mFloor((tickToSeconds % 3600) / 60)
|
||||||
|
local seconds = mFloor(tickToSeconds % 60)
|
||||||
|
return days .. "d " .. hours .. "h " .. minutes .. "m " .. seconds .. "s"
|
||||||
|
end
|
||||||
|
|
||||||
|
UtilsG = Utils
|
||||||
|
return Utils
|
@@ -22,7 +22,7 @@ local chunkUtils = require("libs/ChunkUtils")
|
|||||||
local chunkPropertyUtils = require("libs/ChunkPropertyUtils")
|
local chunkPropertyUtils = require("libs/ChunkPropertyUtils")
|
||||||
local mapUtils = require("libs/MapUtils")
|
local mapUtils = require("libs/MapUtils")
|
||||||
local baseUtils = require("libs/BaseUtils")
|
local baseUtils = require("libs/BaseUtils")
|
||||||
local queryUtils = require("libs/QueryUtils")
|
local Utils = require("libs/Utils")
|
||||||
-- local tendrilUtils = require("libs/TendrilUtils")
|
-- local tendrilUtils = require("libs/TendrilUtils")
|
||||||
|
|
||||||
function tests.chunkCount()
|
function tests.chunkCount()
|
||||||
@@ -364,7 +364,7 @@ function tests.scanEnemy()
|
|||||||
local chunk = mapUtils.getChunkByPosition(map, game.player.character.position)
|
local chunk = mapUtils.getChunkByPosition(map, game.player.character.position)
|
||||||
local universe = map.universe
|
local universe = map.universe
|
||||||
local query = universe.filteredEntitiesEnemyStructureQuery
|
local query = universe.filteredEntitiesEnemyStructureQuery
|
||||||
queryUtils.setAreaInQuery(query, chunk, constants.CHUNK_SIZE)
|
Utils.setAreaInQuery(query, chunk, constants.CHUNK_SIZE)
|
||||||
local buildings = map.surface.find_entities_filtered(query)
|
local buildings = map.surface.find_entities_filtered(query)
|
||||||
local counts = map.chunkScanCounts
|
local counts = map.chunkScanCounts
|
||||||
for i=1,#constants.HIVE_BUILDINGS_TYPES do
|
for i=1,#constants.HIVE_BUILDINGS_TYPES do
|
||||||
|
Reference in New Issue
Block a user