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:
parent
c709c47011
commit
59b364398d
14
Upgrade.lua
14
Upgrade.lua
@ -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
|
||||
|
@ -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
|
||||
|
158
control.lua
158
control.lua
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 {}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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) +
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
32
tests.lua
32
tests.lua
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user