-- 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 . local Upgrade = {} -- imports local Constants = require("libs/Constants") local MapUtils = require("libs/MapUtils") -- local Universe -- Constants 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 KAMIKAZE_PHEROMONE = Constants.KAMIKAZE_PHEROMONE local CHUNK_TICK = Constants.CHUNK_TICK local MINIMUM_EXPANSION_DISTANCE = Constants.MINIMUM_EXPANSION_DISTANCE local DEFINES_COMMAND_GROUP = defines.command.group local DEFINES_COMMAND_WANDER = defines.command.wander local DEFINES_COMMAND_BUILD_BASE = defines.command.build_base local DEFINES_COMMAND_ATTACK_AREA = defines.command.attack_area local DEFINES_COMMAND_GO_TO_LOCATION = defines.command.go_to_location local DEFINES_COMMMAD_COMPOUND = defines.command.compound local DEFINES_COMMAND_FLEE = defines.command.flee local DEFINES_COMMAND_STOP = defines.command.stop local DEFINES_COMPOUND_COMMAND_RETURN_LAST = defines.compound_command.return_last 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 local CHUNK_SIZE = Constants.CHUNK_SIZE local TRIPLE_CHUNK_SIZE = Constants.TRIPLE_CHUNK_SIZE local TICKS_A_MINUTE = Constants.TICKS_A_MINUTE -- imported functions -- module code local function addCommandSet() -- preallocating memory to be used in code, making it fast by reducing garbage generated. Universe.playerForces = {} Universe.enemyForces = {} Universe.npcForces = {} Universe.nonPlayerForces = {} Universe.mapUtilsQueries = {} Universe.mapUtilsQueries.neighbors = { -1, -1, -1, -1, -1, -1, -1, -1 } Universe.mapUtilsQueries.position = {0,0} Universe.chunkOverlapArray = { -1, -1, -1, -1 } Universe.chunkPropertyUtilsQueries = {} Universe.chunkPropertyUtilsQueries.position = {0,0} -- pb Universe.pbFilteredEntitiesPointQueryLimited = { position = {0, 0}, radius = 10, limit = 1, force = Universe.enemyForces, type = { "unit-spawner", "turret" } } -- msec Universe.msecFilteredEntitiesEnemyStructureQuery = { area={ {0,0}, {0,0} }, force=Universe.enemyForces, type={ "turret", "unit-spawner" } } -- oba Universe.obaCreateBuildCloudQuery = { name = "build-clear-cloud-rampant", position = {0,0} } -- sp local spbSharedChunkArea = { {0,0}, {0,0} } Universe.spbHasPlayerStructuresQuery = { area=spbSharedChunkArea, force=Universe.nonPlayerForces, invert=true, limit=1 } Universe.spbFilteredEntitiesPlayerQueryLowest = { area=spbSharedChunkArea, force=Universe.playerForces, collision_mask = "player-layer", type={ "wall", "transport-belt" } } Universe.spbFilteredEntitiesPlayerQueryLow = { area=spbSharedChunkArea, force=Universe.playerForces, collision_mask = "player-layer", type={ "splitter", "pump", "offshore-pump", "lamp", "solar-panel", "programmable-speaker", "accumulator", "assembling-machine", "turret", "ammo-turret" } } Universe.spbFilteredEntitiesPlayerQueryHigh = { area=spbSharedChunkArea, force=Universe.playerForces, collision_mask = "player-layer", type={ "furnace", "lab", "roboport", "beacon", "radar", "electric-turret", "boiler", "generator", "fluid-turret", "mining-drill" } } Universe.spbFilteredEntitiesPlayerQueryHighest = { area=spbSharedChunkArea, force=Universe.playerForces, collision_mask = "player-layer", type={ "artillery-turret", "reactor", "rocket-silo" } } -- is local isSharedChunkArea = { {0,0}, {0,0} } Universe.isFilteredTilesQuery = { collision_mask="player-layer", area=isSharedChunkArea } Universe.isFilteredEntitiesChunkNeutral = { area=isSharedChunkArea, collision_mask = "player-layer", type={ "tree", "simple-entity" } } Universe.isFilteredEntitiesEnemyStructureQuery = { area=isSharedChunkArea, force=Universe.enemyForces, type={ "turret", "unit-spawner" } } Universe.isCountResourcesQuery = { area=isSharedChunkArea, type="resource" } Universe.isFilteredEntitiesUnitQuery = { area=isSharedChunkArea, force=Universe.enemyForces, type="unit" } -- cps local cpsSharedChunkArea = { {0,0}, {0,0} } Universe.cpsFilteredTilesQuery = { collision_mask="player-layer", area=cpsSharedChunkArea } Universe.cpsFilteredEntitiesChunkNeutral = { area=cpsSharedChunkArea, collision_mask = "player-layer", type={ "tree", "simple-entity" } } Universe.cpsFilteredEnemyAnyFound = { area=cpsSharedChunkArea, force=Universe.enemyForces, type={ "turret", "unit-spawner" }, limit = 1 } -- msrc local msrcSharedChunkArea = { {0,0}, {0,0} } Universe.msrcFilteredTilesQuery = { collision_mask="player-layer", area=msrcSharedChunkArea } Universe.msrcFilteredEntitiesChunkNeutral = { area=msrcSharedChunkArea, collision_mask = "player-layer", type={ "tree", "simple-entity" } } Universe.msrcCountResourcesQuery = { area=msrcSharedChunkArea, type="resource" } -- sp local spSharedAreaChunk = { {0,0}, {0,0} } Universe.spFilteredEntitiesCliffQuery = { area=spSharedAreaChunk, type="cliff", limit = 1 } Universe.spFilteredTilesPathQuery = { area=spSharedAreaChunk, collision_mask="player-layer", limit = 1 } -- ouc Universe.oucCliffQuery = { area={ {0,0}, {0,0} }, type="cliff" } -- ppu Universe.processorQueries = {} Universe.processorQueries.createEntityQuery = { name = "", position = {0,0}, raise_built = true } Universe.processorQueries.targetPosition = {0,0} Universe.squadQueries = {} Universe.squadQueries.targetPosition = {0,0} Universe.squadQueries.createEntityQuery = { name = "", position = {0,0} } Universe.squadQueries.attackCommand = { type = DEFINES_COMMAND_ATTACK_AREA, destination = {0,0}, radius = CHUNK_SIZE * 1.5, distraction = DEFINES_DISTRACTION_BY_ANYTHING } Universe.squadQueries.moveCommand = { type = DEFINES_COMMAND_GO_TO_LOCATION, destination = {0,0}, pathfind_flags = { cache = true, prefer_straight_paths = true }, distraction = DEFINES_DISTRACTION_BY_ENEMY, radius = 5 } Universe.squadQueries.settleCommand = { type = DEFINES_COMMAND_BUILD_BASE, destination = {0,0}, distraction = DEFINES_DISTRACTION_BY_ENEMY, ignore_planner = true } Universe.squadQueries.wanderCommand = { type = DEFINES_COMMAND_WANDER, wander_in_group = false, radius = TRIPLE_CHUNK_SIZE*2, ticks_to_wait = 20 * 60 } Universe.squadQueries.wander2Command = { type = DEFINES_COMMAND_WANDER, wander_in_group = true, radius = TRIPLE_CHUNK_SIZE*2, ticks_to_wait = 2 * 60 } Universe.squadQueries.compoundSettleCommand = { type = DEFINES_COMMMAD_COMPOUND, structure_type = DEFINES_COMPOUND_COMMAND_RETURN_LAST, commands = { Universe.squadQueries.settleCommand } } Universe.squadQueries.retreatCommand = { type = DEFINES_COMMAND_GROUP, group = nil, distraction = DEFINES_DISTRACTION_BY_ANYTHING, use_group_distraction = true } Universe.squadQueries.fleeCommand = { type = DEFINES_COMMAND_FLEE, from = nil, distraction = DEFINES_DISTRACTION_NONE } Universe.squadQueries.compoundRetreatGroupCommand = { type = DEFINES_COMMMAD_COMPOUND, structure_type = DEFINES_COMPOUND_COMMAND_RETURN_LAST, commands = { Universe.squadQueries.fleeCommand, Universe.squadQueries.retreatCommand } } Universe.squadQueries.formGroupCommand = { type = DEFINES_COMMAND_GROUP, group = nil, distraction = DEFINES_DISTRACTION_BY_ANYTHING, use_group_distraction = true } Universe.squadQueries.formCommand = { command = Universe.squadQueries.formGroupCommand, unit_count = 0, unit_search_distance = TRIPLE_CHUNK_SIZE } Universe.squadQueries.formRetreatCommand = { command = Universe.squadQueries.compoundRetreatGroupCommand, unit_count = 1, unit_search_distance = CHUNK_SIZE } Universe.squadQueries.searchPath = { {chunk=-1, direction=-1}, {chunk=-1, direction=-1}, {chunk=-1, direction=-1}, {chunk=-1, direction=-1} } Universe.squadQueries.renderText = { target = nil, text = "", target_offset = {0, -10}, color = {r=1,g=1,b=1,a=0.5}, surface = nil, scale = 5 } Universe.squadQueries.createUnitGroup = { position = {0,0} } end function Upgrade.setCommandForces(npcForces, enemyForces) for force in pairs(Universe.playerForces) do Universe.playerForces[force] = nil end for force in pairs(Universe.npcForces) do Universe.npcForces[force] = nil end for force in pairs(Universe.enemyForces) do Universe.enemyForces[force] = nil end for force in pairs(Universe.nonPlayerForces) do Universe.nonPlayerForces[force] = nil end for _,force in pairs(game.forces) do if not npcForces[force.name] and not enemyForces[force.name] then Universe.playerForces[#Universe.playerForces+1] = force.name end end for force in pairs(enemyForces) do Universe.enemyForces[#Universe.enemyForces+1] = force Universe.nonPlayerForces[#Universe.nonPlayerForces+1] = force end for force in pairs(npcForces) do Universe.npcForces[#Universe.npcForces+1] = force Universe.nonPlayerForces[#Universe.nonPlayerForces+1] = force end end function Upgrade.addUniverseProperties() if not global.universePropertyVersion or global.version then for key in pairs(global) do if key ~= "universe" then global[key] = nil end end for key in pairs(Universe) do Universe[key] = nil end global.universePropertyVersion = 0 end if global.universePropertyVersion < 1 then global.universePropertyVersion = 1 Universe.safeEntities = {} Universe.flushPendingChunks = false Universe.aiPointsScaler = settings.global["rampant--aiPointsScaler"].value Universe.aiPointsPrintGainsToChat = settings.global["rampant--aiPointsPrintGainsToChat"].value Universe.aiPointsPrintSpendingToChat = settings.global["rampant--aiPointsPrintSpendingToChat"].value Universe.aiNocturnalMode = settings.global["rampant--permanentNocturnal"].value Universe.retreatThreshold = 0 Universe.rallyThreshold = 0 Universe.formSquadThreshold = 0 Universe.attackWaveSize = 0 Universe.attackWaveDeviation = 0 Universe.attackWaveUpperBound = 0 Universe.unitRefundAmount = 0 Universe.kamikazeThreshold = 0 Universe.attackWaveLowerBound = 1 Universe.settlerWaveDeviation = 0 Universe.settlerWaveSize = 0 Universe.enabledMigration = Universe.expansion and settings.global["rampant--enableMigration"].value Universe.peacefulAIToggle = settings.global["rampant--peacefulAIToggle"].value Universe.printAIStateChanges = settings.global["rampant--printAIStateChanges"].value Universe.debugTemperament = settings.global["rampant--debugTemperament"].value Universe.upgradeId = 0 Universe.eventId = 0 Universe.chunkId = 0 Universe.maps = {} Universe.activeMaps = {} Universe.groupNumberToSquad = {} Universe.pendingChunks = {} Universe.squadIterator = nil Universe.processMapAIIterator = nil Universe.processNestIterator = nil Universe.vengenceQueue = {} Universe.currentMap = nil Universe.mapIterator = nil Universe.builderCount = 0 Universe.squadCount = 0 Universe.processActiveSpawnerIterator = nil Universe.processActiveRaidSpawnerIterator = nil Universe.processMigrationIterator = nil Universe.chunkToNests = {} Universe.chunkToHives = {} Universe.chunkToUtilities = {} Universe.chunkToVictory = {} Universe.chunkToActiveNest = {} Universe.chunkToActiveRaidNest = {} Universe.chunkToDrained = {} Universe.chunkToRetreats = {} Universe.chunkToRallys = {} Universe.chunkToPassScan = {} Universe.hiveData = {} Universe.hives = {} Universe.activeHives = {} Universe.hiveIterator = nil Universe.baseId = 0 Universe.awake = false Universe.recycleBaseIterator = nil Universe.maxPoints = 0 Universe.maxOverflowPoints = 0 Universe.bases = {} Universe.processBaseAIIterator = nil Universe.excludedSurfaces = {} Universe.pendingUpgrades = {} Universe.pendingUpgradesLength = 0 Universe.settlePurpleCloud = {} end if global.universePropertyVersion < 2 then global.universePropertyVersion = 2 Universe.hiveDataIterator = nil end if global.universePropertyVersion < 4 then global.universePropertyVersion = 4 addCommandSet() end end function Upgrade.attempt() if not global.gameVersion then global.gameVersion = 1 Universe.evolutionLevel = game.forces.enemy.evolution_factor Universe.random = game.create_random_generator(Constants.ENEMY_SEED) local minDiffuse = game.map_settings.pollution.min_to_diffuse Universe.pollutionDiffuseMinimum = minDiffuse * 0.75 Universe.expansion = game.map_settings.enemy_expansion.enabled Universe.expansionMaxDistance = game.map_settings.enemy_expansion.max_expansion_distance * CHUNK_SIZE Universe.expansionMinTime = game.map_settings.enemy_expansion.min_expansion_cooldown / TICKS_A_MINUTE Universe.expansionMaxTime = game.map_settings.enemy_expansion.max_expansion_cooldown / TICKS_A_MINUTE Universe.expansionMinSize = game.map_settings.enemy_expansion.settler_group_min_size Universe.expansionMaxSize = game.map_settings.enemy_expansion.settler_group_max_size Universe.expansionLowTargetDistance = (Universe.expansionMaxDistance + MINIMUM_EXPANSION_DISTANCE) * 0.33 Universe.expansionMediumTargetDistance = (Universe.expansionMaxDistance + MINIMUM_EXPANSION_DISTANCE) * 0.50 Universe.expansionHighTargetDistance = (Universe.expansionMaxDistance + MINIMUM_EXPANSION_DISTANCE) * 0.75 Universe.expansionDistanceDeviation = Universe.expansionMediumTargetDistance * 0.33 Universe.modAddedTick = game.tick end if global.gameVersion < 2 then global.gameVersion = 2 game.map_settings.max_failed_behavior_count = 999 for _, squad in pairs(Universe.groupNumberToSquad) do squad.canBeCompressed = 0 squad.retreats = 0 end game.forces.enemy.kill_all_units() for _, map in pairs(Universe.maps) do for _, chunk in pairs(map.processQueue) do chunk[CHUNK_TICK] = 0 chunk[BASE_PHEROMONE] = 0 chunk[PLAYER_PHEROMONE] = 0 chunk[RESOURCE_PHEROMONE] = 0 chunk[ENEMY_PHEROMONE] = 0 chunk[KAMIKAZE_PHEROMONE] = 0 end end end if global.gameVersion < 3 then global.gameVersion = 3 local lookup = {} for _, map in pairs(Universe.maps) do local entities = map.surface.find_entities_filtered({ type = {"unit-spawner", "turret"}, force = "enemy" }) for i = 1, #entities do local entity = entities[i] lookup[entity.unit_number] = entity end end for entityId, hiveData in pairs(Universe.activeHives) do if not lookup[entityId] then Universe.activeHives[entityId] = nil else Universe.hives[entityId] = hiveData end end for entityId, hiveData in pairs(Universe.hives) do if not lookup[entityId] then Universe.hives[entityId] = nil else hiveData.e = lookup[entityId] hiveData.position = hiveData.e.position end end for entityId, hiveData in pairs(Universe.hiveData) do if not lookup[entityId] then Universe.hiveData[entityId] = nil else if not Universe.hives[hiveData.hiveId] then Universe.hiveData[entityId] = nil elseif not hiveData.e then Universe.hiveData[entityId] = nil end end end Universe.hiveIterator = nil Universe.hiveDataIterator = nil end if global.gameVersion < 4 then global.gameVersion = 4 local tick = game.tick for _, map in pairs(Universe.maps) do local surface = map.surface if surface.valid then MapUtils.queueChunks(surface, surface.get_chunks(), true, tick) end end end end function Upgrade.init(universe) Universe = universe end return Upgrade