local upgrade = {} -- imports local constants = require("libs/Constants") local chunkProcessor = require("libs/ChunkProcessor") local mapUtils = require("libs/MapUtils") -- constants 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 -- imported functions local sFind = string.find local queueGeneratedChunk = mapUtils.queueGeneratedChunk local processPendingChunks = chunkProcessor.processPendingChunks -- module code local function addCommandSet(queriesAndCommands) -- preallocating memory to be used in code, making it fast by reducing garbage generated. queriesAndCommands.neighbors = { -1, -1, -1, -1, -1, -1, -1, -1 } queriesAndCommands.cardinalNeighbors = { -1, -1, -1, -1 } queriesAndCommands.position = { x=0, y=0 } queriesAndCommands.position2 = { x=0, y=0 } queriesAndCommands.position3 = { x=0, y=0 } queriesAndCommands.chunkOverlapArray = { -1, -1, -1, -1 } queriesAndCommands.position2Top = {0, 0} queriesAndCommands.position2Bottom = {0, 0} --this is shared between two different queries queriesAndCommands.area = { {0, 0}, {0, 0} } queriesAndCommands.area2 = { queriesAndCommands.position2Top, queriesAndCommands.position2Bottom } queriesAndCommands.buildPositionTop = {0, 0} queriesAndCommands.buildPositionBottom = {0, 0} queriesAndCommands.buildArea = { queriesAndCommands.buildPositionTop, queriesAndCommands.buildPositionBottom } queriesAndCommands.countResourcesQuery = { area=queriesAndCommands.area, type="resource" } queriesAndCommands.filteredEntitiesUnitQuery = { area=queriesAndCommands.area, force="enemy", type="unit" } queriesAndCommands.hasPlayerStructuresQuery = { area=queriesAndCommands.area, force={"enemy","neutral"}, invert=true, limit=1 } queriesAndCommands.filteredEntitiesEnemyStructureQuery = { area=queriesAndCommands.area, force="enemy", type={ "turret", "unit-spawner" } } queriesAndCommands.filteredEntitiesPointQueryLimited = { position = queriesAndCommands.position, radius = 10, limit = 1, force = "enemy", type = { "unit-spawner", "turret" } } queriesAndCommands.createBuildCloudQuery = { name = "build-clear-cloud-rampant", position = queriesAndCommands.position } queriesAndCommands.activePlayerForces = {"player"} for _,force in pairs(game.forces) do local add = true if (force.name ~= "neutral") and (force.name ~= "enemy") then for i=1,#queriesAndCommands.activePlayerForces do if (queriesAndCommands.activePlayerForces[i] == force.name) then add = false break end end if add then queriesAndCommands.activePlayerForces[#queriesAndCommands.activePlayerForces+1] = force.name end end end queriesAndCommands.filteredEntitiesPlayerQueryLowest = { area=queriesAndCommands.area, force=queriesAndCommands.activePlayerForces, collision_mask = "player-layer", type={ "wall", "transport-belt" } } queriesAndCommands.filteredEntitiesPlayerQueryLow = { area=queriesAndCommands.area, force=queriesAndCommands.activePlayerForces, collision_mask = "player-layer", type={ "splitter", "pump", "offshore-pump", "lamp", "solar-panel", "programmable-speaker", "accumulator", "assembling-machine", "turret", "ammo-turret" } } queriesAndCommands.filteredEntitiesPlayerQueryHigh = { area=queriesAndCommands.area, force=queriesAndCommands.activePlayerForces, collision_mask = "player-layer", type={ "furnace", "lab", "roboport", "beacon", "radar", "electric-turret", "boiler", "generator", "fluid-turret", "mining-drill" } } queriesAndCommands.filteredEntitiesPlayerQueryHighest = { area=queriesAndCommands.area, force=queriesAndCommands.activePlayerForces, collision_mask = "player-layer", type={ "artillery-turret", "reactor", "rocket-silo" } } queriesAndCommands.filteredEntitiesChunkNeutral = { area=queriesAndCommands.area, collision_mask = "player-layer", type={ "tree", "simple-entity" } } local sharedArea = { {0,0}, {0,0} } queriesAndCommands.filteredEntitiesCliffQuery = { area=sharedArea, type="cliff", limit = 1 } queriesAndCommands.filteredTilesPathQuery = { area=sharedArea, collision_mask="water-tile", limit = 1 } queriesAndCommands.cliffQuery = { area=queriesAndCommands.area2, type="cliff" } queriesAndCommands.canPlaceQuery = { name="", position={0,0} } queriesAndCommands.filteredTilesQuery = { collision_mask="water-tile", area=queriesAndCommands.area } queriesAndCommands.upgradeEntityQuery = { name = "", position = {0,0} } queriesAndCommands.attackCommand = { type = DEFINES_COMMAND_ATTACK_AREA, destination = queriesAndCommands.position, radius = CHUNK_SIZE * 1.5, distraction = DEFINES_DISTRACTION_BY_ANYTHING } queriesAndCommands.moveCommand = { type = DEFINES_COMMAND_GO_TO_LOCATION, destination = queriesAndCommands.position, pathfind_flags = { cache = true }, distraction = DEFINES_DISTRACTION_BY_ENEMY } queriesAndCommands.settleCommand = { type = DEFINES_COMMAND_BUILD_BASE, destination = queriesAndCommands.position, distraction = DEFINES_DISTRACTION_BY_ENEMY, ignore_planner = true } queriesAndCommands.wanderCommand = { type = DEFINES_COMMAND_WANDER, wander_in_group = false, radius = TRIPLE_CHUNK_SIZE*2, ticks_to_wait = 20 * 60 } queriesAndCommands.wander2Command = { type = DEFINES_COMMAND_WANDER, wander_in_group = true, radius = TRIPLE_CHUNK_SIZE*2, ticks_to_wait = 2 * 60 } queriesAndCommands.stopCommand = { type = DEFINES_COMMAND_STOP } queriesAndCommands.compoundSettleCommand = { type = DEFINES_COMMMAD_COMPOUND, structure_type = DEFINES_COMPOUND_COMMAND_RETURN_LAST, commands = { queriesAndCommands.wonder2Command, queriesAndCommands.settleCommand } } queriesAndCommands.retreatCommand = { type = DEFINES_COMMAND_GROUP, group = nil, distraction = DEFINES_DISTRACTION_BY_ANYTHING, use_group_distraction = true } queriesAndCommands.fleeCommand = { type = DEFINES_COMMAND_FLEE, from = nil, distraction = DEFINES_DISTRACTION_NONE } queriesAndCommands.compoundRetreatGroupCommand = { type = DEFINES_COMMMAD_COMPOUND, structure_type = DEFINES_COMPOUND_COMMAND_RETURN_LAST, commands = { queriesAndCommands.stopCommand, queriesAndCommands.fleeCommand, queriesAndCommands.retreatCommand } } queriesAndCommands.formGroupCommand = { type = DEFINES_COMMAND_GROUP, group = nil, distraction = DEFINES_DISTRACTION_BY_ANYTHING, use_group_distraction = false } queriesAndCommands.formCommand = { command = queriesAndCommands.formGroupCommand, unit_count = 0, unit_search_distance = TRIPLE_CHUNK_SIZE } queriesAndCommands.formRetreatCommand = { command = queriesAndCommands.compoundRetreatGroupCommand, unit_count = 1, unit_search_distance = CHUNK_SIZE } end function upgrade.attempt(universe) local starting = global.version if not global.version or global.version < 114 then global.version = 114 if not universe then universe = {} global.universe = universe end game.forces.enemy.kill_all_units() universe.safeEntities = {} 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.mapIterator = nil universe.retreatThreshold = 0 universe.rallyThreshold = 0 universe.formSquadThreshold = 0 universe.attackWaveSize = 0 universe.attackWaveDeviation = 0 universe.attackWaveUpperBound = 0 universe.unitRefundAmount = 0 universe.regroupIndex = 1 game.map_settings.path_finder.min_steps_to_check_path_find_termination = constants.PATH_FINDER_MIN_STEPS_TO_CHECK_PATH universe.evolutionTableAlignment = {} universe.kamikazeThreshold = 0 universe.attackWaveLowerBound = 1 universe.expansion = game.map_settings.enemy_expansion.enabled universe.expansionMaxDistance = game.map_settings.enemy_expansion.max_expansion_distance * CHUNK_SIZE universe.expansionMaxDistanceDerivation = universe.expansionMaxDistance * 0.33 universe.expansionMinTime = game.map_settings.enemy_expansion.min_expansion_cooldown universe.expansionMaxTime = game.map_settings.enemy_expansion.max_expansion_cooldown universe.expansionMinSize = game.map_settings.enemy_expansion.settler_group_min_size universe.expansionMaxSize = game.map_settings.enemy_expansion.settler_group_max_size universe.settlerCooldown = 0 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.enemyAlignmentLookup = {} game.map_settings.unit_group.min_group_radius = constants.UNIT_GROUP_MAX_RADIUS * 0.5 game.map_settings.unit_group.max_group_radius = constants.UNIT_GROUP_MAX_RADIUS game.map_settings.unit_group.max_member_speedup_when_behind = constants.UNIT_GROUP_MAX_SPEED_UP game.map_settings.unit_group.max_member_slowdown_when_ahead = constants.UNIT_GROUP_MAX_SLOWDOWN game.map_settings.unit_group.max_group_slowdown_factor = constants.UNIT_GROUP_SLOWDOWN_FACTOR game.map_settings.max_failed_behavior_count = 3 game.map_settings.unit_group.member_disown_distance = 10 game.map_settings.unit_group.tick_tolerance_when_member_arrives = 60 game.forces.enemy.ai_controllable = true universe.evolutionLevel = game.forces.enemy.evolution_factor global.pendingChunks = nil global.natives = nil global.map = nil end if global.version < 116 then global.version = 116 universe.maxPoints = 0 end if global.version < 200 then global.version = 200 addCommandSet(universe) universe.eventId = 0 universe.randomGenerator = nil universe.random = game.create_random_generator(settings.startup["rampant--enemySeed"].value+game.default_map_gen_settings.seed) game.forces.enemy.kill_all_units() universe.maps = {} universe.activeMap = nil universe.mapIterator = nil universe.builderCount = 0 universe.squadCount = 0 game.print("Rampant - Version 2.0.0") end return (starting ~= global.version) and global.version end function upgrade.prepMap(universe, surface) local surfaceName = surface.name if sFind(surfaceName, "Factory floor") or sFind(surfaceName, " Orbit") or sFind(surfaceName, "clonespace") or sFind(surfaceName, "BPL_TheLabplayer") or sFind(surfaceName, "starmap-") or (surfaceName == "aai-signals") or sFind(surfaceName, "NiceFill") or sFind(surfaceName, "Asteroid Belt") or sFind(surfaceName, "Vault ") then return end game.print("Rampant - Indexing surface:" .. surface.name .. ", index:" .. tostring(surface.index) .. ", please wait.") local surfaceIndex = surface.index if not universe.maps then universe.maps = {} end local map = {} universe.maps[surfaceIndex] = map map.eventId = 1 map.chunkId = 1 map.maxAggressiveGroups = 1 map.sentAggressiveGroups = 0 map.processedChunks = 0 map.processQueue = {} map.processIndex = 1 map.cleanupIndex = 1 map.scanPlayerIndex = 1 map.scanResourceIndex = 1 map.scanEnemyIndex = 1 map.processStaticIndex = 1 map.outgoingScanWave = true map.outgoingStaticScanWave = true map.pendingUpgrades = {} map.pendingChunks = {} map.chunkToBase = {} map.chunkToNests = {} map.chunkToTurrets = {} map.chunkToTraps = {} map.chunkToUtilities = {} map.chunkToHives = {} map.chunkToNestIds = {} map.chunkToHiveIds = {} map.chunkToTrapIds = {} map.chunkToTurretIds = {} map.chunkToUtilityIds = {} map.chunkToPlayerBase = {} map.chunkToResource = {} map.chunkToPlayerCount = {} map.playerToChunk = {} map.pendingChunks = {} map.chunkToPassScan = {} map.chunkToSquad = {} map.chunkToRetreats = {} map.chunkToRallys = {} map.chunkIdToChunk = {} map.chunkToPassable = {} map.chunkToPathRating = {} map.chunkToDeathGenerator = {} map.chunkToDrained = {} map.chunkToVictory = {} map.chunkToActiveNest = {} map.chunkToActiveRaidNest = {} map.chunkToPassScanIterator = nil map.pendingUpgradeIterator = nil map.squadIterator = nil map.regroupIterator = nil map.deployVengenceIterator = nil map.recycleBaseIterator = nil map.processActiveSpawnerIterator = nil map.processActiveRaidSpawnerIterator = nil map.processMigrationIterator = nil map.processNestIterator = nil map.victoryScentIterator = nil map.chunkScanCounts = {} map.chunkRemovals = {} map.processActiveNest = {} map.tickActiveNest = {} map.emptySquadsOnChunk = {} map.surface = surface map.universe = universe map.vengenceQueue = {} map.bases = {} map.baseIndex = 1 map.baseIncrement = 0 map.points = 0 map.state = constants.AI_STATE_AGGRESSIVE map.baseId = 0 map.squads = nil map.pendingAttack = nil map.building = nil map.evolutionLevel = game.forces.enemy.evolution_factor map.canAttackTick = 0 map.drainPylons = {} map.groupNumberToSquad = {} map.activeRaidNests = 0 map.activeNests = 0 map.destroyPlayerBuildings = 0 map.lostEnemyUnits = 0 map.lostEnemyBuilding = 0 map.rocketLaunched = 0 map.builtEnemyBuilding = 0 map.ionCannonBlasts = 0 map.artilleryBlasts = 0 map.temperament = 0.5 map.temperamentScore = 0 map.stateTick = 0 map.random = universe.random -- queue all current chunks that wont be generated during play local tick = game.tick for chunk in surface.get_chunks() do if surface.is_chunk_generated(chunk) then queueGeneratedChunk(universe, { surface = surface, tick = tick, area = { left_top = { x = chunk.x * 32, y = chunk.y * 32 } } } ) end end processPendingChunks(map, tick, true) end function upgrade.compareTable(entities, option, new) local changed = false if (entities[option] ~= new) then entities[option] = new changed = true end return changed, new end return upgrade