1
0
mirror of https://github.com/veden/Rampant.git synced 2024-12-26 20:54:12 +02:00

reworked squads to work with chunks

This commit is contained in:
Aaron Veden 2018-01-13 21:48:21 -08:00
parent c709c47011
commit 59b364398d
22 changed files with 588 additions and 453 deletions

View File

@ -105,7 +105,7 @@ function upgrade.attempt(natives)
-- used to precompute some values per logic cycle
natives.retreatThreshold = 0
natives.maxSquads = 0
-- natives.maxSquads = 0
natives.rallyThreshold = 0
natives.formSquadThreshold = 0
natives.attackWaveSize = 0
@ -163,10 +163,16 @@ function upgrade.attempt(natives)
game.surfaces[1].print("Rampant - Version 0.15.23")
global.version = constants.VERSION_33
end
if (global.version < constants.VERSION_37) then
if (global.version < constants.VERSION_38) then
game.surfaces[1].print("Rampant - Version 0.16.2")
global.version = constants.VERSION_37
for _,squad in pairs(natives.squads) do
squad.chunk = nil
end
global.regionMap = nil
game.surfaces[1].print("Rampant - Version 0.16.3")
global.version = constants.VERSION_38
end
return starting ~= global.version, natives
end

View File

@ -1,3 +1,13 @@
---------------------------------------------------------------------------------------------------
Version: 0.16.3
Date: 1. 13. 2018
Improvements:
- Removed squad limit
Optimizations:
- Reworked squad regrouping to be surrounding chunks instead comparing against master list
Framework:
- Add squads to chunk map for faster location sensitive lookups
---------------------------------------------------------------------------------------------------
Version: 0.16.2
Date: 1. 2. 2018

View File

@ -36,6 +36,14 @@ local RETREAT_GRAB_RADIUS = constants.RETREAT_GRAB_RADIUS
local RETREAT_SPAWNER_GRAB_RADIUS = constants.RETREAT_SPAWNER_GRAB_RADIUS
local DEFINES_COMMAND_GROUP = defines.command.group
local DEFINES_COMMAND_ATTACK_AREA = defines.command.attack_area
local CHUNK_SIZE = constants.CHUNK_SIZE
local DEFINES_DISTRACTION_NONE = defines.distraction.none
local DEFINES_DISTRACTION_BY_ENEMY = defines.distraction.by_enemy
-- imported functions
local roundToNearest = mathUtils.roundToNearest
@ -80,7 +88,7 @@ local mRandom = math.random
-- local references to global
local regionMap -- manages the chunks that make up the game world
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
@ -96,9 +104,9 @@ local function onIonCannonFired(event)
if (natives.points > AI_MAX_OVERFLOW_POINTS) then
natives.points = AI_MAX_OVERFLOW_POINTS
end
local chunk = getChunkByPosition(regionMap, event.position)
local chunk = getChunkByPosition(map, event.position)
if (chunk ~= SENTINEL_IMPASSABLE_CHUNK) then
rallyUnits(chunk, regionMap, surface, natives, event.tick)
rallyUnits(chunk, map, surface, natives, event.tick)
end
end
end
@ -113,7 +121,7 @@ local function hookEvents()
end
local function onLoad()
regionMap = global.regionMap
map = global.map
natives = global.natives
pendingChunks = global.pendingChunks
@ -130,27 +138,28 @@ end
local function rebuildRegionMap()
game.surfaces[1].print("Rampant - Reindexing chunks, please wait.")
-- clear old regionMap processing Queue
-- clear old map processing Queue
-- prevents queue adding duplicate chunks
-- chunks are by key, so should overwrite old
global.regionMap = {}
regionMap = global.regionMap
regionMap.processQueue = {}
regionMap.processIndex = 1
regionMap.scanIndex = 1
regionMap.chunkToHives = {}
regionMap.chunkToNests = {}
regionMap.chunkToWorms = {}
regionMap.chunkToRetreats = {}
regionMap.chunkToRallys = {}
regionMap.chunkToPlayerBase = {}
regionMap.chunkToResource = {}
regionMap.chunkToPassScan = {}
global.map = {}
map = global.map
map.processQueue = {}
map.processIndex = 1
map.scanIndex = 1
map.chunkToHives = {}
map.chunkToNests = {}
map.chunkToWorms = {}
map.chunkToRetreats = {}
map.chunkToRallys = {}
map.chunkToPlayerBase = {}
map.chunkToResource = {}
map.chunkToPassScan = {}
map.chunkToSquad = {}
-- preallocating memory to be used in code, making it fast by reducing garbage generated.
regionMap.neighbors = { SENTINEL_IMPASSABLE_CHUNK,
map.neighbors = { SENTINEL_IMPASSABLE_CHUNK,
SENTINEL_IMPASSABLE_CHUNK,
SENTINEL_IMPASSABLE_CHUNK,
SENTINEL_IMPASSABLE_CHUNK,
@ -158,29 +167,40 @@ local function rebuildRegionMap()
SENTINEL_IMPASSABLE_CHUNK,
SENTINEL_IMPASSABLE_CHUNK,
SENTINEL_IMPASSABLE_CHUNK }
regionMap.cardinalNeighbors = { SENTINEL_IMPASSABLE_CHUNK,
map.cardinalNeighbors = { SENTINEL_IMPASSABLE_CHUNK,
SENTINEL_IMPASSABLE_CHUNK,
SENTINEL_IMPASSABLE_CHUNK,
SENTINEL_IMPASSABLE_CHUNK }
regionMap.position = {x=0,
map.position = {x=0,
y=0}
--this is shared between two different queries
regionMap.area = {{0, 0}, {0, 0}}
regionMap.countResourcesQuery = { area=regionMap.area, type="resource" }
regionMap.filteredEntitiesEnemyQuery = { area=regionMap.area, force="enemy" }
regionMap.filteredEntitiesEnemyUnitQuery = { area=regionMap.area, force="enemy", type="unit", limit=301 }
regionMap.filteredEntitiesEnemyTypeQuery = { area=regionMap.area, force="enemy", type="unit-spawner" }
regionMap.filteredEntitiesPlayerQuery = { area=regionMap.area, force="player" }
regionMap.canPlaceQuery = { name="", position={0,0} }
regionMap.filteredTilesQuery = { name="", area=regionMap.area }
map.area = {{0, 0}, {0, 0}}
map.countResourcesQuery = { area=map.area, type="resource" }
map.filteredEntitiesEnemyQuery = { area=map.area, force="enemy" }
map.filteredEntitiesEnemyUnitQuery = { area=map.area, force="enemy", type="unit", limit=301 }
map.filteredEntitiesEnemyTypeQuery = { area=map.area, force="enemy", type="unit-spawner" }
map.filteredEntitiesPlayerQuery = { area=map.area, force="player" }
map.canPlaceQuery = { name="", position={0,0} }
map.filteredTilesQuery = { name="", area=map.area }
map.attackAreaCommand = {
type = DEFINES_COMMAND_ATTACK_AREA,
destination = map.position,
radius = CHUNK_SIZE,
distraction = DEFINES_DISTRACTION_BY_ENEMY
}
map.retreatCommand = { type = DEFINES_COMMAND_GROUP,
group = nil,
distraction = DEFINES_DISTRACTION_NONE }
-- switched over to tick event
regionMap.logicTick = roundToNearest(game.tick + INTERVAL_LOGIC, INTERVAL_LOGIC)
regionMap.scanTick = roundToNearest(game.tick + INTERVAL_SCAN, INTERVAL_SCAN)
regionMap.processTick = roundToNearest(game.tick + INTERVAL_PROCESS, INTERVAL_PROCESS)
regionMap.chunkTick = roundToNearest(game.tick + INTERVAL_CHUNK, INTERVAL_CHUNK)
regionMap.squadTick = roundToNearest(game.tick + INTERVAL_SQUAD, INTERVAL_SQUAD)
map.logicTick = roundToNearest(game.tick + INTERVAL_LOGIC, INTERVAL_LOGIC)
map.scanTick = roundToNearest(game.tick + INTERVAL_SCAN, INTERVAL_SCAN)
map.processTick = roundToNearest(game.tick + INTERVAL_PROCESS, INTERVAL_PROCESS)
map.chunkTick = roundToNearest(game.tick + INTERVAL_CHUNK, INTERVAL_CHUNK)
map.squadTick = roundToNearest(game.tick + INTERVAL_SQUAD, INTERVAL_SQUAD)
-- clear pending chunks, will be added when loop runs below
global.pendingChunks = {}
@ -196,7 +216,7 @@ local function rebuildRegionMap()
y = chunk.y * 32 }}})
end
processPendingChunks(natives, regionMap, surface, pendingChunks, tick)
processPendingChunks(natives, map, surface, pendingChunks, tick)
end
local function onModSettingsChange(event)
@ -260,28 +280,28 @@ end
local function onTick(event)
local tick = event.tick
if (tick == regionMap.processTick) then
regionMap.processTick = regionMap.processTick + INTERVAL_PROCESS
if (tick == map.processTick) then
map.processTick = map.processTick + INTERVAL_PROCESS
local gameRef = game
local surface = gameRef.surfaces[1]
processPlayers(gameRef.players, regionMap, surface, natives, tick)
processPlayers(gameRef.players, map, surface, natives, tick)
processMap(regionMap, surface, natives, tick)
processMap(map, surface, natives, tick)
end
if (tick == regionMap.scanTick) then
regionMap.scanTick = regionMap.scanTick + INTERVAL_SCAN
if (tick == map.scanTick) then
map.scanTick = map.scanTick + INTERVAL_SCAN
local surface = game.surfaces[1]
processPendingChunks(natives, regionMap, surface, pendingChunks, tick)
processPendingChunks(natives, map, surface, pendingChunks, tick)
scanMap(regionMap, surface, natives)
scanMap(map, surface, natives)
regionMap.chunkToPassScan = processScanChunks(regionMap, surface)
map.chunkToPassScan = processScanChunks(map, surface)
end
if (tick == regionMap.logicTick) then
regionMap.logicTick = regionMap.logicTick + INTERVAL_LOGIC
if (tick == map.logicTick) then
map.logicTick = map.logicTick + INTERVAL_LOGIC
local gameRef = game
local surface = gameRef.surfaces[1]
@ -292,26 +312,26 @@ local function onTick(event)
surface)
if natives.useCustomAI then
processBases(regionMap, surface, natives, tick)
processBases(map, surface, natives, tick)
end
end
if (tick == regionMap.squadTick) then
regionMap.squadTick = regionMap.squadTick + INTERVAL_SQUAD
if (tick == map.squadTick) then
map.squadTick = map.squadTick + INTERVAL_SQUAD
local gameRef = game
cleanSquads(natives)
regroupSquads(natives)
cleanSquads(natives, map)
regroupSquads(natives, map)
squadsBeginAttack(natives, gameRef.players)
squadsAttack(regionMap, gameRef.surfaces[1], natives)
squadsAttack(map, gameRef.surfaces[1], natives)
end
end
local function onBuild(event)
local entity = event.created_entity
if (entity.surface.index == 1) then
addRemovePlayerEntity(regionMap, entity, natives, true, false)
addRemovePlayerEntity(map, entity, natives, true, false)
if natives.safeBuildings then
if natives.safeEntities[entity.type] or natives.safeEntityName[entity.name] then
entity.destructible = false
@ -324,7 +344,7 @@ local function onMine(event)
local entity = event.entity
local surface = entity.surface
if (surface.index == 1) then
addRemovePlayerEntity(regionMap, entity, natives, false, false)
addRemovePlayerEntity(map, entity, natives, false, false)
end
end
@ -333,7 +353,7 @@ local function onDeath(event)
local surface = entity.surface
if (surface.index == 1) then
local entityPosition = entity.position
local chunk = getChunkByPosition(regionMap, entityPosition)
local chunk = getChunkByPosition(map, entityPosition)
local cause = event.cause
if (entity.force.name == "enemy") then
if (entity.type == "unit") then
@ -350,7 +370,7 @@ local function onDeath(event)
retreatUnits(chunk,
entityPosition,
convertUnitGroupToSquad(natives, entity.unit_group),
regionMap,
map,
surface,
natives,
tick,
@ -358,7 +378,7 @@ local function onDeath(event)
artilleryBlast)
if (mRandom() < natives.rallyThreshold) and not surface.peaceful_mode then
rallyUnits(chunk, regionMap, surface, natives, tick)
rallyUnits(chunk, map, surface, natives, tick)
end
end
end
@ -367,14 +387,14 @@ local function onDeath(event)
local tick = event.tick
if (chunk ~= SENTINEL_IMPASSABLE_CHUNK) then
unregisterEnemyBaseStructure(regionMap, entity)
unregisterEnemyBaseStructure(map, entity)
rallyUnits(chunk, regionMap, surface, natives, tick)
rallyUnits(chunk, map, surface, natives, tick)
retreatUnits(chunk,
entityPosition,
nil,
regionMap,
map,
surface,
natives,
tick,
@ -393,7 +413,7 @@ local function onDeath(event)
if creditNatives and natives.safeBuildings and (natives.safeEntities[entity.type] or natives.safeEntityName[entity.name]) then
makeImmortalEntity(surface, entity)
else
addRemovePlayerEntity(regionMap, entity, natives, false, creditNatives)
addRemovePlayerEntity(map, entity, natives, false, creditNatives)
end
end
end
@ -403,7 +423,7 @@ local function onEnemyBaseBuild(event)
local entity = event.entity
local surface = entity.surface
if (surface.index == 1) then
registerEnemyBaseStructure(regionMap, entity, nil)
registerEnemyBaseStructure(map, entity, nil)
end
end
@ -414,11 +434,11 @@ local function onSurfaceTileChange(event)
local positions = event.positions
for i=1,#positions do
local position = positions[i]
local chunk = mapUtils.getChunkByPosition(regionMap, position, true)
local chunk = mapUtils.getChunkByPosition(map, position, true)
-- weird bug with table pointer equality using name instead pointer comparison
if not chunk.name then
regionMap.chunkToPassScan[chunk] = true
map.chunkToPassScan[chunk] = true
else
local x,y = mapUtils.positionToChunkXY(position)
local addMe = true
@ -444,7 +464,7 @@ end
local function onResourceDepleted(event)
local entity = event.entity
if (entity.surface.index == 1) then
chunkUtils.unregisterResource(entity, regionMap)
chunkUtils.unregisterResource(entity, map)
end
end
@ -455,17 +475,17 @@ local function onUsedCapsule(event)
{event.position.x+0.75,event.position.y+0.75}},
type="cliff"})
for i=1,#cliffs do
entityForPassScan(regionMap, cliffs[i])
entityForPassScan(map, cliffs[i])
end
end
end
local function onInit()
global.regionMap = {}
global.map = {}
global.pendingChunks = {}
global.natives = {}
regionMap = global.regionMap
map = global.map
natives = global.natives
pendingChunks = global.pendingChunks

