From fcd52cf91d7ca9878f3c7ee0106649ceb440d9fc Mon Sep 17 00:00:00 2001 From: Aaron Veden Date: Tue, 19 May 2020 19:37:16 -0700 Subject: [PATCH] currently stuck at factorio crash --- Upgrade.lua | 22 +- changelog.txt | 12 ++ control.lua | 288 ++++++++++++++++++-------- info.json | 2 +- libs/AIAttackWave.lua | 121 +++++------ libs/AIPlanning.lua | 2 +- libs/AIPredicates.lua | 6 +- libs/BaseUtils.lua | 89 +++----- libs/ChunkProcessor.lua | 78 ++++--- libs/ChunkPropertyUtils.lua | 21 +- libs/Constants.lua | 10 +- libs/MapProcessor.lua | 155 ++++++++------ libs/MovementUtils.lua | 17 +- libs/PheromoneUtils.lua | 222 +++++++++++--------- libs/SquadAttack.lua | 284 +++++++++---------------- libs/SquadDefense.lua | 27 +-- libs/UnitGroupUtils.lua | 110 +++++++--- locale/en/locale.cfg | 2 - prototypes/buildings/ChunkScanner.lua | 37 ++++ prototypes/utils/SmokeUtils.lua | 139 +++++++++++++ settings.lua | 18 +- tests.lua | 4 +- 22 files changed, 985 insertions(+), 681 deletions(-) diff --git a/Upgrade.lua b/Upgrade.lua index 439a900..368ef75 100755 --- a/Upgrade.lua +++ b/Upgrade.lua @@ -35,6 +35,7 @@ function upgrade.attempt(natives, setNewSurface, gameSurfaces) natives.state = constants.AI_STATE_AGGRESSIVE natives.safeEntities = {} + natives.vengenceQueue = {} natives.aiPointsScaler = settings.global["rampant-aiPointsScaler"].value natives.aiNocturnalMode = settings.global["rampant-permanentNocturnal"].value @@ -150,15 +151,30 @@ function upgrade.attempt(natives, setNewSurface, gameSurfaces) end end - natives.remainingSquads = 0 natives.groupNumberToSquad = {} game.forces.enemy.kill_all_units() natives.squads = nil natives.pendingAttack = nil natives.building = nil end - if (global.version < 112) then - global.version = 112 + if (global.version < 113) then + global.version = 113 + + natives.baseId = 0 + + local newBases = {} + for i=1,#natives.bases do + local base = natives.bases + base.id = natives.baseId + newBases[base.id] = base + natives.baseId = natives.baseId + 1 + end + natives.bases = newBases + global.pendingChunks = nil + + natives.vengenceQueue = {} + + game.forces.enemy.ai_controllable = true if not setNewSurface then game.get_surface(natives.activeSurface).print("Rampant - Version 0.18.12") diff --git a/changelog.txt b/changelog.txt index f9f58a4..da6a1ce 100755 --- a/changelog.txt +++ b/changelog.txt @@ -1,10 +1,22 @@ --------------------------------------------------------------------------------------------------- Version: 0.18.12 Date: 16. 4 2020 + Improvements: + - Swapped to ai command completed for unit movements + - When squads reach a movement threshold for being in the same spots to much they switch to kamikaze mode + - Added effect when spawners build to destroy build site + - Integrated vanilla AI into Rampant for pollution management Tweaks: - Changed ai credits per rocket launched to 5000 Bugfixes: - fixed add movement penalty not using squad chunk + - fixed landfill and waterfill getting registered properly + Optimizations: + - Cleaned up regroup squads + - Cleaned up invalid group detection + - Minimized use of group.members + - Changed how settlers remove entities around build sites + - Split map processing and unit group creation --------------------------------------------------------------------------------------------------- Version: 0.18.11 diff --git a/control.lua b/control.lua index 2275489..7924da1 100755 --- a/control.lua +++ b/control.lua @@ -22,9 +22,9 @@ local upgrade = require("Upgrade") local config = require("config") local aiPredicates = require("libs/AIPredicates") - -- constants +local DIVISOR_DEATH_TRAIL_TABLE = constants.DIVISOR_DEATH_TRAIL_TABLE local TRIPLE_CHUNK_SIZE = constants.TRIPLE_CHUNK_SIZE local INTERVAL_LOGIC = constants.INTERVAL_LOGIC local INTERVAL_PLAYER_PROCESS = constants.INTERVAL_PLAYER_PROCESS @@ -32,8 +32,9 @@ local INTERVAL_MAP_PROCESS = constants.INTERVAL_MAP_PROCESS local INTERVAL_SCAN = constants.INTERVAL_SCAN local INTERVAL_SQUAD = constants.INTERVAL_SQUAD local INTERVAL_RESQUAD = constants.INTERVAL_RESQUAD -local INTERVAL_BUILDERS = constants.INTERVAL_BUILDERS +-- local INTERVAL_BUILDERS = constants.INTERVAL_BUILDERS local INTERVAL_TEMPERAMENT = constants.INTERVAL_TEMPERAMENT +local INTERVAL_SPAWNER = constants.INTERVAL_SPAWNER local HIVE_BUILDINGS = constants.HIVE_BUILDINGS @@ -52,6 +53,7 @@ local RETREAT_GRAB_RADIUS = constants.RETREAT_GRAB_RADIUS local RETREAT_SPAWNER_GRAB_RADIUS = constants.RETREAT_SPAWNER_GRAB_RADIUS +local DEFINES_BEHAVIOR_RESULT_FAIL = defines.behavior_result.fail local DEFINES_COMMAND_GROUP = defines.command.group local DEFINES_COMMAND_WANDER = defines.command.wander local DEFINES_COMMAND_BUILD_BASE = defines.command.build_base @@ -91,6 +93,9 @@ local positionToChunkXY = mapUtils.positionToChunkXY local temperamentPlanner = aiPlanning.temperamentPlanner +local processVengence = mapProcessor.processVengence +local processSpawners = mapProcessor.processSpawners + local getPlayerBaseGenerator = chunkPropertyUtils.getPlayerBaseGenerator local getChunkByPosition = mapUtils.getChunkByPosition @@ -122,6 +127,10 @@ local findNearbyBase = baseUtils.findNearbyBase local processActiveNests = mapProcessor.processActiveNests +local removeSquadFromChunk = chunkPropertyUtils.removeSquadFromChunk +local addDeathGenerator = chunkPropertyUtils.addDeathGenerator +local getDeathGenerator = chunkPropertyUtils.getDeathGenerator + local retreatUnits = squadDefense.retreatUnits local accountPlayerEntity = chunkUtils.accountPlayerEntity @@ -137,16 +146,18 @@ local cleanSquads = unitGroupUtils.cleanSquads local upgradeEntity = baseUtils.upgradeEntity local rebuildNativeTables = baseUtils.rebuildNativeTables +local mMin = math.min local mRandom = math.random local tRemove = table.remove +local sFind = string.find + -- local references to global local gameSurfaces -- used for manage which surfaces have been visited local map -- manages the chunks that make up the game world local natives -- manages the enemy units, structures, and ai -local pendingChunks -- chunks that have yet to be processed by the mod -- hook functions @@ -175,17 +186,17 @@ end local function onLoad() map = global.map natives = global.natives - pendingChunks = global.pendingChunks gameSurfaces = global.gameSurfaces hookEvents() end local function onChunkGenerated(event) + print("1", game.tick) -- queue generated chunk for delayed processing, queuing is required because some mods (RSO) mess with chunk as they -- are generated, which messes up the scoring. if (event.surface.name == natives.activeSurface) then - pendingChunks[#pendingChunks+1] = event + map.pendingChunks[event] = true end end @@ -201,6 +212,7 @@ local function rebuildMap() map.processIndex = 1 map.scanIndex = 1 + map.pendingChunks = {} map.chunkToBase = {} map.chunkToNests = {} map.chunkToTurrets = {} @@ -215,7 +227,6 @@ local function rebuildMap() map.chunkToRetreats = {} map.chunkToRallys = {} - map.chunkToSettler = {} map.chunkToPassable = {} map.chunkToPathRating = {} @@ -229,6 +240,11 @@ local function rebuildMap() map.squadIterator = nil map.regroupIterator = nil + map.deployVengenceIterator = nil + map.recycleBaseIterator = nil + map.processActiveSpawnerIterator = nil + map.processActiveRaidSpawnerIterator = nil + map.processMigrationIterator = nil -- preallocating memory to be used in code, making it fast by reducing garbage generated. map.neighbors = { @@ -306,6 +322,10 @@ local function rebuildMap() "turret" } } + map.createBuildCloudQuery = { + name = "build-clear-cloud-rampant", + position = map.position + } map.activePlayerForces = {"player"} @@ -434,6 +454,13 @@ local function rebuildMap() use_group_distraction = false } + map.mergeGroupCommand = { + type = DEFINES_COMMAND_GROUP, + group = nil, + distraction = DEFINES_DISTRACTION_NONE, + use_group_distraction = false + } + map.fleeCommand = { type = DEFINES_COMMAND_FLEE, from = nil, @@ -507,14 +534,10 @@ local function onModSettingsChange(event) upgrade.compareTable(natives, "newEnemies", settings.startup["rampant-newEnemies"].value) upgrade.compareTable(natives, "enemySeed", settings.startup["rampant-enemySeed"].value) - upgrade.compareTable(natives, "disableVanillaAI", settings.global["rampant-disableVanillaAI"].value) - natives.enabledMigration = natives.expansion and settings.global["rampant-enableMigration"].value upgrade.compareTable(natives, "ENEMY_VARIATIONS", settings.startup["rampant-newEnemyVariations"].value) - game.forces.enemy.ai_controllable = not natives.disableVanillaAI - return true end @@ -548,10 +571,6 @@ local function prepWorld(rebuild, surfaceName) map.natives = natives natives.map = map - -- clear pending chunks, will be added when loop runs below - global.pendingChunks = {} - pendingChunks = global.pendingChunks - -- queue all current chunks that wont be generated during play local surface = game.get_surface(natives.activeSurface) local tick = game.tick @@ -569,7 +588,7 @@ local function prepWorld(rebuild, surfaceName) end end - processPendingChunks(map, surface, pendingChunks, tick, rebuild) + processPendingChunks(map, surface, tick, rebuild, true) end end @@ -578,6 +597,7 @@ local function onConfigChanged() end local function onBuild(event) + print("2", game.tick) local entity = event.created_entity or event.entity if (entity.surface.name == natives.activeSurface) then if (entity.type == "resource") and (entity.force.name == "neutral") then @@ -594,6 +614,7 @@ local function onBuild(event) end local function onMine(event) + print("3", game.tick) local entity = event.entity local surface = entity.surface if (surface.name == natives.activeSurface) then @@ -608,6 +629,7 @@ local function onMine(event) end local function onDeath(event) + print("4", game.tick) local entity = event.entity if entity.valid then local surface = entity.surface @@ -634,14 +656,15 @@ local function onDeath(event) natives.lostEnemyUnits = natives.lostEnemyUnits + 1 - retreatUnits(chunk, - entityPosition, - entity.unit_group, - map, - surface, - tick, - (artilleryBlast and RETREAT_SPAWNER_GRAB_RADIUS) or RETREAT_GRAB_RADIUS, - artilleryBlast) + -- retreatUnits(chunk, + -- entityPosition, + -- entity.unit_group, + -- map, + -- surface, + -- tick, + -- (artilleryBlast and RETREAT_SPAWNER_GRAB_RADIUS) or RETREAT_GRAB_RADIUS-- , + -- -- artilleryBlast + -- ) if (mRandom() < natives.rallyThreshold) and not surface.peaceful_mode then rallyUnits(chunk, map, surface, tick) @@ -653,19 +676,20 @@ local function onDeath(event) natives.points = natives.points + (((entityType == "unit-spawner") and RECOVER_NEST_COST) or RECOVER_WORM_COST) - if (chunk ~= -1) then - unregisterEnemyBaseStructure(map, entity) + unregisterEnemyBaseStructure(map, entity) + if (chunk ~= -1) then rallyUnits(chunk, map, surface, tick) - retreatUnits(chunk, - entityPosition, - nil, - map, - surface, - tick, - RETREAT_SPAWNER_GRAB_RADIUS, - (cause and ((cause.type == "artillery-wagon") or (cause.type == "artillery-turret")))) + -- retreatUnits(chunk, + -- entityPosition, + -- nil, + -- map, + -- surface, + -- tick, + -- RETREAT_SPAWNER_GRAB_RADIUS-- , + -- -- (cause and ((cause.type == "artillery-wagon") or (cause.type == "artillery-turret"))) + -- ) end end @@ -751,6 +775,7 @@ local function onDeath(event) end local function onEnemyBaseBuild(event) + print("5", game.tick) local entity = event.entity if entity.valid then local surface = entity.surface @@ -782,36 +807,35 @@ local function onEnemyBaseBuild(event) end local function onSurfaceTileChange(event) + print("6", game.tick) local surfaceIndex = event.surface_index or (event.robot and event.robot.surface and event.robot.surface.index) - local surfaceName = game.surfaces[surfaceIndex].name - if (surfaceName == natives.activeSurface) and - ((event.item.name == "landfill") or (event.item.name == "waterfill")) and - event.item - then - local surface = game.get_surface(natives.activeSurface) + local surface = game.get_surface(natives.activeSurface) + if (surface.index == surfaceIndex) then local chunks = {} local tiles = event.tiles - for i=1,#tiles do - local position = tiles[i].position - local chunk = getChunkByPosition(map, position) + if (event.tile.name == "landfill") or sFind(event.tile.name, "water") then + for i=1,#tiles do + local position = tiles[i].position + local chunk = getChunkByPosition(map, position) - if (chunk ~= -1) then - map.chunkToPassScan[chunk] = true - else - local x,y = positionToChunkXY(position) - local addMe = true - for ci=1,#chunks do - local c = chunks[ci] - if (c.x == x) and (c.y == y) then - addMe = false - break + if (chunk ~= -1) then + map.chunkToPassScan[chunk] = true + else + local x,y = positionToChunkXY(position) + local addMe = true + for ci=1,#chunks do + local c = chunks[ci] + if (c.x == x) and (c.y == y) then + addMe = false + break + end + end + if addMe then + local chunkXY = {x=x,y=y} + chunks[#chunks+1] = chunkXY + onChunkGenerated({area = { left_top = chunkXY }, + surface = surface}) end - end - if addMe then - local chunkXY = {x=x,y=y} - chunks[#chunks+1] = chunkXY - onChunkGenerated({area = { left_top = chunkXY }, - surface = surface}) end end end @@ -819,6 +843,7 @@ local function onSurfaceTileChange(event) end local function onResourceDepleted(event) + print("7", game.tick) local entity = event.entity if (entity.surface.name == natives.activeSurface) then unregisterResource(entity, map) @@ -826,7 +851,7 @@ local function onResourceDepleted(event) end local function onRobotCliff(event) - + print("8", game.tick) local surface = event.robot.surface if (surface.name == natives.activeSurface) and (event.item.name == "cliff-explosives") then entityForPassScan(map, event.cliff) @@ -834,6 +859,7 @@ local function onRobotCliff(event) end local function onUsedCapsule(event) + print("9", game.tick) local surface = game.players[event.player_index].surface if (surface.name == natives.activeSurface) and (event.item.name == "cliff-explosives") then map.position2Top.x = event.position.x-0.75 @@ -848,6 +874,7 @@ local function onUsedCapsule(event) end local function onRocketLaunch(event) + print("10", game.tick) local entity = event.rocket_silo or event.rocket if entity and entity.valid and (entity.surface.name == natives.activeSurface) then natives.rocketLaunched = natives.rocketLaunched + 1 @@ -856,6 +883,7 @@ local function onRocketLaunch(event) end local function onTriggerEntityCreated(event) + print("11", game.tick) local entity = event.entity if entity.valid and (entity.surface.name == natives.activeSurface) and (entity.name == "drain-trigger-rampant") then local chunk = getChunkByPosition(map, entity.position) @@ -868,13 +896,11 @@ end local function onInit() global.map = {} - global.pendingChunks = {} global.natives = {} global.gameSurfaces = {} map = global.map natives = global.natives - pendingChunks = global.pendingChunks gameSurfaces = global.gameSurfaces prepWorld(false, "nauvis") @@ -882,6 +908,7 @@ local function onInit() end local function onEntitySpawned(event) + print("12", game.tick) local entity = event.entity if natives.newEnemies and entity.valid then local surface = entity.surface @@ -921,41 +948,76 @@ local function onEntitySpawned(event) end local function onUnitGroupCreated(event) + print("13", game.tick) local group = event.group - if (group.surface.name == natives.activeSurface) and (group.force.name == "enemy") then + local surface = group.surface + if (surface.name == natives.activeSurface) and (group.force.name == "enemy") then if not group.is_script_driven then - squad = createSquad(nil, - nil, - group, - mRandom() < 0.75 and canMigrate(natives, game.get_surface(natives.activeSurface))) - natives.groupNumberToSquad[group.group_number] = squad + if not natives.aiNocturnalMode then + squad = createSquad(nil, + nil, + group, + mRandom() < 0.25 and canMigrate(natives, group.surface)) + natives.groupNumberToSquad[group.group_number] = squad + elseif not (surface.darkness > 0.65) then + group.destroy() + else + squad = createSquad(nil, + nil, + group, + mRandom() < 0.25 and canMigrate(natives, group.surface)) + natives.groupNumberToSquad[group.group_number] = squad + end end end end local function onCommandComplete(event) - -- local msg - -- if (event.result == defines.behavior_result.in_progress) then - -- msg = "progress" - -- elseif (event.result == defines.behavior_result.fail) then - -- msg = "fail" - -- elseif (event.result == defines.behavior_result.success) then - -- msg = "success" - -- elseif (event.result == defines.behavior_result.deleted) then - -- msg = "deleted" - -- end - + print("14", game.tick) local unitNumber = event.unit_number local squad = natives.groupNumberToSquad[unitNumber] if squad then + -- local result = event.result + -- local msg + -- if (result == defines.behavior_result.in_progress) then + -- msg = "progress" + -- elseif (result == defines.behavior_result.fail) then + -- msg = "fail" + -- elseif (result == defines.behavior_result.success) then + -- msg = "success" + -- elseif (result == defines.behavior_result.deleted) then + -- msg = "deleted" + -- end + -- print(msg) + local group = squad.group if group and group.valid and (group.surface.name == natives.activeSurface) then - squadDispatch(map, group.surface, squad, unitNumber) + + if (event.result == DEFINES_BEHAVIOR_RESULT_FAIL) then + print("count", #group.members) + if (#group.members == 0) then + print("dropping") + local deathGen = getDeathGenerator(map, squad.chunk) + local penalties = squad.penalties + for xc=1,mMin(#squad.penalties,5) do + addDeathGenerator(map, + penalties[xc].c, + deathGen * DIVISOR_DEATH_TRAIL_TABLE[xc]) + end + removeSquadFromChunk(map, squad) + group.destroy() + else + squadDispatch(map, group.surface, squad, unitNumber) + end + else + squadDispatch(map, group.surface, squad, unitNumber) + end end end end local function onGroupFinishedGathering(event) + print("15", game.tick) local group = event.group if group.valid then local unitNumber = group.group_number @@ -967,10 +1029,12 @@ local function onGroupFinishedGathering(event) end local function onForceCreated(event) + print("16", game.tick) map.activePlayerForces[#map.activePlayerForces+1] = event.force.name end local function onForceMerged(event) + print("17", game.tick) for i=#map.activePlayerForces,1,-1 do if (map.activePlayerForces[i] == event.source_name) then tRemove(map.activePlayerForces, i) @@ -980,6 +1044,7 @@ local function onForceMerged(event) end local function onSurfaceRenamed(event) + print("18", game.tick) if event.old_name == natives.activeSurface then natives.activeSurface = event.new_name end @@ -990,6 +1055,7 @@ local function onSurfaceRenamed(event) end local function onSurfaceCleared(event) + print("19", game.tick) local surface = game.get_surface(event.surface_index) if surface and surface.valid and (surface.name == natives.activeSurface) then prepWorld(true, natives.activeSurface) @@ -997,6 +1063,7 @@ local function onSurfaceCleared(event) end local function onPlayerChangedSurface(event) + print("20", game.tick) local player = game.players[event.player_index] local surface if player and player.valid and not settings.get_player_settings(player)["rampant-suppress-surface-change-warnings"].value @@ -1020,6 +1087,7 @@ local function onPlayerChangedSurface(event) end local function onSurfaceDeleted(event) + print("21", game.tick) local surface = game.get_surface(event.surface_index) if surface and surface.valid then if (surface.name == natives.activeSurface) then @@ -1035,68 +1103,106 @@ end script.on_nth_tick(INTERVAL_PLAYER_PROCESS, function (event) - + print("22", game.tick) + local profiler = game.create_profiler() local gameRef = game processPlayers(gameRef.connected_players, map, gameRef.get_surface(natives.activeSurface), event.tick) + game.print({"", "player", profiler}) end) script.on_nth_tick(INTERVAL_MAP_PROCESS, function (event) - + print("23", game.tick) + local profiler = game.create_profiler() local gameRef = game - processMap(map, - gameRef.get_surface(natives.activeSurface), - event.tick) + -- processMap(map, + -- gameRef.get_surface(natives.activeSurface), + -- event.tick) + game.print({"", "map", profiler}) end) script.on_nth_tick(INTERVAL_SCAN, function (event) + print("24", game.tick) + local profiler = game.create_profiler() local tick = event.tick local gameRef = game local surface = gameRef.get_surface(natives.activeSurface) - processPendingChunks(map, surface, pendingChunks, tick) + processPendingChunks(map, surface, tick) scanMap(map, surface, tick) processScanChunks(map, surface) + game.print({"", "scan", profiler}) end) script.on_nth_tick(INTERVAL_LOGIC, function (event) + print("25", game.tick) + local profiler = game.create_profiler() local tick = event.tick - planning(natives, game.forces.enemy.evolution_factor, tick) - map.squadIterator = cleanSquads(natives, map.squadIterator) + cleanSquads(natives, map.squadIterator) if natives.newEnemies then recycleBases(natives, tick) end + game.print({"", "logic", profiler}) +end) + +script.on_nth_tick(INTERVAL_SPAWNER, + function (event) + print("26", game.tick) + local profiler = game.create_profiler() + processSpawners(map, + game.get_surface(natives.activeSurface), + event.tick) + game.print({"", "spawners", profiler}) +end) + +script.on_nth_tick(INTERVAL_SQUAD, + function (event) + print("27", game.tick) + local profiler = game.create_profiler() + processVengence(map, + game.get_surface(natives.activeSurface), + event.tick) + game.print({"", "vengence", profiler}) end) script.on_nth_tick(INTERVAL_TEMPERAMENT, function (event) + print("28", game.tick) + local profiler = game.create_profiler() temperamentPlanner(natives) + game.print({"", "temperament", profiler}) end) script.on_nth_tick(INTERVAL_RESQUAD, - function () + function () + print("29", game.tick) + local profiler = game.create_profiler() map.regroupIterator = regroupSquads(natives, map.regroupIterator) + game.print({"", "regroup", profiler}) end) script.on_event(defines.events.on_tick, function (event) - processActiveNests(map, - game.get_surface(natives.activeSurface), - event.tick) + print("30", game.tick) + local profiler = game.create_profiler() + -- processActiveNests(map, + -- game.get_surface(natives.activeSurface), + -- event.tick) + game.print({"", "processActiveNests", profiler}) end) script.on_event(defines.events.on_surface_cleared, onSurfaceCleared) diff --git a/info.json b/info.json index 634704d..b041734 100755 --- a/info.json +++ b/info.json @@ -5,6 +5,6 @@ "title" : "Rampant", "author" : "Veden", "homepage" : "https://forums.factorio.com/viewtopic.php?f=94&t=31445", - "description" : "Improves the enemies tactics by using potential fields/pheromones allowing probing of defenses, retreats, reinforcements, counterattacking, breaching, raids, rallying death cry, and player hunting. Uses blockable biter projectiles. Adds new Enemies (disabled by default). Can completely replace the vanilla AI. Difficulty setting in mod options menu.", + "description" : "Improves the enemies tactics by using potential fields/pheromones allowing probing of defenses, retreats, reinforcements, counterattacking, breaching, raids, rallying death cry, and player hunting. Uses blockable biter projectiles. Adds new Enemies (disabled by default). Difficulty setting in mod options menu.", "dependencies" : ["base >= 0.18.22", "? bobenemies", "? Natural_Evolution_Enemies >= 0.17.0", "? Clockwork", "? Orbital Ion Cannon", "? RampantArsenal", "? RampantResources", "? ArmouredBiters"] } diff --git a/libs/AIAttackWave.lua b/libs/AIAttackWave.lua index 8dccb6d..633f46b 100755 --- a/libs/AIAttackWave.lua +++ b/libs/AIAttackWave.lua @@ -31,7 +31,7 @@ local AI_MAX_SQUAD_COUNT = constants.AI_MAX_SQUAD_COUNT local AI_VENGENCE_SQUAD_COST = constants.AI_VENGENCE_SQUAD_COST local AI_STATE_AGGRESSIVE = constants.AI_STATE_AGGRESSIVE -local INTERVAL_RALLY = constants.INTERVAL_RALLY +local COOLDOWN_RALLY = constants.COOLDOWN_RALLY local CHUNK_ALL_DIRECTIONS = constants.CHUNK_ALL_DIRECTIONS @@ -52,16 +52,12 @@ local randomTickEvent = mathUtils.randomTickEvent local mRandom = math.random -local createSpawnerProxies = baseUtils.createSpawnerProxies - local positionFromDirectionAndChunk = mapUtils.positionFromDirectionAndChunk local getPassable = chunkPropertyUtils.getPassable local getNestCount = chunkPropertyUtils.getNestCount -local getChunkSettlerTick = chunkPropertyUtils.getChunkSettlerTick local getRaidNestActiveness = chunkPropertyUtils.getRaidNestActiveness local getNestActiveness = chunkPropertyUtils.getNestActiveness -local setChunkSettlerTick = chunkPropertyUtils.setChunkSettlerTick local getRallyTick = chunkPropertyUtils.getRallyTick local setRallyTick = chunkPropertyUtils.setRallyTick @@ -121,68 +117,76 @@ local function validUnitGroupLocation(map, neighborChunk) (getNestCount(map, neighborChunk) == 0) 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 - RALLY_CRY_DISTANCE + endX = cX + RALLY_CRY_DISTANCE + stepX = 32 + startY = cY - RALLY_CRY_DISTANCE + endY = cY + RALLY_CRY_DISTANCE + stepY = 32 + elseif (o == 1) then + startX = cX + RALLY_CRY_DISTANCE + endX = cX - RALLY_CRY_DISTANCE + stepX = -32 + startY = cY + RALLY_CRY_DISTANCE + endY = cY - RALLY_CRY_DISTANCE + stepY = -32 + elseif (o == 2) then + startX = cX - RALLY_CRY_DISTANCE + endX = cX + RALLY_CRY_DISTANCE + stepX = 32 + startY = cY + RALLY_CRY_DISTANCE + endY = cY - RALLY_CRY_DISTANCE + stepY = -32 + elseif (o == 3) then + startX = cX + RALLY_CRY_DISTANCE + endX = cX - RALLY_CRY_DISTANCE + stepX = -32 + startY = cY - RALLY_CRY_DISTANCE + endY = cY + RALLY_CRY_DISTANCE + stepY = 32 + end + return startX, endX, stepX, startY, endY, stepY +end + function aiAttackWave.rallyUnits(chunk, map, surface, tick) - if ((tick - getRallyTick(map, chunk) > INTERVAL_RALLY) and (map.natives.points >= AI_VENGENCE_SQUAD_COST)) then + if ((tick - getRallyTick(map, chunk) > COOLDOWN_RALLY) and (map.natives.points >= AI_VENGENCE_SQUAD_COST)) then setRallyTick(map, chunk, tick) local cX = chunk.x local cY = chunk.y - for x=cX - RALLY_CRY_DISTANCE, cX + RALLY_CRY_DISTANCE, 32 do - for y=cY - RALLY_CRY_DISTANCE, cY + RALLY_CRY_DISTANCE, 32 do + local startX, endX, stepX, startY, endY, stepY = visitPattern(tick % 4, cX, cY, RALLY_CRY_DISTANCE) + local vengenceQueue = map.natives.vengenceQueue + 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 (getNestCount(map, rallyChunk) > 0) then - if not aiAttackWave.formVengenceSquad(map, surface, rallyChunk) then - return false + local count = vengenceQueue[rallyChunk] + if not count then + count = 0 + vengenceQueue[rallyChunk] = count end + vengenceQueue[rallyChunk] = count + 1 end end end end + return true end end -function aiAttackWave.formAttackWave(chunk, map, surface, tick) - if (map.natives.points >= AI_SQUAD_COST) then - local cX = chunk.x - local cY = chunk.y - for x=cX - RALLY_CRY_DISTANCE, cX + RALLY_CRY_DISTANCE, 32 do - for y=cY - RALLY_CRY_DISTANCE, cY + RALLY_CRY_DISTANCE, 32 do - if (x ~= cX) and (y ~= cY) then - local rallyChunk = getChunkByXY(map, x, y) - if (rallyChunk ~= -1) and (getNestCount(map, rallyChunk) > 0) then - if not aiAttackWave.formSquads(map, surface, rallyChunk, tick) then - return false - end - end - end - end - end - return true - end - return false -end - -local function noNearbySettlers(map, chunk, tick) - local cX = chunk.x - local cY = chunk.y - for x=cX - SETTLER_DISTANCE, cX + SETTLER_DISTANCE, 32 do - for y=cY - SETTLER_DISTANCE, cY + SETTLER_DISTANCE, 32 do - if (x ~= cX) and (y ~= cY) then - local c = getChunkByXY(map, x, y) - if (c ~= -1) and ((tick - getChunkSettlerTick(map, c)) < 0) then - return false - end - end - end - end - return true -end - function aiAttackWave.formSettlers(map, surface, chunk, tick) - local natives = map.natives - if (mRandom() < natives.formSquadThreshold) and (natives.remainingSquads > 0) then + local natives = map.natives + if (mRandom() < natives.formSquadThreshold) and ((natives.points - AI_SETTLER_COST) > 0) then local squadPath, squadDirection if (natives.state == AI_STATE_SIEGE) then squadPath, squadDirection = scoreNeighborsForFormation(getNeighborChunks(map, chunk.x, chunk.y), @@ -197,7 +201,7 @@ function aiAttackWave.formSettlers(map, surface, chunk, tick) map) end - if (squadPath ~= -1) and noNearbySettlers(map, chunk, tick) then + if (squadPath ~= -1) then local squadPosition = surface.find_non_colliding_position("chunk-scanner-squad-rampant", positionFromDirectionAndChunk(squadDirection, chunk, @@ -214,14 +218,12 @@ function aiAttackWave.formSettlers(map, surface, chunk, tick) 10, natives.expansionMaxDistance) + local scaledWaveSize = settlerWaveScaling(natives) map.formGroupCommand.group = squad.group map.formCommand.unit_count = scaledWaveSize local foundUnits = surface.set_multi_command(map.formCommand) if (foundUnits > 0) then - createSpawnerProxies(map, surface, chunk, foundUnits) - setChunkSettlerTick(map, squadPath, tick + natives.settlerCooldown) - natives.remainingSquads = natives.remainingSquads - 1 natives.points = natives.points - AI_SETTLER_COST natives.groupNumberToSquad[squad.groupNumber] = squad else @@ -232,13 +234,11 @@ function aiAttackWave.formSettlers(map, surface, chunk, tick) end end end - - return ((natives.points - AI_SETTLER_COST) > 0) and (natives.remainingSquads > 0) end function aiAttackWave.formVengenceSquad(map, surface, chunk) local natives = map.natives - if (mRandom() < natives.formSquadThreshold) then + if (mRandom() < natives.formSquadThreshold) and ((natives.points - AI_VENGENCE_SQUAD_COST) > 0) then local squadPath, squadDirection = scoreNeighborsForFormation(getNeighborChunks(map, chunk.x, chunk.y), validUnitGroupLocation, scoreUnitGroupLocation, @@ -262,7 +262,6 @@ function aiAttackWave.formVengenceSquad(map, surface, chunk) map.formCommand.unit_count = scaledWaveSize local foundUnits = surface.set_multi_command(map.formCommand) if (foundUnits > 0) then - createSpawnerProxies(map, surface, chunk, foundUnits) natives.groupNumberToSquad[squad.groupNumber] = squad natives.points = natives.points - AI_VENGENCE_SQUAD_COST else @@ -273,15 +272,13 @@ function aiAttackWave.formVengenceSquad(map, surface, chunk) end end end - - return (natives.points - AI_VENGENCE_SQUAD_COST) > 0 end function aiAttackWave.formSquads(map, surface, chunk, tick) local natives = map.natives if attackWaveValidCandidate(chunk, natives, map) and (mRandom() < natives.formSquadThreshold) and - (natives.remainingSquads > 0) + ((natives.points - AI_SQUAD_COST) > 0) then local squadPath, squadDirection = scoreNeighborsForFormation(getNeighborChunks(map, chunk.x, chunk.y), validUnitGroupLocation, @@ -308,13 +305,11 @@ function aiAttackWave.formSquads(map, surface, chunk, tick) if (foundUnits > 0) then createSpawnerProxies(map, surface, chunk, foundUnits) natives.points = natives.points - AI_SQUAD_COST - natives.remainingSquads = natives.remainingSquads - 1 natives.groupNumberToSquad[squad.groupNumber] = squad if tick and (natives.state == AI_STATE_AGGRESSIVE) then natives.canAttackTick = randomTickEvent(tick, AGGRESSIVE_CAN_ATTACK_WAIT_MIN_DURATION, AGGRESSIVE_CAN_ATTACK_WAIT_MAX_DURATION) - return false end else if (squad.group.valid) then @@ -324,8 +319,6 @@ function aiAttackWave.formSquads(map, surface, chunk, tick) end end end - - return ((natives.points - AI_SQUAD_COST) > 0) and (natives.remainingSquads > 0) end diff --git a/libs/AIPlanning.lua b/libs/AIPlanning.lua index 392f25e..666ba3a 100755 --- a/libs/AIPlanning.lua +++ b/libs/AIPlanning.lua @@ -71,7 +71,7 @@ function aiPlanning.planning(natives, evolution_factor, tick) local attackWaveMaxSize = natives.attackWaveMaxSize natives.retreatThreshold = linearInterpolation(evolution_factor, RETREAT_MOVEMENT_PHEROMONE_LEVEL_MIN, RETREAT_MOVEMENT_PHEROMONE_LEVEL_MAX) natives.rallyThreshold = BASE_RALLY_CHANCE + (evolution_factor * BONUS_RALLY_CHANCE) - natives.formSquadThreshold = mMax((0.25 * evolution_factor), 0.10) + natives.formSquadThreshold = mMax((0.20 * evolution_factor), 0.05) natives.attackWaveSize = attackWaveMaxSize * (evolution_factor ^ 1.15) natives.attackWaveDeviation = (natives.attackWaveSize * 0.333) diff --git a/libs/AIPredicates.lua b/libs/AIPredicates.lua index 2466489..a24eb60 100755 --- a/libs/AIPredicates.lua +++ b/libs/AIPredicates.lua @@ -19,8 +19,10 @@ local AI_STATE_ONSLAUGHT = constants.AI_STATE_ONSLAUGHT -- module code -function aiPredicates.canAttack(natives, surface) - local goodAI = ((natives.state == AI_STATE_AGGRESSIVE) or (natives.state == AI_STATE_RAIDING) or (natives.state == AI_STATE_ONSLAUGHT)) +function aiPredicates.canAttack(natives, surface, tick) + local goodAI = (((natives.state == AI_STATE_AGGRESSIVE) and (natives.canAttackTick > tick)) or + (natives.state == AI_STATE_RAIDING) or + (natives.state == AI_STATE_ONSLAUGHT)) local notPeaceful = not surface.peaceful_mode local noctural = (not natives.aiNocturnalMode) or (natives.aiNocturnalMode and surface.darkness > 0.65) return goodAI and notPeaceful and noctural diff --git a/libs/BaseUtils.lua b/libs/BaseUtils.lua index 9561e33..c0ce704 100755 --- a/libs/BaseUtils.lua +++ b/libs/BaseUtils.lua @@ -76,6 +76,8 @@ local setChunkBase = chunkPropertyUtils.setChunkBase local getResourceGenerator = chunkPropertyUtils.getResourceGenerator +local next = next + local tRemove = table.remove local mRandom = math.random @@ -95,50 +97,6 @@ local function evoToTier(natives, evolutionFactor) return v end -function baseUtils.createSpawnerProxies(map, surface, chunk, foundUnits) - local query = map.placeSpawnerProxyQuery - if (foundUnits >= 60) then - query.name = "spawner-proxy-2-rampant" - elseif (foundUnits >= 120) then - query.name = "spawner-proxy-3-rampant" - else - query.name = "spawner-proxy-1-rampant" - end - local position3 = map.position3 - local chunkX = chunk.x + 5 - local chunkY = chunk.y + 5 - position3.x = chunkX - position3.y = chunkY - local entity = surface.create_entity(query) - entity.destructible = false - position3.x = chunkX+CHUNK_SIZE - position3.y = chunkY - entity = surface.create_entity(query) - entity.destructible = false - position3.x = chunkX-CHUNK_SIZE - position3.y = chunkY - entity = surface.create_entity(query) - entity.destructible = false - position3.x = chunkX - position3.y = chunkY-CHUNK_SIZE - entity = surface.create_entity(query) - entity.destructible = false - position3.x = chunkX - position3.y = chunkY+CHUNK_SIZE - entity = surface.create_entity(query) - entity.destructible = false -end - -function baseUtils.createSpawnerProxy(map, surface, chunk) - local query = map.placeSpawnerProxyQuery - query.name = "spawner-proxy-1-rampant" - local position3 = map.position3 - position3.x = chunk.x + 5 - position3.y = chunk.y + 5 - local entity = surface.create_entity(query) - entity.destructible = false -end - function baseUtils.findNearbyBase(map, chunk) local x = chunk.x local y = chunk.y @@ -150,8 +108,7 @@ function baseUtils.findNearbyBase(map, chunk) local bases = map.natives.bases local closet = MAGIC_MAXIMUM_NUMBER - for i=1, #bases do - local base = bases[i] + for _, base in pairs(bases) do local distance = euclideanDistancePoints(base.x, base.y, x, y) if (distance <= base.distanceThreshold) and (distance < closet) then closet = distance @@ -283,7 +240,8 @@ local function findEntityUpgrade(baseAlignment, currentEvo, evoIndex, originalEn end local function findBaseInitialAlignment(natives, evoIndex) - local evoTop = gaussianRandomRange(evoIndex, evoIndex * 0.3, 0, evoIndex) + local dev = evoIndex * 0.3 + local evoTop = gaussianRandomRange(evoIndex - dev, dev, 0, evoIndex) local result if mRandom() < 0.05 then @@ -298,21 +256,23 @@ end function baseUtils.recycleBases(natives, tick) local baseIndex = natives.baseIndex local bases = natives.bases - - local endIndex = mMin(baseIndex+BASE_QUEUE_SIZE, #bases) - for index = endIndex, baseIndex, -1 do - local base = bases[index] - - if ((tick - base.tick) > BASE_COLLECTION_THRESHOLD) then - tRemove(bases, index) + local id, base = next(bases, natives.map.recycleBaseIterator) + for i=1,2 do + if not id then + natives.map.recycleBaseIterator = nil + return + else + if ((tick - base.tick) > BASE_COLLECTION_THRESHOLD) then + local nextId + nextId, base = next(bases, id) + bases[id] = nil + id = nextId + else + id, base = next(bases, id) + end end end - - if (endIndex == #bases) then - natives.baseIndex = 1 - else - natives.baseIndex = endIndex + 1 - end + natives.map.recycleBaseIterator = id end @@ -495,12 +455,14 @@ function baseUtils.createBase(natives, chunk, tick, rebuilding) temperamentTick = 0, createdTick = tick, temperament = 0, - points = 0 + points = 0, + id = natives.baseId } + natives.baseId = natives.baseId + 1 setChunkBase(natives.map, chunk, base) - natives.bases[#natives.bases+1] = base + natives.bases[base.id] = base return base end @@ -653,8 +615,7 @@ function baseUtils.rebuildNativeTables(natives, surface, rg) local evoIndex = evoToTier(natives, natives.evolutionLevel) - for i=1,#natives.bases do - local base = natives.bases[i] + for id,base in pairs(natives.bases) do for x=1,#base.alignment do local alignment = base.alignment[x] if not natives.buildingEvolveLookup[alignment] then diff --git a/libs/ChunkProcessor.lua b/libs/ChunkProcessor.lua index 62dd9e8..8573ea3 100755 --- a/libs/ChunkProcessor.lua +++ b/libs/ChunkProcessor.lua @@ -27,6 +27,8 @@ local euclideanDistanceNamed = mathUtils.euclideanDistanceNamed local tSort = table.sort local abs = math.abs +local next = next +local table_size = table_size local tRemove = table.remove @@ -49,49 +51,67 @@ local function sorter(a, b) return (aDistance < bDistance) end -function chunkProcessor.processPendingChunks(map, surface, pendingStack, tick, rebuilding) +function chunkProcessor.processPendingChunks(map, surface, tick, rebuilding, flush) + local profiler = game.create_profiler() local processQueue = map.processQueue + local pendingChunks = map.pendingChunks local area = map.area local topOffset = area[1] local bottomOffset = area[2] - for i=#pendingStack, 1, -1 do - local event = pendingStack[i] - pendingStack[i] = nil - - local topLeft = event.area.left_top - local x = topLeft.x - local y = topLeft.y - - topOffset[1] = x - topOffset[2] = y - bottomOffset[1] = x + CHUNK_SIZE - bottomOffset[2] = y + CHUNK_SIZE - - if map[x] and map[x][y] then - mapScanChunk(map[x][y], surface, map) + local event = next(pendingChunks, map.chunkProcessorIterator) + local endCount = 5 + if flush then + endCount = table_size(pendingChunks) + end + for i=1,endCount do + if not event then + map.chunkProcessorIterator = nil + if (table_size(pendingChunks) == 0) then + -- this is needed as the next command remembers the max length a table has been + map.pendingChunks = {} + end + break else - if map[x] == nil then - map[x] = {} - end - - local chunk = createChunk(x, y) - - chunk = initialScan(chunk, surface, map, tick, rebuilding) - - if (chunk ~= -1) then - map[x][y] = chunk - processQueue[#processQueue+1] = chunk + local topLeft = event.area.left_top + local x = topLeft.x + local y = topLeft.y + + topOffset[1] = x + topOffset[2] = y + bottomOffset[1] = x + CHUNK_SIZE + bottomOffset[2] = y + CHUNK_SIZE + + if map[x] and map[x][y] then + mapScanChunk(map[x][y], surface, map) + else + if map[x] == nil then + map[x] = {} + end + + local chunk = createChunk(x, y) + + chunk = initialScan(chunk, surface, map, tick, rebuilding) + + if (chunk ~= -1) then + map[x][y] = chunk + processQueue[#processQueue+1] = chunk + end end + local newEvent,_ = next(pendingChunks, event) + pendingChunks[event] = nil + event = newEvent end end + map.chunkProcessorIterator = event if (#processQueue > map.nextChunkSort) or - (((tick - map.nextChunkSortTick) > MAX_TICKS_BEFORE_SORT_CHUNKS) and ((map.nextChunkSort - 75) ~= #processQueue)) + (((tick - map.nextChunkSortTick) > MAX_TICKS_BEFORE_SORT_CHUNKS) and + ((map.nextChunkSort - 150) < #processQueue)) then - map.nextChunkSort = #processQueue + 75 + map.nextChunkSort = #processQueue + 150 map.nextChunkSortTick = tick tSort(processQueue, sorter) end diff --git a/libs/ChunkPropertyUtils.lua b/libs/ChunkPropertyUtils.lua index bec4187..5dd35e2 100755 --- a/libs/ChunkPropertyUtils.lua +++ b/libs/ChunkPropertyUtils.lua @@ -79,23 +79,14 @@ end function chunkPropertyUtils.setNestCount(map, chunk, count) if (count <= 0) then map.chunkToNests[chunk] = nil + if (map.processMigrationIterator == chunk) then + map.processMigrationIterator = nil + end else map.chunkToNests[chunk] = count end end -function chunkPropertyUtils.getChunkSettlerTick(map, chunk) - return map.chunkToSettler[chunk] or 0 -end - -function chunkPropertyUtils.setChunkSettlerTick(map, chunk, tick) - if (tick <= 0) then - map.chunkToSettler[chunk] = nil - else - map.chunkToSettler[chunk] = tick - end -end - function chunkPropertyUtils.getNestCount(map, chunk) return map.chunkToNests[chunk] or 0 end @@ -163,6 +154,9 @@ function chunkPropertyUtils.setRaidNestActiveness(map, chunk, value) if (map.chunkToActiveRaidNest[chunk] ~= nil) then map.natives.activeRaidNests = map.natives.activeRaidNests - 1 end + if (map.processActiveRaidSpawnerIterator == chunk) then + map.processActiveRaidSpawnerIterator = nil + end map.chunkToActiveRaidNest[chunk] = nil else if (map.chunkToActiveRaidNest[chunk] == nil) then @@ -193,6 +187,9 @@ function chunkPropertyUtils.setNestActiveness(map, chunk, value) if (map.chunkToActiveNest[chunk] ~= nil) then map.natives.activeNests = map.natives.activeNests - 1 end + if (map.processActiveSpawnerIterator == chunk) then + map.processActiveSpawnerIterator = nil + end map.chunkToActiveNest[chunk] = nil else if (map.chunkToActiveNest[chunk] == nil) then diff --git a/libs/Constants.lua b/libs/Constants.lua index 21794f1..a9a577f 100755 --- a/libs/Constants.lua +++ b/libs/Constants.lua @@ -58,15 +58,15 @@ constants.CHUNK_PASS_THRESHOLD = 0.25 constants.INTERVAL_PLAYER_PROCESS = (settings.startup["rampant-liteMode"].value and 124) or 62 constants.INTERVAL_MAP_PROCESS = (settings.startup["rampant-liteMode"].value and 8) or 5 constants.INTERVAL_SCAN = (settings.startup["rampant-liteMode"].value and 42) or 21 -constants.INTERVAL_CHUNK = 17 constants.INTERVAL_LOGIC = 60 constants.INTERVAL_TEMPERAMENT = 121 -constants.INTERVAL_SQUAD = 41 +constants.INTERVAL_SQUAD = 17 constants.INTERVAL_RESQUAD = 101 +constants.INTERVAL_SPAWNER = 31 constants.INTERVAL_BUILDERS = 300 -constants.INTERVAL_SPAWNER = constants.TICKS_A_SECOND * 10 -constants.INTERVAL_RALLY = constants.TICKS_A_SECOND * 10 -constants.INTERVAL_RETREAT = constants.TICKS_A_SECOND * 10 + +constants.COOLDOWN_RALLY = constants.TICKS_A_SECOND * 10 +constants.COOLDOWN_RETREAT = constants.TICKS_A_SECOND * 10 constants.RESOURCE_NORMALIZER = 1 / 1024 diff --git a/libs/MapProcessor.lua b/libs/MapProcessor.lua index e776323..091e622 100755 --- a/libs/MapProcessor.lua +++ b/libs/MapProcessor.lua @@ -36,15 +36,12 @@ local AI_VENGENCE_SQUAD_COST = constants.AI_VENGENCE_SQUAD_COST local AI_SETTLER_COST = constants.AI_SETTLER_COST local AI_STATE_AGGRESSIVE = constants.AI_STATE_AGGRESSIVE -local processNestActiveness = chunkPropertyUtils.processNestActiveness - local MOVEMENT_PHEROMONE = constants.MOVEMENT_PHEROMONE local PLAYER_PHEROMONE = constants.PLAYER_PHEROMONE local BASE_PHEROMONE = constants.BASE_PHEROMONE -local INTERVAL_RALLY = constants.INTERVAL_RALLY -local INTERVAL_RETREAT = constants.INTERVAL_RETREAT -local INTERVAL_SPAWNER = constants.INTERVAL_SPAWNER +local COOLDOWN_RALLY = constants.COOLDOWN_RALLY +local COOLDOWN_RETREAT = constants.COOLDOWN_RETREAT local BASE_PROCESS_INTERVAL = constants.BASE_PROCESS_INTERVAL @@ -52,15 +49,17 @@ local AI_MAX_SQUADS_PER_CYCLE = constants.AI_MAX_SQUADS_PER_CYCLE -- imported functions --- local scents = pheromoneUtils.scents local processPheromone = pheromoneUtils.processPheromone local commitPheromone = pheromoneUtils.commitPheromone local playerScent = pheromoneUtils.playerScent +local processBase = baseUtils.processBase + +local processNestActiveness = chunkPropertyUtils.processNestActiveness + local formSquads = aiAttackWave.formSquads -local formAttackWave = aiAttackWave.formAttackWave -local formSettlers = aiAttackWave.formSettlers local formVengenceSquad = aiAttackWave.formVengenceSquad +local formSettlers = aiAttackWave.formSettlers local getChunkByPosition = mapUtils.getChunkByPosition local getChunkByXY = mapUtils.getChunkByXY @@ -89,11 +88,9 @@ local canMigrate = aiPredicates.canMigrate local findNearbySquad = unitGroupUtils.findNearbySquad -local processBase = baseUtils.processBase - local mMin = math.min --- local mMax = math.max +local next = next local mRandom = math.random -- module code @@ -109,47 +106,21 @@ function mapProcessor.processMap(map, surface, tick) local roll = map.processRoll local index = map.processIndex - local chunkToBase = map.chunkToBase - - local natives = map.natives - if (index == 1) then roll = mRandom() map.processRoll = roll - natives.remainingSquads = AI_MAX_SQUADS_PER_CYCLE end - local newEnemies = natives.newEnemies local scentStaging = map.scentStaging - local squads = canAttack(natives, surface) and (roll <= 0.45) and (natives.points >= AI_SQUAD_COST) - if squads and (natives.state == AI_STATE_AGGRESSIVE) and (tick < natives.canAttackTick) then - squads = false - end - local settlers = canMigrate(natives, surface) and (roll <= 0.45) and (natives.points >= AI_SETTLER_COST) - local processQueue = map.processQueue local endIndex = mMin(index + PROCESS_QUEUE_SIZE, #processQueue) local i = 1 + for x=index,endIndex do local chunk = processQueue[x] - if (chunk[CHUNK_TICK] ~= tick) then processPheromone(map, chunk, scentStaging[i]) - - if settlers and (getNestCount(map, chunk) > 0) then - settlers = formSettlers(map, surface, chunk, tick) - end - if squads then - squads = formAttackWave(chunk, map, surface, tick) - end - - if newEnemies then - local base = chunkToBase[chunk] - if base and ((tick - base.tick) > BASE_PROCESS_INTERVAL) then - processBase(chunk, surface, natives, tick, base) - end - end end i = i + 1 end @@ -170,12 +141,11 @@ function mapProcessor.processMap(map, surface, tick) end end - local function queueNestSpawners(map, chunk, tick) local limitPerActiveChunkTick = map.natives.activeNests * DURATION_ACTIVE_NEST_DIVIDER local processActiveNest = map.processActiveNest - + if ((getNestActiveness(map, chunk) > 0) and (getNestActiveTick(map, chunk) == 0)) then local nextTick = tick + DURATION_ACTIVE_NEST local slot = processActiveNest[nextTick] @@ -223,8 +193,6 @@ function mapProcessor.processPlayers(players, map, surface, tick) local scentStaging = map.scentStaging - local squads = allowingAttacks and (0.11 <= roll) and (roll <= 0.20) and (natives.points >= AI_SQUAD_COST) - -- not looping everyone because the cost is high enough already in multiplayer if (#players > 0) then local player = players[mRandom(#players)] @@ -233,9 +201,10 @@ function mapProcessor.processPlayers(players, map, surface, tick) if (playerChunk ~= -1) then local i = 1 - local vengence = (allowingAttacks and - (natives.points >= AI_VENGENCE_SQUAD_COST) and - ((getEnemyStructureCount(map, playerChunk) > 0) or (playerChunk[MOVEMENT_PHEROMONE] < -natives.retreatThreshold))) + local vengence = allowingAttacks and + (natives.points >= AI_VENGENCE_SQUAD_COST) and + ((getEnemyStructureCount(map, playerChunk) > 0) or + (playerChunk[MOVEMENT_PHEROMONE] < -natives.retreatThreshold)) for x=playerChunk.x - PROCESS_PLAYER_BOUND, playerChunk.x + PROCESS_PLAYER_BOUND, 32 do for y=playerChunk.y - PROCESS_PLAYER_BOUND, playerChunk.y + PROCESS_PLAYER_BOUND, 32 do @@ -246,9 +215,14 @@ function mapProcessor.processPlayers(players, map, surface, tick) processNestActiveness(map, chunk, natives, surface) queueNestSpawners(map, chunk, tick) - + if vengence and (getNestCount(map, chunk) > 0) then - vengence = formVengenceSquad(map, surface, chunk) + local count = natives.vengenceQueue[chunk] + if not count then + count = 0 + natives.vengenceQueue[chunk] = count + end + natives.vengenceQueue[chunk] = count + 1 end end i = i + 1 @@ -293,8 +267,6 @@ function mapProcessor.scanMap(map, surface, tick) local retreats = map.chunkToRetreats local rallys = map.chunkToRallys - -- local spawners = map.chunkToSpawner - local settlers = map.chunkToSettler local drained = map.chunkToDrained local processQueue = map.processQueue @@ -314,20 +286,15 @@ function mapProcessor.scanMap(map, surface, tick) offset[2] = chunk.y + CHUNK_SIZE local retreatTick = retreats[chunk] - if retreatTick and ((tick - retreatTick) > INTERVAL_RETREAT) then + if retreatTick and ((tick - retreatTick) > COOLDOWN_RETREAT) then retreats[chunk] = nil end local rallyTick = rallys[chunk] - if rallyTick and ((tick - rallyTick) > INTERVAL_RALLY) then + if rallyTick and ((tick - rallyTick) > COOLDOWN_RALLY) then rallys[chunk] = nil end - local settlerTick = settlers[chunk] - if settlerTick and ((tick - settlerTick) > 0) then - settlers[chunk] = nil - end - local drainTick = drained[chunk] if drainTick and ((tick - drainTick) > 0) then drained[chunk] = nil @@ -362,11 +329,11 @@ function mapProcessor.processActiveNests(map, surface, tick) local processActiveNest = map.processActiveNest local slot = processActiveNest[tick] if slot then + local natives = map.natives for i=1,#slot do local chunk = slot[i] if (getNestActiveness(map, chunk) > 0) then - createSpawnerProxy(map, surface, chunk) - -- setNestActiveTick(map, chunk, tick) + processNestActiveness(map, chunk, natives, surface) local nextTick = tick + DURATION_ACTIVE_NEST local nextSlot = processActiveNest[nextTick] if not nextSlot then @@ -382,7 +349,75 @@ function mapProcessor.processActiveNests(map, surface, tick) end end +function mapProcessor.processVengence(map, surface, tick) + local natives = map.natives + local ss = natives.vengenceQueue + -- local generated = 0 + local chunk, count = next(ss, map.deployVengenceIterator) + -- for i=1,3 do + if not chunk then + map.deployVengenceIterator = nil + -- break + else + -- generated = generated + 1 + formVengenceSquad(map, surface, chunk) + local nextChunk + nextChunk, count = next(ss, chunk) + ss[chunk] = nil + chunk = nextChunk + end + -- end + map.deployVengenceIterator = chunk +end + +local function processSpawners(map, surface, tick, natives, iteration, iterator, chunks) + + local bases = map.chunkToBase + local chunk, count = next(chunks, map[iterator]) + for i=1,iteration do + if not chunk then + map[iterator] = nil + return + else + if canMigrate(natives, surface) then + formSettlers(map, surface, chunk, tick) + elseif (canAttack(natives, surface, tick)) then + formSquads(map, surface, chunk, tick) + end + + + if (natives.newEnemies) then + local base = bases[chunk] + if base and ((tick - base.tick) > BASE_PROCESS_INTERVAL) then + processBase(chunk, surface, natives, tick, base) + end + end + + chunk, count = next(chunks, chunk) + end + end + map[iterator] = chunk +end + +function mapProcessor.processSpawners(map, surface, tick) + local chunks + local natives = map.natives + + if (natives.state ~= AI_STATE_PEACEFUL) then + if (natives.state == AI_STATE_MIGRATING) or + ((natives.state == AI_STATE_SIEGE) and natives.temperament <= 0.5) + then + processSpawners(map, surface, tick, natives, 2, "processMigrationIterator", map.chunkToNests) + else + if (natives.state ~= AI_STATE_AGGRESSIVE) then + processSpawners(map, surface, tick, natives, 1, "processActiveSpawnerIterator", map.chunkToActiveNest) + processSpawners(map, surface, tick, natives, 1, "processActiveRaidSpawnerIterator", map.chunkToActiveRaidNest) + else + processSpawners(map, surface, tick, natives, 2, "processActiveSpawnerIterator", map.chunkToActiveNest) + end + end + end +end + mapProcessorG = mapProcessor return mapProcessor - - diff --git a/libs/MovementUtils.lua b/libs/MovementUtils.lua index 5ac61c7..1082c9f 100755 --- a/libs/MovementUtils.lua +++ b/libs/MovementUtils.lua @@ -31,25 +31,25 @@ local distortPosition = mathUtils.distortPosition function movementUtils.findMovementPosition(surface, position) local pos = position - if not surface.can_place_entity({name="chunk-scanner-squad-movement-rampant", position=pos}) then - pos = surface.find_non_colliding_position("chunk-scanner-squad-movement-rampant", pos, 15, 2, true) - end + -- if not surface.can_place_entity({name="chunk-scanner-squad-movement-rampant", position=pos}) then + pos = surface.find_non_colliding_position("chunk-scanner-squad-movement-rampant", pos, 10, 2, false) + -- end return pos end function movementUtils.findMovementPositionEntity(entityName, surface, position) local pos = position - if not surface.can_place_entity({name=entityName, position=pos}) then + -- if not surface.can_place_entity({name=entityName, position=pos}) then pos = surface.find_non_colliding_position(entityName, pos, 5, 4, true) - end + -- end return pos end function movementUtils.findMovementPositionDistort(surface, position) local pos = position - if not surface.can_place_entity({name="chunk-scanner-squad-movement-rampant", position=pos}) then - pos = surface.find_non_colliding_position("chunk-scanner-squad-movement-rampant", pos, 15, 2, true) - end + -- if not surface.can_place_entity({name="chunk-scanner-squad-movement-rampant", position=pos}) then + pos = surface.find_non_colliding_position("chunk-scanner-squad-movement-rampant", pos, 10, 2, false) + -- end return distortPosition(pos, 8) end @@ -176,7 +176,6 @@ function movementUtils.scoreNeighborsForSettling(map, chunk, neighborDirectionCh neighborDirectionChunks = getNeighborChunks(map, highestChunk.x, highestChunk.y) for x=1,8 do local neighborChunk = neighborDirectionChunks[x] - if ((neighborChunk ~= -1) and (neighborChunk ~= chunk) and canMoveChunkDirection(map, x, highestChunk, neighborChunk)) then local score = scoreFunction(squad, neighborChunk) diff --git a/libs/PheromoneUtils.lua b/libs/PheromoneUtils.lua index 3ce11e0..2223c9a 100755 --- a/libs/PheromoneUtils.lua +++ b/libs/PheromoneUtils.lua @@ -104,7 +104,7 @@ function pheromoneUtils.processPheromone(map, chunk, staging) local baseTotal = 0 local playerTotal = 0 local resourceTotal = 0 - + local neighborCount = 0 local neighbor @@ -113,151 +113,175 @@ function pheromoneUtils.processPheromone(map, chunk, staging) local chunkPass = getPassable(map, chunk) if (chunkPass == CHUNK_ALL_DIRECTIONS) then - neighbor = tempNeighbors[2] - neighborPass = getPassable(map, neighbor) - if ((neighbor ~= -1) and - ((neighborPass == CHUNK_ALL_DIRECTIONS) or (neighborPass == CHUNK_NORTH_SOUTH))) then - neighborCount = neighborCount + 1 - movementTotal = movementTotal + (neighbor[MOVEMENT_PHEROMONE] - chunkMovement) - baseTotal = baseTotal + (neighbor[BASE_PHEROMONE] - chunkBase) - playerTotal = playerTotal + (neighbor[PLAYER_PHEROMONE] - chunkPlayer) - resourceTotal = resourceTotal + (neighbor[RESOURCE_PHEROMONE] - chunkResource) + neighbor = tempNeighbors[2] + if (neighbor ~= -1) then + neighborPass = getPassable(map, neighbor) + if ((neighborPass == CHUNK_ALL_DIRECTIONS) or (neighborPass == CHUNK_NORTH_SOUTH)) then + neighborCount = neighborCount + 1 + -- movementTotal = movementTotal + (neighbor[MOVEMENT_PHEROMONE] - chunkMovement) + -- baseTotal = baseTotal + (neighbor[BASE_PHEROMONE] - chunkBase) + -- playerTotal = playerTotal + (neighbor[PLAYER_PHEROMONE] - chunkPlayer) + -- resourceTotal = resourceTotal + (neighbor[RESOURCE_PHEROMONE] - chunkResource) + movementTotal = movementTotal + neighbor[MOVEMENT_PHEROMONE] + baseTotal = baseTotal + neighbor[BASE_PHEROMONE] + playerTotal = playerTotal + neighbor[PLAYER_PHEROMONE] + resourceTotal = resourceTotal + neighbor[RESOURCE_PHEROMONE] + end end neighbor = tempNeighbors[7] - neighborPass = getPassable(map, neighbor) - if ((neighbor ~= -1) and - ((neighborPass == CHUNK_ALL_DIRECTIONS) or (neighborPass == CHUNK_NORTH_SOUTH))) then - neighborCount = neighborCount + 1 - movementTotal = movementTotal + (neighbor[MOVEMENT_PHEROMONE] - chunkMovement) - baseTotal = baseTotal + (neighbor[BASE_PHEROMONE] - chunkBase) - playerTotal = playerTotal + (neighbor[PLAYER_PHEROMONE] - chunkPlayer) - resourceTotal = resourceTotal + (neighbor[RESOURCE_PHEROMONE] - chunkResource) + if (neighbor ~= -1) then + neighborPass = getPassable(map, neighbor) + if ((neighborPass == CHUNK_ALL_DIRECTIONS) or (neighborPass == CHUNK_NORTH_SOUTH)) then + neighborCount = neighborCount + 1 + movementTotal = movementTotal + neighbor[MOVEMENT_PHEROMONE] + baseTotal = baseTotal + neighbor[BASE_PHEROMONE] + playerTotal = playerTotal + neighbor[PLAYER_PHEROMONE] + resourceTotal = resourceTotal + neighbor[RESOURCE_PHEROMONE] + end end neighbor = tempNeighbors[4] - neighborPass = getPassable(map, neighbor) - if ((neighbor ~= -1) and - ((neighborPass == CHUNK_ALL_DIRECTIONS) or (neighborPass == CHUNK_EAST_WEST))) then - neighborCount = neighborCount + 1 - movementTotal = movementTotal + (neighbor[MOVEMENT_PHEROMONE] - chunkMovement) - baseTotal = baseTotal + (neighbor[BASE_PHEROMONE] - chunkBase) - playerTotal = playerTotal + (neighbor[PLAYER_PHEROMONE] - chunkPlayer) - resourceTotal = resourceTotal + (neighbor[RESOURCE_PHEROMONE] - chunkResource) + if (neighbor ~= -1) then + neighborPass = getPassable(map, neighbor) + if ((neighborPass == CHUNK_ALL_DIRECTIONS) or (neighborPass == CHUNK_EAST_WEST)) then + neighborCount = neighborCount + 1 + movementTotal = movementTotal + neighbor[MOVEMENT_PHEROMONE] + baseTotal = baseTotal + neighbor[BASE_PHEROMONE] + playerTotal = playerTotal + neighbor[PLAYER_PHEROMONE] + resourceTotal = resourceTotal + neighbor[RESOURCE_PHEROMONE] + end end neighbor = tempNeighbors[5] - neighborPass = getPassable(map, neighbor) - if ((neighbor ~= -1) and - ((neighborPass == CHUNK_ALL_DIRECTIONS) or (neighborPass == CHUNK_EAST_WEST))) then - neighborCount = neighborCount + 1 - movementTotal = movementTotal + (neighbor[MOVEMENT_PHEROMONE] - chunkMovement) - baseTotal = baseTotal + (neighbor[BASE_PHEROMONE] - chunkBase) - playerTotal = playerTotal + neighbor[PLAYER_PHEROMONE] - chunkPlayer - resourceTotal = resourceTotal + (neighbor[RESOURCE_PHEROMONE] - chunkResource) + if (neighbor ~= -1) then + neighborPass = getPassable(map, neighbor) + if ((neighborPass == CHUNK_ALL_DIRECTIONS) or (neighborPass == CHUNK_EAST_WEST)) then + neighborCount = neighborCount + 1 + movementTotal = movementTotal + neighbor[MOVEMENT_PHEROMONE] + baseTotal = baseTotal + neighbor[BASE_PHEROMONE] + playerTotal = playerTotal + neighbor[PLAYER_PHEROMONE] + resourceTotal = resourceTotal + neighbor[RESOURCE_PHEROMONE] + end end neighbor = tempNeighbors[1] - neighborPass = getPassable(map, neighbor) - if (neighbor ~= -1) and (neighborPass == CHUNK_ALL_DIRECTIONS) then - neighborCount = neighborCount + 1 - movementTotal = movementTotal + (neighbor[MOVEMENT_PHEROMONE] - chunkMovement) - baseTotal = baseTotal + (neighbor[BASE_PHEROMONE] - chunkBase) - playerTotal = playerTotal + neighbor[PLAYER_PHEROMONE] - chunkPlayer - resourceTotal = resourceTotal + (neighbor[RESOURCE_PHEROMONE] - chunkResource) + if (neighbor ~= -1) then + neighborPass = getPassable(map, neighbor) + if (neighborPass == CHUNK_ALL_DIRECTIONS) then + neighborCount = neighborCount + 1 + movementTotal = movementTotal + neighbor[MOVEMENT_PHEROMONE] + baseTotal = baseTotal + neighbor[BASE_PHEROMONE] + playerTotal = playerTotal + neighbor[PLAYER_PHEROMONE] + resourceTotal = resourceTotal + neighbor[RESOURCE_PHEROMONE] + end end neighbor = tempNeighbors[3] - neighborPass = getPassable(map, neighbor) - if (neighbor ~= -1) and (neighborPass == CHUNK_ALL_DIRECTIONS) then - neighborCount = neighborCount + 1 - movementTotal = movementTotal + (neighbor[MOVEMENT_PHEROMONE] - chunkMovement) - baseTotal = baseTotal + (neighbor[BASE_PHEROMONE] - chunkBase) - playerTotal = playerTotal + (neighbor[PLAYER_PHEROMONE] - chunkPlayer) - resourceTotal = resourceTotal + (neighbor[RESOURCE_PHEROMONE] - chunkResource) + if (neighbor ~= -1) then + neighborPass = getPassable(map, neighbor) + if (neighborPass == CHUNK_ALL_DIRECTIONS) then + neighborCount = neighborCount + 1 + movementTotal = movementTotal + neighbor[MOVEMENT_PHEROMONE] + baseTotal = baseTotal + neighbor[BASE_PHEROMONE] + playerTotal = playerTotal + neighbor[PLAYER_PHEROMONE] + resourceTotal = resourceTotal + neighbor[RESOURCE_PHEROMONE] + end end neighbor = tempNeighbors[6] - neighborPass = getPassable(map, neighbor) - if (neighbor ~= -1) and (neighborPass == CHUNK_ALL_DIRECTIONS) then - neighborCount = neighborCount + 1 - movementTotal = movementTotal + (neighbor[MOVEMENT_PHEROMONE] - chunkMovement) - baseTotal = baseTotal + (neighbor[BASE_PHEROMONE] - chunkBase) - playerTotal = playerTotal + (neighbor[PLAYER_PHEROMONE] - chunkPlayer) - resourceTotal = resourceTotal + (neighbor[RESOURCE_PHEROMONE] - chunkResource) + if (neighbor ~= -1) then + neighborPass = getPassable(map, neighbor) + if (neighborPass == CHUNK_ALL_DIRECTIONS) then + neighborCount = neighborCount + 1 + movementTotal = movementTotal + neighbor[MOVEMENT_PHEROMONE] + baseTotal = baseTotal + neighbor[BASE_PHEROMONE] + playerTotal = playerTotal + neighbor[PLAYER_PHEROMONE] + resourceTotal = resourceTotal + neighbor[RESOURCE_PHEROMONE] + end end neighbor = tempNeighbors[8] - neighborPass = getPassable(map, neighbor) - if (neighbor ~= -1) and (neighborPass == CHUNK_ALL_DIRECTIONS) then - neighborCount = neighborCount + 1 - movementTotal = movementTotal + (neighbor[MOVEMENT_PHEROMONE] - chunkMovement) - baseTotal = baseTotal + (neighbor[BASE_PHEROMONE] - chunkBase) - playerTotal = playerTotal + (neighbor[PLAYER_PHEROMONE] - chunkPlayer) - resourceTotal = resourceTotal + (neighbor[RESOURCE_PHEROMONE] - chunkResource) + if (neighbor ~= -1) then + neighborPass = getPassable(map, neighbor) + if (neighborPass == CHUNK_ALL_DIRECTIONS) then + neighborCount = neighborCount + 1 + movementTotal = movementTotal + neighbor[MOVEMENT_PHEROMONE] + baseTotal = baseTotal + neighbor[BASE_PHEROMONE] + playerTotal = playerTotal + neighbor[PLAYER_PHEROMONE] + resourceTotal = resourceTotal + neighbor[RESOURCE_PHEROMONE] + end end elseif (chunkPass == CHUNK_EAST_WEST) then neighbor = tempNeighbors[4] - neighborPass = getPassable(map, neighbor) - if ((neighbor ~= -1) and - ((neighborPass == CHUNK_ALL_DIRECTIONS) or (neighborPass == CHUNK_EAST_WEST))) then - neighborCount = neighborCount + 1 - movementTotal = movementTotal + (neighbor[MOVEMENT_PHEROMONE] - chunkMovement) - baseTotal = baseTotal + (neighbor[BASE_PHEROMONE] - chunkBase) - playerTotal = playerTotal + (neighbor[PLAYER_PHEROMONE] - chunkPlayer) - resourceTotal = resourceTotal + (neighbor[RESOURCE_PHEROMONE] - chunkResource) + if (neighbor ~= -1) then + neighborPass = getPassable(map, neighbor) + if ((neighborPass == CHUNK_ALL_DIRECTIONS) or (neighborPass == CHUNK_EAST_WEST)) then + neighborCount = neighborCount + 1 + movementTotal = movementTotal + neighbor[MOVEMENT_PHEROMONE] + baseTotal = baseTotal + neighbor[BASE_PHEROMONE] + playerTotal = playerTotal + neighbor[PLAYER_PHEROMONE] + resourceTotal = resourceTotal + neighbor[RESOURCE_PHEROMONE] + end end neighbor = tempNeighbors[5] - neighborPass = getPassable(map, neighbor) - if ((neighbor ~= -1) and - ((neighborPass == CHUNK_ALL_DIRECTIONS) or (neighborPass == CHUNK_EAST_WEST))) then - neighborCount = neighborCount + 1 - movementTotal = movementTotal + (neighbor[MOVEMENT_PHEROMONE] - chunkMovement) - baseTotal = baseTotal + (neighbor[BASE_PHEROMONE] - chunkBase) - playerTotal = playerTotal + neighbor[PLAYER_PHEROMONE] - chunkPlayer - resourceTotal = resourceTotal + (neighbor[RESOURCE_PHEROMONE] - chunkResource) + if (neighbor ~= -1) then + neighborPass = getPassable(map, neighbor) + if ((neighborPass == CHUNK_ALL_DIRECTIONS) or (neighborPass == CHUNK_EAST_WEST)) then + neighborCount = neighborCount + 1 + movementTotal = movementTotal + neighbor[MOVEMENT_PHEROMONE] + baseTotal = baseTotal + neighbor[BASE_PHEROMONE] + playerTotal = playerTotal + neighbor[PLAYER_PHEROMONE] + resourceTotal = resourceTotal + neighbor[RESOURCE_PHEROMONE] + end end elseif (chunkPass == CHUNK_NORTH_SOUTH) then neighbor = tempNeighbors[2] - neighborPass = getPassable(map, neighbor) - if ((neighbor ~= -1) and - ((neighborPass == CHUNK_ALL_DIRECTIONS) or (neighborPass == CHUNK_NORTH_SOUTH))) then - neighborCount = neighborCount + 1 - movementTotal = movementTotal + (neighbor[MOVEMENT_PHEROMONE] - chunkMovement) - baseTotal = baseTotal + (neighbor[BASE_PHEROMONE] - chunkBase) - playerTotal = playerTotal + (neighbor[PLAYER_PHEROMONE] - chunkPlayer) - resourceTotal = resourceTotal + (neighbor[RESOURCE_PHEROMONE] - chunkResource) + if (neighbor ~= -1) then + neighborPass = getPassable(map, neighbor) + if ((neighborPass == CHUNK_ALL_DIRECTIONS) or (neighborPass == CHUNK_NORTH_SOUTH)) then + neighborCount = neighborCount + 1 + movementTotal = movementTotal + neighbor[MOVEMENT_PHEROMONE] + baseTotal = baseTotal + neighbor[BASE_PHEROMONE] + playerTotal = playerTotal + neighbor[PLAYER_PHEROMONE] + resourceTotal = resourceTotal + neighbor[RESOURCE_PHEROMONE] + end end neighbor = tempNeighbors[7] - neighborPass = getPassable(map, neighbor) - if ((neighbor ~= -1) and - ((neighborPass == CHUNK_ALL_DIRECTIONS) or (neighborPass == CHUNK_NORTH_SOUTH))) then - neighborCount = neighborCount + 1 - movementTotal = movementTotal + (neighbor[MOVEMENT_PHEROMONE] - chunkMovement) - baseTotal = baseTotal + (neighbor[BASE_PHEROMONE] - chunkBase) - playerTotal = playerTotal + (neighbor[PLAYER_PHEROMONE] - chunkPlayer) - resourceTotal = resourceTotal + (neighbor[RESOURCE_PHEROMONE] - chunkResource) + if (neighbor ~= -1) then + neighborPass = getPassable(map, neighbor) + if ((neighborPass == CHUNK_ALL_DIRECTIONS) or (neighborPass == CHUNK_NORTH_SOUTH)) then + neighborCount = neighborCount + 1 + movementTotal = movementTotal + neighbor[MOVEMENT_PHEROMONE] + baseTotal = baseTotal + neighbor[BASE_PHEROMONE] + playerTotal = playerTotal + neighbor[PLAYER_PHEROMONE] + resourceTotal = resourceTotal + neighbor[RESOURCE_PHEROMONE] + end end end local neighborDiv = 0 if neighborCount ~= 0 then neighborDiv = NEIGHBOR_DIVIDER[neighborCount] + chunkMovement = chunkMovement + (neighborDiv * (movementTotal - (chunkMovement * neighborCount))) + chunkBase = chunkBase + (neighborDiv * (baseTotal - (chunkBase * neighborCount))) + chunkPlayer = chunkPlayer + (neighborDiv * (playerTotal - (chunkPlayer * neighborCount))) + chunkResource = chunkResource + (neighborDiv * (resourceTotal - (chunkResource * neighborCount))) end - staging[MOVEMENT_PHEROMONE] = (chunkMovement + (neighborDiv * movementTotal)) * MOVEMENT_PHEROMONE_PERSISTANCE * chunkPathRating - staging[BASE_PHEROMONE] = (chunkBase + (neighborDiv * baseTotal)) * BASE_PHEROMONE_PERSISTANCE * chunkPathRating - staging[PLAYER_PHEROMONE] = (chunkPlayer + (neighborDiv * playerTotal)) * PLAYER_PHEROMONE_PERSISTANCE * chunkPathRating + staging[MOVEMENT_PHEROMONE] = chunkMovement * MOVEMENT_PHEROMONE_PERSISTANCE * chunkPathRating + staging[BASE_PHEROMONE] = chunkBase * BASE_PHEROMONE_PERSISTANCE * chunkPathRating + staging[PLAYER_PHEROMONE] = chunkPlayer * PLAYER_PHEROMONE_PERSISTANCE * chunkPathRating if clear then - staging[RESOURCE_PHEROMONE] = (chunkResource + (neighborDiv * resourceTotal)) * RESOURCE_PHEROMONE_PERSISTANCE * chunkPathRating + staging[RESOURCE_PHEROMONE] = chunkResource * RESOURCE_PHEROMONE_PERSISTANCE * chunkPathRating else - staging[RESOURCE_PHEROMONE] = (chunkResource + (neighborDiv * resourceTotal)) * 0.01 + staging[RESOURCE_PHEROMONE] = chunkResource * 0.01 end end diff --git a/libs/SquadAttack.lua b/libs/SquadAttack.lua index 4a0d812..91c301d 100755 --- a/libs/SquadAttack.lua +++ b/libs/SquadAttack.lua @@ -76,7 +76,6 @@ local positionFromDirectionAndChunk = mapUtils.positionFromDirectionAndChunk local positionFromDirectionAndFlat = mapUtils.positionFromDirectionAndFlat local createSquad = unitGroupUtils.createSquad -local membersToSquad = unitGroupUtils.membersToSquad local euclideanDistanceNamed = mathUtils.euclideanDistanceNamed @@ -108,14 +107,14 @@ local function scoreAttackLocation(natives, squad, neighborChunk) if (movementPheromone >= 0) then damage = movementPheromone + (neighborChunk[BASE_PHEROMONE]) + (neighborChunk[PLAYER_PHEROMONE] * PLAYER_PHEROMONE_MULTIPLER) else - damage = (neighborChunk[BASE_PHEROMONE] * (1 - (movementPheromone / -natives.retreatThreshold))) + (neighborChunk[PLAYER_PHEROMONE] * PLAYER_PHEROMONE_MULTIPLER) + damage = (neighborChunk[BASE_PHEROMONE] * (1 - (movementPheromone / -natives.retreatThreshold))) + (neighborChunk[PLAYER_PHEROMONE] * PLAYER_PHEROMONE_MULTIPLER) + (getPlayerBaseGenerator(natives.map, neighborChunk) * PLAYER_PHEROMONE_MULTIPLER) end return damage -- - lookupMovementPenalty(squad, neighborChunk) end local function scoreAttackKamikazeLocation(natives, squad, neighborChunk) - local damage = neighborChunk[BASE_PHEROMONE] + (neighborChunk[PLAYER_PHEROMONE] * PLAYER_PHEROMONE_MULTIPLER) + local damage = neighborChunk[BASE_PHEROMONE] + (neighborChunk[PLAYER_PHEROMONE] * PLAYER_PHEROMONE_MULTIPLER) + (getPlayerBaseGenerator(natives.map, neighborChunk) * PLAYER_PHEROMONE_MULTIPLER) return damage -- - lookupMovementPenalty(squad, neighborChunk) end @@ -140,7 +139,6 @@ local function settleMove(map, squad, surface) groupPosition.y, squad.originPosition.x, squad.originPosition.y) - local cmd local position local position2 @@ -165,18 +163,7 @@ local function settleMove(map, squad, surface) squad.status = SQUAD_BUILDING - map.buildPositionTop.x = position.x - BASE_CLEAN_DISTANCE - map.buildPositionTop.y = position.y - BASE_CLEAN_DISTANCE - map.buildPositionBottom.x = position.x + BASE_CLEAN_DISTANCE - map.buildPositionBottom.y = position.y + BASE_CLEAN_DISTANCE - - local entities = surface.find_entities_filtered(map.filteredEntitiesClearBuildingQuery) - for i=1,#entities do - local entity = entities[i] - if entity.valid and (entity.type ~= "cliff") then - entity.die() - end - end + surface.create_entity(map.createBuildCloudQuery) group.set_command(cmd) else @@ -186,70 +173,52 @@ local function settleMove(map, squad, surface) scoreFunction, squad) + if (attackChunk == -1) then cmd = map.wonderCommand group.set_command(cmd) return - else - positionFromDirectionAndFlat(attackDirection, groupPosition, targetPosition) - position = findMovementPosition(surface, targetPosition) - + elseif (attackDirection ~= 0) then local attackPlayerThreshold = natives.attackPlayerThreshold - if not position then + if (nextAttackChunk ~= -1) then + attackChunk = nextAttackChunk + positionFromDirectionAndFlat(attackDirection, groupPosition, targetPosition) + positionFromDirectionAndFlat(nextAttackDirection, targetPosition, targetPosition2) + position = findMovementPosition(surface, targetPosition2) + else + positionFromDirectionAndFlat(attackDirection, groupPosition, targetPosition) + position = findMovementPosition(surface, targetPosition) + end + + if position then + targetPosition.x = position.x + targetPosition.y = position.y + else cmd = map.wonderCommand group.set_command(cmd) return + end + + if (getPlayerBaseGenerator(map, attackChunk) ~= 0) or + (attackChunk[PLAYER_PHEROMONE] >= attackPlayerThreshold) + then + cmd = map.attackCommand + + if not squad.rabid then + squad.frenzy = true + squad.frenzyPosition.x = groupPosition.x + squad.frenzyPosition.y = groupPosition.y + end else - targetPosition.x = position.x - targetPosition.y = position.y - - if (getPlayerBaseGenerator(map, attackChunk) ~= 0) or - (attackChunk[PLAYER_PHEROMONE] >= attackPlayerThreshold) - then - cmd = map.attackCommand - - if not squad.rabid then - squad.frenzy = true - squad.frenzyPosition.x = groupPosition.x - squad.frenzyPosition.y = groupPosition.y - end + cmd = map.moveCommand + if squad.rabid or squad.kamikaze then + cmd.distraction = DEFINES_DISTRACTION_NONE else - cmd = map.moveCommand - if squad.rabid or squad.kamikaze then - cmd.distraction = DEFINES_DISTRACTION_NONE - else - cmd.distraction = DEFINES_DISTRACTION_BY_ENEMY - end + cmd.distraction = DEFINES_DISTRACTION_BY_ENEMY end end - - if (nextAttackChunk ~= -1) then - positionFromDirectionAndFlat(nextAttackDirection, targetPosition, targetPosition2) - - position2 = findMovementPosition(surface, targetPosition2) - - if position2 then - targetPosition.x = position2.x - targetPosition.y = position2.y - - if ((cmd ~= map.attackCommand) and - ((getPlayerBaseGenerator(map, nextAttackChunk) ~= 0) or - (nextAttackChunk[PLAYER_PHEROMONE] >= attackPlayerThreshold))) - then - cmd = map.attackCommand - - if not squad.rabid then - squad.frenzy = true - squad.frenzyPosition.x = groupPosition.x - squad.frenzyPosition.y = groupPosition.y - end - end - end - end - end - - if (attackDirection == 0) then + else cmd = map.settleCommand if squad.kamikaze then cmd.distraction = DEFINES_DISTRACTION_NONE @@ -259,32 +228,21 @@ local function settleMove(map, squad, surface) squad.status = SQUAD_BUILDING - map.buildPositionTop.x = targetPosition.x - BASE_CLEAN_DISTANCE - map.buildPositionTop.y = targetPosition.y - BASE_CLEAN_DISTANCE - map.buildPositionBottom.x = targetPosition.x + BASE_CLEAN_DISTANCE - map.buildPositionBottom.y = targetPosition.y + BASE_CLEAN_DISTANCE - - local entities = surface.find_entities_filtered(map.filteredEntitiesClearBuildingQuery) - for i=1,#entities do - local entity = entities[i] - if entity.valid and (entity.type ~= "cliff") then - entity.die() - end - end - - group.set_command(cmd) - else - group.set_command(cmd) + surface.create_entity(map.createBuildCloudQuery) end + + group.set_command(cmd) end end local function attackMove(map, squad, surface) + local targetPosition = map.position local targetPosition2 = map.position2 local group = squad.group + local position local groupPosition = group.position local x, y = positionToChunkXY(groupPosition) local chunk = getChunkByXY(map, x, y) @@ -305,68 +263,45 @@ local function attackMove(map, squad, surface) cmd = map.wonderCommand group.set_command(cmd) return + elseif (nextAttackChunk ~= -1) then + attackChunk = nextAttackChunk + positionFromDirectionAndFlat(attackDirection, groupPosition, targetPosition) + positionFromDirectionAndFlat(nextAttackDirection, targetPosition, targetPosition2) + position = findMovementPosition(surface, targetPosition2) else positionFromDirectionAndFlat(attackDirection, groupPosition, targetPosition) - - local attackPlayerThreshold = map.natives.attackPlayerThreshold - local position = findMovementPosition(surface, targetPosition) - - if not position then - cmd = map.wonderCommand - group.set_command(cmd) - return - else - targetPosition.x = position.x - targetPosition.y = position.y - - if (getPlayerBaseGenerator(map, attackChunk) ~= 0) and - (attackChunk[PLAYER_PHEROMONE] >= attackPlayerThreshold) - then - cmd = map.attackCommand - - if not squad.rabid then - squad.frenzy = true - squad.frenzyPosition.x = groupPosition.x - squad.frenzyPosition.y = groupPosition.y - end - else - cmd = map.moveCommand - if squad.rabid or squad.frenzy then - cmd.distraction = DEFINES_DISTRACTION_BY_ANYTHING - else - cmd.distraction = DEFINES_DISTRACTION_BY_ENEMY - end - end - end - - local position2 - - if (nextAttackChunk ~= -1) then - positionFromDirectionAndFlat(nextAttackDirection, targetPosition, targetPosition2) - - position2 = findMovementPosition(surface, targetPosition2) - - if position2 then - targetPosition.x = position2.x - targetPosition.y = position2.y - - if (cmd ~= map.attackCommand) and - ((getPlayerBaseGenerator(map, nextAttackChunk) ~= 0) or - (nextAttackChunk[PLAYER_PHEROMONE] >= attackPlayerThreshold)) - then - cmd = map.attackCommand - - if not squad.rabid then - squad.frenzy = true - squad.frenzyPosition.x = groupPosition.x - squad.frenzyPosition.y = groupPosition.y - end - end - end - end - - group.set_command(cmd) + position = findMovementPosition(surface, targetPosition) end + + if not position then + cmd = map.wonderCommand + group.set_command(cmd) + return + else + targetPosition.x = position.x + targetPosition.y = position.y + end + + if (getPlayerBaseGenerator(map, attackChunk) ~= 0) and + (attackChunk[PLAYER_PHEROMONE] >= map.natives.attackPlayerThreshold) + then + cmd = map.attackCommand + + if not squad.rabid then + squad.frenzy = true + squad.frenzyPosition.x = groupPosition.x + squad.frenzyPosition.y = groupPosition.y + end + else + cmd = map.moveCommand + if squad.rabid or squad.frenzy then + cmd.distraction = DEFINES_DISTRACTION_BY_ANYTHING + else + cmd.distraction = DEFINES_DISTRACTION_BY_ENEMY + end + end + + group.set_command(cmd) end local function buildMove(map, squad, surface) @@ -385,53 +320,36 @@ local function buildMove(map, squad, surface) end function squadAttack.squadDispatch(map, surface, squad) + -- local profiler = game.create_profiler() local group = squad.group if group and group.valid then - local memberCount = #group.members - if (memberCount == 0) then - removeSquadFromChunk(map, squad) - local deathGen = getDeathGenerator(map, squad.chunk) - local penalties = squad.penalties - for xc=1,mMin(#squad.penalties,5) do - addDeathGenerator(map, - penalties[xc].c, - deathGen * DIVISOR_DEATH_TRAIL_TABLE[xc]) - end - group.destroy() - elseif (memberCount > AI_MAX_BITER_GROUP_SIZE) then - local members = group.members - unitGroupUtils.recycleBiters(natives, members) - removeSquadFromChunk(map, squad) - group.destroy() - else - local status = squad.status - - if (status == SQUAD_RAIDING) then - attackMove(map, squad, surface) - elseif (status == SQUAD_SETTLING) then + local status = squad.status + if (status == SQUAD_RAIDING) then + attackMove(map, squad, surface) + elseif (status == SQUAD_SETTLING) then + settleMove(map, squad, surface) + elseif (status == SQUAD_RETREATING) then + if squad.settlers then + squad.status = SQUAD_SETTLING settleMove(map, squad, surface) - elseif (status == SQUAD_RETREATING) then - if squad.settlers then - squad.status = SQUAD_SETTLING - settleMove(map, squad, surface) - else - squad.status = SQUAD_RAIDING - attackMove(map, squad, surface) - end - elseif (status == SQUAD_BUILDING) then - removeSquadFromChunk(map, squad) - buildMove(map, squad, surface) - elseif (status == SQUAD_GUARDING) then - if squad.settlers then - squad.status = SQUAD_SETTLING - settleMove(map, squad, surface) - else - squad.status = SQUAD_RAIDING - attackMove(map, squad, surface) - end + else + squad.status = SQUAD_RAIDING + attackMove(map, squad, surface) + end + elseif (status == SQUAD_BUILDING) then + removeSquadFromChunk(map, squad) + buildMove(map, squad, surface) + elseif (status == SQUAD_GUARDING) then + if squad.settlers then + squad.status = SQUAD_SETTLING + settleMove(map, squad, surface) + else + squad.status = SQUAD_RAIDING + attackMove(map, squad, surface) end end end + -- game.print({"", "--dispatch4 ", profiler}) end squadAttackG = squadAttack diff --git a/libs/SquadDefense.lua b/libs/SquadDefense.lua index cd34637..7d1178f 100755 --- a/libs/SquadDefense.lua +++ b/libs/SquadDefense.lua @@ -21,7 +21,7 @@ local PLAYER_PHEROMONE_MULTIPLER = constants.PLAYER_PHEROMONE_MULTIPLER local SQUAD_RETREATING = constants.SQUAD_RETREATING -local INTERVAL_RETREAT = constants.INTERVAL_RETREAT +local COOLDOWN_RETREAT = constants.COOLDOWN_RETREAT -- imported functions @@ -54,23 +54,24 @@ local function scoreRetreatLocation(map, neighborChunk) -(getPlayerBaseGenerator(map, neighborChunk) * 1000)) end -function aiDefense.retreatUnits(chunk, position, group, map, surface, tick, radius, artilleryBlast) - if (tick - getRetreatTick(map, chunk) > INTERVAL_RETREAT) and - ((getEnemyStructureCount(map, chunk) == 0) or artilleryBlast) - then +function aiDefense.retreatUnits(chunk, position, group, map, surface, tick, radius) + if (tick - getRetreatTick(map, chunk) > COOLDOWN_RETREAT) and (getEnemyStructureCount(map, chunk) == 0) then local performRetreat = false local enemiesToSquad = nil local squad + local groupMembers if group and group.valid then squad = map.natives.groupNumberToSquad[group.group_number] if not squad then squad = createSquad(position, surface, group) - squad.kamikaze = mRandom() < calculateKamikazeThreshold(group.members, natives) + groupMembers = group.members + squad.kamikaze = mRandom() < calculateKamikazeThreshold(#groupMembers, natives) end end if not squad then + print("grabbing people", position.x, position.y) enemiesToSquad = map.enemiesToSquad local unitCount = 0 local units = surface.find_enemy_units(position, radius) @@ -83,16 +84,16 @@ function aiDefense.retreatUnits(chunk, position, group, map, surface, tick, radi end enemiesToSquad.len = unitCount if (mRandom() < calculateKamikazeThreshold(unitCount, map.natives)) then - setRetreatTick(map, chunk, tick) - return - end - performRetreat = unitCount > 6 + performRetreat = false + else + performRetreat = unitCount > 6 + end elseif squad.group and squad.group.valid and (squad.status ~= SQUAD_RETREATING) and not squad.kamikaze then - performRetreat = #squad.group.members > 6 + performRetreat = #groupMembers > 6 end + setRetreatTick(map, chunk, tick) if performRetreat then - setRetreatTick(map, chunk, tick) local exitPath,exitDirection,nextExitPath,nextExitDirection = scoreNeighborsForRetreat(chunk, getNeighborChunks(map, chunk.x, @@ -140,7 +141,7 @@ function aiDefense.retreatUnits(chunk, position, group, map, surface, tick, radi if enemiesToSquad then membersToSquad(cmd, enemiesToSquad.len, enemiesToSquad, artilleryBlast) else - membersToSquad(cmd, #squad.group.members, squad.group.members, true) + membersToSquad(cmd, #groupMembers, groupMembers, true) if squad.rabid then newSquad.rabid = true end diff --git a/libs/UnitGroupUtils.lua b/libs/UnitGroupUtils.lua index 619a9d4..ad8565d 100755 --- a/libs/UnitGroupUtils.lua +++ b/libs/UnitGroupUtils.lua @@ -15,6 +15,7 @@ local movementUtils = require("MovementUtils") local DEFINES_GROUP_FINISHED = defines.group_state.finished +local DIVISOR_DEATH_TRAIL_TABLE = constants.DIVISOR_DEATH_TRAIL_TABLE local SQUAD_QUEUE_SIZE = constants.SQUAD_QUEUE_SIZE local DEFINES_GROUP_STATE_ATTACKING_TARGET = defines.group_state.attacking_target @@ -36,6 +37,11 @@ local mRandom = math.random local findMovementPosition = movementUtils.findMovementPosition local removeSquadFromChunk = chunkPropertyUtils.removeSquadFromChunk +local addDeathGenerator = chunkPropertyUtils.addDeathGenerator +local getDeathGenerator = chunkPropertyUtils.getDeathGenerator + +local next = next +local table_size = table_size local mLog = math.log10 @@ -139,15 +145,21 @@ function unitGroupUtils.createSquad(position, surface, group, settlers) end function unitGroupUtils.cleanSquads(natives, iterator) + local profiler = game.create_profiler() local squads = natives.groupNumberToSquad local map = natives.map local k, squad = next(squads, iterator) local nextK - for i=1,2 do - if not k then - return nil - elseif not squad.group.valid then + -- for i=1,2 do + if not k then + if (table_size(squads) == 0) then + -- this is needed as the next command remembers the max length a table has been + natives.groupNumberToSquad = {} + end + else + local group = squad.group + if not group.valid then removeSquadFromChunk(map, squad) if (map.regroupIterator == k) then map.regroupIterator = nil @@ -155,9 +167,39 @@ function unitGroupUtils.cleanSquads(natives, iterator) nextK,squad = next(squads, k) squads[k] = nil k = nextK + -- else + -- game.print({"", "3b", profiler}) + -- profiler.restart() + -- local members = group.members + -- local memberCount = #members + -- if (memberCount == 0) then + -- game.print({"", "4a", profiler}) + -- profiler.restart() + -- local deathGen = getDeathGenerator(map, squad.chunk) + -- local penalties = squad.penalties + -- for xc=1,mMin(#squad.penalties,5) do + -- addDeathGenerator(map, + -- penalties[xc].c, + -- deathGen * DIVISOR_DEATH_TRAIL_TABLE[xc]) + -- end + -- removeSquadFromChunk(map, squad) + -- group.destroy() + -- game.print({"", "4ea", profiler}) + -- profiler.restart() + -- elseif (memberCount > AI_MAX_BITER_GROUP_SIZE) then + -- game.print({"", "4b", profiler}) + -- profiler.restart() + -- unitGroupUtils.recycleBiters(natives, members) + -- removeSquadFromChunk(map, squad) + -- group.destroy() + -- game.print({"", "4eb", profiler}) + -- profiler.restart() + -- end + -- game.print({"", "3be", profiler}) end end - return k + -- end + map.squadIterator = k end function unitGroupUtils.membersToSquad(cmd, size, members, overwriteGroup) @@ -185,6 +227,7 @@ end function unitGroupUtils.regroupSquads(natives, iterator) local map = natives.map local squads = natives.groupNumberToSquad + local cmd = map.mergeGroupCommand local k, squad = iterator, nil for i=1,SQUAD_QUEUE_SIZE do @@ -194,42 +237,45 @@ function unitGroupUtils.regroupSquads(natives, iterator) else local group = squad.group if group and group.valid then + cmd.group = group local groupState = group.state if (groupState ~= DEFINES_GROUP_STATE_ATTACKING_TARGET) and (groupState ~= DEFINES_GROUP_STATE_ATTACKING_DISTRACTION) then - local memberCount = #group.members - if (memberCount < AI_SQUAD_MERGE_THRESHOLD) then - local status = squad.status - local chunk = squad.chunk + -- local memberCount = #group.members + -- if (memberCount < AI_SQUAD_MERGE_THRESHOLD) then + local status = squad.status + local chunk = squad.chunk - if (chunk ~= -1) then - for _,mergeSquad in pairs(getSquadsOnChunk(map, chunk)) do - if (mergeSquad ~= squad) then - local mergeGroup = mergeSquad.group - if mergeGroup and mergeGroup.valid and (mergeSquad.status == status) then - local mergeGroupState = mergeGroup.state - if (mergeGroupState ~= DEFINES_GROUP_STATE_ATTACKING_TARGET) and - (mergeGroupState ~= DEFINES_GROUP_STATE_ATTACKING_DISTRACTION) - then - local mergeMembers = mergeGroup.members - local mergeCount = #mergeMembers - if ((mergeCount + memberCount) < AI_MAX_BITER_GROUP_SIZE) then - for memberIndex=1, mergeCount do - group.add_member(mergeMembers[memberIndex]) - end - mergeGroup.destroy() - end - squad.status = SQUAD_GUARDING - memberCount = memberCount + mergeCount - if (memberCount > AI_SQUAD_MERGE_THRESHOLD) then - break - end - end + if (chunk ~= -1) then + for _,mergeSquad in pairs(getSquadsOnChunk(map, chunk)) do + if (mergeSquad ~= squad) then + local mergeGroup = mergeSquad.group + if mergeGroup and mergeGroup.valid and (mergeSquad.status == status) then + local mergeGroupState = mergeGroup.state + if (mergeGroupState ~= DEFINES_GROUP_STATE_ATTACKING_TARGET) and + (mergeGroupState ~= DEFINES_GROUP_STATE_ATTACKING_DISTRACTION) + then + print("merging group") + mergeGroup.set_command(cmd) + -- local mergeMembers = mergeGroup.members + -- local mergeCount = #mergeMembers + -- if ((mergeCount + memberCount) < AI_MAX_BITER_GROUP_SIZE) then + -- for memberIndex=1, mergeCount do + -- group.add_member(mergeMembers[memberIndex]) + -- end + -- mergeGroup.destroy() + -- end + squad.status = SQUAD_GUARDING + -- memberCount = memberCount + mergeCount + -- if (memberCount > AI_SQUAD_MERGE_THRESHOLD) then + -- break + -- end end end end end + -- end end end end diff --git a/locale/en/locale.cfg b/locale/en/locale.cfg index e09f447..07d0d48 100755 --- a/locale/en/locale.cfg +++ b/locale/en/locale.cfg @@ -18710,7 +18710,6 @@ rampant-enableNEUnits=World: Enable NE as a faction rampant-disallowFriendlyFire=Safety: Disallow Friendly Fire Splash Damage rampant-tierStart=World: Beginning enemy level rampant-tierEnd=World: Ending enemy level -rampant-disableVanillaAI=AI: Disable Vanilla AI rampant-enableMigration=AI: Enable Migration rampant-liteMode=Optimization: Lite mode @@ -18806,7 +18805,6 @@ rampant-newEnemyVariations=This number corresponds to the number of variations p rampant-disallowFriendlyFire=Prevents enemy spitters and worms from damaging units or buildings from the same force through splash damage rampant-tierStart=This is the starting level of the enemy that will be used when generating the set of enemies for the specified number of tiers and increases linearly to the end tier for as many tiers selected. Roughly healthwise each tier is a follows (1-15,2-75,3-150,4-250,5-1000,6-2000,7-3500,8-7500,9-15000,10-30000). min 1, max 10 rampant-tierEnd=This is the ending tier level that is reached after increasing linearly from the start tier for as many tiers selected. Roughly healthwise each tier is a follows (1-15,2-75,3-150,4-250,5-1000,6-2000,7-3500,8-7500,9-15000,10-30000). min 1, max 10 -rampant-disableVanillaAI=This will turn off the vanilla attack waves and the vanilla biter expansion. rampant-enableMigration=This will allow the ai to expand its worms and nests. Respects the map settings for biter expansion. rampant-enableShrinkNestsAndWorms=Reduce the collision box size of nests and worms by 50%. May cause overlapping entities due to the collision box being smaller than the entity graphic. diff --git a/prototypes/buildings/ChunkScanner.lua b/prototypes/buildings/ChunkScanner.lua index 0a982c6..b87e89d 100755 --- a/prototypes/buildings/ChunkScanner.lua +++ b/prototypes/buildings/ChunkScanner.lua @@ -1,5 +1,6 @@ local biterFunctions = require("prototypes/utils/BiterUtils") local constants = require("libs/Constants") +local smokeUtils = require("prototypes/utils/SmokeUtils") data:extend({ biterFunctions.makeBiter({ @@ -83,3 +84,39 @@ for t=1,11 do ) end end + + +smokeUtils.makeNewCloud( + { + name = "build-clear", + wind = false, + scale = 9, + duration = 540, + cooldown = 10, + tint = { r=0.7, g=0.2, b=0.7 } + }, + { + type = "area", + radius = 17, + force = "not-same", + action_delivery = + { + type = "instant", + target_effects = + { + { + type = "damage", + damage = { amount = 8, type = "poison"} + }, + { + type = "damage", + damage = { amount = 8, type = "acid"} + }, + { + type = "damage", + damage = { amount = 8, type = "fire"} + } + } + } + } +) diff --git a/prototypes/utils/SmokeUtils.lua b/prototypes/utils/SmokeUtils.lua index 2567aee..7f58c3a 100755 --- a/prototypes/utils/SmokeUtils.lua +++ b/prototypes/utils/SmokeUtils.lua @@ -2,6 +2,145 @@ local smokeUtils = {} -- module code +function smokeUtils.makeNewCloud(attributes, attack) + local name = attributes.name .. "-cloud-rampant" + + data:extend({ + { + type = "smoke-with-trigger", + name = "dummy-" .. name, + flags = {"not-on-map"}, + show_when_smoke_off = true, + particle_count = 24, + particle_spread = { 3.6 * 1.05, 3.6 * 0.6 * 1.05 }, + particle_distance_scale_factor = 0.5, + particle_scale_factor = { 1, 0.707 }, + particle_duration_variation = 60 * 3, + wave_speed = { 0.5 / 80, 0.5 / 60 }, + wave_distance = { 1, 0.5 }, + spread_duration_variation = 300 - 20, + + render_layer = "object", + + affected_by_wind = attributes.wind, + cyclic = true, + duration = attributes.duration or 60 * 20 + 4 * 60, + fade_away_duration = 3 * 60, + spread_duration = (300 - 20) / 2 , + color = attributes.tint or { r = 0.7, g = 0.9, b = 0.2 }, -- #035b6452 + + animation = + { + width = 152, + height = 120, + line_length = 5, + frame_count = 60, + shift = {-0.53125, -0.4375}, + priority = "high", + animation_speed = 0.25, + scale = (attributes.scale and attributes.scale * 0.75) or 2.25, + filename = "__base__/graphics/entity/smoke/smoke.png", + flags = { "smoke" } + } + }, + { + type = "smoke-with-trigger", + name = name, + flags = {"not-on-map"}, + show_when_smoke_off = true, + animation = + { + width = 152, + height = 120, + line_length = 5, + frame_count = 60, + shift = {-0.53125, -0.4375}, + priority = "high", + animation_speed = 0.25, + filename = "__base__/graphics/entity/smoke/smoke.png", + flags = { "smoke" }, + scale = attributes.scale or 3, + tint = attributes.tint or { r = 0.7, g = 0.9, b = 0.2 } + }, + created_effect = + { + { + type = "cluster", + cluster_count = (attributes.scale and attributes.scale * 0.5) and 10, + distance = (attributes.scale and attributes.scale * 0.8) and 4, + distance_deviation = 5, + action_delivery = + { + type = "instant", + target_effects = + { + type = "create-smoke", + show_in_tooltip = false, + entity_name = "dummy-" .. name, + initial_height = 0 + } + } + }, + { + type = "cluster", + cluster_count = (attributes.scale and attributes.scale * 0.65) and 11, + distance = (attributes.scale and attributes.scale * 1.8) and 8 * 1.1, + distance_deviation = 2, + action_delivery = + { + type = "instant", + target_effects = + { + type = "create-smoke", + show_in_tooltip = false, + entity_name = "dummy-" .. name, + initial_height = 0 + } + } + } + }, + slow_down_factor = attributes.slowdown or 0, + affected_by_wind = attributes.wind, + cyclic = true, + render_layer = "higher-object-above", + duration = attributes.duration or 60 * 20, + fade_away_duration = attributes.outDuration or (attributes.duration and attributes.duration * 0.25) or 2 * 60, + spread_duration = attributes.inDuration or (attributes.duration and attributes.duration * 0.25) or 10, + action = attack or + { + type = "direct", + action_delivery = + { + type = "instant", + target_effects = + { + type = "nested-result", + action = + { + type = "area", + radius = 11, + entity_flags = {"breaths-air"}, + action_delivery = + { + type = "instant", + target_effects = + { + type = "damage", + damage = { amount = 8, type = "poison"} + } + } + } + } + } + }, + action_cooldown = attributes.cooldown or 30 + } + }) + + return name +end + + function smokeUtils.makeCloud(attributes, attack) local name = attributes.name .. "-cloud-rampant" diff --git a/settings.lua b/settings.lua index c418f5b..9871f23 100755 --- a/settings.lua +++ b/settings.lua @@ -393,15 +393,15 @@ data:extend({ per_user = false }, - { - type = "bool-setting", - name = "rampant-disableVanillaAI", - description = "rampant-disableVanillaAI", - setting_type = 'runtime-global', - default_value = true, - order = "m[total]-a[ai]", - per_user = false - }, + -- { + -- type = "bool-setting", + -- name = "rampant-disableVanillaAI", + -- description = "rampant-disableVanillaAI", + -- setting_type = 'runtime-global', + -- default_value = true, + -- order = "m[total]-a[ai]", + -- per_user = false + -- }, { type = "bool-setting", diff --git a/tests.lua b/tests.lua index 9e85de5..d58c812 100755 --- a/tests.lua +++ b/tests.lua @@ -219,9 +219,9 @@ function tests.attackOrigin() max_distance = 1000}) if (enemy ~= nil) and enemy.valid then print(enemy, enemy.unit_number) - enemy.set_command({type=defines.command.attack_area, + enemy.set_command({type=defines.command.go_to_location, destination={0,0}, - radius=32}) + radius=15}) end end