From 49a82fcff42f2db62818fb4e34f0cd597502d056 Mon Sep 17 00:00:00 2001 From: Aaron Veden Date: Tue, 23 May 2017 23:46:23 -0700 Subject: [PATCH] cleanup + optimizations + indestructible --- Upgrade.lua | 28 +++++++-- control.lua | 45 +++++++------- libs/AIAttack.lua | 11 ++-- libs/AIBuilding.lua | 22 ++++--- libs/AIDefense.lua | 14 ++--- libs/ChunkProcessor.lua | 4 +- libs/ChunkUtils.lua | 18 ++++-- libs/Constants.lua | 6 +- libs/EntityUtils.lua | 24 +------- libs/MapProcessor.lua | 66 ++++++++++++++------ libs/MapUtils.lua | 67 ++++++++------------ libs/NeighborUtils.lua | 22 ++++--- libs/PheromoneUtils.lua | 23 ++++--- libs/UnitGroupUtils.lua | 132 +++++++++++++++++++++++----------------- locale/en/locale.cfg | 2 +- 15 files changed, 270 insertions(+), 214 deletions(-) diff --git a/Upgrade.lua b/Upgrade.lua index 52edceb..4275fc6 100644 --- a/Upgrade.lua +++ b/Upgrade.lua @@ -56,9 +56,6 @@ function upgrade.attempt(natives, regionMap) if (global.version < constants.VERSION_11) then natives.state = constants.AI_STATE_AGGRESSIVE natives.temperament = 0 - -- needs to be on inner logic tick loop interval - natives.stateTick = roundToNearest(game.tick + INTERVAL_LOGIC, INTERVAL_LOGIC) - natives.temperamentTick = roundToNearest(game.tick + INTERVAL_LOGIC, INTERVAL_LOGIC) global.version = constants.VERSION_11 end @@ -74,9 +71,6 @@ function upgrade.attempt(natives, regionMap) global.version = constants.VERSION_12 end if (global.version < constants.VERSION_13) then - -- switched over to tick event - regionMap.logicTick = roundToNearest(game.tick + INTERVAL_LOGIC, INTERVAL_LOGIC) - regionMap.processTick = roundToNearest(game.tick + INTERVAL_PROCESS, INTERVAL_PROCESS) -- used to rate limit the number of rally cries during a period of time natives.rallyCries = MAX_RALLY_CRIES @@ -124,6 +118,28 @@ function upgrade.attempt(natives, regionMap) game.surfaces[1].print("Rampant - Version 0.15.9") global.version = constants.VERSION_21 end + if (global.version < constants.VERSION_22) then + + -- been made redundant + natives.rallyCries = nil + + -- switched over to tick event + regionMap.logicTick = roundToNearest(game.tick + INTERVAL_LOGIC, INTERVAL_LOGIC) + regionMap.processTick = roundToNearest(game.tick + INTERVAL_PROCESS, INTERVAL_PROCESS) + -- needs to be on inner logic tick loop interval + natives.stateTick = roundToNearest(game.tick + INTERVAL_LOGIC, INTERVAL_LOGIC) + natives.temperamentTick = roundToNearest(game.tick + INTERVAL_LOGIC, INTERVAL_LOGIC) + + game.map_settings.path_finder.short_request_ratio = 0.8 + game.map_settings.path_finder.short_cache_size = 25 + game.map_settings.path_finder.long_cache_size = 5 + game.map_settings.path_finder.min_steps_to_check_path_find_termination = 300 + + game.map_settings.max_failed_behavior_count = 6 + + game.surfaces[1].print("Rampant - Version 0.15.10") + global.version = constants.VERSION_22 + end return starting ~= global.version end diff --git a/control.lua b/control.lua index f65d21a..c5afd7e 100644 --- a/control.lua +++ b/control.lua @@ -12,7 +12,6 @@ local aiDefense = require("libs/AIDefense") local aiAttack = require("libs/AIAttack") local aiBuilding = require("libs/AIBuilding") local aiPlanning = require("libs/AIPlanning") -local mathUtils = require("libs/MathUtils") local interop = require("libs/Interop") local tests = require("tests") @@ -21,8 +20,6 @@ local tests = require("tests") local INTERVAL_LOGIC = constants.INTERVAL_LOGIC local INTERVAL_PROCESS = constants.INTERVAL_PROCESS -local MAX_RALLY_CRIES = constants.MAX_RALLY_CRIES - local MOVEMENT_PHEROMONE = constants.MOVEMENT_PHEROMONE local BASE_RALLY_CHANCE = constants.BASE_RALLY_CHANCE @@ -47,6 +44,7 @@ local rallyUnits = aiBuilding.rallyUnits local deathScent = pheromoneUtils.deathScent local victoryScent = pheromoneUtils.victoryScent +local cleanSquads = unitGroupUtils.cleanSquads local regroupSquads = unitGroupUtils.regroupSquads local convertUnitGroupToSquad = unitGroupUtils.convertUnitGroupToSquad @@ -55,11 +53,9 @@ local squadBeginAttack = aiAttack.squadBeginAttack local retreatUnits = aiDefense.retreatUnits -local regenerateEntity = entityUtils.regenerateEntity +-- local regenerateEntity = entityUtils.regenerateEntity local addRemoveEntity = entityUtils.addRemoveEntity ---local makeImmortalEntity = entityUtils.makeImmortalEntity - -local roundToNearest = mathUtils.roundToNearest +local makeImmortalEntity = entityUtils.makeImmortalEntity -- local references to global @@ -102,7 +98,7 @@ local function onModSettingsChange(event) natives.safeEntityName["big-electric-pole-2"] = poles natives.safeEntityName["big-electric-pole-3"] = poles natives.safeEntityName["big-electric-pole-4"] = poles - + natives.attackUsePlayer = settings.global["rampant-attackWaveGenerationUsePlayerProximity"].value natives.attackUsePollution = settings.global["rampant-attackWaveGenerationUsePollution"].value @@ -147,17 +143,16 @@ local function onTick(event) local evolutionFactor = game.forces.enemy.evolution_factor local players = game.players - processPendingChunks(regionMap, surface, pendingChunks) + processPendingChunks(natives, regionMap, surface, pendingChunks) scanMap(regionMap, surface, natives, evolutionFactor) if (tick == regionMap.logicTick) then regionMap.logicTick = regionMap.logicTick + INTERVAL_LOGIC - natives.rallyCries = MAX_RALLY_CRIES - planning(natives, evolutionFactor, tick, surface) - - regroupSquads(natives, evolutionFactor) + + cleanSquads(natives, evolutionFactor) + -- regroupSquads(natives, evolutionFactor) processPlayers(players, regionMap, surface, natives, evolutionFactor, tick) @@ -170,7 +165,13 @@ local function onTick(event) end local function onBuild(event) - addRemoveEntity(regionMap, event.created_entity, natives, true, false) + local entity = event.created_entity + addRemoveEntity(regionMap, entity, natives, true, false) + if natives.safeBuildings then + if natives.safeEntities[entity.type] or natives.safeEntityName[entity.name] then + entity.destructible = false + end + end end local function onPickUp(event) @@ -185,13 +186,14 @@ local function onDeath(event) if (entity.type == "unit") then local entityPosition = entity.position local deathChunk = getChunkByPosition(regionMap, entityPosition.x, entityPosition.y) - + if (deathChunk ~= nil) then -- drop death pheromone where unit died deathScent(deathChunk) if ((event.force ~= nil) and (event.force.name == "player")) then local evolutionFactor = game.forces.enemy.evolution_factor + local tick = event.tick if (deathChunk[MOVEMENT_PHEROMONE] < -(evolutionFactor * RETREAT_MOVEMENT_PHEROMONE_LEVEL)) then retreatUnits(deathChunk, @@ -200,15 +202,15 @@ local function onDeath(event) regionMap, surface, natives, - event.tick) + tick) local rallyThreshold = BASE_RALLY_CHANCE + (evolutionFactor * BONUS_RALLY_CHANCE) - if (natives.rallyCries >= 0) and (math.random() < rallyThreshold) then - natives.rallyCries = natives.rallyCries - 1 + if (math.random() < rallyThreshold) then rallyUnits(deathChunk, regionMap, surface, natives, - evolutionFactor) + evolutionFactor, + tick) end end end @@ -227,10 +229,7 @@ local function onDeath(event) victoryScent(victoryChunk, entity.type) end if creditNatives and natives.safeBuildings and (natives.safeEntities[entity.type] or natives.safeEntityName[entity.name]) then - -- makeImmortalEntity(surface, entity) - - -- hack version - regenerateEntity(entity, entityPosition, surface) + makeImmortalEntity(surface, entity) else addRemoveEntity(regionMap, entity, natives, false, creditNatives) end diff --git a/libs/AIAttack.lua b/libs/AIAttack.lua index a52e456..21ea416 100644 --- a/libs/AIAttack.lua +++ b/libs/AIAttack.lua @@ -62,8 +62,9 @@ function aiAttack.squadAttack(regionMap, surface, natives) for i=1,#squads do local squad = squads[i] local group = squad.group - if group.valid and (squad.status == SQUAD_RAIDING) then - if (group.state == defines.group_state.finished) or (group.state == defines.group_state.gathering) or ((group.state == defines.group_state.moving) and (squad.cycles == 0)) then + if group.valid and (squad.status == SQUAD_RAIDING) then + local groupState = group.state + if (groupState == defines.group_state.finished) or (groupState == defines.group_state.gathering) or ((groupState == defines.group_state.moving) and (squad.cycles == 0)) then local chunk = getChunkByPosition(regionMap, group.position.x, group.position.y) if (chunk ~= nil) then local attackChunk, attackDirection = scoreNeighborsWithDirection(chunk, @@ -75,9 +76,9 @@ function aiAttack.squadAttack(regionMap, surface, natives) attackPosition, true) addSquadMovementPenalty(squad, chunk.cX, chunk.cY) - if (attackChunk ~= nil) then + if attackChunk then if (attackChunk[PLAYER_BASE_GENERATOR] == 0) or - ((group.state == defines.group_state.finished) or (group.state == defines.group_state.gathering)) then + ((groupState == defines.group_state.finished) or (groupState == defines.group_state.gathering)) then positionFromDirectionAndChunk(attackDirection, squad.group.position, attackPosition) @@ -100,7 +101,7 @@ function aiAttack.squadAttack(regionMap, surface, natives) group.set_command(attackCmd) group.start_moving() elseif not squad.frenzy and not squad.rabid and - ((group.state == defines.group_state.attacking_distraction) or (group.state == defines.group_state.attacking_target) or + ((groupState == defines.group_state.attacking_distraction) or (groupState == defines.group_state.attacking_target) or (attackChunk[PLAYER_BASE_GENERATOR] ~= 0)) then squad.frenzy = true squad.frenzyPosition.x = squad.group.position.x diff --git a/libs/AIBuilding.lua b/libs/AIBuilding.lua index fa19052..937e17c 100644 --- a/libs/AIBuilding.lua +++ b/libs/AIBuilding.lua @@ -23,7 +23,8 @@ local AI_MAX_SQUAD_COUNT = constants.AI_MAX_SQUAD_COUNT local AI_SQUAD_COST = constants.AI_SQUAD_COST local AI_VENGENCE_SQUAD_COST = constants.AI_VENGENCE_SQUAD_COST -local AI_STATE_NOCTURNAL = constants.AI_STATE_NOCTURNAL +local RALLY_TRIGGERED = constants.RALLY_TRIGGERED +local INTERVAL_LOGIC = constants.INTERVAL_LOGIC local HALF_CHUNK_SIZE = constants.HALF_CHUNK_SIZE local CHUNK_SIZE = constants.CHUNK_SIZE @@ -123,14 +124,17 @@ end -- --]] -- end -function aiBuilding.rallyUnits(chunk, regionMap, surface, natives, evolutionFactor) - local cX = chunk.cX - local cY = chunk.cY - for x=cX - RALLY_CRY_DISTANCE, cX + RALLY_CRY_DISTANCE do - for y=cY - RALLY_CRY_DISTANCE, cY + RALLY_CRY_DISTANCE do - local rallyChunk = getChunkByIndex(regionMap, x, y) - if (rallyChunk ~= nil) and (x ~= cX) and (y ~= cY) and (rallyChunk[ENEMY_BASE_GENERATOR] ~= 0) then - aiBuilding.formSquads(regionMap, surface, natives, rallyChunk, evolutionFactor, AI_VENGENCE_SQUAD_COST) +function aiBuilding.rallyUnits(chunk, regionMap, surface, natives, evolutionFactor, tick) + if (tick - chunk[RALLY_TRIGGERED] > INTERVAL_LOGIC) then + chunk[RALLY_TRIGGERED] = tick + local cX = chunk.cX + local cY = chunk.cY + for x=cX - RALLY_CRY_DISTANCE, cX + RALLY_CRY_DISTANCE do + for y=cY - RALLY_CRY_DISTANCE, cY + RALLY_CRY_DISTANCE do + local rallyChunk = getChunkByIndex(regionMap, x, y) + if (rallyChunk ~= nil) and (x ~= cX) and (y ~= cY) and (rallyChunk[ENEMY_BASE_GENERATOR] ~= 0) then + aiBuilding.formSquads(regionMap, surface, natives, rallyChunk, evolutionFactor, AI_VENGENCE_SQUAD_COST) + end end end end diff --git a/libs/AIDefense.lua b/libs/AIDefense.lua index 0994454..5ddb6c4 100644 --- a/libs/AIDefense.lua +++ b/libs/AIDefense.lua @@ -53,19 +53,15 @@ function aiDefense.retreatUnits(chunk, squad, regionMap, surface, natives, tick) if (tick - chunk[RETREAT_TRIGGERED] > INTERVAL_LOGIC) and (chunk[ENEMY_BASE_GENERATOR] == 0) then local performRetreat = false local enemiesToSquad - + if (squad == nil) then enemiesToSquad = surface.find_enemy_units({x=chunk.pX, y=chunk.pY}, RETREAT_GRAB_RADIUS) - if (#enemiesToSquad > 0) then - performRetreat = true - end + performRetreat = #enemiesToSquad > 0 elseif squad.group.valid and (squad.status ~= SQUAD_RETREATING) and not squad.kamikaze then - if (#squad.group.members > 1) then - performRetreat = true - end + performRetreat = #squad.group.members > 1 end - + if performRetreat then chunk[RETREAT_TRIGGERED] = tick local retreatPosition = {x=0, y=0} @@ -77,7 +73,7 @@ function aiDefense.retreatUnits(chunk, squad, regionMap, surface, natives, tick) surface, retreatPosition, false) - if (exitPath ~= nil) then + if exitPath then retreatPosition.x = exitPath.pX + HALF_CHUNK_SIZE retreatPosition.y = exitPath.pY + HALF_CHUNK_SIZE diff --git a/libs/ChunkProcessor.lua b/libs/ChunkProcessor.lua index 8fac8b5..121d46f 100644 --- a/libs/ChunkProcessor.lua +++ b/libs/ChunkProcessor.lua @@ -12,7 +12,7 @@ local scoreChunk = chunkUtils.scoreChunk -- module code -function chunkProcessor.processPendingChunks(regionMap, surface, pendingStack) +function chunkProcessor.processPendingChunks(natives, regionMap, surface, pendingStack) local processQueue = regionMap.processQueue for _=#pendingStack, 1, -1 do @@ -29,7 +29,7 @@ function chunkProcessor.processPendingChunks(regionMap, surface, pendingStack) regionMap[chunkX][chunk.cY] = chunk checkChunkPassability(chunk, surface) - scoreChunk(chunk, surface) + scoreChunk(natives, chunk, surface) processQueue[#processQueue+1] = chunk end end diff --git a/libs/ChunkUtils.lua b/libs/ChunkUtils.lua index aa0a178..2d367e0 100644 --- a/libs/ChunkUtils.lua +++ b/libs/ChunkUtils.lua @@ -25,6 +25,7 @@ local ENEMY_BASE_PHEROMONE_GENERATOR_AMOUNT = constants.ENEMY_BASE_PHEROMONE_GEN local CHUNK_TICK = constants.CHUNK_TICK local RETREAT_TRIGGERED = constants.RETREAT_TRIGGERED +local RALLY_TRIGGERED = constants.RALLY_TRIGGERED -- module code @@ -68,7 +69,7 @@ function chunkUtils.checkChunkPassability(chunk, surface) chunk[NORTH_SOUTH_PASSABLE] = passableNorthSouth end -function chunkUtils.scoreChunk(chunk, surface) +function chunkUtils.scoreChunk(natives, chunk, surface) local x = chunk.pX local y = chunk.pY @@ -92,10 +93,18 @@ function chunkUtils.scoreChunk(chunk, surface) chunk[ENEMY_BASE_GENERATOR] = (entities * ENEMY_BASE_PHEROMONE_GENERATOR_AMOUNT) + worms entities = surface.find_entities_filtered(playerChunkQuery) - + + local safeBuildings = natives.safeBuildings for i=1, #entities do - local entityType = entities[i].type - + local entity = entities[i] + local entityType = entity.type + + if safeBuildings then + if natives.safeEntities[entityType] or natives.safeEntityName[entity.name] then + entity.destructible = false + end + end + local entityScore = BUILDING_PHEROMONES[entityType] if (entityScore ~= nil) then playerObjects = playerObjects + entityScore @@ -121,6 +130,7 @@ function chunkUtils.createChunk(topX, topY) chunk[EAST_WEST_PASSABLE] = false chunk[CHUNK_TICK] = 0 chunk[RETREAT_TRIGGERED] = 0 + chunk[RALLY_TRIGGERED] = 0 return chunk end diff --git a/libs/Constants.lua b/libs/Constants.lua index 4235d13..feb981a 100644 --- a/libs/Constants.lua +++ b/libs/Constants.lua @@ -16,6 +16,7 @@ constants.VERSION_18 = 18 constants.VERSION_19 = 19 constants.VERSION_20 = 20 constants.VERSION_21 = 21 +constants.VERSION_22 = 22 -- misc @@ -29,8 +30,8 @@ constants.PROCESS_PLAYER_BOUND = 4 constants.TICKS_A_SECOND = 60 constants.TICKS_A_MINUTE = constants.TICKS_A_SECOND * 60 -constants.INTERVAL_PROCESS = 20 -constants.INTERVAL_LOGIC = 40 +constants.INTERVAL_PROCESS = 19 +constants.INTERVAL_LOGIC = 38 -- ai @@ -100,6 +101,7 @@ constants.EAST_WEST_PASSABLE = 7 constants.CHUNK_TICK = 8 constants.RETREAT_TRIGGERED = 9 +constants.RALLY_TRIGGERED = 10 -- Squad status diff --git a/libs/EntityUtils.lua b/libs/EntityUtils.lua index 3edc83f..ac09abe 100644 --- a/libs/EntityUtils.lua +++ b/libs/EntityUtils.lua @@ -117,9 +117,8 @@ function entityUtils.addRemoveEntity(regionMap, entity, natives, addObject, cred end end - -function entityUtils.regenerateEntity(entity, entityPosition, surface) - local repairPosition = entityPosition +function entityUtils.makeImmortalEntity(surface, entity) + local repairPosition = entity.position local repairName = entity.name local repairForce = entity.force local repairDirection = entity.direction @@ -150,25 +149,8 @@ function entityUtils.regenerateEntity(entity, entityPosition, surface) end end end - -- forces enemy to disperse - local enemies = surface.find_enemy_units({repairPosition.x, repairPosition.y}, CHUNK_SIZE) - for i=1, #enemies do - enemies[i].set_command({type=defines.command.wander, - distraction=defines.distraction.by_enemy}) - end -end -function entityUtils.makeImmortalEntity(surface, entity) - local repairPosition = entity.position - local repairName = entity.name - local repairForce = entity.force - local repairDirection = entity.direction - entity.destroy() - local entityRepaired = surface.create_entity({position=repairPosition, - name=repairName, - direction=repairDirection, - force=repairForce}) - entityRepaired.destructible = false + newEntity.destructible = false end return entityUtils diff --git a/libs/MapProcessor.lua b/libs/MapProcessor.lua index b08f8a1..1584985 100644 --- a/libs/MapProcessor.lua +++ b/libs/MapProcessor.lua @@ -46,6 +46,8 @@ local getChunkByPosition = mapUtils.getChunkByPosition local playerScent = pheromoneUtils.playerScent +local euclideanDistanceNamed = mapUtils.euclideanDistanceNamed + local canAttackNocturnal = nocturnalUtils.canAttack local mMin = math.min @@ -182,34 +184,54 @@ end function mapProcessor.scanMap(regionMap, surface, natives, evolution_factor) local index = regionMap.scanPointer + local chunkPosition = {x=0,y=0} + local chunkBox = {chunkPosition, + {x=chunkPosition.x + CHUNK_SIZE, y=chunkPosition.y + CHUNK_SIZE}} + local playerQuery = {area = chunkBox, + force = "player"} + local spawnerQuery = {area = chunkBox, + type = "unit-spawner", + force = "enemy"} + local wormsQuery = {area = chunkBox, + type = "turret", + force = "enemy"} + local unitCountQuery = {area = chunkBox, + type = "unit", + force = "enemy"} + local processQueue = regionMap.processQueue local endIndex = mMin(index + SCAN_QUEUE_SIZE, #processQueue) + for x=index,endIndex do local chunk = processQueue[x] - local entities = surface.find_entities_filtered({area = {{chunk.pX, chunk.pY}, - {chunk.pX + CHUNK_SIZE, chunk.pY + CHUNK_SIZE}}, - force = "player"}) - - local spawners = surface.count_entities_filtered({area = {{chunk.pX, chunk.pY}, - {chunk.pX + CHUNK_SIZE, chunk.pY + CHUNK_SIZE}}, - type = "unit-spawner", - force = "enemy"}) + chunkPosition.x = chunk.pX + chunkPosition.y = chunk.pY - local worms = surface.count_entities_filtered({area = {{chunk.pX, chunk.pY}, - {chunk.pX + CHUNK_SIZE, chunk.pY + CHUNK_SIZE}}, - type = "turret", - force = "enemy"}) + chunkBox[2].x = chunkPosition.x + CHUNK_SIZE + chunkBox[2].y = chunkPosition.y + CHUNK_SIZE - local unitCount = surface.count_entities_filtered({area = {{chunk.pX, chunk.pY}, - {chunk.pX + CHUNK_SIZE, chunk.pY + CHUNK_SIZE}}, - type = "unit", - force = "enemy"}) + local entities = surface.find_entities_filtered(playerQuery) + + local spawners = surface.count_entities_filtered(spawnerQuery) - if (unitCount > 550) then + local worms = surface.count_entities_filtered(wormsQuery) + + local unitCount = surface.count_entities_filtered(unitCountQuery) + + local closeBy = false + if (unitCount > 300) then + for i=1,#natives.squads do + local squadGroup = natives.squads[i].group + if (euclideanDistanceNamed(squadGroup.position, chunkPosition) < CHUNK_SIZE * 2) then + closeBy = true + end + end + end + + if (unitCount > 300) and not closeBy then local weight = AI_UNIT_REFUND * evolution_factor - local units = surface.find_enemy_units({chunk.pX, chunk.pY}, - CHUNK_SIZE * 3) + local units = surface.find_enemy_units(chunkPosition, CHUNK_SIZE * 3) for i=1,#units do units[i].destroy() @@ -222,9 +244,15 @@ function mapProcessor.scanMap(regionMap, surface, natives, evolution_factor) end local playerBaseGenerator = 0 + local safeBuildings = natives.safeBuildings for i=1,#entities do local entity = entities[i] local value = BUILDING_PHEROMONES[entity.type] + if safeBuildings then + if natives.safeEntities[entity.type] or natives.safeEntityName[entity.name] then + entity.destructible = false + end + end if (value ~= nil) then playerBaseGenerator = playerBaseGenerator + value end diff --git a/libs/MapUtils.lua b/libs/MapUtils.lua index e949fca..74733e5 100644 --- a/libs/MapUtils.lua +++ b/libs/MapUtils.lua @@ -45,36 +45,27 @@ end 6 7 8 ]]-- function mapUtils.getNeighborChunks(regionMap, chunkX, chunkY) - local neighbors = {1,2,3,4,5,6,7,8} + local neighbors = {false,false,false,false,false,false,false,false} + local chunkYRow1 = chunkY - 1 + local chunkYRow3 = chunkY + 1 local xChunks = regionMap[chunkX-1] if (xChunks ~= nil) then - neighbors[1] = xChunks[chunkY-1] + neighbors[1] = xChunks[chunkYRow1] neighbors[4] = xChunks[chunkY] - neighbors[6] = xChunks[chunkY+1] - else - neighbors[1] = nil - neighbors[4] = nil - neighbors[6] = nil + neighbors[6] = xChunks[chunkYRow3] end xChunks = regionMap[chunkX+1] if (xChunks ~= nil) then - neighbors[3] = xChunks[chunkY-1] + neighbors[3] = xChunks[chunkYRow1] neighbors[5] = xChunks[chunkY] - neighbors[8] = xChunks[chunkY+1] - else - neighbors[3] = nil - neighbors[5] = nil - neighbors[8] = nil + neighbors[8] = xChunks[chunkYRow3] end xChunks = regionMap[chunkX] if (xChunks ~= nil) then - neighbors[2] = xChunks[chunkY-1] - neighbors[7] = xChunks[chunkY+1] - else - neighbors[2] = nil - neighbors[7] = nil + neighbors[2] = xChunks[chunkYRow1] + neighbors[7] = xChunks[chunkYRow3] end return neighbors end @@ -93,25 +84,28 @@ function mapUtils.canMoveChunkDirection(direction, startChunk, endChunk) end function mapUtils.getNeighborChunksWithDirection(regionMap, chunkX, chunkY) - local neighbors = {{d=1},{d=2},{d=3},{d=4},{d=5},{d=6},{d=7},{d=8}} + local neighbors = {false,false,false,false,false,false,false,false} + local chunkYRow1 = chunkY - 1 + local chunkYRow3 = chunkY + 1 + local xChunks = regionMap[chunkX-1] if (xChunks ~= nil) then - neighbors[1].c = xChunks[chunkY-1] - neighbors[4].c = xChunks[chunkY] - neighbors[6].c = xChunks[chunkY+1] + neighbors[1] = xChunks[chunkYRow1] + neighbors[4] = xChunks[chunkY] + neighbors[6] = xChunks[chunkYRow3] end xChunks = regionMap[chunkX+1] if (xChunks ~= nil) then - neighbors[3].c = xChunks[chunkY-1] - neighbors[5].c = xChunks[chunkY] - neighbors[8].c = xChunks[chunkY+1] + neighbors[3] = xChunks[chunkYRow1] + neighbors[5] = xChunks[chunkY] + neighbors[8] = xChunks[chunkYRow3] end xChunks = regionMap[chunkX] if (xChunks ~= nil) then - neighbors[2].c = xChunks[chunkY-1] - neighbors[7].c = xChunks[chunkY+1] + neighbors[2] = xChunks[chunkYRow1] + neighbors[7] = xChunks[chunkYRow3] end return neighbors end @@ -124,48 +118,41 @@ end 4 ]]-- function mapUtils.getCardinalChunksWithDirection(regionMap, chunkX, chunkY) - local neighbors = {{d=1},{d=2},{d=3},{d=4}} + local neighbors = {false, false, false, false} local xChunks = regionMap[chunkX] if (xChunks ~= nil) then - neighbors[1].c = xChunks[chunkY-1] - neighbors[4].c = xChunks[chunkY+1] + neighbors[1] = xChunks[chunkY-1] + neighbors[4] = xChunks[chunkY+1] end xChunks = regionMap[chunkX-1] if (xChunks ~= nil) then - neighbors[2].c = xChunks[chunkY] + neighbors[2] = xChunks[chunkY] end xChunks = regionMap[chunkX+1] if (xChunks ~= nil) then - neighbors[3].c = xChunks[chunkY] + neighbors[3] = xChunks[chunkY] end return neighbors end function mapUtils.getCardinalChunks(regionMap, chunkX, chunkY) - local neighbors = {1,2,3,4} + local neighbors = {false,false,false,false} local xChunks = regionMap[chunkX] if (xChunks ~= nil) then neighbors[1] = xChunks[chunkY-1] neighbors[4] = xChunks[chunkY+1] - else - neighbors[1] = nil - neighbors[4] = nil end xChunks = regionMap[chunkX-1] if (xChunks ~= nil) then neighbors[2] = xChunks[chunkY] - else - neighbors[2] = nil end xChunks = regionMap[chunkX+1] if (xChunks ~= nil) then neighbors[3] = xChunks[chunkY] - else - neighbors[3] = nil end return neighbors end diff --git a/libs/NeighborUtils.lua b/libs/NeighborUtils.lua index 3d6dc74..afba73e 100644 --- a/libs/NeighborUtils.lua +++ b/libs/NeighborUtils.lua @@ -10,21 +10,25 @@ local MAGIC_MAXIMUM_NUMBER = constants.MAGIC_MAXIMUM_NUMBER -- module code + +--[[ + Expects all neighbors adjacent to a chunk +--]] function neighborUtils.scoreNeighborsWithDirection(chunk, neighborDirectionChunks, validFunction, scoreFunction, squad, surface, position, scoreSelf) local highestChunk local highestScore = -MAGIC_MAXIMUM_NUMBER local highestDirection - for x=1,#neighborDirectionChunks do - local neighborDirectionChunk = neighborDirectionChunks[x] - local neighborChunk = neighborDirectionChunk.c - if (neighborChunk ~= nil) and validFunction(x, chunk, neighborChunk) then + for x=1,8 do + local neighborChunk = neighborDirectionChunks[x] + + if neighborChunk and validFunction(x, chunk, neighborChunk) then position.x = neighborChunk.pX position.y = neighborChunk.pY local score = scoreFunction(position, squad, neighborChunk, surface) if (score > highestScore) then highestScore = score highestChunk = neighborChunk - highestDirection = neighborDirectionChunk.d + highestDirection = x end end end @@ -36,12 +40,16 @@ function neighborUtils.scoreNeighborsWithDirection(chunk, neighborDirectionChunk return highestChunk, highestDirection end + +--[[ + Expects all neighbors adjacent to a chunk +--]] function neighborUtils.scoreNeighbors(chunk, neighborChunks, validFunction, scoreFunction, squad, surface, position, scoreSelf) local highestChunk local highestScore = -MAGIC_MAXIMUM_NUMBER - for x=1,#neighborChunks do + for x=1,8 do local neighborChunk = neighborChunks[x] - if (neighborChunk ~= nil) and validFunction(x, chunk, neighborChunk) then + if neighborChunk and validFunction(x, chunk, neighborChunk) then position.x = neighborChunk.pX position.y = neighborChunk.pY local score = scoreFunction(position, squad, neighborChunk, surface) diff --git a/libs/PheromoneUtils.lua b/libs/PheromoneUtils.lua index 08e6f45..daa2988 100644 --- a/libs/PheromoneUtils.lua +++ b/libs/PheromoneUtils.lua @@ -64,21 +64,26 @@ function pheromoneUtils.processPheromone(regionMap, chunk) return end + local neighbors = getCardinalChunks(regionMap, chunk.cX, chunk.cY) + local chunkMovement = chunk[MOVEMENT_PHEROMONE] + local chunkBase = chunk[BASE_PHEROMONE] + local chunkPlayer = chunk[PLAYER_PHEROMONE] + local totalMovement = 0 local totalBase = 0 local totalPlayer = 0 - local neighbors = getCardinalChunks(regionMap, chunk.cX, chunk.cY) - for i=1,#neighbors do + + for i=1,4 do local neighborChunk = neighbors[i] - if (neighborChunk ~= nil) then - totalMovement = totalMovement + (neighborChunk[MOVEMENT_PHEROMONE] - chunk[MOVEMENT_PHEROMONE]) - totalBase = totalBase + (neighborChunk[BASE_PHEROMONE] - chunk[BASE_PHEROMONE]) - totalPlayer = totalPlayer + (neighborChunk[PLAYER_PHEROMONE] - chunk[PLAYER_PHEROMONE]) + if neighborChunk then + totalMovement = totalMovement + (neighborChunk[MOVEMENT_PHEROMONE] - chunkMovement) + totalBase = totalBase + (neighborChunk[BASE_PHEROMONE] - chunkBase) + totalPlayer = totalPlayer + (neighborChunk[PLAYER_PHEROMONE] - chunkPlayer) end end - chunk[MOVEMENT_PHEROMONE] = (chunk[MOVEMENT_PHEROMONE] + (0.125 * totalMovement)) * MOVEMENT_PHEROMONE_PERSISTANCE - chunk[BASE_PHEROMONE] = (chunk[BASE_PHEROMONE] + (0.25 * totalBase)) * BASE_PHEROMONE_PERSISTANCE - chunk[PLAYER_PHEROMONE] = (chunk[PLAYER_PHEROMONE] + (0.25 * totalPlayer)) * PLAYER_PHEROMONE_PERSISTANCE + chunk[MOVEMENT_PHEROMONE] = (chunkMovement + (0.125 * totalMovement)) * MOVEMENT_PHEROMONE_PERSISTANCE + chunk[BASE_PHEROMONE] = (chunkBase + (0.25 * totalBase)) * BASE_PHEROMONE_PERSISTANCE + chunk[PLAYER_PHEROMONE] = (chunkPlayer + (0.25 * totalPlayer)) * PLAYER_PHEROMONE_PERSISTANCE end return pheromoneUtils diff --git a/libs/UnitGroupUtils.lua b/libs/UnitGroupUtils.lua index 0c90bd1..8b5dd0e 100644 --- a/libs/UnitGroupUtils.lua +++ b/libs/UnitGroupUtils.lua @@ -4,7 +4,6 @@ local unitGroupUtils = {} local mapUtils = require("MapUtils") local constants = require("Constants") -local nocturnalUtils = require("NocturnalUtils") -- constants @@ -22,12 +21,16 @@ local NO_RETREAT_BASE_PERCENT = constants.NO_RETREAT_BASE_PERCENT local NO_RETREAT_EVOLUTION_BONUS_MAX = constants.NO_RETREAT_EVOLUTION_BONUS_MAX local NO_RETREAT_SQUAD_SIZE_BONUS_MAX = constants.NO_RETREAT_SQUAD_SIZE_BONUS_MAX +local AI_MAX_POINTS = constants.AI_MAX_POINTS +local AI_UNIT_REFUND = constants.AI_UNIT_REFUND + local AI_MAX_BITER_GROUP_SIZE = constants.AI_MAX_BITER_GROUP_SIZE -- imported functions local mLog = math.log10 + local tableRemove = table.remove local tableInsert = table.insert local euclideanDistanceNamed = mapUtils.euclideanDistanceNamed @@ -142,40 +145,78 @@ local function isAttacking(group) return (state == GROUP_STATE_ATTACKING_TARGET) or (state == GROUP_STATE_ATTACKING_DISTRACTION) end -function unitGroupUtils.regroupSquads(natives, evolution_factor) +function unitGroupUtils.cleanSquads(natives, evolution_factor) local squads = natives.squads local squadCount = #squads - for i=1,squadCount do - local squad = squads[i] - local group = squad.group - if group.valid then - local memberCount = #group.members - if (memberCount == 0) or (memberCount > AI_MAX_BITER_GROUP_SIZE) then - group.destroy() - end - end - end + local weight = AI_UNIT_REFUND * evolution_factor - local groupThreshold = AI_MAX_BITER_GROUP_SIZE * 0.75 + local cleanSquads = {} for i=1,squadCount do local squad = squads[i] local group = squad.group if group.valid then - local status = squad.status local memberCount = #group.members - local squadPosition = group.position - - if not isAttacking(group) and (memberCount < groupThreshold) then + if (memberCount == 0) then + group.destroy() + elseif (memberCount > AI_MAX_BITER_GROUP_SIZE) then + local members = group.members + for x=1,memberCount do + members[x].destroy() + natives.points = natives.points + weight + end + + if (natives.points > (AI_MAX_POINTS * 3)) then + natives.points = (AI_MAX_POINTS * 3) + end + group.destroy() + else + local status = squad.status + local squadPosition = group.position + local cycles = squad.cycles + if (status == SQUAD_RETREATING) and (cycles == 0) then + squad.status = SQUAD_GUARDING + squad.frenzy = true + squad.frenzyPosition.x = squadPosition.x + squad.frenzyPosition.y = squadPosition.y + elseif (group.state == GROUP_STATE_FINISHED) then + squad.status = SQUAD_GUARDING + elseif (cycles > 0) then + squad.cycles = cycles - 1 + end + + cleanSquads[#cleanSquads+1] = squad + end + end + end + + natives.squads = cleanSquads +end + + +function unitGroupUtils.regroupSquads(natives, evolution_factor) + local groupThreshold = AI_MAX_BITER_GROUP_SIZE * 0.75 + + local squads = natives.squads + local squadCount = #squads + + for i=1,squadCount do + local squad = squads[i] + local group = squad.group + if group.valid and not isAttacking(group) then + local status = squad.status + local memberCount = #group.members + if (memberCount < groupThreshold) then + local squadPosition = group.position local mergedSquads = false for x=i+1,squadCount do - local mergeSquad = squads[x] - local mergeGroup = mergeSquad.group - if mergeGroup.valid then - local mergeMembers = mergeGroup.members - local mergeCount = #mergeMembers - if ((mergeCount + memberCount) < AI_MAX_BITER_GROUP_SIZE) and (euclideanDistanceNamed(squadPosition, mergeGroup.position) < GROUP_MERGE_DISTANCE) and (mergeSquad.status == status) and not isAttacking(mergeGroup) then + local mergeSquad = squads[x] + local mergeGroup = mergeSquad.group + if mergeGroup.valid and (mergeSquad.status == status) and not isAttacking(mergeGroup) and (euclideanDistanceNamed(squadPosition, mergeGroup.position) < GROUP_MERGE_DISTANCE) 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 @@ -184,45 +225,22 @@ function unitGroupUtils.regroupSquads(natives, evolution_factor) end mergedSquads = true mergeGroup.destroy() - end - memberCount = memberCount + mergeCount - if (memberCount > groupThreshold) then - break - end - end + end + memberCount = memberCount + mergeCount + if (memberCount > groupThreshold) then + break + end + end end if mergedSquads and not squad.kamikaze then - local kamikazeThreshold = unitGroupUtils.calculateKamikazeThreshold(squad, natives, evolution_factor) - if (math.random() < kamikazeThreshold) then - squad.kamikaze = true - end + local kamikazeThreshold = unitGroupUtils.calculateKamikazeThreshold(squad, natives, evolution_factor) + if (math.random() < kamikazeThreshold) then + squad.kamikaze = true + end end end - - local cycles = squad.cycles - if (status == SQUAD_RETREATING) and (cycles == 0) then - squad.status = SQUAD_GUARDING - squad.frenzy = true - squad.frenzyPosition.x = squadPosition.x - squad.frenzyPosition.y = squadPosition.y - elseif (group.state == GROUP_STATE_FINISHED) then - squad.status = SQUAD_GUARDING - elseif (cycles > 0) then - squad.cycles = cycles - 1 - end end - end - - local cleanSquads = {} - for i=1,squadCount do - local squad = squads[i] - if squad.group.valid then - cleanSquads[#cleanSquads+1] = squad - end - end - - natives.squads = cleanSquads + end end - return unitGroupUtils diff --git a/locale/en/locale.cfg b/locale/en/locale.cfg index d885a08..d6a44c2 100644 --- a/locale/en/locale.cfg +++ b/locale/en/locale.cfg @@ -47,7 +47,7 @@ rampant-attackWaveGenerationUsePlayerProximity=Include player pheromones amount rampant-attackWaveGenerationThresholdMax=The total score that a chunk must reach in order for an attack wave to spawn. DOES NOT affect vanilla biters waves. Scaling linearly with evolution factor (starting threshold @ 0.0 evolution) rampant-attackWaveGenerationThresholdMin=The total score that a chunk must reach in order for an attack wave to spawn. DOES NOT affect vanilla biters waves. Scaling linearly with evolution factor (ending threshold @ 100.0 evolution) rampant-attackWaveMaxSize=If you wish to change how the attack wave scales with evolution you will need to modify the config.lua in the zip file. -rampant-safeBuildings=This needs to be toggle for the safe building toggles to work. Understand this can cause weird behavior due to the fact that this is a stopgap until a bug is fixed in factorio. +rampant-safeBuildings=This needs to be toggle for the safe building toggles to work. This will cause entities made safe to regenerate with the destructible flag set to false, which causes them to never be targeted or damaged again. rampant-safeBuildings-curvedRail=Make curved rails safe from biters rampant-safeBuildings-straightRail=Make straight rails safe from biters rampant-safeBuildings-bigElectricPole=Make big electric poles safe from biters