View File

@ -81,23 +81,25 @@ local function scoreUnitGroupLocation(neighborChunk)
return neighborChunk[PLAYER_PHEROMONE] + neighborChunk[MOVEMENT_PHEROMONE] + neighborChunk[BASE_PHEROMONE]
end
local function validUnitGroupLocation(regionMap, neighborChunk)
return neighborChunk[PASSABLE] == CHUNK_ALL_DIRECTIONS and (getNestCount(regionMap, neighborChunk) == 0)
local function validUnitGroupLocation(map, neighborChunk)
return neighborChunk[PASSABLE] == CHUNK_ALL_DIRECTIONS and (getNestCount(map, neighborChunk) == 0)
end
function aiAttackWave.rallyUnits(chunk, regionMap, surface, natives, tick)
if ((tick - getRallyTick(regionMap, chunk) > INTERVAL_LOGIC) and (natives.points >= AI_VENGENCE_SQUAD_COST) and
(#natives.squads < natives.maxSquads)) then
setRallyTick(regionMap, chunk, tick)
function aiAttackWave.rallyUnits(chunk, map, surface, natives, tick)
if ((tick - getRallyTick(map, chunk) > INTERVAL_LOGIC) and (natives.points >= AI_VENGENCE_SQUAD_COST) -- and
-- (#natives.squads < natives.maxSquads)
) 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
if (x ~= cX) and (y ~= cY) then
local rallyChunk = getChunkByXY(regionMap, x, y)
if (rallyChunk ~= SENTINEL_IMPASSABLE_CHUNK) and (getNestCount(regionMap, rallyChunk) > 0) then
aiAttackWave.formSquads(regionMap, surface, natives, rallyChunk, AI_VENGENCE_SQUAD_COST)
if (natives.points < AI_VENGENCE_SQUAD_COST) and (#natives.squads < natives.maxSquads) then
local rallyChunk = getChunkByXY(map, x, y)
if (rallyChunk ~= SENTINEL_IMPASSABLE_CHUNK) and (getNestCount(map, rallyChunk) > 0) then
aiAttackWave.formSquads(map, surface, natives, rallyChunk, AI_VENGENCE_SQUAD_COST)
if (natives.points < AI_VENGENCE_SQUAD_COST) -- and (#natives.squads < natives.maxSquads)
then
return
end
end
@ -107,20 +109,20 @@ function aiAttackWave.rallyUnits(chunk, regionMap, surface, natives, tick)
end
end
function aiAttackWave.formSquads(regionMap, surface, natives, chunk, cost)
function aiAttackWave.formSquads(map, surface, natives, chunk, cost)
local valid = (cost == AI_VENGENCE_SQUAD_COST) or ((cost == AI_SQUAD_COST) and attackWaveValidCandidate(chunk, natives, surface))
if valid and (mRandom() < natives.formSquadThreshold) then
local squadPath, squadDirection = scoreNeighborsForFormation(getNeighborChunks(regionMap, chunk.x, chunk.y),
local squadPath, squadDirection = scoreNeighborsForFormation(getNeighborChunks(map, chunk.x, chunk.y),
validUnitGroupLocation,
scoreUnitGroupLocation,
regionMap)
map)
if (squadPath ~= SENTINEL_IMPASSABLE_CHUNK) then
local squadPosition = surface.find_non_colliding_position("biter-spawner-hive-rampant",
positionFromDirectionAndChunk(squadDirection,
chunk,
regionMap.position,
map.position,
0.98),
CHUNK_SIZE,
4)

View File

@ -65,7 +65,7 @@ function aiPlanning.planning(natives, evolution_factor, tick, surface)
local attackWaveMaxSize = natives.attackWaveMaxSize
natives.retreatThreshold = -(evolution_factor * RETREAT_MOVEMENT_PHEROMONE_LEVEL)
natives.maxSquads = AI_MAX_SQUAD_COUNT * evolution_factor
-- natives.maxSquads = AI_MAX_SQUAD_COUNT * evolution_factor
natives.rallyThreshold = BASE_RALLY_CHANCE + (evolution_factor * BONUS_RALLY_CHANCE)
natives.formSquadThreshold = mMax((0.25 * evolution_factor), 0.10)
natives.attackWaveSize = attackWaveMaxSize * (evolution_factor ^ 1.66667)

View File

@ -15,7 +15,8 @@ local AI_STATE_NOCTURNAL = constants.AI_STATE_NOCTURNAL
function aiPredicates.canAttack(natives, surface)
return (((natives.state == AI_STATE_AGGRESSIVE) or aiPredicates.canAttackDark(natives, surface))
and not surface.peaceful_mode and (#natives.squads < natives.maxSquads))
and not surface.peaceful_mode -- and (#natives.squads < natives.maxSquads)
)
end
function aiPredicates.isDark(surface)

View File

@ -19,7 +19,7 @@ local advanceTendrils = tendrilUtils.advanceTendrils
-- module code
function baseProcessor.processBases(regionMap, surface, natives, tick)
function baseProcessor.processBases(map, surface, natives, tick)
local baseIndex = natives.baseIndex
local bases = natives.bases
@ -27,8 +27,8 @@ function baseProcessor.processBases(regionMap, surface, natives, tick)
for index = baseIndex, endIndex do
local base = bases[index]
buildOrder(regionMap, natives, base, surface, tick)
advanceTendrils(regionMap, base, surface, tick, natives)
buildOrder(map, natives, base, surface, tick)
advanceTendrils(map, base, surface, tick, natives)
end
if (endIndex == #bases) then

View File

@ -47,7 +47,7 @@ function baseUtils.findNearbyBase(natives, position)
return foundBase
end
function baseUtils.createBase(regionMap, natives, position, surface, tick)
function baseUtils.createBase(map, natives, position, surface, tick)
local bases = natives.bases
local distance = euclideanDistancePoints(position.x, position.y, 0, 0)
local base = {
@ -65,10 +65,10 @@ function baseUtils.createBase(regionMap, natives, position, surface, tick)
pattern = mRandom(MAGIC_MAXIMUM_BASE_NUMBER),
level = mFloor(distance / 200)
}
if not buildHive(regionMap, base, surface) then
if not buildHive(map, base, surface) then
return nil
end
-- buildTendril(regionMap, natives, base, surface, tick)
-- buildTendril(map, natives, base, surface, tick)
bases[#bases+1] = base
return base
end

View File

@ -19,10 +19,10 @@ local chunkPassScan = chunkUtils.chunkPassScan
-- module code
function chunkProcessor.processPendingChunks(natives, regionMap, surface, pendingStack)
local processQueue = regionMap.processQueue
function chunkProcessor.processPendingChunks(natives, map, surface, pendingStack)
local processQueue = map.processQueue
local area = regionMap.area
local area = map.area
local topOffset = area[1]
local bottomOffset = area[2]
@ -41,30 +41,30 @@ function chunkProcessor.processPendingChunks(natives, regionMap, surface, pendin
bottomOffset[1] = x + CHUNK_SIZE
bottomOffset[2] = y + CHUNK_SIZE
chunk = initialScan(chunk, natives, surface, regionMap)
chunk = initialScan(chunk, natives, surface, map)
if (chunk ~= SENTINEL_IMPASSABLE_CHUNK) then
local chunkX = chunk.x
if regionMap[chunkX] == nil then
regionMap[chunkX] = {}
if map[chunkX] == nil then
map[chunkX] = {}
end
regionMap[chunkX][chunk.y] = chunk
map[chunkX][chunk.y] = chunk
processQueue[#processQueue+1] = chunk
end
end
end
function chunkProcessor.processScanChunks(regionMap, surface)
local area = regionMap.area
function chunkProcessor.processScanChunks(map, surface)
local area = map.area
local topOffset = area[1]
local bottomOffset = area[2]
local removals = {}
for chunk,_ in pairs(regionMap.chunkToPassScan) do
for chunk,_ in pairs(map.chunkToPassScan) do
local x = chunk.x
local y = chunk.y
@ -73,17 +73,17 @@ function chunkProcessor.processScanChunks(regionMap, surface)
bottomOffset[1] = x + CHUNK_SIZE
bottomOffset[2] = y + CHUNK_SIZE
chunk = chunkPassScan(chunk, surface, regionMap)
chunk = chunkPassScan(chunk, surface, map)
if (chunk == SENTINEL_IMPASSABLE_CHUNK) then
regionMap[x][y] = nil
map[x][y] = nil
removals[#removals+1] = chunk
end
end
for i=#removals,1,-1 do
table.remove(regionMap.processQueue, i)
table.remove(map.processQueue, i)
end
return {}

View File

@ -39,6 +39,8 @@ local RESOURCE_GENERATOR_INCREMENT = constants.RESOURCE_GENERATOR_INCREMENT
local getChunkByUnalignedXY = mapUtils.getChunkByUnalignedXY
local tRemove = table.remove
local mFloor = math.floor
-- module code
@ -76,12 +78,12 @@ local function fullScan(chunk, can_place_entity, canPlaceQuery)
return passableNorthSouth, passableEastWest
end
local function addEnemyStructureToChunk(regionMap, chunk, entity, base)
local function addEnemyStructureToChunk(map, chunk, entity, base)
local lookup
if (entity.type == "unit-spawner") then
lookup = regionMap.chunkToNests
lookup = map.chunkToNests
elseif (entity.type == "turret") then
lookup = regionMap.chunkToWorms
lookup = map.chunkToWorms
else
return
end
@ -93,22 +95,22 @@ local function addEnemyStructureToChunk(regionMap, chunk, entity, base)
lookup[chunk] = lookup[chunk] + 1
-- if base then
-- local baseCollection = regionMap.chunkToBases[chunk]
-- local baseCollection = map.chunkToBases[chunk]
-- if not baseCollection then
-- baseCollection = {}
-- regionMap.chunkToBases[chunk] = baseCollection
-- map.chunkToBases[chunk] = baseCollection
-- end
-- baseCollection[base.id] = chunk
-- end
end
local function removeEnemyStructureFromChunk(regionMap, chunk, entity)
local function removeEnemyStructureFromChunk(map, chunk, entity)
local lookup
if (entity.type == "unit-spawner") then
lookup = regionMap.chunkToNests
lookup = map.chunkToNests
elseif (entity.type == "turret") then
lookup = regionMap.chunkToWorms
lookup = map.chunkToWorms
else
return
end
@ -137,7 +139,7 @@ local function removeEnemyStructureFromChunk(regionMap, chunk, entity)
end
end
local function getEntityOverlapChunks(regionMap, entity)
local function getEntityOverlapChunks(map, entity)
local boundingBox = entity.prototype.collision_box or entity.prototype.selection_box;
local leftTopChunk = SENTINEL_IMPASSABLE_CHUNK
@ -179,15 +181,15 @@ local function getEntityOverlapChunks(regionMap, entity)
local rightBottomChunkX = rightTopChunkX
local rightBottomChunkY = leftBottomChunkY
leftTopChunk = getChunkByUnalignedXY(regionMap, leftTopChunkX, leftTopChunkY)
leftTopChunk = getChunkByUnalignedXY(map, leftTopChunkX, leftTopChunkY)
if (leftTopChunkX ~= rightTopChunkX) then
rightTopChunk = getChunkByUnalignedXY(regionMap, rightTopChunkX, rightTopChunkY)
rightTopChunk = getChunkByUnalignedXY(map, rightTopChunkX, rightTopChunkY)
end
if (leftTopChunkY ~= leftBottomChunkY) then
leftBottomChunk = getChunkByUnalignedXY(regionMap, leftBottomChunkX, leftBottomChunkY)
leftBottomChunk = getChunkByUnalignedXY(map, leftBottomChunkX, leftBottomChunkY)
end
if (leftTopChunkX ~= rightBottomChunkX) and (leftTopChunkY ~= rightBottomChunkY) then
rightBottomChunk = getChunkByUnalignedXY(regionMap, rightBottomChunkX, rightBottomChunkY)
rightBottomChunk = getChunkByUnalignedXY(map, rightBottomChunkX, rightBottomChunkY)
end
end
return leftTopChunk, rightTopChunk, leftBottomChunk, rightBottomChunk
@ -195,9 +197,9 @@ end
-- external functions
function chunkUtils.calculatePassScore(surface, regionMap)
function chunkUtils.calculatePassScore(surface, map)
local count_tiles_filtered = surface.count_tiles_filtered
local filteredTilesQuery = regionMap.filteredTilesQuery
local filteredTilesQuery = map.filteredTilesQuery
local passScore = 0
for i=1,#WATER_TILE_NAMES do
@ -208,11 +210,11 @@ function chunkUtils.calculatePassScore(surface, regionMap)
return 1 - (passScore * 0.0009765625)
end
function chunkUtils.scanChunkPaths(chunk, surface, regionMap)
function chunkUtils.scanChunkPaths(chunk, surface, map)
local pass = CHUNK_IMPASSABLE
local passableNorthSouth, passableEastWest = fullScan(chunk,
surface.can_place_entity,
regionMap.canPlaceQuery)
map.canPlaceQuery)
if passableEastWest and passableNorthSouth then
pass = CHUNK_ALL_DIRECTIONS
@ -224,8 +226,8 @@ function chunkUtils.scanChunkPaths(chunk, surface, regionMap)
return pass
end
function chunkUtils.scorePlayerBuildings(surface, regionMap, natives)
local entities = surface.find_entities_filtered(regionMap.filteredEntitiesPlayerQuery)
function chunkUtils.scorePlayerBuildings(surface, map, natives)
local entities = surface.find_entities_filtered(map.filteredEntitiesPlayerQuery)
local playerObjects = 0
local safeBuildings = natives.safeBuildings
@ -251,38 +253,38 @@ function chunkUtils.scorePlayerBuildings(surface, regionMap, natives)
return playerObjects
end
function chunkUtils.scoreEnemyBuildings(surface, regionMap)
local query = regionMap.filteredEntitiesEnemyTypeQuery
function chunkUtils.scoreEnemyBuildings(surface, map)
local query = map.filteredEntitiesEnemyTypeQuery
query.type = "unit-spawner"
local nests = surface.count_entities_filtered(regionMap.filteredEntitiesEnemyTypeQuery)
local nests = surface.count_entities_filtered(map.filteredEntitiesEnemyTypeQuery)
query.type = "turret"
local worms = surface.count_entities_filtered(regionMap.filteredEntitiesEnemyTypeQuery)
local worms = surface.count_entities_filtered(map.filteredEntitiesEnemyTypeQuery)
return nests, worms
end
function chunkUtils.initialScan(chunk, natives, surface, regionMap)
local passScore = chunkUtils.calculatePassScore(surface, regionMap)
function chunkUtils.initialScan(chunk, natives, surface, map)
local passScore = chunkUtils.calculatePassScore(surface, map)
if (passScore >= 0.40) then
local pass = chunkUtils.scanChunkPaths(chunk, surface, regionMap)
local pass = chunkUtils.scanChunkPaths(chunk, surface, map)
local playerObjects = chunkUtils.scorePlayerBuildings(surface, regionMap, natives)
local playerObjects = chunkUtils.scorePlayerBuildings(surface, map, natives)
local nests, worms = chunkUtils.scoreEnemyBuildings(surface, regionMap)
local nests, worms = chunkUtils.scoreEnemyBuildings(surface, map)
local resources = surface.count_entities_filtered(regionMap.countResourcesQuery) * 0.001
local resources = surface.count_entities_filtered(map.countResourcesQuery) * 0.001
if ((playerObjects > 0) or (nests > 0)) and (pass == CHUNK_IMPASSABLE) then
pass = CHUNK_ALL_DIRECTIONS
end
chunkUtils.setNestCount(regionMap, chunk, nests)
chunkUtils.setPlayerBaseGenerator(regionMap, chunk, playerObjects)
chunkUtils.setResourceGenerator(regionMap, chunk, resources)
chunkUtils.setWormCount(regionMap, chunk, worms)
chunkUtils.setNestCount(map, chunk, nests)
chunkUtils.setPlayerBaseGenerator(map, chunk, playerObjects)
chunkUtils.setResourceGenerator(map, chunk, resources)
chunkUtils.setWormCount(map, chunk, worms)
chunk[PASSABLE] = pass
chunk[PATH_RATING] = passScore
@ -293,15 +295,15 @@ function chunkUtils.initialScan(chunk, natives, surface, regionMap)
return SENTINEL_IMPASSABLE_CHUNK
end
function chunkUtils.chunkPassScan(chunk, surface, regionMap)
local passScore = chunkUtils.calculatePassScore(surface, regionMap)
function chunkUtils.chunkPassScan(chunk, surface, map)
local passScore = chunkUtils.calculatePassScore(surface, map)
if (passScore >= 0.40) then
local pass = chunkUtils.scanChunkPaths(chunk, surface, regionMap)
local pass = chunkUtils.scanChunkPaths(chunk, surface, map)
local playerObjects = chunkUtils.getPlayerBaseGenerator(regionMap, chunk)
local playerObjects = chunkUtils.getPlayerBaseGenerator(map, chunk)
local nests = chunkUtils.getNestCount(regionMap, chunk)
local nests = chunkUtils.getNestCount(map, chunk)
if ((playerObjects > 0) or (nests > 0)) and (pass == CHUNK_IMPASSABLE) then
pass = CHUNK_ALL_DIRECTIONS
@ -316,12 +318,12 @@ function chunkUtils.chunkPassScan(chunk, surface, regionMap)
return SENTINEL_IMPASSABLE_CHUNK
end
function chunkUtils.analyzeChunk(chunk, natives, surface, regionMap)
local playerObjects = chunkUtils.scorePlayerBuildings(surface, regionMap, natives)
chunkUtils.setPlayerBaseGenerator(regionMap, chunk, playerObjects)
function chunkUtils.analyzeChunk(chunk, natives, surface, map)
local playerObjects = chunkUtils.scorePlayerBuildings(surface, map, natives)
chunkUtils.setPlayerBaseGenerator(map, chunk, playerObjects)
end
-- function chunkUtils.remakeChunk(regionMap, chunk, surface, natives, tick, tempQuery)
-- function chunkUtils.remakeChunk(map, chunk, surface, natives, tick, tempQuery)
-- tempQuery.force = "enemy"
-- local enemies = surface.find_entities_filtered(tempQuery)
@ -340,94 +342,129 @@ end
-- enemy.destroy()
-- end
-- end
-- -- local foundBase = findNearbyBase(natives, chunk) or createBase(regionMap, natives, chunk, surface, tick)
-- -- local foundBase = findNearbyBase(natives, chunk) or createBase(map, natives, chunk, surface, tick)
-- -- if foundBase then
-- -- foundBase.upgradePoints = foundBase.upgradePoints + points
-- -- end
-- end
function chunkUtils.getNestCount(regionMap, chunk)
return regionMap.chunkToNests[chunk] or 0
function chunkUtils.getNestCount(map, chunk)
return map.chunkToNests[chunk] or 0
end
function chunkUtils.getWormCount(regionMap, chunk)
return regionMap.chunkToWorms[chunk] or 0
function chunkUtils.getWormCount(map, chunk)
return map.chunkToWorms[chunk] or 0
end
function chunkUtils.setWormCount(regionMap, chunk, count)
function chunkUtils.setWormCount(map, chunk, count)
if (count == 0) then
regionMap.chunkToWorms[chunk] = nil
map.chunkToWorms[chunk] = nil
else
regionMap.chunkToWorms[chunk] = count
map.chunkToWorms[chunk] = count
end
end
function chunkUtils.setNestCount(regionMap, chunk, count)
function chunkUtils.setNestCount(map, chunk, count)
if (count == 0) then
regionMap.chunkToNests[chunk] = nil
map.chunkToNests[chunk] = nil
else
regionMap.chunkToNests[chunk] = count
map.chunkToNests[chunk] = count
end
end
function chunkUtils.getNestCount(regionMap, chunk)
return regionMap.chunkToNests[chunk] or 0
function chunkUtils.getNestCount(map, chunk)
return map.chunkToNests[chunk] or 0
end
function chunkUtils.getWormCount(regionMap, chunk)
return regionMap.chunkToWorms[chunk] or 0
function chunkUtils.getWormCount(map, chunk)
return map.chunkToWorms[chunk] or 0
end
function chunkUtils.getEnemyStructureCount(regionMap, chunk)
return (regionMap.chunkToNests[chunk] or 0) + (regionMap.chunkToWorms[chunk] or 0)
function chunkUtils.getEnemyStructureCount(map, chunk)
return (map.chunkToNests[chunk] or 0) + (map.chunkToWorms[chunk] or 0)
end
function chunkUtils.getRetreatTick(regionMap, chunk)
return regionMap.chunkToRetreats[chunk] or 0
function chunkUtils.getRetreatTick(map, chunk)
return map.chunkToRetreats[chunk] or 0
end
function chunkUtils.getRallyTick(regionMap, chunk)
return regionMap.chunkToRallys[chunk] or 0
function chunkUtils.getRallyTick(map, chunk)
return map.chunkToRallys[chunk] or 0
end
function chunkUtils.setRallyTick(regionMap, chunk, tick)
regionMap.chunkToRallys[chunk] = tick
function chunkUtils.setRallyTick(map, chunk, tick)
map.chunkToRallys[chunk] = tick
end
function chunkUtils.setRetreatTick(regionMap, chunk, tick)
regionMap.chunkToRetreats[chunk] = tick
function chunkUtils.setRetreatTick(map, chunk, tick)
map.chunkToRetreats[chunk] = tick
end
function chunkUtils.setResourceGenerator(regionMap, chunk, resourceGenerator)
function chunkUtils.setResourceGenerator(map, chunk, resourceGenerator)
if (resourceGenerator == 0) then
regionMap.chunkToResource[chunk] = nil
map.chunkToResource[chunk] = nil
else
regionMap.chunkToResource[chunk] = resourceGenerator
map.chunkToResource[chunk] = resourceGenerator
end
end
function chunkUtils.getResourceGenerator(regionMap, chunk)
return regionMap.chunkToResource[chunk] or 0
function chunkUtils.getResourceGenerator(map, chunk)
return map.chunkToResource[chunk] or 0
end
function chunkUtils.addResourceGenerator(regionMap, chunk, delta)
regionMap.chunkToResource[chunk] = (regionMap.chunkToResource[chunk] or 0) + delta
function chunkUtils.addResourceGenerator(map, chunk, delta)
map.chunkToResource[chunk] = (map.chunkToResource[chunk] or 0) + delta
end
function chunkUtils.getPlayerBaseGenerator(regionMap, chunk)
return regionMap.chunkToPlayerBase[chunk] or 0
function chunkUtils.getPlayerBaseGenerator(map, chunk)
return map.chunkToPlayerBase[chunk] or 0
end
function chunkUtils.setPlayerBaseGenerator(regionMap, chunk, playerGenerator)
function chunkUtils.addSquadToChunk(map, chunk, squad)
if (chunk ~= squad.chunk) then
local chunkToSquad = map.chunkToSquad
chunkUtils.removeSquadFromChunk(map, squad)
if not chunkToSquad[chunk] then
chunkToSquad[chunk] = {}
end
chunkToSquad[chunk][#chunkToSquad[chunk]+1] = squad
squad.chunk = chunk
end
end
function chunkUtils.removeSquadFromChunk(map, squad)
local chunkToSquad = map.chunkToSquad
if squad.chunk then
local squads = chunkToSquad[squad.chunk]
if squads then
for i=#squads, 1, -1 do
if (squads[i] == squad) then
tRemove(squads, i)
break
end
end
if (#squads == 0) then
chunkToSquad[squad.chunk] = nil
end
end
end
end
function chunkUtils.getSquadsOnChunk(map, chunk)
return map.chunkToSquad[chunk] or {}
end
function chunkUtils.setPlayerBaseGenerator(map, chunk, playerGenerator)
if (playerGenerator == 0) then
regionMap.chunkToPlayerBase[chunk] = nil
map.chunkToPlayerBase[chunk] = nil
else
regionMap.chunkToPlayerBase[chunk] = playerGenerator
map.chunkToPlayerBase[chunk] = playerGenerator
end
end
function chunkUtils.addPlayerBaseGenerator(regionMap, chunk, playerGenerator)
regionMap.chunkToPlayerBase[chunk] = (regionMap.chunkToPlayerBase[chunk] or 0) + playerGenerator
function chunkUtils.addPlayerBaseGenerator(map, chunk, playerGenerator)
map.chunkToPlayerBase[chunk] = (map.chunkToPlayerBase[chunk] or 0) + playerGenerator
end
function chunkUtils.createChunk(topX, topY)
@ -456,70 +493,70 @@ function chunkUtils.colorChunk(x, y, tileType, surface)
surface.set_tiles(tiles, false)
end
function chunkUtils.entityForPassScan(regionMap, entity)
local leftTop, rightTop, leftBottom, rightBottom = getEntityOverlapChunks(regionMap, entity)
function chunkUtils.entityForPassScan(map, entity)
local leftTop, rightTop, leftBottom, rightBottom = getEntityOverlapChunks(map, entity)
if (leftTop ~= SENTINEL_IMPASSABLE_CHUNK) then
regionMap.chunkToPassScan[leftTop] = true
map.chunkToPassScan[leftTop] = true
end
if (rightTop ~= SENTINEL_IMPASSABLE_CHUNK) then
regionMap.chunkToPassScan[rightTop] = true
map.chunkToPassScan[rightTop] = true
end
if (leftBottom ~= SENTINEL_IMPASSABLE_CHUNK) then
regionMap.chunkToPassScan[leftBottom] = true
map.chunkToPassScan[leftBottom] = true
end
if (rightBottom ~= SENTINEL_IMPASSABLE_CHUNK) then
regionMap.chunkToPassScan[rightBottom] = true
map.chunkToPassScan[rightBottom] = true
end
end
function chunkUtils.registerEnemyBaseStructure(regionMap, entity, base)
function chunkUtils.registerEnemyBaseStructure(map, entity, base)
local entityType = entity.type
if ((entityType == "unit-spawner") or (entityType == "turret")) and (entity.force.name == "enemy") then
local leftTop, rightTop, leftBottom, rightBottom = getEntityOverlapChunks(regionMap, entity)
local leftTop, rightTop, leftBottom, rightBottom = getEntityOverlapChunks(map, entity)
if (leftTop ~= SENTINEL_IMPASSABLE_CHUNK) then
addEnemyStructureToChunk(regionMap, leftTop, entity, base)
addEnemyStructureToChunk(map, leftTop, entity, base)
end
if (rightTop ~= SENTINEL_IMPASSABLE_CHUNK) then
addEnemyStructureToChunk(regionMap, rightTop, entity, base)
addEnemyStructureToChunk(map, rightTop, entity, base)
end
if (leftBottom ~= SENTINEL_IMPASSABLE_CHUNK) then
addEnemyStructureToChunk(regionMap, leftBottom, entity, base)
addEnemyStructureToChunk(map, leftBottom, entity, base)
end
if (rightBottom ~= SENTINEL_IMPASSABLE_CHUNK) then
addEnemyStructureToChunk(regionMap, rightBottom, entity, base)
addEnemyStructureToChunk(map, rightBottom, entity, base)
end
end
end
function chunkUtils.unregisterEnemyBaseStructure(regionMap, entity)
function chunkUtils.unregisterEnemyBaseStructure(map, entity)
local entityType = entity.type
if ((entityType == "unit-spawner") or (entityType == "turret")) and (entity.force.name == "enemy") then
local leftTop, rightTop, leftBottom, rightBottom = getEntityOverlapChunks(regionMap, entity)
local leftTop, rightTop, leftBottom, rightBottom = getEntityOverlapChunks(map, entity)
if (leftTop ~= SENTINEL_IMPASSABLE_CHUNK) then
removeEnemyStructureFromChunk(regionMap, leftTop, entity)
removeEnemyStructureFromChunk(map, leftTop, entity)
end
if (rightTop ~= SENTINEL_IMPASSABLE_CHUNK) then
removeEnemyStructureFromChunk(regionMap, rightTop, entity)
removeEnemyStructureFromChunk(map, rightTop, entity)
end
if (leftBottom ~= SENTINEL_IMPASSABLE_CHUNK) then
removeEnemyStructureFromChunk(regionMap, leftBottom, entity)
removeEnemyStructureFromChunk(map, leftBottom, entity)
end
if (rightBottom ~= SENTINEL_IMPASSABLE_CHUNK) then
removeEnemyStructureFromChunk(regionMap, rightBottom, entity)
removeEnemyStructureFromChunk(map, rightBottom, entity)
end
end
end
function chunkUtils.addRemovePlayerEntity(regionMap, entity, natives, addObject, creditNatives)
function chunkUtils.addRemovePlayerEntity(map, entity, natives, addObject, creditNatives)
local leftTop, rightTop, leftBottom, rightBottom
local entityValue
if (BUILDING_PHEROMONES[entity.type] ~= nil) and (entity.force.name == "player") then
entityValue = BUILDING_PHEROMONES[entity.type]
leftTop, rightTop, leftBottom, rightBottom = getEntityOverlapChunks(regionMap, entity)
leftTop, rightTop, leftBottom, rightBottom = getEntityOverlapChunks(map, entity)
if not addObject then
if creditNatives then
natives.points = natives.points + entityValue
@ -527,38 +564,38 @@ function chunkUtils.addRemovePlayerEntity(regionMap, entity, natives, addObject,
entityValue = -entityValue
end
if (leftTop ~= SENTINEL_IMPASSABLE_CHUNK) then
chunkUtils.addPlayerBaseGenerator(regionMap, leftTop, entityValue)
chunkUtils.addPlayerBaseGenerator(map, leftTop, entityValue)
end
if (rightTop ~= SENTINEL_IMPASSABLE_CHUNK) then
chunkUtils.addPlayerBaseGenerator(regionMap, rightTop, entityValue)
chunkUtils.addPlayerBaseGenerator(map, rightTop, entityValue)
end
if (leftBottom ~= SENTINEL_IMPASSABLE_CHUNK) then
chunkUtils.addPlayerBaseGenerator(regionMap, leftBottom, entityValue)
chunkUtils.addPlayerBaseGenerator(map, leftBottom, entityValue)
end
if (rightBottom ~= SENTINEL_IMPASSABLE_CHUNK) then
chunkUtils.addPlayerBaseGenerator(regionMap, rightBottom, entityValue)
chunkUtils.addPlayerBaseGenerator(map, rightBottom, entityValue)
end
end
return entity
end
function chunkUtils.unregisterResource(entity, regionMap)
function chunkUtils.unregisterResource(entity, map)
if entity.prototype.infinite_resource then
return
end
local leftTop, rightTop, leftBottom, rightBottom = getEntityOverlapChunks(regionMap, entity)
local leftTop, rightTop, leftBottom, rightBottom = getEntityOverlapChunks(map, entity)
if (leftTop ~= SENTINEL_IMPASSABLE_CHUNK) then
chunkUtils.addResourceGenerator(regionMap, leftTop, -RESOURCE_GENERATOR_INCREMENT)
chunkUtils.addResourceGenerator(map, leftTop, -RESOURCE_GENERATOR_INCREMENT)
end
if (rightTop ~= SENTINEL_IMPASSABLE_CHUNK) then
chunkUtils.addResourceGenerator(regionMap, rightTop, -RESOURCE_GENERATOR_INCREMENT)
chunkUtils.addResourceGenerator(map, rightTop, -RESOURCE_GENERATOR_INCREMENT)
end
if (leftBottom ~= SENTINEL_IMPASSABLE_CHUNK) then
chunkUtils.addResourceGenerator(regionMap, leftBottom, -RESOURCE_GENERATOR_INCREMENT)
chunkUtils.addResourceGenerator(map, leftBottom, -RESOURCE_GENERATOR_INCREMENT)
end
if (rightBottom ~= SENTINEL_IMPASSABLE_CHUNK) then
chunkUtils.addResourceGenerator(regionMap, rightBottom, -RESOURCE_GENERATOR_INCREMENT)
chunkUtils.addResourceGenerator(map, rightBottom, -RESOURCE_GENERATOR_INCREMENT)
end
end

View File

@ -16,7 +16,7 @@ constants.VERSION_26 = 26
constants.VERSION_27 = 27
constants.VERSION_28 = 28
constants.VERSION_33 = 33
constants.VERSION_37 = 37
constants.VERSION_38 = 38
-- misc
@ -79,7 +79,7 @@ constants.AI_MAX_OVERFLOW_POINTS = constants.AI_MAX_POINTS * 3
constants.AI_UNIT_REFUND = 3
constants.AI_MAX_SQUAD_COUNT = 30
-- constants.AI_MAX_SQUAD_COUNT = 30
constants.AI_MAX_BITER_GROUP_SIZE = 450
constants.AI_SQUAD_MERGE_THRESHOLD = constants.AI_MAX_BITER_GROUP_SIZE * 0.75
@ -189,8 +189,8 @@ constants.BUILDING_PHEROMONES["fluid-turret"] = 28
constants.BUILDING_PHEROMONES["turret"] = 10
constants.BUILDING_PHEROMONES["artillery-turret"] = 100
constants.retreatFilter = {}
constants.retreatFilter[constants.SQUAD_RETREATING] = true
constants.RETREAT_FILTER = {}
constants.RETREAT_FILTER[constants.SQUAD_RETREATING] = true
-- map settings tweaks
@ -221,8 +221,8 @@ constants.SENTINEL_IMPASSABLE_CHUNK[constants.RESOURCE_PHEROMONE] = constants.IM
constants.SENTINEL_IMPASSABLE_CHUNK[constants.PASSABLE] = constants.CHUNK_IMPASSABLE
constants.SENTINEL_IMPASSABLE_CHUNK[constants.CHUNK_TICK] = 0
constants.SENTINEL_IMPASSABLE_CHUNK[constants.PATH_RATING] = 0
constants.SENTINEL_IMPASSABLE_CHUNK.x = 0
constants.SENTINEL_IMPASSABLE_CHUNK.y = 0
constants.SENTINEL_IMPASSABLE_CHUNK.x = -1
constants.SENTINEL_IMPASSABLE_CHUNK.y = -1
return constants

View File

@ -83,18 +83,18 @@ end
In theory, this might be fine as smaller bases have less surface to attack and need to have
pheromone dissipate at a faster rate.
--]]
function mapProcessor.processMap(regionMap, surface, natives, tick)
local roll = regionMap.processRoll
local index = regionMap.processIndex
function mapProcessor.processMap(map, surface, natives, tick)
local roll = map.processRoll
local index = map.processIndex
if (index == 1) then
roll = mRandom()
regionMap.processRoll = roll
map.processRoll = roll
end
local squads = canAttack(natives, surface) and (0.11 <= roll) and (roll <= 0.35) and (natives.points >= AI_SQUAD_COST)
local processQueue = regionMap.processQueue
local processQueue = map.processQueue
local endIndex = mMin(index + PROCESS_QUEUE_SIZE, #processQueue)
for x=index,endIndex do
local chunk = processQueue[x]
@ -102,21 +102,21 @@ function mapProcessor.processMap(regionMap, surface, natives, tick)
if (chunk[CHUNK_TICK] ~= tick) then
chunk[CHUNK_TICK] = tick
processPheromone(regionMap, chunk)
processPheromone(map, chunk)
if squads and (getNestCount(regionMap, chunk) > 0) then
formSquads(regionMap, surface, natives, chunk, AI_SQUAD_COST)
squads = (natives.points >= AI_SQUAD_COST) and (#natives.squads < natives.maxSquads)
if squads and (getNestCount(map, chunk) > 0) then
formSquads(map, surface, natives, chunk, AI_SQUAD_COST)
squads = (natives.points >= AI_SQUAD_COST) -- and (#natives.squads < natives.maxSquads)
end
scents(regionMap, chunk)
scents(map, chunk)
end
end
if (endIndex == #processQueue) then
regionMap.processIndex = 1
map.processIndex = 1
else
regionMap.processIndex = endIndex + 1
map.processIndex = endIndex + 1
end
end
@ -126,7 +126,7 @@ end
vs
the slower passive version processing the entire map in multiple passes.
--]]
function mapProcessor.processPlayers(players, regionMap, surface, natives, tick)
function mapProcessor.processPlayers(players, map, surface, natives, tick)
-- put down player pheromone for player hunters
-- randomize player order to ensure a single player isn't singled out
local playerOrdering = nonRepeatingRandom(players)
@ -140,7 +140,7 @@ function mapProcessor.processPlayers(players, regionMap, surface, natives, tick)
for i=1,#playerOrdering do
local player = players[playerOrdering[i]]
if validPlayer(player) then
local playerChunk = getChunkByPosition(regionMap, player.character.position)
local playerChunk = getChunkByPosition(map, player.character.position)
if (playerChunk ~= SENTINEL_IMPASSABLE_CHUNK) then
playerScent(playerChunk)
@ -150,34 +150,34 @@ function mapProcessor.processPlayers(players, regionMap, surface, natives, tick)
for i=1,#playerOrdering do
local player = players[playerOrdering[i]]
if validPlayer(player) then
local playerChunk = getChunkByPosition(regionMap, player.character.position)
local playerChunk = getChunkByPosition(map, player.character.position)
if (playerChunk ~= SENTINEL_IMPASSABLE_CHUNK) then
local vengence = (allowingAttacks and
(natives.points >= AI_VENGENCE_SQUAD_COST) and
((getEnemyStructureCount(regionMap, playerChunk) > 0) or (playerChunk[MOVEMENT_PHEROMONE] < natives.retreatThreshold)))
((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
local chunk = getChunkByXY(regionMap, x, y)
local chunk = getChunkByXY(map, x, y)
if (chunk ~= SENTINEL_IMPASSABLE_CHUNK) and (chunk[CHUNK_TICK] ~= tick) then
chunk[CHUNK_TICK] = tick
processPheromone(regionMap, chunk)
processPheromone(map, chunk)
if (getNestCount(regionMap, chunk) > 0) then
if (getNestCount(map, chunk) > 0) then
if squads then
formSquads(regionMap, surface, natives, chunk, AI_SQUAD_COST)
squads = (natives.points >= AI_SQUAD_COST) and (#natives.squads < natives.maxSquads)
formSquads(map, surface, natives, chunk, AI_SQUAD_COST)
squads = (natives.points >= AI_SQUAD_COST) -- and (#natives.squads < natives.maxSquads)
end
if vengence then
formSquads(regionMap, surface, natives, chunk, AI_VENGENCE_SQUAD_COST)
vengence = (natives.points >= AI_VENGENCE_SQUAD_COST) and (#natives.squads < natives.maxSquads)
formSquads(map, surface, natives, chunk, AI_VENGENCE_SQUAD_COST)
vengence = (natives.points >= AI_VENGENCE_SQUAD_COST) -- and (#natives.squads < natives.maxSquads)
end
end
scents(regionMap, chunk)
scents(map, chunk)
end
end
end
@ -189,14 +189,14 @@ end
--[[
Passive scan to find entities that have been generated outside the factorio event system
--]]
function mapProcessor.scanMap(regionMap, surface, natives)
local index = regionMap.scanIndex
function mapProcessor.scanMap(map, surface, natives)
local index = map.scanIndex
local unitCountQuery = regionMap.filteredEntitiesEnemyUnitQuery
local unitCountQuery = map.filteredEntitiesEnemyUnitQuery
local offset = unitCountQuery.area[2]
local chunkBox = unitCountQuery.area[1]
local processQueue = regionMap.processQueue
local processQueue = map.processQueue
local endIndex = mMin(index + SCAN_QUEUE_SIZE, #processQueue)
for x=index,endIndex do
@ -226,13 +226,13 @@ function mapProcessor.scanMap(regionMap, surface, natives)
end
end
analyzeChunk(chunk, natives, surface, regionMap)
analyzeChunk(chunk, natives, surface, map)
end
if (endIndex == #processQueue) then
regionMap.scanIndex = 1
map.scanIndex = 1
else
regionMap.scanIndex = endIndex + 1
map.scanIndex = endIndex + 1
end
end

View File

@ -25,16 +25,16 @@ local mFloor = math.floor
-- module code
function mapUtils.getChunkByXY(regionMap, x, y)
local chunkX = regionMap[x]
function mapUtils.getChunkByXY(map, x, y)
local chunkX = map[x]
if chunkX then
return chunkX[y] or SENTINEL_IMPASSABLE_CHUNK
end
return SENTINEL_IMPASSABLE_CHUNK
end
function mapUtils.getChunkByPosition(regionMap, position)
local chunkX = regionMap[mFloor(position.x * CHUNK_SIZE_DIVIDER) * CHUNK_SIZE]
function mapUtils.getChunkByPosition(map, position)
local chunkX = map[mFloor(position.x * CHUNK_SIZE_DIVIDER) * CHUNK_SIZE]
if chunkX then
local chunkY = mFloor(position.y * CHUNK_SIZE_DIVIDER) * CHUNK_SIZE
return chunkX[chunkY] or SENTINEL_IMPASSABLE_CHUNK
@ -42,8 +42,8 @@ function mapUtils.getChunkByPosition(regionMap, position)
return SENTINEL_IMPASSABLE_CHUNK
end
function mapUtils.getChunkByUnalignedXY(regionMap, x, y)
local chunkX = regionMap[mFloor(x * CHUNK_SIZE_DIVIDER) * CHUNK_SIZE]
function mapUtils.getChunkByUnalignedXY(map, x, y)
local chunkX = map[mFloor(x * CHUNK_SIZE_DIVIDER) * CHUNK_SIZE]
if chunkX then
local chunkY = mFloor(y * CHUNK_SIZE_DIVIDER) * CHUNK_SIZE
return chunkX[chunkY] or SENTINEL_IMPASSABLE_CHUNK
@ -64,11 +64,11 @@ end
/|\
6 7 8
]]--
function mapUtils.getNeighborChunks(regionMap, x, y)
local neighbors = regionMap.neighbors
function mapUtils.getNeighborChunks(map, x, y)
local neighbors = map.neighbors
local chunkYRow1 = y - CHUNK_SIZE
local chunkYRow3 = y + CHUNK_SIZE
local xChunks = regionMap[x-CHUNK_SIZE]
local xChunks = map[x-CHUNK_SIZE]
if xChunks then
neighbors[1] = xChunks[chunkYRow1] or SENTINEL_IMPASSABLE_CHUNK
neighbors[4] = xChunks[y] or SENTINEL_IMPASSABLE_CHUNK
@ -79,7 +79,7 @@ function mapUtils.getNeighborChunks(regionMap, x, y)
neighbors[6] = SENTINEL_IMPASSABLE_CHUNK
end
xChunks = regionMap[x+CHUNK_SIZE]
xChunks = map[x+CHUNK_SIZE]
if xChunks then
neighbors[3] = xChunks[chunkYRow1] or SENTINEL_IMPASSABLE_CHUNK
neighbors[5] = xChunks[y] or SENTINEL_IMPASSABLE_CHUNK
@ -90,7 +90,7 @@ function mapUtils.getNeighborChunks(regionMap, x, y)
neighbors[8] = SENTINEL_IMPASSABLE_CHUNK
end
xChunks = regionMap[x]
xChunks = map[x]
if xChunks then
neighbors[2] = xChunks[chunkYRow1] or SENTINEL_IMPASSABLE_CHUNK
neighbors[7] = xChunks[chunkYRow3] or SENTINEL_IMPASSABLE_CHUNK
@ -117,9 +117,9 @@ function mapUtils.canMoveChunkDirection(direction, startChunk, endChunk)
return canMove
end
function mapUtils.getCardinalChunks(regionMap, x, y)
local neighbors = regionMap.cardinalNeighbors
local xChunks = regionMap[x]
function mapUtils.getCardinalChunks(map, x, y)
local neighbors = map.cardinalNeighbors
local xChunks = map[x]
if xChunks then
neighbors[1] = xChunks[y-CHUNK_SIZE] or SENTINEL_IMPASSABLE_CHUNK
neighbors[4] = xChunks[y+CHUNK_SIZE] or SENTINEL_IMPASSABLE_CHUNK
@ -128,14 +128,14 @@ function mapUtils.getCardinalChunks(regionMap, x, y)
neighbors[4] = SENTINEL_IMPASSABLE_CHUNK
end
xChunks = regionMap[x-CHUNK_SIZE]
xChunks = map[x-CHUNK_SIZE]
if xChunks then
neighbors[2] = xChunks[y] or SENTINEL_IMPASSABLE_CHUNK
else
neighbors[2] = SENTINEL_IMPASSABLE_CHUNK
end
xChunks = regionMap[x+CHUNK_SIZE]
xChunks = map[x+CHUNK_SIZE]
if xChunks then
neighbors[3] = xChunks[y] or SENTINEL_IMPASSABLE_CHUNK
else

View File

@ -97,7 +97,7 @@ function movementUtils.scoreNeighborsForAttack(chunk, neighborDirectionChunks, s
end
end
if scoreFunction(squad, chunk) > highestScore then
if (chunk ~= SENTINEL_IMPASSABLE_CHUNK) and (scoreFunction(squad, chunk) > highestScore) then
return SENTINEL_IMPASSABLE_CHUNK, -1
end
@ -133,14 +133,14 @@ end
--[[
Expects all neighbors adjacent to a chunk
--]]
function movementUtils.scoreNeighborsForRetreat(chunk, neighborDirectionChunks, scoreFunction, regionMap)
function movementUtils.scoreNeighborsForRetreat(chunk, neighborDirectionChunks, scoreFunction, map)
local highestChunk = SENTINEL_IMPASSABLE_CHUNK
local highestScore = -MAGIC_MAXIMUM_NUMBER
local highestDirection
for x=1,8 do
local neighborChunk = neighborDirectionChunks[x]
if (neighborChunk ~= SENTINEL_IMPASSABLE_CHUNK) and canMoveChunkDirection(x, chunk, neighborChunk) then
local score = scoreFunction(regionMap, neighborChunk)
local score = scoreFunction(map, neighborChunk)
if (score > highestScore) then
highestScore = score
highestChunk = neighborChunk
@ -156,13 +156,13 @@ end
--[[
Expects all neighbors adjacent to a chunk
--]]
function movementUtils.scoreNeighborsForFormation(neighborChunks, validFunction, scoreFunction, regionMap)
function movementUtils.scoreNeighborsForFormation(neighborChunks, validFunction, scoreFunction, map)
local highestChunk = SENTINEL_IMPASSABLE_CHUNK
local highestScore = -MAGIC_MAXIMUM_NUMBER
local highestDirection
for x=1,8 do
local neighborChunk = neighborChunks[x]
if (neighborChunk ~= SENTINEL_IMPASSABLE_CHUNK) and validFunction(regionMap, neighborChunk) then
if (neighborChunk ~= SENTINEL_IMPASSABLE_CHUNK) and validFunction(map, neighborChunk) then
local score = scoreFunction(neighborChunk)
if (score > highestScore) then
highestScore = score

View File

@ -31,21 +31,21 @@ local getChunkByPosition = mapUtils.getChunkByPosition
-- module code
function nestUtils.buildNest(regionMap, base, surface, targetPosition, name)
function nestUtils.buildNest(map, base, surface, targetPosition, name)
local position = surface.find_non_colliding_position(name, targetPosition, DOUBLE_CHUNK_SIZE, 2)
local chunk = getChunkByPosition(regionMap, position)
local chunk = getChunkByPosition(map, position)
local nest = nil
if position and (chunk ~= SENTINEL_IMPASSABLE_CHUNK) and (chunk[NEST_COUNT] < 3) then
local biterSpawner = {name=name, position=position}
nest = surface.create_entity(biterSpawner)
registerEnemyBaseStructure(regionMap, nest, base)
registerEnemyBaseStructure(map, nest, base)
end
return nest
end
function nestUtils.buildHive(regionMap, base, surface)
function nestUtils.buildHive(map, base, surface)
local valid = false
local hive = nestUtils.buildNest(regionMap, base, surface, base, "biter-spawner-hive-rampant")
local hive = nestUtils.buildNest(map, base, surface, base, "biter-spawner-hive-rampant")
if hive then
if (#base.hives == 0) then
base.x = hive.position.x
@ -57,7 +57,7 @@ function nestUtils.buildHive(regionMap, base, surface)
return valid
end
function nestUtils.buildOutpost(regionMap, natives, base, surface, tendril)
function nestUtils.buildOutpost(map, natives, base, surface, tendril)
local foundHive = false
for _,_ in pairs(base.hives) do
foundHive = true
@ -101,7 +101,7 @@ function nestUtils.buildOutpost(regionMap, natives, base, surface, tendril)
y = position.y + (distortion * math.sin(pos))}
local biterSpawner = {name=thing, position=nestPosition}
if surface.can_place_entity(biterSpawner) then
registerEnemyBaseStructure(natives, regionMap, surface.create_entity(biterSpawner), base)
registerEnemyBaseStructure(natives, map, surface.create_entity(biterSpawner), base)
base.upgradePoints = base.upgradePoints - cost
end
pos = pos + slice
@ -109,7 +109,7 @@ function nestUtils.buildOutpost(regionMap, natives, base, surface, tendril)
end
end
function nestUtils.buildOrder(regionMap, natives, base, surface)
function nestUtils.buildOrder(map, natives, base, surface)
local foundHive = false
for _,_ in pairs(base.hives) do
foundHive = true
@ -149,7 +149,7 @@ function nestUtils.buildOrder(regionMap, natives, base, surface)
y = base.y + (distortion * math.sin(pos))}
local biterSpawner = {name=thing, position=nestPosition}
if surface.can_place_entity(biterSpawner) then
registerEnemyBaseStructure(natives, regionMap, surface.create_entity(biterSpawner), base)
registerEnemyBaseStructure(natives, map, surface.create_entity(biterSpawner), base)
base.upgradePoints = base.upgradePoints - cost
end
pos = pos + slice

View File

@ -36,10 +36,10 @@ local getResourceGenerator = chunkUtils.getResourceGenerator
-- module code
function pheromoneUtils.scents(regionMap, chunk)
chunk[BASE_PHEROMONE] = chunk[BASE_PHEROMONE] + getPlayerBaseGenerator(regionMap, chunk)
local resourceGenerator = getResourceGenerator(regionMap, chunk)
if (resourceGenerator > 0) and (getEnemyStructureCount(regionMap, chunk) == 0) then
function pheromoneUtils.scents(map, chunk)
chunk[BASE_PHEROMONE] = chunk[BASE_PHEROMONE] + getPlayerBaseGenerator(map, chunk)
local resourceGenerator = getResourceGenerator(map, chunk)
if (resourceGenerator > 0) and (getEnemyStructureCount(map, chunk) == 0) then
chunk[RESOURCE_PHEROMONE] = chunk[RESOURCE_PHEROMONE] + mMax(resourceGenerator * 100, 90)
end
end
@ -59,7 +59,7 @@ function pheromoneUtils.playerScent(playerChunk)
playerChunk[PLAYER_PHEROMONE] = playerChunk[PLAYER_PHEROMONE] + PLAYER_PHEROMONE_GENERATOR_AMOUNT
end
function pheromoneUtils.processPheromone(regionMap, chunk)
function pheromoneUtils.processPheromone(map, chunk)
local chunkMovement = chunk[MOVEMENT_PHEROMONE]
local chunkBase = chunk[BASE_PHEROMONE]
@ -67,7 +67,7 @@ function pheromoneUtils.processPheromone(regionMap, chunk)
local chunkResource = chunk[RESOURCE_PHEROMONE]
local chunkPathRating = chunk[PATH_RATING]
local tempNeighbors = getCardinalChunks(regionMap, chunk.x, chunk.y)
local tempNeighbors = getCardinalChunks(map, chunk.x, chunk.y)
local totalMovement = ((tempNeighbors[1][MOVEMENT_PHEROMONE] - chunkMovement) +
(tempNeighbors[2][MOVEMENT_PHEROMONE] - chunkMovement) +

View File

@ -19,11 +19,8 @@ local BASE_PHEROMONE = constants.BASE_PHEROMONE
local SQUAD_RAIDING = constants.SQUAD_RAIDING
local SQUAD_GUARDING = constants.SQUAD_GUARDING
local CHUNK_SIZE = constants.CHUNK_SIZE
local PLAYER_PHEROMONE_MULTIPLER = constants.PLAYER_PHEROMONE_MULTIPLER
local DEFINES_COMMAND_ATTACK_AREA = defines.command.attack_area
local DEFINES_GROUP_FINISHED = defines.group_state.finished
local DEFINES_GROUP_GATHERING = defines.group_state.gathering
local DEFINES_GROUP_MOVING = defines.group_state.moving
@ -41,7 +38,9 @@ local mRandom = math.random
local findMovementPosition = movementUtils.findMovementPosition
local getNeighborChunks = mapUtils.getNeighborChunks
local getChunkByPosition = mapUtils.getChunkByPosition
local addSquadToChunk = chunkUtils.addSquadToChunk
local getChunkByXY = mapUtils.getChunkByXY
local positionToChunkXY = mapUtils.positionToChunkXY
local addMovementPenalty = movementUtils.addMovementPenalty
local lookupMovementPenalty = movementUtils.lookupMovementPenalty
local calculateKamikazeThreshold = unitGroupUtils.calculateKamikazeThreshold
@ -52,8 +51,6 @@ local euclideanDistanceNamed = mathUtils.euclideanDistanceNamed
local playersWithinProximityToPosition = playerUtils.playersWithinProximityToPosition
local getPlayerBaseGenerator = chunkUtils.getPlayerBaseGenerator
local positionToChunkXY = mapUtils.positionToChunkXY
local scoreNeighborsForAttack = movementUtils.scoreNeighborsForAttack
-- module code
@ -63,19 +60,11 @@ local function scoreAttackLocation(squad, neighborChunk)
return damage - lookupMovementPenalty(squad, neighborChunk)
end
function squadAttack.squadsAttack(regionMap, surface, natives)
function squadAttack.squadsAttack(map, surface, natives)
local squads = natives.squads
local attackPosition
local attackCmd
if (#squads > 0) then
attackPosition = regionMap.position
attackCmd = { type = DEFINES_COMMAND_ATTACK_AREA,
destination = attackPosition,
radius = CHUNK_SIZE,
distraction = DEFINES_DISTRACTION_BY_ENEMY }
end
local attackPosition = map.position
local attackCmd = map.attackAreaCommand
for i=1,#squads do
local squad = squads[i]
local group = squad.group
@ -83,45 +72,50 @@ function squadAttack.squadsAttack(regionMap, surface, natives)
local groupState = group.state
if (groupState == DEFINES_GROUP_FINISHED) or (groupState == DEFINES_GROUP_GATHERING) or ((groupState == DEFINES_GROUP_MOVING) and (squad.cycles == 0)) then
local groupPosition = group.position
local chunk = getChunkByPosition(regionMap, groupPosition)
local x, y = positionToChunkXY(groupPosition)
local chunk = getChunkByXY(map, x, y)
local attackChunk, attackDirection = scoreNeighborsForAttack(chunk,
getNeighborChunks(map, x, y),
scoreAttackLocation,
squad)
if (chunk ~= SENTINEL_IMPASSABLE_CHUNK) then
local attackChunk, attackDirection = scoreNeighborsForAttack(chunk,
getNeighborChunks(regionMap, chunk.x, chunk.y),
scoreAttackLocation,
squad)
addSquadToChunk(map, chunk, squad)
addMovementPenalty(natives, squad, chunk)
if group.valid and (attackChunk ~= SENTINEL_IMPASSABLE_CHUNK) then
local playerBaseGenerator = getPlayerBaseGenerator(regionMap, attackChunk)
if (playerBaseGenerator == 0) or ((groupState == DEFINES_GROUP_FINISHED) or (groupState == DEFINES_GROUP_GATHERING)) then
squad.cycles = ((#squad.group.members > 80) and 6) or 4
elseif (attackChunk ~= SENTINEL_IMPASSABLE_CHUNK) then
addSquadToChunk(map, attackChunk, squad)
addMovementPenalty(natives, squad, attackChunk)
end
if group.valid and (attackChunk ~= SENTINEL_IMPASSABLE_CHUNK) then
local playerBaseGenerator = getPlayerBaseGenerator(map, attackChunk)
if (playerBaseGenerator == 0) or ((groupState == DEFINES_GROUP_FINISHED) or (groupState == DEFINES_GROUP_GATHERING)) then
squad.cycles = ((#squad.group.members > 80) and 6) or 4
local moreFrenzy = not squad.rabid and squad.frenzy and (euclideanDistanceNamed(groupPosition, squad.frenzyPosition) < 100)
squad.frenzy = moreFrenzy
if squad.rabid or squad.frenzy then
attackCmd.distraction = DEFINES_DISTRACTION_BY_ANYTHING
else
attackCmd.distraction = DEFINES_DISTRACTION_BY_ENEMY
end
local position = findMovementPosition(surface, positionFromDirectionAndChunk(attackDirection, groupPosition, attackPosition, 1.35))
if position then
attackPosition.x = position.x
attackPosition.y = position.y
group.set_command(attackCmd)
group.start_moving()
else
addMovementPenalty(natives, squad, attackChunk)
end
elseif not squad.frenzy and not squad.rabid and
((groupState == DEFINES_GROUP_ATTACKING_DISTRACTION) or (groupState == DEFINES_GROUP_ATTACKING_TARGET) or
(playerBaseGenerator ~= 0)) then
squad.frenzy = true
squad.frenzyPosition.x = groupPosition.x
squad.frenzyPosition.y = groupPosition.y
local moreFrenzy = not squad.rabid and squad.frenzy and (euclideanDistanceNamed(groupPosition, squad.frenzyPosition) < 100)
squad.frenzy = moreFrenzy
if squad.rabid or squad.frenzy then
attackCmd.distraction = DEFINES_DISTRACTION_BY_ANYTHING
else
attackCmd.distraction = DEFINES_DISTRACTION_BY_ENEMY
end
local position = findMovementPosition(surface, positionFromDirectionAndChunk(attackDirection, groupPosition, attackPosition, 1.35))
if position then
attackPosition.x = position.x
attackPosition.y = position.y
group.set_command(attackCmd)
group.start_moving()
else
addMovementPenalty(natives, squad, attackChunk)
end
elseif not squad.frenzy and not squad.rabid and
((groupState == DEFINES_GROUP_ATTACKING_DISTRACTION) or (groupState == DEFINES_GROUP_ATTACKING_TARGET) or
(playerBaseGenerator ~= 0)) then
squad.frenzy = true
squad.frenzyPosition.x = groupPosition.x
squad.frenzyPosition.y = groupPosition.y
end
end
end

View File

@ -10,26 +10,24 @@ local chunkUtils = require("ChunkUtils")
-- constants
local RETREAT_SPAWNER_GRAB_RADIUS = constants.RETREAT_SPAWNER_GRAB_RADIUS
-- local RETREAT_SPAWNER_GRAB_RADIUS = constants.RETREAT_SPAWNER_GRAB_RADIUS
local MOVEMENT_PHEROMONE = constants.MOVEMENT_PHEROMONE
local PLAYER_PHEROMONE = constants.PLAYER_PHEROMONE
local BASE_PHEROMONE = constants.BASE_PHEROMONE
local HALF_CHUNK_SIZE = constants.HALF_CHUNK_SIZE
local SQUAD_RETREATING = constants.SQUAD_RETREATING
local RETREAT_FILTER = constants.RETREAT_FILTER
local INTERVAL_LOGIC = constants.INTERVAL_LOGIC
local SENTINEL_IMPASSABLE_CHUNK = constants.SENTINEL_IMPASSABLE_CHUNK
-- imported functions
local addSquadToChunk = chunkUtils.addSquadToChunk
local positionFromDirectionAndChunk = mapUtils.positionFromDirectionAndChunk
local getNeighborChunks = mapUtils.getNeighborChunks
local findNearBySquad = unitGroupUtils.findNearBySquad
local findNearbySquadFiltered = unitGroupUtils.findNearbySquadFiltered
local addMovementPenalty = movementUtils.addMovementPenalty
local createSquad = unitGroupUtils.createSquad
local membersToSquad = unitGroupUtils.membersToSquad
@ -43,12 +41,12 @@ local getEnemyStructureCount = chunkUtils.getEnemyStructureCount
-- module code
local function scoreRetreatLocation(regionMap, neighborChunk)
return -(neighborChunk[BASE_PHEROMONE] + -neighborChunk[MOVEMENT_PHEROMONE] + (neighborChunk[PLAYER_PHEROMONE] * 100) + (getPlayerBaseGenerator(regionMap, neighborChunk) * 20))
local function scoreRetreatLocation(map, neighborChunk)
return -(neighborChunk[BASE_PHEROMONE] + -neighborChunk[MOVEMENT_PHEROMONE] + (neighborChunk[PLAYER_PHEROMONE] * 100) + (getPlayerBaseGenerator(map, neighborChunk) * 20))
end
function aiDefense.retreatUnits(chunk, position, squad, regionMap, surface, natives, tick, radius, force)
if (tick - getRetreatTick(regionMap, chunk) > INTERVAL_LOGIC) and ((getEnemyStructureCount(regionMap, chunk) == 0) or force) then
function aiDefense.retreatUnits(chunk, position, squad, map, surface, natives, tick, radius, force)
if (tick - getRetreatTick(map, chunk) > INTERVAL_LOGIC) and ((getEnemyStructureCount(map, chunk) == 0) or force) then
local performRetreat = false
local enemiesToSquad = nil
@ -60,14 +58,14 @@ function aiDefense.retreatUnits(chunk, position, squad, regionMap, surface, nati
end
if performRetreat then
setRetreatTick(regionMap, chunk, tick)
setRetreatTick(map, chunk, tick)
local exitPath,exitDirection = scoreNeighborsForRetreat(chunk,
getNeighborChunks(regionMap, chunk.x, chunk.y),
getNeighborChunks(map, chunk.x, chunk.y),
scoreRetreatLocation,
regionMap)
map)
if (exitPath ~= SENTINEL_IMPASSABLE_CHUNK) then
local retreatPosition = findMovementPosition(surface,
positionFromDirectionAndChunk(exitDirection, position, regionMap.position, 0.98),
positionFromDirectionAndChunk(exitDirection, position, map.position, 0.98),
false)
if not retreatPosition then
@ -77,7 +75,7 @@ function aiDefense.retreatUnits(chunk, position, squad, regionMap, surface, nati
-- in order for units in a group attacking to retreat, we have to create a new group and give the command to join
-- to each unit, this is the only way I have found to have snappy mid battle retreats even after 0.14.4
local newSquad = findNearBySquad(natives, retreatPosition, HALF_CHUNK_SIZE, RETREAT_FILTER)
local newSquad = findNearbySquadFiltered(map, exitPath, retreatPosition)
if not newSquad then
newSquad = createSquad(retreatPosition, surface, natives)
@ -86,15 +84,18 @@ function aiDefense.retreatUnits(chunk, position, squad, regionMap, surface, nati
end
if newSquad then
local cmd = map.retreatCommand
cmd.group = newSquad.group
if enemiesToSquad then
membersToSquad(newSquad, enemiesToSquad, force)
membersToSquad(cmd, enemiesToSquad, force)
else
membersToSquad(newSquad, squad.group.members, true)
membersToSquad(cmd, squad.group.members, true)
newSquad.penalties = squad.penalties
if squad.rabid then
newSquad.rabid = true
end
end
addSquadToChunk(map, chunk, newSquad)
addMovementPenalty(natives, newSquad, chunk)
end
end

View File

@ -55,11 +55,11 @@ local function removeTendril(base, tendril)
end
end
local function buildTendrilPath(regionMap, tendril, surface, base, tick, natives)
local function buildTendrilPath(map, tendril, surface, base, tick, natives)
local tendrilUnit = tendril.unit
if not tendrilUnit.valid then
removeTendril(base, tendril)
tendrilUtils.buildTendril(regionMap, natives, base, surface, tick)
tendrilUtils.buildTendril(map, natives, base, surface, tick)
return
end
if (tendril.cycles > 0) then
@ -67,17 +67,17 @@ local function buildTendrilPath(regionMap, tendril, surface, base, tick, natives
return
end
local tendrilPosition = tendrilUnit.position
local chunk = getChunkByPosition(regionMap, tendrilPosition)
local chunk = getChunkByPosition(map, tendrilPosition)
if (chunk ~= SENTINEL_IMPASSABLE_CHUNK) then
local tendrilPath,tendrilDirection = scoreNeighborsForResource(chunk,
getNeighborChunks(regionMap, chunk.x, chunk.y),
getNeighborChunks(map, chunk.x, chunk.y),
scoreTendrilChunk,
nil)
if (tendrilDirection == -1) then
if (chunk[RESOURCE_GENERATOR] ~= 0) then
buildOutpost(regionMap, natives, base, surface, tendril)
buildOutpost(map, natives, base, surface, tendril)
removeTendril(base, tendril)
tendrilUtils.buildTendril(regionMap, natives, base, surface, tick)
tendrilUtils.buildTendril(map, natives, base, surface, tick)
colorChunk(chunk.x, chunk.y, "hazard-concrete-left", surface)
end
return
@ -90,7 +90,7 @@ local function buildTendrilPath(regionMap, tendril, surface, base, tick, natives
32,
2)
if position then
buildNest(regionMap, base, surface, tendril.unit.position, "spitter-spawner")
buildNest(map, base, surface, tendril.unit.position, "spitter-spawner")
-- tendril.cycles = 3
tendrilUnit.set_command({ type = defines.command.go_to_location,
destination = position,
@ -101,9 +101,9 @@ local function buildTendrilPath(regionMap, tendril, surface, base, tick, natives
end
end
function tendrilUtils.advanceTendrils(regionMap, base, surface, tick, natives)
function tendrilUtils.advanceTendrils(map, base, surface, tick, natives)
for i=1, #base.tendrils do
buildTendrilPath(regionMap, base.tendrils[i], surface, base, tick, natives)
buildTendrilPath(map, base.tendrils[i], surface, base, tick, natives)
end
end
@ -128,11 +128,11 @@ function tendrilUtils.createTendril(base, surface)
return tendril
end
function tendrilUtils.buildTendril(regionMap, natives, base, surface, tick)
-- local chunk = getChunkByPosition(regionMap, base.x, base.y)
function tendrilUtils.buildTendril(map, natives, base, surface, tick)
-- local chunk = getChunkByPosition(map, base.x, base.y)
-- if chunk then
-- local tempNeighbors = {nil, nil, nil, nil, nil, nil, nil, nil}
-- buildTendrilPath(regionMap, chunk, surface, base, tempNeighbors)
-- buildTendrilPath(map, chunk, surface, base, tempNeighbors)
local tendril = tendrilUtils.createTendril(base, surface)
if tendril then
base.tendrils[#base.tendrils+1] = tendril

View File

@ -1,14 +1,14 @@
local tunnelUtils = {}
function tunnelUtils.digTunnel(regionMap, surface, natives, startChunk, endChunk)
function tunnelUtils.digTunnel(map, surface, natives, startChunk, endChunk)
end
function tunnelUtils.fillTunnel(regionMap, surface, natives, tilePositions)
function tunnelUtils.fillTunnel(map, surface, natives, tilePositions)
local tunnels = natives.tunnels
for i=1, #tunnels do
end
end
return tunnelUtils
return tunnelUtils

View File

@ -2,23 +2,27 @@ local unitGroupUtils = {}
-- imports
local mapUtils = require("MapUtils")
local mathUtils = require("MathUtils")
local constants = require("Constants")
local chunkUtils = require("ChunkUtils")
-- constants
local HALF_CHUNK_SIZE = constants.HALF_CHUNK_SIZE
local SQUAD_QUEUE_SIZE = constants.SQUAD_QUEUE_SIZE
local DEFINES_GROUP_STATE_FINISHED = defines.group_state.finished
local DEFINES_GROUP_STATE_ATTACKING_TARGET = defines.group_state.attacking_target
local DEFINES_GROUP_STATE_ATTACKING_DISTRACTION = defines.group_state.attacking_distraction
local DEFINES_COMMAND_GROUP = defines.command.group
local DEFINES_DISTRACTION_NONE = defines.distraction.none
local SQUAD_RETREATING = constants.SQUAD_RETREATING
local SQUAD_GUARDING = constants.SQUAD_GUARDING
local GROUP_MERGE_DISTANCE = constants.GROUP_MERGE_DISTANCE
local RETREAT_FILTER = constants.RETREAT_FILTER
local NO_RETREAT_SQUAD_SIZE_BONUS_MAX = constants.NO_RETREAT_SQUAD_SIZE_BONUS_MAX
local AI_MAX_OVERFLOW_POINTS = constants.AI_MAX_OVERFLOW_POINTS
@ -34,29 +38,37 @@ local mLog = math.log10
local mMin = math.min
local getSquadsOnChunk = chunkUtils.getSquadsOnChunk
local removeSquadFromChunk = chunkUtils.removeSquadFromChunk
local getNeighborChunks = mapUtils.getNeighborChunks
local euclideanDistanceNamed = mathUtils.euclideanDistanceNamed
-- module code
function unitGroupUtils.findNearBySquad(natives, position, distance, filter)
local squads = natives.squads
function unitGroupUtils.findNearbySquadFiltered(map, chunk, position)
if filter then
for i=1,#squads do
local squad = squads[i]
local unitGroup = squad.group
if unitGroup.valid and filter[squad.status] then
if (euclideanDistanceNamed(unitGroup.position, position) <= distance) then
return squad
end
local squads = getSquadsOnChunk(map, chunk)
for s=1,#squads do
local squad = squads[s]
local unitGroup = squad.group
if unitGroup.valid and RETREAT_FILTER[squad.status] then
if (euclideanDistanceNamed(unitGroup.position, position) <= HALF_CHUNK_SIZE) then
return squad
end
end
else
for i=1,#squads do
local squad = squads[i]
end
local neighbors = getNeighborChunks(map, chunk.x, chunk.y)
for i=1,#neighbors do
squads = getSquadsOnChunk(map, neighbors[i])
for s=1,#squads do
local squad = squads[s]
local unitGroup = squad.group
if unitGroup.valid then
if (euclideanDistanceNamed(unitGroup.position, position) <= distance) then
if unitGroup.valid and RETREAT_FILTER[squad.status] then
if (euclideanDistanceNamed(unitGroup.position, position) <= HALF_CHUNK_SIZE) then
return squad
end
end
@ -64,27 +76,41 @@ function unitGroupUtils.findNearBySquad(natives, position, distance, filter)
end
end
-- function unitGroupUtils.findNearbySquad(natives, position, distance)
-- local squads = natives.squads
-- for i=1,#squads do
-- local squad = squads[i]
-- local unitGroup = squad.group
-- if unitGroup.valid then
-- if (euclideanDistanceNamed(unitGroup.position, position) <= distance) then
-- return squad
-- end
-- end
-- end
-- end
function unitGroupUtils.createSquad(position, surface, natives)
local unitGroup = surface.create_unit_group({position=position})
local squad = { group = unitGroup,
status = SQUAD_GUARDING,
penalties = {},
rabid = false,
frenzy = false,
kamikaze = false,
frenzyPosition = {x = 0,
y = 0},
cycles = 0 }
local squad = {
group = unitGroup,
status = SQUAD_GUARDING,
penalties = {},
rabid = false,
frenzy = false,
kamikaze = false,
frenzyPosition = {x = 0,
y = 0},
cycles = 0,
chunk = nil
}
natives.squads[#natives.squads+1] = squad
return squad
end
function unitGroupUtils.membersToSquad(squad, members, overwriteGroup)
function unitGroupUtils.membersToSquad(cmd, members, overwriteGroup)
if (members ~= nil) then
local cmd = { type = DEFINES_COMMAND_GROUP,
group = squad.group,
distraction = DEFINES_DISTRACTION_NONE }
for i=1,#members do
local member = members[i]
if member.valid and (overwriteGroup or (not overwriteGroup and not member.unit_group)) then
@ -118,7 +144,7 @@ local function isAttacking(group)
return (state == DEFINES_GROUP_STATE_ATTACKING_TARGET) or (state == DEFINES_GROUP_STATE_ATTACKING_DISTRACTION)
end
function unitGroupUtils.cleanSquads(natives)
function unitGroupUtils.cleanSquads(natives, map)
local squads = natives.squads
local squadCount = #squads
@ -130,10 +156,12 @@ function unitGroupUtils.cleanSquads(natives)
if group.valid then
local memberCount = #group.members
if (memberCount == 0) then
removeSquadFromChunk(map, squad)
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
@ -170,46 +198,82 @@ function unitGroupUtils.recycleBiters(natives, biters)
end
end
function unitGroupUtils.regroupSquads(natives)
local groupThreshold = AI_SQUAD_MERGE_THRESHOLD
local function mergeGroups(squads, squad, group, status, position, memberCount)
local merge = false
local maxed = false
for x=1, #squads do
local mergeSquad = squads[x]
if mergeSquad ~= squad then
local mergeGroup = mergeSquad.group
if mergeGroup.valid and (euclideanDistanceNamed(position, mergeGroup.position) < GROUP_MERGE_DISTANCE) and (mergeSquad.status == status) and not isAttacking(mergeGroup) 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
if mergeSquad.kamikaze then
squad.kamikaze = true
end
merge = true
mergeGroup.destroy()
end
memberCount = memberCount + mergeCount
if (memberCount > AI_SQUAD_MERGE_THRESHOLD) then
maxed = true
break
end
end
end
end
return merge, memberCount, maxed
end
function unitGroupUtils.regroupSquads(natives, map)
local squads = natives.squads
local squadCount = #squads
local startIndex = natives.regroupIndex
local maxSquadIndex = mMin(startIndex + SQUAD_QUEUE_SIZE, squadCount)
for i=startIndex,maxSquadIndex 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
if (memberCount < AI_SQUAD_MERGE_THRESHOLD) then
local status = squad.status
local squadPosition = group.position
local mergedSquads = false
for x=i+1,squadCount do
local mergeSquad = squads[x]
local mergeGroup = mergeSquad.group
if mergeGroup.valid and (euclideanDistanceNamed(squadPosition, mergeGroup.position) < GROUP_MERGE_DISTANCE) and (mergeSquad.status == status) and not isAttacking(mergeGroup) 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
if mergeSquad.kamikaze then
squad.kamikaze = true
end
mergedSquads = true
mergeGroup.destroy()
end
memberCount = memberCount + mergeCount
if (memberCount > groupThreshold) then
break
end
end
end
local mergedSquads
local maxed
local chunk = squad.chunk
if chunk then
mergedSquads, memberCount, maxed = mergeGroups(getSquadsOnChunk(map, chunk),
squad,
group,
status,
squadPosition,
memberCount)
if not maxed then
local neighbors = getNeighborChunks(map, chunk.x, chunk.y)
for x=1,#neighbors do
mergedSquads, memberCount, maxed = mergeGroups(getSquadsOnChunk(map, neighbors[x]),
squad,
group,
status,
squadPosition,
memberCount)
if maxed then
break
end
end
end
end
if mergedSquads and not squad.kamikaze then
local kamikazeThreshold = unitGroupUtils.calculateKamikazeThreshold(squad, natives)
if (mRandom() < kamikazeThreshold) then

View File

@ -17,19 +17,19 @@ function tests.pheromoneLevels(size)
size = size * constants.CHUNK_SIZE
end
print("------")
print(#global.regionMap.processQueue)
print(#global.map.processQueue)
print(playerChunkX .. ", " .. playerChunkY)
print("--")
for y=playerChunkY-size, playerChunkY+size,32 do
for x=playerChunkX-size, playerChunkX+size,32 do
if (global.regionMap[x] ~= nil) then
local chunk = global.regionMap[x][y]
if (global.map[x] ~= nil) then
local chunk = global.map[x][y]
if (chunk ~= nil) then
local str = ""
for i=1,#chunk do
str = str .. " " .. tostring(i) .. "/" .. tostring(chunk[i])
end
str = str .. " " .. "p/" .. game.surfaces[1].get_pollution(chunk) .. " " .. "n/" .. chunkUtils.getNestCount(global.regionMap, chunk) .. " " .. "w/" .. chunkUtils.getWormCount(global.regionMap, chunk)
str = str .. " " .. "p/" .. game.surfaces[1].get_pollution(chunk) .. " " .. "n/" .. chunkUtils.getNestCount(global.map, chunk) .. " " .. "w/" .. chunkUtils.getWormCount(global.map, chunk)
if (chunk.x == playerChunkX) and (chunk.y == playerChunkY) then
print("=============")
print(chunk.x, chunk.y, str)
@ -124,7 +124,7 @@ function tests.getOffsetChunk(x, y)
local playerPosition = game.players[1].position
local chunkX = math.floor(playerPosition.x * 0.03125)
local chunkY = math.floor(playerPosition.y * 0.03125)
local chunk = mapUtils.getChunkByIndex(global.regionMap, chunkX + x, chunkY + y)
local chunk = mapUtils.getChunkByIndex(global.map, chunkX + x, chunkY + y)
print(serpent.dump(chunk))
end
@ -163,7 +163,7 @@ end
function tests.registeredNest(x)
local entity = tests.createEnemy(x)
chunk.registerEnemyBaseStructure(global.regionMap,
chunk.registerEnemyBaseStructure(global.map,
entity,
nil)
end
@ -272,7 +272,7 @@ end
function tests.showMovementGrid()
local chunks = global.regionMap.processQueue
local chunks = global.map.processQueue
for i=1,#chunks do
local chunk = chunks[i]
local color = "concrete"
@ -288,7 +288,7 @@ function tests.showMovementGrid()
end
function tests.colorResourcePoints()
local chunks = global.regionMap.processQueue
local chunks = global.map.processQueue
for i=1,#chunks do
local chunk = chunks[i]
local color = "concrete"
@ -307,7 +307,7 @@ end
function tests.exportAiState(onTick)
local printState = function ()
local chunks = global.regionMap.processQueue
local chunks = global.map.processQueue
local s = ""
for i=1,#chunks do
local chunk = chunks[i]
@ -321,12 +321,12 @@ function tests.exportAiState(onTick)
chunk[constants.PATH_RATING],
chunk.x,
chunk.y,
chunkUtils.getNestCount(global.regionMap, chunk),
chunkUtils.getWormCount(global.regionMap, chunk),
chunkUtils.getRallyTick(global.regionMap, chunk),
chunkUtils.getRetreatTick(global.regionMap, chunk),
chunkUtils.getResourceGenerator(global.regionMap, chunk),
chunkUtils.getPlayerBaseGenerator(global.regionMap, chunk)}, ",") .. "\n"
chunkUtils.getNestCount(global.map, chunk),
chunkUtils.getWormCount(global.map, chunk),
chunkUtils.getRallyTick(global.map, chunk),
chunkUtils.getRetreatTick(global.map, chunk),
chunkUtils.getResourceGenerator(global.map, chunk),
chunkUtils.getPlayerBaseGenerator(global.map, chunk)}, ",") .. "\n"
end
game.write_file("rampantState.txt", s, false)
end
@ -357,7 +357,7 @@ end
function tests.stepAdvanceTendrils()
-- for _, base in pairs(global.natives.bases) do
-- tendrilUtils.advanceTendrils(global.regionMap, base, game.surfaces[1], {nil,nil,nil,nil,nil,nil,nil,nil})
-- tendrilUtils.advanceTendrils(global.map, base, game.surfaces[1], {nil,nil,nil,nil,nil,nil,nil,nil})
-- end
end